AsyncSocket::handleInitialReadWrite();
}
+void AsyncSSLSocket::prepareReadBuffer(void** buf, size_t* buflen) noexcept {
+ CHECK(readCallback_);
+#ifdef SSL_MODE_MOVE_BUFFER_OWNERSHIP
+ // turn on the buffer movable in openssl
+ if (!isBufferMovable_ && readCallback_->isBufferMovable()) {
+ SSL_set_mode(ssl_, SSL_get_mode(ssl_) | SSL_MODE_MOVE_BUFFER_OWNERSHIP);
+ *buf = nullptr;
+ *buflen = 0;
+ isBufferMovable_ = true;
+ return;
+ }
+#endif
+ // buf is necessary for SSLSocket without SSL_MODE_MOVE_BUFFER_OWNERSHIP
+ readCallback_->getReadBuffer(buf, buflen);
+}
+
void
AsyncSSLSocket::handleRead() noexcept {
VLOG(5) << "AsyncSSLSocket::handleRead() this=" << this << ", fd=" << fd_
}
ssize_t
-AsyncSSLSocket::performRead(void* buf, size_t buflen) {
+AsyncSSLSocket::performRead(void** buf, size_t* buflen, size_t* offset) {
+ VLOG(4) << "AsyncSSLSocket::performRead() this=" << this
+ << ", buf=" << *buf << ", buflen=" << *buflen;
+
if (sslState_ == STATE_UNENCRYPTED) {
- return AsyncSocket::performRead(buf, buflen);
+ return AsyncSocket::performRead(buf, buflen, offset);
}
errno = 0;
- ssize_t bytes = SSL_read(ssl_, buf, buflen);
+ ssize_t bytes = 0;
+ if (!isBufferMovable_) {
+ bytes = SSL_read(ssl_, *buf, *buflen);
+ }
+#ifdef SSL_MODE_MOVE_BUFFER_OWNERSHIP
+ else {
+ bytes = SSL_read_buf(ssl_, buf, (int *) offset, (int *) buflen);
+ }
+#endif
+
if (server_ && renegotiateAttempted_) {
LOG(ERROR) << "AsyncSSLSocket(fd=" << fd_ << ", state=" << int(state_)
<< ", sslstate=" << sslState_ << ", events=" << eventFlags_
// Inherit event notification methods from AsyncSocket except
// the following.
-
+ void prepareReadBuffer(void** buf, size_t* buflen) noexcept override;
void handleRead() noexcept override;
void handleWrite() noexcept override;
void handleAccept() noexcept;
void handleInitialReadWrite() noexcept override {}
int interpretSSLError(int rc, int error);
- ssize_t performRead(void* buf, size_t buflen) override;
+ ssize_t performRead(void** buf, size_t* buflen, size_t* offset) override;
ssize_t performWrite(const iovec* vec, uint32_t count, WriteFlags flags,
uint32_t* countWritten, uint32_t* partialWritten)
override;
}
}
-ssize_t AsyncSocket::performRead(void* buf, size_t buflen) {
- ssize_t bytes = recv(fd_, buf, buflen, MSG_DONTWAIT);
+ssize_t AsyncSocket::performRead(void** buf, size_t* buflen, size_t* offset) {
+ VLOG(5) << "AsyncSocket::performRead() this=" << this
+ << ", buf=" << *buf << ", buflen=" << *buflen;
+
+ ssize_t bytes = recv(fd_, *buf, *buflen, MSG_DONTWAIT);
if (bytes < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// No more data to read right now.
}
}
+void AsyncSocket::prepareReadBuffer(void** buf, size_t* buflen) noexcept {
+ // no matter what, buffer should be preapared for non-ssl socket
+ CHECK(readCallback_);
+ readCallback_->getReadBuffer(buf, buflen);
+}
+
void AsyncSocket::handleRead() noexcept {
VLOG(5) << "AsyncSocket::handleRead() this=" << this << ", fd=" << fd_
<< ", state=" << state_;
while (readCallback_ && eventBase_ == originalEventBase) {
// Get the buffer to read into.
void* buf = nullptr;
- size_t buflen = 0;
+ size_t buflen = 0, offset = 0;
try {
- readCallback_->getReadBuffer(&buf, &buflen);
+ prepareReadBuffer(&buf, &buflen);
+ VLOG(5) << "prepareReadBuffer() buf=" << buf << ", buflen=" << buflen;
} catch (const AsyncSocketException& ex) {
return failRead(__func__, ex);
} catch (const std::exception& ex) {
"non-exception type");
return failRead(__func__, ex);
}
- if (buf == nullptr || buflen == 0) {
+ if (!isBufferMovable_ && (buf == nullptr || buflen == 0)) {
AsyncSocketException ex(AsyncSocketException::BAD_ARGS,
"ReadCallback::getReadBuffer() returned "
"empty buffer");
}
// Perform the read
- ssize_t bytesRead = performRead(buf, buflen);
+ ssize_t bytesRead = performRead(&buf, &buflen, &offset);
+ VLOG(4) << "this=" << this << ", AsyncSocket::handleRead() got "
+ << bytesRead << " bytes";
if (bytesRead > 0) {
- readCallback_->readDataAvailable(bytesRead);
+ if (!isBufferMovable_) {
+ readCallback_->readDataAvailable(bytesRead);
+ } else {
+ CHECK(kOpenSslModeMoveBufferOwnership);
+ VLOG(5) << "this=" << this << ", AsyncSocket::handleRead() got "
+ << "buf=" << buf << ", " << bytesRead << "/" << buflen
+ << ", offset=" << offset;
+ auto readBuf = folly::IOBuf::takeOwnership(buf, buflen);
+ readBuf->trimStart(offset);
+ readBuf->trimEnd(buflen - offset - bytesRead);
+ readCallback_->readBufferAvailable(std::move(readBuf));
+ }
+
// Fall through and continue around the loop if the read
// completely filled the available buffer.
// Note that readCallback_ may have been uninstalled or changed inside
void ioReady(uint16_t events) noexcept;
virtual void checkForImmediateRead() noexcept;
virtual void handleInitialReadWrite() noexcept;
+ virtual void prepareReadBuffer(void** buf, size_t* buflen) noexcept;
virtual void handleRead() noexcept;
virtual void handleWrite() noexcept;
virtual void handleConnect() noexcept;
* READ_ERROR on error, or READ_BLOCKING if the operation will
* block.
*/
- virtual ssize_t performRead(void* buf, size_t buflen);
+ virtual ssize_t performRead(void** buf, size_t* buflen, size_t* offset);
/**
* Populate an iovec array from an IOBuf and attempt to write it.
ShutdownSocketSet* shutdownSocketSet_;
size_t appBytesReceived_; ///< Num of bytes received from socket
size_t appBytesWritten_; ///< Num of bytes written to socket
+ bool isBufferMovable_{false};
};
#include <folly/io/async/EventBase.h>
#include <folly/io/async/AsyncSocketBase.h>
+#include <openssl/ssl.h>
+
+constexpr bool kOpenSslModeMoveBufferOwnership =
+#ifdef SSL_MODE_MOVE_BUFFER_OWNERSHIP
+ true
+#else
+ false
+#endif
+;
+
namespace folly {
class AsyncSocketException;
*
* @param len The number of bytes placed in the buffer.
*/
+
virtual void readDataAvailable(size_t len) noexcept = 0;
+ /**
+ * When data becomes available, isBufferMovable() will be invoked to figure
+ * out which API will be used, readBufferAvailable() or
+ * readDataAvailable(). If isBufferMovable() returns true, that means
+ * ReadCallback supports the IOBuf ownership transfer and
+ * readBufferAvailable() will be used. Otherwise, not.
+
+ * By default, isBufferMovable() always return false. If
+ * readBufferAvailable() is implemented and to be invoked, You should
+ * overwrite isBufferMovable() and return true in the inherited class.
+ *
+ * This method allows the AsyncSocket/AsyncSSLSocket do buffer allocation by
+ * itself until data becomes available. Compared with the pre/post buffer
+ * allocation in getReadBuffer()/readDataAvailabe(), readBufferAvailable()
+ * has two advantages. First, this can avoid memcpy. E.g., in
+ * AsyncSSLSocket, the decrypted data was copied from the openssl internal
+ * buffer to the readbuf buffer. With the buffer ownership transfer, the
+ * internal buffer can be directly "moved" to ReadCallback. Second, the
+ * memory allocation can be more precise. The reason is
+ * AsyncSocket/AsyncSSLSocket can allocate the memory of precise size
+ * because they have more context about the available data than
+ * ReadCallback. Think about the getReadBuffer() pre-allocate 4072 bytes
+ * buffer, but the available data is always 16KB (max OpenSSL record size).
+ */
+
+ virtual bool isBufferMovable() noexcept {
+ return false;
+ }
+
+ /**
+ * readBufferAvailable() will be invoked when data has been successfully
+ * read.
+ *
+ * Note that only either readBufferAvailable() or readDataAvailable() will
+ * be invoked according to the return value of isBufferMovable(). The timing
+ * and aftereffect of readBufferAvailable() are the same as
+ * readDataAvailable()
+ *
+ * @param readBuf The unique pointer of read buffer.
+ */
+
+ virtual void readBufferAvailable(std::unique_ptr<IOBuf> readBuf)
+ noexcept {};
+
/**
* readEOF() will be invoked when the transport is closed.
*