Skip to content

Commit

Permalink
nghttpx: Rewrite TLS async handshake using memchunk buffers
Browse files Browse the repository at this point in the history
  • Loading branch information
tatsuhiro-t committed Aug 12, 2015
1 parent 72c661f commit e91a576
Show file tree
Hide file tree
Showing 12 changed files with 420 additions and 129 deletions.
152 changes: 152 additions & 0 deletions src/memchunk.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,31 @@ template <typename T> struct Pool {
template <typename Memchunk> struct Memchunks {
Memchunks(Pool<Memchunk> *pool)
: pool(pool), head(nullptr), tail(nullptr), len(0) {}
Memchunks(const Memchunks &) = delete;
Memchunks(Memchunks &&other)
: pool(other.pool), head(other.head), tail(other.head), len(other.len) {
// keep other.pool
other.head = other.tail = nullptr;
other.len = 0;
}
Memchunks &operator=(const Memchunks &) = delete;
Memchunks &operator=(Memchunks &&other) {
if (this == &other) {
return *this;
}

reset();

pool = other.pool;
head = other.head;
tail = other.tail;
len = other.len;

other.head = other.tail = nullptr;
other.len = 0;

return *this;
}
~Memchunks() {
if (!pool) {
return;
Expand Down Expand Up @@ -223,15 +248,142 @@ template <typename Memchunk> struct Memchunks {
return i;
}
size_t rleft() const { return len; }
void reset() {
for (auto m = head; m;) {
auto next = m->next;
pool->recycle(m);
m = next;
}
len = 0;
head = tail = nullptr;
}

Pool<Memchunk> *pool;
Memchunk *head, *tail;
size_t len;
};

// Wrapper around Memchunks to offer "peeking" functionality.
template <typename Memchunk> struct PeekMemchunks {
PeekMemchunks(Pool<Memchunk> *pool)
: memchunks(pool), cur(nullptr), cur_pos(nullptr), cur_last(nullptr),
len(0), peeking(true) {}
PeekMemchunks(const PeekMemchunks &) = delete;
PeekMemchunks(PeekMemchunks &&other)
: memchunks(std::move(other.memchunks)), cur(other.cur),
cur_pos(other.cur_pos), cur_last(other.cur_last), len(other.len),
peeking(other.peeking) {
other.reset();
}
PeekMemchunks &operator=(const PeekMemchunks &) = delete;
PeekMemchunks &operator=(PeekMemchunks &&other) {
if (this == &other) {
return *this;
}

memchunks = std::move(other.memchunks);
cur = other.cur;
cur_pos = other.cur_pos;
cur_last = other.cur_last;
len = other.len;
peeking = other.peeking;

other.reset();

return *this;
}
size_t append(const void *src, size_t count) {
count = memchunks.append(src, count);
len += count;
return count;
}
size_t remove(void *dest, size_t count) {
if (!peeking) {
count = memchunks.remove(dest, count);
len -= count;
return count;
}

if (count == 0 || len == 0) {
return 0;
}

if (!cur) {
cur = memchunks.head;
cur_pos = cur->pos;
}

// cur_last could be updated in append
cur_last = cur->last;

if (cur_pos == cur_last) {
assert(cur->next);
cur = cur->next;
}

auto first = static_cast<uint8_t *>(dest);
auto last = first + count;

for (;;) {
auto n = std::min(last - first, cur_last - cur_pos);

first = std::copy_n(cur_pos, n, first);
cur_pos += n;
len -= n;

if (first == last) {
break;
}
assert(cur_pos == cur_last);
if (!cur->next) {
break;
}
cur = cur->next;
cur_pos = cur->pos;
cur_last = cur->last;
}
return first - static_cast<uint8_t *>(dest);
}
size_t rleft() const { return len; }
size_t rleft_buffered() const { return memchunks.rleft(); }
void disable_peek(bool drain) {
if (!peeking) {
return;
}
if (drain) {
auto n = rleft_buffered() - rleft();
memchunks.drain(n);
assert(len == memchunks.rleft());
} else {
len = memchunks.rleft();
}
cur = nullptr;
cur_pos = cur_last = nullptr;
peeking = false;
}
void reset() {
memchunks.reset();
cur = nullptr;
cur_pos = cur_last = nullptr;
len = 0;
peeking = true;
}
Memchunks<Memchunk> memchunks;
// Pointer to the Memchunk currently we are reading/writing.
Memchunk *cur;
// Region inside cur, we have processed to cur_pos.
uint8_t *cur_pos, *cur_last;
// This is the length we have left unprocessed. len <=
// memchunk.rleft() must hold.
size_t len;
// true if peeking is enabled. Initially it is true.
bool peeking;
};

using Memchunk16K = Memchunk<16_k>;
using MemchunkPool = Pool<Memchunk16K>;
using DefaultMemchunks = Memchunks<Memchunk16K>;
using DefaultPeekMemchunks = PeekMemchunks<Memchunk16K>;

#define DEFAULT_WR_IOVCNT 16

Expand Down
141 changes: 141 additions & 0 deletions src/memchunk_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ void test_pool_recycle(void) {
using Memchunk16 = Memchunk<16>;
using MemchunkPool16 = Pool<Memchunk16>;
using Memchunks16 = Memchunks<Memchunk16>;
using PeekMemchunks16 = PeekMemchunks<Memchunk16>;

void test_memchunks_append(void) {
MemchunkPool16 pool;
Expand Down Expand Up @@ -196,4 +197,144 @@ void test_memchunks_recycle(void) {
CU_ASSERT(nullptr == m->next);
}

void test_memchunks_reset(void) {
MemchunkPool16 pool;
Memchunks16 chunks(&pool);

std::array<uint8_t, 32> b{};

chunks.append(b.data(), b.size());

CU_ASSERT(32 == chunks.rleft());

chunks.reset();

CU_ASSERT(0 == chunks.rleft());
CU_ASSERT(nullptr == chunks.head);
CU_ASSERT(nullptr == chunks.tail);

auto m = pool.freelist;

CU_ASSERT(nullptr != m);
CU_ASSERT(nullptr != m->next);
CU_ASSERT(nullptr == m->next->next);
}

void test_peek_memchunks_append(void) {
MemchunkPool16 pool;
PeekMemchunks16 pchunks(&pool);

std::array<uint8_t, 32> b{{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4',
'5', '6', '7', '8', '9', '0', '1',
}},
d;

pchunks.append(b.data(), b.size());

CU_ASSERT(32 == pchunks.rleft());
CU_ASSERT(32 == pchunks.rleft_buffered());

CU_ASSERT(0 == pchunks.remove(nullptr, 0));

CU_ASSERT(32 == pchunks.rleft());
CU_ASSERT(32 == pchunks.rleft_buffered());

CU_ASSERT(12 == pchunks.remove(d.data(), 12));

CU_ASSERT(std::equal(std::begin(b), std::begin(b) + 12, std::begin(d)));

CU_ASSERT(20 == pchunks.rleft());
CU_ASSERT(32 == pchunks.rleft_buffered());

CU_ASSERT(20 == pchunks.remove(d.data(), d.size()));

CU_ASSERT(std::equal(std::begin(b) + 12, std::end(b), std::begin(d)));

CU_ASSERT(0 == pchunks.rleft());
CU_ASSERT(32 == pchunks.rleft_buffered());
}

void test_peek_memchunks_disable_peek_drain(void) {
MemchunkPool16 pool;
PeekMemchunks16 pchunks(&pool);

std::array<uint8_t, 32> b{{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4',
'5', '6', '7', '8', '9', '0', '1',
}},
d;

pchunks.append(b.data(), b.size());

CU_ASSERT(12 == pchunks.remove(d.data(), 12));

pchunks.disable_peek(true);

CU_ASSERT(!pchunks.peeking);
CU_ASSERT(20 == pchunks.rleft());
CU_ASSERT(20 == pchunks.rleft_buffered());

CU_ASSERT(20 == pchunks.remove(d.data(), d.size()));

CU_ASSERT(std::equal(std::begin(b) + 12, std::end(b), std::begin(d)));

CU_ASSERT(0 == pchunks.rleft());
CU_ASSERT(0 == pchunks.rleft_buffered());
}

void test_peek_memchunks_disable_peek_no_drain(void) {
MemchunkPool16 pool;
PeekMemchunks16 pchunks(&pool);

std::array<uint8_t, 32> b{{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4',
'5', '6', '7', '8', '9', '0', '1',
}},
d;

pchunks.append(b.data(), b.size());

CU_ASSERT(12 == pchunks.remove(d.data(), 12));

pchunks.disable_peek(false);

CU_ASSERT(!pchunks.peeking);
CU_ASSERT(32 == pchunks.rleft());
CU_ASSERT(32 == pchunks.rleft_buffered());

CU_ASSERT(32 == pchunks.remove(d.data(), d.size()));

CU_ASSERT(std::equal(std::begin(b), std::end(b), std::begin(d)));

CU_ASSERT(0 == pchunks.rleft());
CU_ASSERT(0 == pchunks.rleft_buffered());
}

void test_peek_memchunks_reset(void) {
MemchunkPool16 pool;
PeekMemchunks16 pchunks(&pool);

std::array<uint8_t, 32> b{{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4',
'5', '6', '7', '8', '9', '0', '1',
}},
d;

pchunks.append(b.data(), b.size());

CU_ASSERT(12 == pchunks.remove(d.data(), 12));

pchunks.disable_peek(true);
pchunks.reset();

CU_ASSERT(0 == pchunks.rleft());
CU_ASSERT(0 == pchunks.rleft_buffered());

CU_ASSERT(nullptr == pchunks.cur);
CU_ASSERT(nullptr == pchunks.cur_pos);
CU_ASSERT(nullptr == pchunks.cur_last);
CU_ASSERT(pchunks.peeking);
}

} // namespace nghttp2
5 changes: 5 additions & 0 deletions src/memchunk_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ void test_memchunks_append(void);
void test_memchunks_drain(void);
void test_memchunks_riovec(void);
void test_memchunks_recycle(void);
void test_memchunks_reset(void);
void test_peek_memchunks_append(void);
void test_peek_memchunks_disable_peek_drain(void);
void test_peek_memchunks_disable_peek_no_drain(void);
void test_peek_memchunks_reset(void);

} // namespace nghttp2

Expand Down
11 changes: 10 additions & 1 deletion src/shrpx-unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,16 @@ int main(int argc, char *argv[]) {
!CU_add_test(pSuite, "memchunk_drain", nghttp2::test_memchunks_drain) ||
!CU_add_test(pSuite, "memchunk_riovec", nghttp2::test_memchunks_riovec) ||
!CU_add_test(pSuite, "memchunk_recycle",
nghttp2::test_memchunks_recycle)) {
nghttp2::test_memchunks_recycle) ||
!CU_add_test(pSuite, "memchunk_reset", nghttp2::test_memchunks_reset) ||
!CU_add_test(pSuite, "peek_memchunk_append",
nghttp2::test_peek_memchunks_append) ||
!CU_add_test(pSuite, "peek_memchunk_disable_peek_drain",
nghttp2::test_peek_memchunks_disable_peek_drain) ||
!CU_add_test(pSuite, "peek_memchunk_disable_peek_no_drain",
nghttp2::test_peek_memchunks_disable_peek_no_drain) ||
!CU_add_test(pSuite, "peek_memchunk_reset",
nghttp2::test_peek_memchunks_reset)) {
CU_cleanup_registry();
return CU_get_error();
}
Expand Down
3 changes: 2 additions & 1 deletion src/shrpx_client_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,8 @@ int ClientHandler::upstream_http1_connhd_read() {

ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
const char *ipaddr, const char *port)
: conn_(worker->get_loop(), fd, ssl, get_config()->upstream_write_timeout,
: conn_(worker->get_loop(), fd, ssl, worker->get_mcpool(),
get_config()->upstream_write_timeout,
get_config()->upstream_read_timeout, get_config()->write_rate,
get_config()->write_burst, get_config()->read_rate,
get_config()->read_burst, writecb, readcb, timeoutcb, this),
Expand Down
Loading

0 comments on commit e91a576

Please sign in to comment.