+ /**
+ * Virtual method for reading a socket option returning integer
+ * value, which is the most typical case. Convenient for overriding
+ * and mocking.
+ *
+ * @param level same as the "level" parameter in getsockopt().
+ * @param optname same as the "optname" parameter in getsockopt().
+ * @param optval same as "optval" parameter in getsockopt().
+ * @param optlen same as "optlen" parameter in getsockopt().
+ * @return same as the return value of getsockopt().
+ */
+ virtual int
+ getSockOptVirtual(int level, int optname, void* optval, socklen_t* optlen) {
+ return getsockopt(fd_, level, optname, optval, optlen);
+ }
+
+ /**
+ * Virtual method for setting a socket option accepting integer
+ * value, which is the most typical case. Convenient for overriding
+ * and mocking.
+ *
+ * @param level same as the "level" parameter in setsockopt().
+ * @param optname same as the "optname" parameter in setsockopt().
+ * @param optval same as "optval" parameter in setsockopt().
+ * @param optlen same as "optlen" parameter in setsockopt().
+ * @return same as the return value of setsockopt().
+ */
+ virtual int setSockOptVirtual(
+ int level,
+ int optname,
+ void const* optval,
+ socklen_t optlen) {
+ return setsockopt(fd_, level, optname, optval, optlen);
+ }
+
+ /**
+ * Set pre-received data, to be returned to read callback before any data
+ * from the socket.
+ */
+ virtual void setPreReceivedData(std::unique_ptr<IOBuf> data) {
+ if (preReceivedData_) {
+ preReceivedData_->prependChain(std::move(data));
+ } else {
+ preReceivedData_ = std::move(data);
+ }
+ }
+
+ /**
+ * Enables TFO behavior on the AsyncSocket if FOLLY_ALLOW_TFO
+ * is set.
+ */
+ void enableTFO() {
+ // No-op if folly does not allow tfo
+#if FOLLY_ALLOW_TFO
+ tfoEnabled_ = true;
+#endif
+ }
+
+ void disableTransparentTls() {
+ noTransparentTls_ = true;
+ }
+
+ void disableTSocks() {
+ noTSocks_ = true;
+ }
+
+ enum class StateEnum : uint8_t {
+ UNINIT,
+ CONNECTING,
+ ESTABLISHED,
+ CLOSED,
+ ERROR,
+ FAST_OPEN,
+ };
+
+ void setBufferCallback(BufferCallback* cb);
+
+ // Callers should set this prior to connecting the socket for the safest
+ // behavior.
+ void setEvbChangedCallback(std::unique_ptr<EvbChangeCallback> cb) {
+ evbChangeCb_ = std::move(cb);
+ }
+
+ /**
+ * Attempt to cache the current local and peer addresses (if not already
+ * cached) so that they are available from getPeerAddress() and
+ * getLocalAddress() even after the socket is closed.
+ */
+ void cacheAddresses();
+
+ /**
+ * Returns true if there is any zero copy write in progress
+ * Needs to be called from within the socket's EVB thread
+ */
+ bool isZeroCopyWriteInProgress() const noexcept;
+
+ /**
+ * writeReturn is the total number of bytes written, or WRITE_ERROR on error.
+ * If no data has been written, 0 is returned.
+ * exception is a more specific exception that cause a write error.
+ * Not all writes have exceptions associated with them thus writeReturn
+ * should be checked to determine whether the operation resulted in an error.
+ */
+ struct WriteResult {
+ explicit WriteResult(ssize_t ret) : writeReturn(ret) {}
+
+ WriteResult(ssize_t ret, std::unique_ptr<const AsyncSocketException> e)
+ : writeReturn(ret), exception(std::move(e)) {}
+
+ ssize_t writeReturn;
+ std::unique_ptr<const AsyncSocketException> exception;
+ };
+
+ /**
+ * readReturn is the number of bytes read, or READ_EOF on EOF, or
+ * READ_ERROR on error, or READ_BLOCKING if the operation will
+ * block.
+ * exception is a more specific exception that may have caused a read error.
+ * Not all read errors have exceptions associated with them thus readReturn
+ * should be checked to determine whether the operation resulted in an error.
+ */
+ struct ReadResult {
+ explicit ReadResult(ssize_t ret) : readReturn(ret) {}
+
+ ReadResult(ssize_t ret, std::unique_ptr<const AsyncSocketException> e)
+ : readReturn(ret), exception(std::move(e)) {}
+
+ ssize_t readReturn;
+ std::unique_ptr<const AsyncSocketException> exception;
+ };
+
+ /**
+ * A WriteRequest object tracks information about a pending write operation.
+ */
+ class WriteRequest {
+ public:
+ WriteRequest(AsyncSocket* socket, WriteCallback* callback) :
+ socket_(socket), callback_(callback) {}
+
+ virtual void start() {}
+
+ virtual void destroy() = 0;
+
+ virtual WriteResult performWrite() = 0;
+
+ virtual void consume() = 0;
+
+ virtual bool isComplete() = 0;
+
+ WriteRequest* getNext() const {
+ return next_;
+ }
+
+ WriteCallback* getCallback() const {
+ return callback_;
+ }
+
+ uint32_t getTotalBytesWritten() const {
+ return totalBytesWritten_;
+ }
+
+ void append(WriteRequest* next) {
+ assert(next_ == nullptr);
+ next_ = next;
+ }
+
+ void fail(const char* fn, const AsyncSocketException& ex) {
+ socket_->failWrite(fn, ex);
+ }
+
+ void bytesWritten(size_t count) {
+ totalBytesWritten_ += uint32_t(count);
+ socket_->appBytesWritten_ += count;
+ }
+
+ protected:
+ // protected destructor, to ensure callers use destroy()
+ virtual ~WriteRequest() {}
+
+ AsyncSocket* socket_; ///< parent socket
+ WriteRequest* next_{nullptr}; ///< pointer to next WriteRequest
+ WriteCallback* callback_; ///< completion callback
+ uint32_t totalBytesWritten_{0}; ///< total bytes written
+ };
+