using folly::AsyncSSLSocket;
using folly::Optional;
-/** Try to avoid calling SSL_write() for buffers smaller than this: */
-size_t MIN_WRITE_SIZE = 1500;
-
// We have one single dummy SSL context so that we can implement attach
// and detach methods in a thread safe fashion without modifying opnessl.
static SSLContext *dummyCtx = nullptr;
const uint8_t TASYNCSSLSOCKET_F_PERFORM_READ = 90;
const uint8_t TASYNCSSLSOCKET_F_PERFORM_WRITE = 91;
+// If given min write size is less than this, buffer will be allocated on
+// stack, otherwise it is allocated on heap
+const size_t MAX_STACK_BUF_SIZE = 2048;
+
// This converts "illegal" shutdowns into ZERO_RETURN
inline bool zero_return(int error, int rc) {
return (error == SSL_ERROR_ZERO_RETURN || (rc == 0 && errno == 0));
bool cork = isSet(flags, WriteFlags::CORK);
CorkGuard guard(fd_, count > 1, cork, &corked_);
+ // Declare a buffer used to hold small write requests. It could point to a
+ // memory block either on stack or on heap. If it is on heap, we release it
+ // manually when scope exits
+ char* combinedBuf{nullptr};
+ SCOPE_EXIT {
+ // Note, always keep this check consistent with what we do below
+ if (combinedBuf != nullptr && minWriteSize_ > MAX_STACK_BUF_SIZE) {
+ delete[] combinedBuf;
+ }
+ };
+
*countWritten = 0;
*partialWritten = 0;
ssize_t totalWritten = 0;
ssize_t bytes;
errno = 0;
uint32_t buffersStolen = 0;
- if ((len < MIN_WRITE_SIZE) && ((i + 1) < count)) {
+ if ((len < minWriteSize_) && ((i + 1) < count)) {
// Combine this buffer with part or all of the next buffers in
// order to avoid really small-grained calls to SSL_write().
// Each call to SSL_write() produces a separate record in
// header and the first part of the response body in two
// separate SSL records (even if those two records are in
// the same TCP packet).
- char combinedBuf[MIN_WRITE_SIZE];
+
+ if (combinedBuf == nullptr) {
+ if (minWriteSize_ > MAX_STACK_BUF_SIZE) {
+ // Allocate the buffer on heap
+ combinedBuf = new char[minWriteSize_];
+ } else {
+ // Allocate the buffer on stack
+ combinedBuf = (char*)alloca(minWriteSize_);
+ }
+ }
+ assert(combinedBuf != nullptr);
+
memcpy(combinedBuf, buf, len);
do {
// INVARIANT: i + buffersStolen == complete chunks serialized
uint32_t nextIndex = i + buffersStolen + 1;
bytesStolenFromNextBuffer = std::min(vec[nextIndex].iov_len,
- MIN_WRITE_SIZE - len);
+ minWriteSize_ - len);
memcpy(combinedBuf + len, vec[nextIndex].iov_base,
bytesStolenFromNextBuffer);
len += bytesStolenFromNextBuffer;
bytesStolenFromNextBuffer = 0;
buffersStolen++;
}
- } while ((i + buffersStolen + 1) < count && (len < MIN_WRITE_SIZE));
+ } while ((i + buffersStolen + 1) < count && (len < minWriteSize_));
bytes = eorAwareSSLWrite(
ssl_, combinedBuf, len,
(isSet(flags, WriteFlags::EOR) && i + buffersStolen + 1 == count));
return clientHelloInfo_.get();
}
+ void setMinWriteSize(size_t minWriteSize) {
+ minWriteSize_ = minWriteSize;
+ }
+
+ size_t getMinWriteSize() {
+ return minWriteSize_;
+ }
+
protected:
/**
// Only one app EOR byte can be tracked.
size_t appEorByteNo_{0};
+ // Try to avoid calling SSL_write() for buffers smaller than this.
+ // It doesn't take effect when it is 0.
+ size_t minWriteSize_{1500};
+
// When openssl is about to sendmsg() across the minEorRawBytesNo_,
// it will pass MSG_EOR to sendmsg().
size_t minEorRawByteNo_{0};
EXPECT_FALSE(server.handshakeSuccess_);
EXPECT_TRUE(server.handshakeError_);
}
+
+TEST(AsyncSSLSocketTest, MinWriteSizeTest) {
+ EventBase eb;
+
+ // Set up SSL context.
+ auto sslContext = std::make_shared<SSLContext>();
+ sslContext->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+
+ // create SSL socket
+ AsyncSSLSocket::UniquePtr socket(new AsyncSSLSocket(sslContext, &eb));
+
+ EXPECT_EQ(1500, socket->getMinWriteSize());
+
+ socket->setMinWriteSize(0);
+ EXPECT_EQ(0, socket->getMinWriteSize());
+ socket->setMinWriteSize(50000);
+ EXPECT_EQ(50000, socket->getMinWriteSize());
+}
}
///////////////////////////////////////////////////////////////////////////