apr_status_t h2_conn_io_writeb(h2_conn_io *io, apr_bucket *b)
{
APR_BRIGADE_INSERT_TAIL(io->output, b);
- io->unflushed = 1;
return APR_SUCCESS;
}
-apr_status_t h2_conn_io_consider_flush(h2_conn_io *io)
-{
- apr_status_t status = APR_SUCCESS;
-
- /* The HTTP/1.1 network output buffer/flush behaviour does not
- * give optimal performance in the HTTP/2 case, as the pattern of
- * buckets (data/eor/eos) is different.
- * As long as we have not found out the "best" way to deal with
- * this, force a flush at least every WRITE_BUFFER_SIZE amount
- * of data.
- */
- if (io->unflushed) {
- apr_off_t len = 0;
- if (!APR_BRIGADE_EMPTY(io->output)) {
- apr_brigade_length(io->output, 0, &len);
- }
- len += io->buflen;
- if (len >= WRITE_BUFFER_SIZE) {
- return h2_conn_io_pass(io);
- }
- }
- return status;
-}
-
static apr_status_t h2_conn_io_flush_int(h2_conn_io *io, int force, int eoc)
{
- if (io->unflushed || force) {
+ if (io->buflen > 0 || !APR_BRIGADE_EMPTY(io->output)) {
pass_out_ctx ctx;
if (io->buflen > 0) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->connection,
"h2_conn_io: flush, flushing %ld bytes", (long)io->buflen);
bucketeer_buffer(io);
- io->buflen = 0;
}
if (force) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->connection,
"h2_conn_io: flush");
/* Send it out */
- io->unflushed = 0;
-
+ io->buflen = 0;
ctx.c = io->connection;
ctx.io = eoc? NULL : io;
+
return pass_out(io->output, &ctx);
/* no more access after this, as we might have flushed an EOC bucket
* that de-allocated us all. */
return APR_SUCCESS;
}
-apr_status_t h2_conn_io_write_eoc(h2_conn_io *io, apr_bucket *b)
+apr_status_t h2_conn_io_pass(h2_conn_io *io, int flush)
{
- APR_BRIGADE_INSERT_TAIL(io->output, b);
- return h2_conn_io_flush_int(io, 1, 1);
+ return h2_conn_io_flush_int(io, flush, 0);
}
-apr_status_t h2_conn_io_flush(h2_conn_io *io)
+apr_status_t h2_conn_io_consider_pass(h2_conn_io *io)
{
- return h2_conn_io_flush_int(io, 1, 0);
+ apr_off_t len = 0;
+
+ if (!APR_BRIGADE_EMPTY(io->output)) {
+ apr_brigade_length(io->output, 0, &len);
+ }
+ len += io->buflen;
+ if (len >= WRITE_BUFFER_SIZE) {
+ return h2_conn_io_pass(io, 0);
+ }
+ return APR_SUCCESS;
}
-apr_status_t h2_conn_io_pass(h2_conn_io *io)
+apr_status_t h2_conn_io_write_eoc(h2_conn_io *io, h2_session *session)
{
- return h2_conn_io_flush_int(io, 0, 0);
+ apr_bucket *b = h2_bucket_eoc_create(io->connection->bucket_alloc, session);
+ APR_BRIGADE_INSERT_TAIL(io->output, b);
+ b = apr_bucket_flush_create(io->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(io->output, b);
+ return h2_conn_io_flush_int(io, 0, 1);
}
apr_status_t h2_conn_io_write(h2_conn_io *io,
ctx.c = io->connection;
ctx.io = io;
- io->unflushed = 1;
if (io->bufsize > 0) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->connection,
"h2_conn_io: buffering %ld bytes", (long)length);
if (!APR_BRIGADE_EMPTY(io->output)) {
- status = h2_conn_io_pass(io);
- io->unflushed = 1;
+ status = h2_conn_io_flush_int(io, 0, 0);
}
while (length > 0 && (status == APR_SUCCESS)) {
char *buffer;
apr_size_t buflen;
apr_size_t bufsize;
- int unflushed;
} h2_conn_io;
apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c,
int h2_conn_io_is_buffered(h2_conn_io *io);
+/**
+ * Append data to the buffered output.
+ * @param buf the data to append
+ * @param length the length of the data to append
+ */
apr_status_t h2_conn_io_write(h2_conn_io *io,
const char *buf,
size_t length);
-
+
+/**
+ * Append a bucket to the buffered output.
+ * @param io the connection io
+ * @param b the bucket to append
+ */
apr_status_t h2_conn_io_writeb(h2_conn_io *io, apr_bucket *b);
-apr_status_t h2_conn_io_consider_flush(h2_conn_io *io);
+/**
+ * Append an End-Of-Connection bucket to the output that, once destroyed,
+ * will tear down the complete http2 session.
+ */
+apr_status_t h2_conn_io_write_eoc(h2_conn_io *io, struct h2_session *session);
-apr_status_t h2_conn_io_pass(h2_conn_io *io);
-apr_status_t h2_conn_io_flush(h2_conn_io *io);
-apr_status_t h2_conn_io_write_eoc(h2_conn_io *io, apr_bucket *b);
+/**
+ * Pass any buffered data on to the connection output filters.
+ * @param io the connection io
+ * @param flush if a flush bucket should be appended to any output
+ */
+apr_status_t h2_conn_io_pass(h2_conn_io *io, int flush);
+
+/**
+ * Check the amount of buffered output and pass it on if enough has accumulated.
+ * @param io the connection io
+ * @param flush if a flush bucket should be appended to any output
+ */
+apr_status_t h2_conn_io_consider_pass(h2_conn_io *io);
#endif /* defined(__mod_h2__h2_conn_io__) */
if (status == APR_SUCCESS) {
stream->data_frames_sent++;
- h2_conn_io_consider_flush(&session->io);
+ h2_conn_io_consider_pass(&session->io);
return 0;
}
else {
(long)session->frames_sent);
}
++session->frames_sent;
+ switch (frame->hd.type) {
+ case NGHTTP2_HEADERS:
+ case NGHTTP2_DATA:
+ /* no explicit flushing necessary */
+ break;
+ default:
+ session->flush = 1;
+ break;
+ }
return 0;
}
h2_mplx_get_max_stream_started(session->mplx),
reason, (uint8_t*)err, err? strlen(err):0);
status = nghttp2_session_send(session->ngh2);
- h2_conn_io_flush(&session->io);
+ h2_conn_io_pass(&session->io, 1);
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03069)
"session(%ld): sent GOAWAY, err=%d, msg=%s",
session->id, reason, err? err : "");
nghttp2_strerror(*rv));
}
}
+
+ h2_conn_io_pass(&session->io, 1);
return status;
}
break;
default:
h2_session_shutdown(session, arg, msg, 1);
- h2_conn_io_flush(&session->io);
break;
}
}
* time here for the next frame to arrive, before handing
* it to keep_alive processing of the mpm.
*/
- h2_filter_cin_timeout_set(session->cin,
- no_streams? apr_time_from_msec(200)
- : session->s->timeout);
- status = h2_session_read(session, 1, 10);
+ if (no_streams) {
+ status = h2_session_read(session, 0, 20);
+ }
+ else {
+ h2_filter_cin_timeout_set(session->cin, session->s->timeout);
+ status = h2_session_read(session, 1, 20);
+ }
if (status == APR_SUCCESS) {
have_read = 1;
case H2_SESSION_ST_REMOTE_SHUTDOWN:
if (nghttp2_session_want_read(session->ngh2)) {
h2_filter_cin_timeout_set(session->cin, session->s->timeout);
- status = h2_session_read(session, 0, 10);
+ status = h2_session_read(session, 0, 20);
if (status == APR_SUCCESS) {
have_read = 1;
dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
}
}
- if (nghttp2_session_want_write(session->ngh2)) {
+ while (nghttp2_session_want_write(session->ngh2)) {
ap_update_child_status(session->c->sbh, SERVER_BUSY_WRITE, NULL);
status = h2_session_send(session);
if (status == APR_SUCCESS) {
break;
}
- if (have_written) {
- h2_conn_io_flush(&session->io);
- }
- else if (!nghttp2_session_want_read(session->ngh2)
+ h2_conn_io_pass(&session->io, 1);
+ if (!nghttp2_session_want_read(session->ngh2)
&& !nghttp2_session_want_write(session->ngh2)) {
dispatch_event(session, H2_SESSION_EV_NGH2_DONE, 0, NULL);
}
}
out:
- if (have_written) {
- h2_conn_io_flush(&session->io);
- }
+ h2_conn_io_pass(&session->io, session->flush);
+ session->flush = 0;
ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
"h2_session(%ld): [%s] process returns",
if (session->state == H2_SESSION_ST_DONE) {
if (!session->eoc_written) {
session->eoc_written = 1;
- h2_conn_io_write_eoc(&session->io,
- h2_bucket_eoc_create(session->c->bucket_alloc, session));
+ h2_conn_io_write_eoc(&session->io, session);
}
}
h2_session_state state; /* state session is in */
unsigned int reprioritize : 1; /* scheduled streams priority changed */
unsigned int eoc_written : 1; /* h2 eoc bucket written */
+ unsigned int flush : 1; /* flushing output necessary */
apr_interval_time_t wait_us; /* timout during BUSY_WAIT state, micro secs */
int unsent_submits; /* number of submitted, but not yet written responses. */