|
|
|
@ -280,6 +280,43 @@ static void start_tls_p3( conn_t *conn, int ok )
|
|
|
|
|
|
|
|
|
|
#endif /* HAVE_LIBSSL */ |
|
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBZ |
|
|
|
|
|
|
|
|
|
static void z_fake_cb( void * ); |
|
|
|
|
|
|
|
|
|
void |
|
|
|
|
socket_start_deflate( conn_t *conn ) |
|
|
|
|
{ |
|
|
|
|
int result; |
|
|
|
|
|
|
|
|
|
conn->in_z = nfcalloc( sizeof(*conn->in_z) ); |
|
|
|
|
result = inflateInit2( |
|
|
|
|
conn->in_z, |
|
|
|
|
-15 /* Use raw deflate */ |
|
|
|
|
); |
|
|
|
|
if (result != Z_OK) { |
|
|
|
|
error( "Fatal: Cannot initialize decompression: %s\n", conn->in_z->msg ); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
conn->out_z = nfcalloc( sizeof(*conn->out_z) ); |
|
|
|
|
result = deflateInit2( |
|
|
|
|
conn->out_z, |
|
|
|
|
Z_DEFAULT_COMPRESSION, /* Compression level */ |
|
|
|
|
Z_DEFLATED, /* Only valid value */ |
|
|
|
|
-15, /* Use raw deflate */ |
|
|
|
|
8, /* Default memory usage */ |
|
|
|
|
Z_DEFAULT_STRATEGY /* Don't try to do anything fancy */ |
|
|
|
|
); |
|
|
|
|
if (result != Z_OK) { |
|
|
|
|
error( "Fatal: Cannot initialize compression: %s\n", conn->out_z->msg ); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
init_wakeup( &conn->z_fake, z_fake_cb, conn ); |
|
|
|
|
} |
|
|
|
|
#endif /* HAVE_LIBZ */ |
|
|
|
|
|
|
|
|
|
static void socket_fd_cb( int, void * ); |
|
|
|
|
static void socket_fake_cb( void * ); |
|
|
|
|
|
|
|
|
@ -500,6 +537,17 @@ socket_close( conn_t *sock )
|
|
|
|
|
sock->ssl = 0; |
|
|
|
|
wipe_wakeup( &sock->ssl_fake ); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
#ifdef HAVE_LIBZ |
|
|
|
|
if (sock->in_z) { |
|
|
|
|
inflateEnd( sock->in_z ); |
|
|
|
|
free( sock->in_z ); |
|
|
|
|
sock->in_z = 0; |
|
|
|
|
deflateEnd( sock->out_z ); |
|
|
|
|
free( sock->out_z ); |
|
|
|
|
sock->out_z = 0; |
|
|
|
|
wipe_wakeup( &sock->z_fake ); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
while (sock->write_buf) |
|
|
|
|
dispose_chunk( sock ); |
|
|
|
@ -507,23 +555,30 @@ socket_close( conn_t *sock )
|
|
|
|
|
sock->append_buf = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void |
|
|
|
|
socket_fill( conn_t *sock ) |
|
|
|
|
static int |
|
|
|
|
prepare_read( conn_t *sock, char **buf, int *len ) |
|
|
|
|
{ |
|
|
|
|
char *buf; |
|
|
|
|
int n = sock->offset + sock->bytes; |
|
|
|
|
int len = sizeof(sock->buf) - n; |
|
|
|
|
if (!len) { |
|
|
|
|
if (!(*len = sizeof(sock->buf) - n)) { |
|
|
|
|
error( "Socket error: receive buffer full. Probably protocol error.\n" ); |
|
|
|
|
socket_fail( sock ); |
|
|
|
|
return; |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
*buf = sock->buf + n; |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int |
|
|
|
|
do_read( conn_t *sock, char *buf, int len ) |
|
|
|
|
{ |
|
|
|
|
int n; |
|
|
|
|
|
|
|
|
|
assert( sock->fd >= 0 ); |
|
|
|
|
buf = sock->buf + n; |
|
|
|
|
#ifdef HAVE_LIBSSL |
|
|
|
|
if (sock->ssl) { |
|
|
|
|
if ((n = ssl_return( "read from", sock, SSL_read( sock->ssl, buf, len ) )) <= 0) |
|
|
|
|
return; |
|
|
|
|
return n; |
|
|
|
|
|
|
|
|
|
if (n == len && SSL_pending( sock->ssl )) |
|
|
|
|
conf_wakeup( &sock->ssl_fake, 0 ); |
|
|
|
|
} else |
|
|
|
@ -532,15 +587,71 @@ socket_fill( conn_t *sock )
|
|
|
|
|
if ((n = read( sock->fd, buf, len )) < 0) { |
|
|
|
|
sys_error( "Socket error: read from %s", sock->name ); |
|
|
|
|
socket_fail( sock ); |
|
|
|
|
return; |
|
|
|
|
} else if (!n) { |
|
|
|
|
error( "Socket error: read from %s: unexpected EOF\n", sock->name ); |
|
|
|
|
socket_fail( sock ); |
|
|
|
|
return; |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
sock->bytes += n; |
|
|
|
|
sock->read_callback( sock->callback_aux ); |
|
|
|
|
|
|
|
|
|
return n; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBZ |
|
|
|
|
static void |
|
|
|
|
socket_fill_z( conn_t *sock ) |
|
|
|
|
{ |
|
|
|
|
char *buf; |
|
|
|
|
int len; |
|
|
|
|
|
|
|
|
|
if (prepare_read( sock, &buf, &len ) < 0) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
sock->in_z->avail_out = len; |
|
|
|
|
sock->in_z->next_out = (unsigned char *)buf; |
|
|
|
|
|
|
|
|
|
if (inflate( sock->in_z, Z_SYNC_FLUSH ) != Z_OK) { |
|
|
|
|
error( "Error decompressing data from %s: %s\n", sock->name, sock->in_z->msg ); |
|
|
|
|
socket_fail( sock ); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!sock->in_z->avail_out) |
|
|
|
|
conf_wakeup( &sock->z_fake, 0 ); |
|
|
|
|
|
|
|
|
|
if ((len = (char *)sock->in_z->next_out - buf)) { |
|
|
|
|
sock->bytes += len; |
|
|
|
|
sock->read_callback( sock->callback_aux ); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
static void |
|
|
|
|
socket_fill( conn_t *sock ) |
|
|
|
|
{ |
|
|
|
|
#ifdef HAVE_LIBZ |
|
|
|
|
if (sock->in_z) { |
|
|
|
|
/* The timer will preempt reads until the buffer is empty. */ |
|
|
|
|
assert( !sock->in_z->avail_in ); |
|
|
|
|
sock->in_z->next_in = (uchar *)sock->z_buf; |
|
|
|
|
if ((sock->in_z->avail_in = do_read( sock, sock->z_buf, sizeof(sock->z_buf) )) <= 0) |
|
|
|
|
return; |
|
|
|
|
socket_fill_z( sock ); |
|
|
|
|
} else |
|
|
|
|
#endif |
|
|
|
|
{ |
|
|
|
|
char *buf; |
|
|
|
|
int len; |
|
|
|
|
|
|
|
|
|
if (prepare_read( sock, &buf, &len ) < 0) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
if ((len = do_read( sock, buf, len )) <= 0) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
sock->bytes += len; |
|
|
|
|
sock->read_callback( sock->callback_aux ); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int |
|
|
|
@ -655,6 +766,49 @@ do_append( conn_t *conn, buff_chunk_t *bc )
|
|
|
|
|
* sufficiently small to keep SSL latency low with a slow uplink. */ |
|
|
|
|
#define WRITE_CHUNK_SIZE 1024 |
|
|
|
|
|
|
|
|
|
static void |
|
|
|
|
do_flush( conn_t *conn ) |
|
|
|
|
{ |
|
|
|
|
buff_chunk_t *bc = conn->append_buf; |
|
|
|
|
#ifdef HAVE_LIBZ |
|
|
|
|
if (conn->out_z) { |
|
|
|
|
int buf_avail = conn->append_avail; |
|
|
|
|
do { |
|
|
|
|
if (!bc) { |
|
|
|
|
buf_avail = WRITE_CHUNK_SIZE; |
|
|
|
|
bc = nfmalloc( offsetof(buff_chunk_t, data) + buf_avail ); |
|
|
|
|
bc->len = 0; |
|
|
|
|
} |
|
|
|
|
conn->out_z->next_in = Z_NULL; |
|
|
|
|
conn->out_z->avail_in = 0; |
|
|
|
|
conn->out_z->next_out = (uchar *)bc->data + bc->len; |
|
|
|
|
conn->out_z->avail_out = buf_avail; |
|
|
|
|
if (deflate( conn->out_z, Z_PARTIAL_FLUSH ) != Z_OK) { |
|
|
|
|
error( "Fatal: Compression error: %s\n", conn->out_z->msg ); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
bc->len = (char *)conn->out_z->next_out - bc->data; |
|
|
|
|
if (bc->len) { |
|
|
|
|
do_append( conn, bc ); |
|
|
|
|
bc = 0; |
|
|
|
|
buf_avail = 0; |
|
|
|
|
} else { |
|
|
|
|
buf_avail = conn->out_z->avail_out; |
|
|
|
|
} |
|
|
|
|
} while (!conn->out_z->avail_out); |
|
|
|
|
conn->append_buf = bc; |
|
|
|
|
conn->append_avail = buf_avail; |
|
|
|
|
} else |
|
|
|
|
#endif |
|
|
|
|
if (bc) { |
|
|
|
|
do_append( conn, bc ); |
|
|
|
|
conn->append_buf = 0; |
|
|
|
|
#ifdef HAVE_LIBZ |
|
|
|
|
conn->append_avail = 0; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int |
|
|
|
|
socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt ) |
|
|
|
|
{ |
|
|
|
@ -663,29 +817,54 @@ socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < iovcnt; i++) |
|
|
|
|
total += iov[i].len; |
|
|
|
|
bc = conn->append_buf; |
|
|
|
|
if (bc && total >= WRITE_CHUNK_SIZE) { |
|
|
|
|
if (total >= WRITE_CHUNK_SIZE) { |
|
|
|
|
/* If the new data is too big, queue the pending buffer to avoid latency. */ |
|
|
|
|
do_append( conn, bc ); |
|
|
|
|
bc = 0; |
|
|
|
|
do_flush( conn ); |
|
|
|
|
} |
|
|
|
|
bc = conn->append_buf; |
|
|
|
|
#ifdef HAVE_LIBZ |
|
|
|
|
buf_avail = conn->append_avail; |
|
|
|
|
#endif |
|
|
|
|
while (total) { |
|
|
|
|
if (!bc) { |
|
|
|
|
/* We don't do anything special when compressing, as there is no way to
|
|
|
|
|
* predict a reasonable output buffer size anyway - deflatePending() does |
|
|
|
|
* not account for consumed but not yet compressed input, and adding up |
|
|
|
|
* the deflateBound()s would be a tad *too* pessimistic. */ |
|
|
|
|
buf_avail = total > WRITE_CHUNK_SIZE ? total : WRITE_CHUNK_SIZE; |
|
|
|
|
bc = nfmalloc( offsetof(buff_chunk_t, data) + buf_avail ); |
|
|
|
|
bc->len = 0; |
|
|
|
|
#ifndef HAVE_LIBZ |
|
|
|
|
} else { |
|
|
|
|
/* A pending buffer will always be of standard size - over-sized
|
|
|
|
|
* buffers are immediately filled and queued. */ |
|
|
|
|
buf_avail = WRITE_CHUNK_SIZE - bc->len; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
while (total) { |
|
|
|
|
len = iov->len - offset; |
|
|
|
|
if (len > buf_avail) |
|
|
|
|
len = buf_avail; |
|
|
|
|
memcpy( bc->data + bc->len, iov->buf + offset, len ); |
|
|
|
|
bc->len += len; |
|
|
|
|
buf_avail -= len; |
|
|
|
|
#ifdef HAVE_LIBZ |
|
|
|
|
if (conn->out_z) { |
|
|
|
|
conn->out_z->next_in = (uchar *)iov->buf + offset; |
|
|
|
|
conn->out_z->avail_in = len; |
|
|
|
|
conn->out_z->next_out = (uchar *)bc->data + bc->len; |
|
|
|
|
conn->out_z->avail_out = buf_avail; |
|
|
|
|
if (deflate( conn->out_z, Z_NO_FLUSH ) != Z_OK) { |
|
|
|
|
error( "Fatal: Compression error: %s\n", conn->out_z->msg ); |
|
|
|
|
abort(); |
|
|
|
|
} |
|
|
|
|
bc->len = (char *)conn->out_z->next_out - bc->data; |
|
|
|
|
buf_avail = conn->out_z->avail_out; |
|
|
|
|
len -= conn->out_z->avail_in; |
|
|
|
|
} else |
|
|
|
|
#endif |
|
|
|
|
{ |
|
|
|
|
if (len > buf_avail) |
|
|
|
|
len = buf_avail; |
|
|
|
|
memcpy( bc->data + bc->len, iov->buf + offset, len ); |
|
|
|
|
bc->len += len; |
|
|
|
|
buf_avail -= len; |
|
|
|
|
} |
|
|
|
|
offset += len; |
|
|
|
|
total -= len; |
|
|
|
|
if (offset == iov->len) { |
|
|
|
@ -702,8 +881,16 @@ socket_write( conn_t *conn, conn_iovec_t *iov, int iovcnt )
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
conn->append_buf = bc; |
|
|
|
|
#ifdef HAVE_LIBZ |
|
|
|
|
conn->append_avail = buf_avail; |
|
|
|
|
#endif |
|
|
|
|
/* Queue the pending write once the main loop goes idle. */ |
|
|
|
|
conf_wakeup( &conn->fd_fake, bc ? 0 : -1 ); |
|
|
|
|
conf_wakeup( &conn->fd_fake, |
|
|
|
|
#ifdef HAVE_LIBZ |
|
|
|
|
/* Always give zlib a chance to flush its internal buffer. */ |
|
|
|
|
conn->out_z || |
|
|
|
|
#endif |
|
|
|
|
bc ? 0 : -1 ); |
|
|
|
|
/* If no writes were queued before, ensure that flushing commences. */ |
|
|
|
|
if (!exwb) |
|
|
|
|
return do_queued_write( conn ); |
|
|
|
@ -763,13 +950,22 @@ socket_fake_cb( void *aux )
|
|
|
|
|
conn_t *conn = (conn_t *)aux; |
|
|
|
|
|
|
|
|
|
buff_chunk_t *exwb = conn->write_buf; |
|
|
|
|
do_append( conn, conn->append_buf ); |
|
|
|
|
conn->append_buf = 0; |
|
|
|
|
do_flush( conn ); |
|
|
|
|
/* If no writes were queued before, ensure that flushing commences. */ |
|
|
|
|
if (!exwb) |
|
|
|
|
do_queued_write( conn ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBZ |
|
|
|
|
static void |
|
|
|
|
z_fake_cb( void *aux ) |
|
|
|
|
{ |
|
|
|
|
conn_t *conn = (conn_t *)aux; |
|
|
|
|
|
|
|
|
|
socket_fill_z( conn ); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBSSL |
|
|
|
|
static void |
|
|
|
|
ssl_fake_cb( void *aux ) |
|
|
|
|