nghttpx: Fix TLS write error

SSL_write requires the same arguments (buf pointer and its length) on
SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.  get_write_limit() may
return smaller length than previously passed to SSL_write, which
violates OpenSSL assumption.  To avoid this, we keep last legnth
passed to SSL_write to tls_last_writelen_ if SSL_write indicated I/O
blocking.
This commit is contained in:
Tatsuhiro Tsujikawa 2015-01-08 23:03:56 +09:00
parent 019f1e9fc7
commit 419c03daa2
2 changed files with 31 additions and 10 deletions

View File

@ -236,6 +236,10 @@ int ClientHandler::read_tls() {
rb_.reset(); rb_.reset();
struct iovec iov[2]; struct iovec iov[2];
auto iovcnt = rb_.wiovec(iov); auto iovcnt = rb_.wiovec(iov);
// SSL_read requires the same arguments (buf pointer and its
// length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
// rlimit_.avail() does not change if we don't read anything, so
// we don't do anything special here.
iovcnt = limit_iovec(iov, iovcnt, rlimit_.avail()); iovcnt = limit_iovec(iov, iovcnt, rlimit_.avail());
if (iovcnt == 0) { if (iovcnt == 0) {
return 0; return 0;
@ -281,15 +285,29 @@ int ClientHandler::write_tls() {
size_t len; size_t len;
std::tie(p, len) = wb_.get(); std::tie(p, len) = wb_.get();
len = std::min(len, wlimit_.avail()); // SSL_write requires the same arguments (buf pointer and its
if (len == 0) { // length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
return 0; // get_write_limit() may return smaller length than previously
// passed to SSL_write, which violates OpenSSL assumption. To
// avoid this, we keep last legnth passed to SSL_write to
// tls_last_writelen_ if SSL_write indicated I/O blocking.
if (tls_last_writelen_ == 0) {
len = std::min(len, wlimit_.avail());
if (len == 0) {
return 0;
}
auto limit = get_write_limit();
if (limit != -1) {
len = std::min(len, static_cast<size_t>(limit));
}
} else {
assert(len >= tls_last_writelen_);
len = tls_last_writelen_;
tls_last_writelen_ = 0;
} }
auto limit = get_write_limit();
if (limit != -1) {
len = std::min(len, static_cast<size_t>(limit));
}
auto rv = SSL_write(ssl_, p, len); auto rv = SSL_write(ssl_, p, len);
if (rv == 0) { if (rv == 0) {
@ -300,10 +318,12 @@ int ClientHandler::write_tls() {
auto err = SSL_get_error(ssl_, rv); auto err = SSL_get_error(ssl_, rv);
switch (err) { switch (err) {
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
tls_last_writelen_ = len;
wlimit_.stopw(); wlimit_.stopw();
ev_timer_stop(loop_, &wt_); ev_timer_stop(loop_, &wt_);
return 0; return 0;
case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_WRITE:
tls_last_writelen_ = len;
wlimit_.startw(); wlimit_.startw();
ev_timer_again(loop_, &wt_); ev_timer_again(loop_, &wt_);
return 0; return 0;
@ -456,9 +476,9 @@ ClientHandler::ClientHandler(struct ev_loop *loop, int fd, SSL *ssl,
loop_(loop), dconn_pool_(dconn_pool), http2session_(nullptr), loop_(loop), dconn_pool_(dconn_pool), http2session_(nullptr),
http1_connect_blocker_(nullptr), ssl_(ssl), worker_stat_(worker_stat), http1_connect_blocker_(nullptr), ssl_(ssl), worker_stat_(worker_stat),
last_write_time_(0), warmup_writelen_(0), last_write_time_(0), warmup_writelen_(0),
left_connhd_len_(NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN), fd_(fd), left_connhd_len_(NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN),
should_close_after_write_(false), tls_handshake_(false), tls_last_writelen_(0), fd_(fd), should_close_after_write_(false),
tls_renegotiation_(false) { tls_handshake_(false), tls_renegotiation_(false) {
++worker_stat->num_connections; ++worker_stat->num_connections;

View File

@ -173,6 +173,7 @@ private:
size_t warmup_writelen_; size_t warmup_writelen_;
// The number of bytes of HTTP/2 client connection header to read // The number of bytes of HTTP/2 client connection header to read
size_t left_connhd_len_; size_t left_connhd_len_;
size_t tls_last_writelen_;
int fd_; int fd_;
bool should_close_after_write_; bool should_close_after_write_;
bool tls_handshake_; bool tls_handshake_;