crypto: lower RSS usage for TLSCallbacks
Don't allocate any BIO buffers initially, do this on a first read from the TCP connection. Allocate different amount of data for initial read and for consequent reads: small buffer for hello+certificate, big buffer for better throughput. see #8416
This commit is contained in:
parent
9d95774722
commit
2122a77f51
@ -272,6 +272,8 @@ size_t NodeBIO::Read(char* out, size_t size) {
|
||||
|
||||
|
||||
void NodeBIO::FreeEmpty() {
|
||||
if (write_head_ == NULL)
|
||||
return;
|
||||
Buffer* child = write_head_->next_;
|
||||
if (child == write_head_ || child == read_head_)
|
||||
return;
|
||||
@ -281,13 +283,6 @@ void NodeBIO::FreeEmpty() {
|
||||
|
||||
Buffer* prev = child;
|
||||
while (cur != read_head_) {
|
||||
// Skip embedded buffer, and continue deallocating again starting from it
|
||||
if (cur == &head_) {
|
||||
prev->next_ = cur;
|
||||
prev = cur;
|
||||
cur = head_.next_;
|
||||
continue;
|
||||
}
|
||||
assert(cur != write_head_);
|
||||
assert(cur->write_pos_ == cur->read_pos_);
|
||||
|
||||
@ -295,7 +290,6 @@ void NodeBIO::FreeEmpty() {
|
||||
delete cur;
|
||||
cur = next;
|
||||
}
|
||||
assert(prev == child || prev == &head_);
|
||||
prev->next_ = cur;
|
||||
}
|
||||
|
||||
@ -330,7 +324,7 @@ size_t NodeBIO::IndexOf(char delim, size_t limit) {
|
||||
}
|
||||
|
||||
// Move to next buffer
|
||||
if (current->read_pos_ + avail == kBufferLength) {
|
||||
if (current->read_pos_ + avail == current->len_) {
|
||||
current = current->next_;
|
||||
}
|
||||
}
|
||||
@ -343,10 +337,14 @@ size_t NodeBIO::IndexOf(char delim, size_t limit) {
|
||||
void NodeBIO::Write(const char* data, size_t size) {
|
||||
size_t offset = 0;
|
||||
size_t left = size;
|
||||
|
||||
// Allocate initial buffer if the ring is empty
|
||||
TryAllocateForWrite(left);
|
||||
|
||||
while (left > 0) {
|
||||
size_t to_write = left;
|
||||
assert(write_head_->write_pos_ <= kBufferLength);
|
||||
size_t avail = kBufferLength - write_head_->write_pos_;
|
||||
assert(write_head_->write_pos_ <= write_head_->len_);
|
||||
size_t avail = write_head_->len_ - write_head_->write_pos_;
|
||||
|
||||
if (to_write > avail)
|
||||
to_write = avail;
|
||||
@ -361,12 +359,12 @@ void NodeBIO::Write(const char* data, size_t size) {
|
||||
offset += to_write;
|
||||
length_ += to_write;
|
||||
write_head_->write_pos_ += to_write;
|
||||
assert(write_head_->write_pos_ <= kBufferLength);
|
||||
assert(write_head_->write_pos_ <= write_head_->len_);
|
||||
|
||||
// Go to next buffer if there still are some bytes to write
|
||||
if (left != 0) {
|
||||
assert(write_head_->write_pos_ == kBufferLength);
|
||||
TryAllocateForWrite();
|
||||
assert(write_head_->write_pos_ == write_head_->len_);
|
||||
TryAllocateForWrite(left);
|
||||
write_head_ = write_head_->next_;
|
||||
|
||||
// Additionally, since we're moved to the next buffer, read head
|
||||
@ -379,7 +377,9 @@ void NodeBIO::Write(const char* data, size_t size) {
|
||||
|
||||
|
||||
char* NodeBIO::PeekWritable(size_t* size) {
|
||||
size_t available = kBufferLength - write_head_->write_pos_;
|
||||
TryAllocateForWrite(*size);
|
||||
|
||||
size_t available = write_head_->len_ - write_head_->write_pos_;
|
||||
if (*size != 0 && available > *size)
|
||||
available = *size;
|
||||
else
|
||||
@ -392,12 +392,12 @@ char* NodeBIO::PeekWritable(size_t* size) {
|
||||
void NodeBIO::Commit(size_t size) {
|
||||
write_head_->write_pos_ += size;
|
||||
length_ += size;
|
||||
assert(write_head_->write_pos_ <= kBufferLength);
|
||||
assert(write_head_->write_pos_ <= write_head_->len_);
|
||||
|
||||
// Allocate new buffer if write head is full,
|
||||
// and there're no other place to go
|
||||
TryAllocateForWrite();
|
||||
if (write_head_->write_pos_ == kBufferLength) {
|
||||
TryAllocateForWrite(0);
|
||||
if (write_head_->write_pos_ == write_head_->len_) {
|
||||
write_head_ = write_head_->next_;
|
||||
|
||||
// Additionally, since we're moved to the next buffer, read head
|
||||
@ -407,19 +407,35 @@ void NodeBIO::Commit(size_t size) {
|
||||
}
|
||||
|
||||
|
||||
void NodeBIO::TryAllocateForWrite() {
|
||||
void NodeBIO::TryAllocateForWrite(size_t hint) {
|
||||
Buffer* w = write_head_;
|
||||
Buffer* r = read_head_;
|
||||
// If write head is full, next buffer is either read head or not empty.
|
||||
if (write_head_->write_pos_ == kBufferLength &&
|
||||
(write_head_->next_ == read_head_ ||
|
||||
write_head_->next_->write_pos_ != 0)) {
|
||||
Buffer* next = new Buffer();
|
||||
next->next_ = write_head_->next_;
|
||||
write_head_->next_ = next;
|
||||
if (w == NULL ||
|
||||
(w->write_pos_ == w->len_ &&
|
||||
(w->next_ == r || w->next_->write_pos_ != 0))) {
|
||||
size_t len = w == NULL ? initial_ :
|
||||
kThroughputBufferLength;
|
||||
if (len < hint)
|
||||
len = hint;
|
||||
Buffer* next = new Buffer(len);
|
||||
|
||||
if (w == NULL) {
|
||||
next->next_ = next;
|
||||
write_head_ = next;
|
||||
read_head_ = next;
|
||||
} else {
|
||||
next->next_ = w->next_;
|
||||
w->next_ = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NodeBIO::Reset() {
|
||||
if (read_head_ == NULL)
|
||||
return;
|
||||
|
||||
while (read_head_->read_pos_ != read_head_->write_pos_) {
|
||||
assert(read_head_->write_pos_ > read_head_->read_pos_);
|
||||
|
||||
@ -435,12 +451,15 @@ void NodeBIO::Reset() {
|
||||
|
||||
|
||||
NodeBIO::~NodeBIO() {
|
||||
Buffer* current = head_.next_;
|
||||
while (current != &head_) {
|
||||
if (read_head_ == NULL)
|
||||
return;
|
||||
|
||||
Buffer* current = read_head_;
|
||||
do {
|
||||
Buffer* next = current->next_;
|
||||
delete current;
|
||||
current = next;
|
||||
}
|
||||
} while (current != read_head_);
|
||||
|
||||
read_head_ = NULL;
|
||||
write_head_ = NULL;
|
||||
|
@ -29,9 +29,10 @@ namespace node {
|
||||
|
||||
class NodeBIO {
|
||||
public:
|
||||
NodeBIO() : length_(0), read_head_(&head_), write_head_(&head_) {
|
||||
// Loop head
|
||||
head_.next_ = &head_;
|
||||
NodeBIO() : initial_(kInitialBufferLength),
|
||||
length_(0),
|
||||
read_head_(NULL),
|
||||
write_head_(NULL) {
|
||||
}
|
||||
|
||||
~NodeBIO();
|
||||
@ -42,7 +43,7 @@ class NodeBIO {
|
||||
void TryMoveReadHead();
|
||||
|
||||
// Allocate new buffer for write if needed
|
||||
void TryAllocateForWrite();
|
||||
void TryAllocateForWrite(size_t hint);
|
||||
|
||||
// Read `len` bytes maximum into `out`, return actual number of read bytes
|
||||
size_t Read(char* out, size_t size);
|
||||
@ -76,11 +77,16 @@ class NodeBIO {
|
||||
// Commit reserved data
|
||||
void Commit(size_t size);
|
||||
|
||||
|
||||
// Return size of buffer in bytes
|
||||
size_t inline Length() {
|
||||
inline size_t Length() const {
|
||||
return length_;
|
||||
}
|
||||
|
||||
inline void set_initial(size_t initial) {
|
||||
initial_ = initial;
|
||||
}
|
||||
|
||||
static inline NodeBIO* FromBIO(BIO* bio) {
|
||||
assert(bio->ptr != NULL);
|
||||
return static_cast<NodeBIO*>(bio->ptr);
|
||||
@ -95,24 +101,34 @@ class NodeBIO {
|
||||
static int Gets(BIO* bio, char* out, int size);
|
||||
static long Ctrl(BIO* bio, int cmd, long num, void* ptr);
|
||||
|
||||
// NOTE: Size is maximum TLS frame length, this is required if we want
|
||||
// to fit whole ClientHello into one Buffer of NodeBIO.
|
||||
static const size_t kBufferLength = 16 * 1024 + 5;
|
||||
// Enough to handle the most of the client hellos
|
||||
static const size_t kInitialBufferLength = 1024;
|
||||
static const size_t kThroughputBufferLength = 16384;
|
||||
|
||||
static const BIO_METHOD method;
|
||||
|
||||
class Buffer {
|
||||
public:
|
||||
Buffer() : read_pos_(0), write_pos_(0), next_(NULL) {
|
||||
explicit Buffer(size_t len) : read_pos_(0),
|
||||
write_pos_(0),
|
||||
len_(len),
|
||||
next_(NULL) {
|
||||
data_ = new char[len];
|
||||
}
|
||||
|
||||
~Buffer() {
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
size_t read_pos_;
|
||||
size_t write_pos_;
|
||||
size_t len_;
|
||||
Buffer* next_;
|
||||
char data_[kBufferLength];
|
||||
char* data_;
|
||||
};
|
||||
|
||||
size_t initial_;
|
||||
size_t length_;
|
||||
Buffer head_;
|
||||
Buffer* read_head_;
|
||||
Buffer* write_head_;
|
||||
};
|
||||
|
@ -192,6 +192,8 @@ void TLSCallbacks::InitSSL() {
|
||||
if (is_server()) {
|
||||
SSL_set_accept_state(ssl_);
|
||||
} else if (is_client()) {
|
||||
// Enough space for server response (hello, cert)
|
||||
NodeBIO::FromBIO(enc_in_)->set_initial(kInitialClientBufferLength);
|
||||
SSL_set_connect_state(ssl_);
|
||||
} else {
|
||||
// Unexpected
|
||||
@ -254,6 +256,7 @@ void TLSCallbacks::Receive(const FunctionCallbackInfo<Value>& args) {
|
||||
wrap->DoAlloc(reinterpret_cast<uv_handle_t*>(stream), len, &buf);
|
||||
size_t copy = buf.len > len ? len : buf.len;
|
||||
memcpy(buf.base, data, copy);
|
||||
buf.len = copy;
|
||||
wrap->DoRead(stream, buf.len, &buf, UV_UNKNOWN_HANDLE);
|
||||
|
||||
data += copy;
|
||||
@ -615,8 +618,9 @@ void TLSCallbacks::AfterWrite(WriteWrap* w) {
|
||||
void TLSCallbacks::DoAlloc(uv_handle_t* handle,
|
||||
size_t suggested_size,
|
||||
uv_buf_t* buf) {
|
||||
buf->base = NodeBIO::FromBIO(enc_in_)->PeekWritable(&suggested_size);
|
||||
buf->len = suggested_size;
|
||||
size_t size = 0;
|
||||
buf->base = NodeBIO::FromBIO(enc_in_)->PeekWritable(&size);
|
||||
buf->len = size;
|
||||
}
|
||||
|
||||
|
||||
@ -720,6 +724,7 @@ void TLSCallbacks::EnableHelloParser(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
TLSCallbacks* wrap = Unwrap<TLSCallbacks>(args.Holder());
|
||||
|
||||
NodeBIO::FromBIO(wrap->enc_in_)->set_initial(kMaxHelloLength);
|
||||
wrap->hello_parser_.Start(SSLWrap<TLSCallbacks>::OnClientHello,
|
||||
OnClientHelloParseEnd,
|
||||
wrap);
|
||||
|
@ -74,6 +74,12 @@ class TLSCallbacks : public crypto::SSLWrap<TLSCallbacks>,
|
||||
protected:
|
||||
static const int kClearOutChunkSize = 1024;
|
||||
|
||||
// Maximum number of bytes for hello parser
|
||||
static const int kMaxHelloLength = 16384;
|
||||
|
||||
// Usual ServerHello + Certificate size
|
||||
static const int kInitialClientBufferLength = 4096;
|
||||
|
||||
// Maximum number of buffers passed to uv_write()
|
||||
static const int kSimultaneousBufferCount = 10;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user