io->input = apr_brigade_create(c->pool, c->bucket_alloc);
io->output = apr_brigade_create(c->pool, c->bucket_alloc);
io->buflen = 0;
- /* That is where we start with,
- * see https://issues.apache.org/jira/browse/TS-2503 */
- io->tls_warmup_size = h2_config_geti64(cfg, H2_CONF_TLS_WARMUP_SIZE);
- io->tls_cooldown_usecs = (h2_config_geti(cfg, H2_CONF_TLS_COOLDOWN_SECS)
- * APR_USEC_PER_SEC);
- io->write_size = WRITE_SIZE_INITIAL;
- io->last_write = 0;
- io->buffer_output = h2_h2_is_tls(c);
-
- if (APLOGctrace1(c)) {
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, io->connection,
- "h2_conn_io(%ld): init, buffering=%d, warmup_size=%ld, cd_secs=%f",
- io->connection->id, io->buffer_output, (long)io->tls_warmup_size,
- ((float)io->tls_cooldown_usecs/APR_USEC_PER_SEC));
- }
- /* Currently we buffer only for TLS output. The reason this gives
- * improved performance is that buckets send to the mod_ssl network
- * filter will be encrypted in chunks. There is a special filter
- * that tries to aggregate data, but that does not work well when
- * bucket sizes alternate between tiny frame headers and large data
- * chunks.
- */
+ io->is_tls = h2_h2_is_tls(c);
+ io->buffer_output = io->is_tls;
+
if (io->buffer_output) {
io->bufsize = WRITE_BUFFER_SIZE;
io->buffer = apr_pcalloc(c->pool, io->bufsize);
io->bufsize = 0;
}
+ if (io->is_tls) {
+ /* That is where we start with,
+ * see https://issues.apache.org/jira/browse/TS-2503 */
+ io->warmup_size = h2_config_geti64(cfg, H2_CONF_TLS_WARMUP_SIZE);
+ io->cooldown_usecs = (h2_config_geti(cfg, H2_CONF_TLS_COOLDOWN_SECS)
+ * APR_USEC_PER_SEC);
+ io->write_size = WRITE_SIZE_INITIAL;
+ }
+ else {
+ io->warmup_size = 0;
+ io->cooldown_usecs = 0;
+ io->write_size = io->bufsize;
+ }
+
+ if (APLOGctrace1(c)) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, io->connection,
+ "h2_conn_io(%ld): init, buffering=%d, warmup_size=%ld, cd_secs=%f",
+ io->connection->id, io->buffer_output, (long)io->warmup_size,
+ ((float)io->cooldown_usecs/APR_USEC_PER_SEC));
+ }
+
return APR_SUCCESS;
}
io->output = NULL;
}
+int h2_conn_io_is_buffered(h2_conn_io *io)
+{
+ return io->bufsize > 0;
+}
+
static apr_status_t h2_conn_io_bucket_read(h2_conn_io *io,
apr_read_type_e block,
h2_conn_io_on_read_cb on_read_cb,
}
ap_update_child_status(io->connection->sbh, SERVER_BUSY_WRITE, NULL);
- status = apr_brigade_length(bb, 1, &bblen);
+ status = apr_brigade_length(bb, 0, &bblen);
if (status == APR_SUCCESS) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, io->connection,
- "h2_conn_io(%ld): flush, brigade %ld bytes",
+ "h2_conn_io(%ld): pass_out brigade %ld bytes",
io->connection->id, (long)bblen);
status = ap_pass_brigade(io->connection->output_filters, bb);
if (status == APR_SUCCESS) {
int bcount, i;
if (io->write_size > WRITE_SIZE_INITIAL
- && (io->tls_cooldown_usecs > 0)
- && (apr_time_now() - io->last_write) >= io->tls_cooldown_usecs) {
+ && (io->cooldown_usecs > 0)
+ && (apr_time_now() - io->last_write) >= io->cooldown_usecs) {
/* long time not written, reset write size */
io->write_size = WRITE_SIZE_INITIAL;
io->bytes_written = 0;
(long)io->connection->id, (long)io->write_size);
}
else if (io->write_size < WRITE_SIZE_MAX
- && io->bytes_written >= io->tls_warmup_size) {
+ && io->bytes_written >= io->warmup_size) {
/* connection is hot, use max size */
io->write_size = WRITE_SIZE_MAX;
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, io->connection,
apr_status_t status = APR_SUCCESS;
io->unflushed = 1;
- if (io->buffer_output) {
+ if (io->bufsize > 0) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, io->connection,
"h2_conn_io: buffering %ld bytes", (long)length);
while (length > 0 && (status == APR_SUCCESS)) {
}
}
+ else if (1) {
+ apr_bucket *b;
+
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, io->connection,
+ "h2_conn_io: passing %ld transient bytes to output filters",
+ (long)length);
+ b = apr_bucket_transient_create(buf,length, io->output->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(io->output, b);
+ status = pass_out(io->output, io);
+ }
else {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, io->connection,
+ "h2_conn_io: writing %ld bytes to brigade", (long)length);
status = apr_brigade_write(io->output, pass_out, io, buf, length);
- if (status != APR_SUCCESS) {
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, io->connection,
- "h2_conn_io: write error");
- }
}
return status;
case NGHTTP2_GOAWAY:
session->flush = 1;
break;
+ case NGHTTP2_DATA:
+
default:
break;
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
+ "h2_stream(%ld-%d): send_data_cb for %ld bytes",
+ session->id, (int)stream_id, (long)length);
+
status = send_data(session, (const char *)framehd, 9);
if (status == APR_SUCCESS) {
if (padlen) {
/* The session wants to send more DATA for the given stream.
*/
+
+typedef struct {
+ char *buf;
+ size_t offset;
+ h2_session *session;
+ h2_stream *stream;
+} cpy_ctx;
+
+static apr_status_t copy_buffer(void *ctx, const char *data, apr_size_t len)
+{
+ cpy_ctx *c = ctx;
+
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c->session->c,
+ "h2_stream(%ld-%d): copy %ld bytes for DATA #%ld",
+ c->session->id, c->stream->id,
+ (long)c->stream->data_frames_sent, (long)len);
+ memcpy(c->buf + c->offset, data, len);
+ c->offset += len;
+ return APR_SUCCESS;
+}
+
static ssize_t stream_data_cb(nghttp2_session *ng2s,
int32_t stream_id,
uint8_t *buf,
AP_DEBUG_ASSERT(!h2_stream_is_suspended(stream));
- status = h2_stream_prep_read(stream, &nread, &eos);
- if (nread) {
- *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
+ if (h2_conn_io_is_buffered(&session->io)) {
+ status = h2_stream_prep_read(stream, &nread, &eos);
+ if (nread) {
+ *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
+ }
+ }
+ else {
+ cpy_ctx ctx;
+ ctx.buf = (char *)buf;
+ ctx.offset = 0;
+ ctx.session = session;
+ ctx.stream = stream;
+
+ status = h2_stream_readx(stream, copy_buffer, &ctx, &nread, &eos);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, session->c,
+ "h2_stream(%ld-%d): read %ld bytes (DATA #%ld)",
+ session->id, (int)stream_id, (long)nread,
+ (long)stream->data_frames_sent);
+ stream->data_frames_sent++;
}
switch (status) {
stream->bbout = apr_brigade_create(stream->pool,
stream->m->c->bucket_alloc);
}
+ /* TODO: this does not move complete file buckets.*/
status = h2_util_move(stream->bbout, bb, 16 * 1024, NULL,
"h2_stream_set_response");
}
}
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->m->c,
"h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d",
- stream->m->id, stream->id,
- src, (long)*plen, *peos);
+ stream->m->id, stream->id, src, (long)*plen, *peos);
return status;
}
h2_io_data_cb *cb, void *ctx,
apr_size_t *plen, int *peos)
{
+ apr_status_t status = APR_SUCCESS;
+ const char *src;
+
if (stream->rst_error) {
return APR_ECONNRESET;
}
if (stream->bbout && !APR_BRIGADE_EMPTY(stream->bbout)) {
- return h2_util_bb_readx(stream->bbout, cb, ctx, plen, peos);
+ src = "stream";
+ status = h2_util_bb_readx(stream->bbout, cb, ctx, plen, peos);
+ if (status == APR_SUCCESS && !*peos && !*plen) {
+ apr_brigade_cleanup(stream->bbout);
+ return h2_stream_readx(stream, cb, ctx, plen, peos);
+ }
}
- return h2_mplx_out_readx(stream->m, stream->id,
- cb, ctx, plen, peos);
+ else {
+ src = "mplx";
+ status = h2_mplx_out_readx(stream->m, stream->id, cb, ctx, plen, peos);
+ }
+ if (status == APR_SUCCESS && !*peos && !*plen) {
+ status = APR_EAGAIN;
+ }
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->m->c,
+ "h2_stream(%ld-%d): readx %s, len=%ld eos=%d",
+ stream->m->id, stream->id, src, (long)*plen, *peos);
+ return status;
}