You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2016/03/02 12:21:29 UTC
svn commit: r1733259 [3/4] - in /httpd/httpd/branches/2.4.x: ./
modules/http2/
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_response.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_response.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_response.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_response.h Wed Mar 2 11:21:28 2016
@@ -16,18 +16,7 @@
#ifndef __mod_h2__h2_response__
#define __mod_h2__h2_response__
-struct h2_request;
-struct h2_push;
-
-typedef struct h2_response {
- int stream_id;
- int rst_error;
- int http_status;
- apr_off_t content_length;
- apr_table_t *headers;
- apr_table_t *trailers;
- const char *sos_filter;
-} h2_response;
+#include "h2.h"
/**
* Create the response from the status and parsed header lines.
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_session.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_session.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_session.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_session.c Wed Mar 2 11:21:28 2016
@@ -14,10 +14,13 @@
*/
#include <assert.h>
+#include <stddef.h>
#include <apr_thread_cond.h>
#include <apr_base64.h>
#include <apr_strings.h>
+#include <ap_mpm.h>
+
#include <httpd.h>
#include <http_core.h>
#include <http_config.h>
@@ -36,7 +39,6 @@
#include "h2_request.h"
#include "h2_response.h"
#include "h2_stream.h"
-#include "h2_stream_set.h"
#include "h2_from_h1.h"
#include "h2_task.h"
#include "h2_session.h"
@@ -45,8 +47,6 @@
#include "h2_workers.h"
-static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen);
-
static int h2_session_status_from_apr_status(apr_status_t rv)
{
if (rv == APR_SUCCESS) {
@@ -89,11 +89,12 @@ h2_stream *h2_session_open_stream(h2_ses
}
else {
apr_pool_create(&stream_pool, session->pool);
+ apr_pool_tag(stream_pool, "h2_stream");
}
stream = h2_stream_open(stream_id, stream_pool, session);
- h2_stream_set_add(session->streams, stream);
+ h2_ihash_add(session->streams, stream);
if (H2_STREAM_CLIENT_INITIATED(stream_id)
&& stream_id > session->max_stream_received) {
++session->requests_received;
@@ -103,8 +104,6 @@ h2_stream *h2_session_open_stream(h2_ses
return stream;
}
-#ifdef H2_NG2_STREAM_API
-
/**
* Determine the importance of streams when scheduling tasks.
* - if both stream depend on the same one, compare weights
@@ -158,20 +157,6 @@ static int stream_pri_cmp(int sid1, int
return spri_cmp(sid1, s1, sid2, s2, session);
}
-#else /* ifdef H2_NG2_STREAM_API */
-
-/* In absence of nghttp2_stream API, which gives information about
- * priorities since nghttp2 1.3.x, we just sort the streams by
- * their identifier, aka. order of arrival.
- */
-static int stream_pri_cmp(int sid1, int sid2, void *ctx)
-{
- (void)ctx;
- return sid1 - sid2;
-}
-
-#endif /* (ifdef else) H2_NG2_STREAM_API */
-
static apr_status_t stream_schedule(h2_session *session,
h2_stream *stream, int eos)
{
@@ -214,7 +199,7 @@ static int on_invalid_frame_recv_cb(nght
if (APLOGcdebug(session->c)) {
char buffer[256];
- frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
+ h2_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03063)
"h2_session(%ld): recv unknown FRAME[%s], frames=%ld/%ld (r/s)",
session->id, buffer, (long)session->frames_received,
@@ -375,7 +360,7 @@ static int on_frame_recv_cb(nghttp2_sess
if (APLOGcdebug(session->c)) {
char buffer[256];
- frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
+ h2_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03066)
"h2_session(%ld): recv FRAME[%s], frames=%ld/%ld (r/s)",
session->id, buffer, (long)session->frames_received,
@@ -464,8 +449,8 @@ static int on_frame_recv_cb(nghttp2_sess
if (APLOGctrace2(session->c)) {
char buffer[256];
- frame_print(frame, buffer,
- sizeof(buffer)/sizeof(buffer[0]));
+ h2_util_frame_print(frame, buffer,
+ sizeof(buffer)/sizeof(buffer[0]));
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
"h2_session: on_frame_rcv %s", buffer);
}
@@ -584,7 +569,7 @@ static int on_send_data_cb(nghttp2_sessi
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 {
@@ -605,13 +590,22 @@ static int on_frame_send_cb(nghttp2_sess
if (APLOGcdebug(session->c)) {
char buffer[256];
- frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
+ h2_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03068)
"h2_session(%ld): sent FRAME[%s], frames=%ld/%ld (r/s)",
session->id, buffer, (long)session->frames_received,
(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;
}
@@ -672,23 +666,20 @@ static void h2_session_destroy(h2_sessio
if (APLOGctrace1(session->c)) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
"h2_session(%ld): destroy, %d streams open",
- session->id, (int)h2_stream_set_size(session->streams));
+ session->id, (int)h2_ihash_count(session->streams));
}
if (session->mplx) {
h2_mplx_set_consumed_cb(session->mplx, NULL, NULL);
h2_mplx_release_and_join(session->mplx, session->iowait);
session->mplx = NULL;
}
- if (session->streams) {
- h2_stream_set_destroy(session->streams);
- session->streams = NULL;
- }
if (session->pool) {
apr_pool_destroy(session->pool);
}
}
-static apr_status_t h2_session_shutdown(h2_session *session, int reason, const char *msg)
+static apr_status_t h2_session_shutdown(h2_session *session, int reason,
+ const char *msg, int force_close)
{
apr_status_t status = APR_SUCCESS;
const char *err = msg;
@@ -701,11 +692,16 @@ static apr_status_t h2_session_shutdown(
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 : "");
dispatch_event(session, H2_SESSION_EV_LOCAL_GOAWAY, reason, err);
+
+ if (force_close) {
+ h2_mplx_abort(session->mplx);
+ }
+
return status;
}
@@ -796,6 +792,7 @@ static h2_session *h2_session_create_int
if (status != APR_SUCCESS) {
return NULL;
}
+ apr_pool_tag(pool, "h2_session");
session = apr_pcalloc(pool, sizeof(h2_session));
if (session) {
@@ -815,24 +812,16 @@ static h2_session *h2_session_create_int
session->max_stream_count = h2_config_geti(session->config, H2_CONF_MAX_STREAMS);
session->max_stream_mem = h2_config_geti(session->config, H2_CONF_STREAM_MAX_MEM);
- session->timeout_secs = h2_config_geti(session->config, H2_CONF_TIMEOUT_SECS);
- if (session->timeout_secs <= 0) {
- session->timeout_secs = apr_time_sec(session->s->timeout);
- }
- session->keepalive_secs = h2_config_geti(session->config, H2_CONF_KEEPALIVE_SECS);
- if (session->keepalive_secs <= 0) {
- session->keepalive_secs = apr_time_sec(session->s->keep_alive_timeout);
- }
-
+
status = apr_thread_cond_create(&session->iowait, session->pool);
if (status != APR_SUCCESS) {
return NULL;
}
- session->streams = h2_stream_set_create(session->pool, session->max_stream_count);
-
+ session->streams = h2_ihash_create(session->pool,offsetof(h2_stream, id));
session->workers = workers;
- session->mplx = h2_mplx_create(c, session->pool, session->config, workers);
+ session->mplx = h2_mplx_create(c, session->pool, session->config,
+ session->s->timeout, workers);
h2_mplx_set_consumed_cb(session->mplx, update_window, session);
@@ -896,12 +885,9 @@ static h2_session *h2_session_create_int
if (APLOGcdebug(c)) {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03200)
- "session(%ld) created, timeout=%d, keepalive_timeout=%d, "
- "max_streams=%d, stream_mem=%d, push_diary(type=%d,N=%d)",
- session->id, session->timeout_secs, session->keepalive_secs,
- (int)session->max_stream_count, (int)session->max_stream_mem,
- session->push_diary->dtype,
- (int)session->push_diary->N);
+ "session(%ld) created, max_streams=%d, stream_mem=%d, push_diary(type=%d,N=%d)",
+ session->id, (int)session->max_stream_count, (int)session->max_stream_mem,
+ session->push_diary->dtype, (int)session->push_diary->N);
}
}
return session;
@@ -1031,6 +1017,8 @@ static apr_status_t h2_session_start(h2_
nghttp2_strerror(*rv));
}
}
+
+ h2_conn_io_pass(&session->io, 1);
return status;
}
@@ -1039,8 +1027,9 @@ typedef struct {
int resume_count;
} resume_ctx;
-static int resume_on_data(void *ctx, h2_stream *stream)
+static int resume_on_data(void *ctx, void *val)
{
+ h2_stream *stream = val;
resume_ctx *rctx = (resume_ctx*)ctx;
h2_session *session = rctx->session;
AP_DEBUG_ASSERT(session);
@@ -1066,7 +1055,7 @@ static int resume_on_data(void *ctx, h2_
static int h2_session_resume_streams_with_data(h2_session *session)
{
AP_DEBUG_ASSERT(session);
- if (!h2_stream_set_is_empty(session->streams)
+ if (!h2_ihash_is_empty(session->streams)
&& session->mplx && !session->mplx->aborted) {
resume_ctx ctx;
@@ -1075,7 +1064,7 @@ static int h2_session_resume_streams_wit
/* Resume all streams where we have data in the out queue and
* which had been suspended before. */
- h2_stream_set_iter(session->streams, resume_on_data, &ctx);
+ h2_ihash_iter(session->streams, resume_on_data, &ctx);
return ctx.resume_count;
}
return 0;
@@ -1084,7 +1073,7 @@ static int h2_session_resume_streams_wit
h2_stream *h2_session_get_stream(h2_session *session, int stream_id)
{
if (!session->last_stream || stream_id != session->last_stream->id) {
- session->last_stream = h2_stream_set_get(session->streams, stream_id);
+ session->last_stream = h2_ihash_get(session->streams, stream_id);
}
return session->last_stream;
}
@@ -1445,16 +1434,16 @@ apr_status_t h2_session_stream_destroy(h
apr_pool_t *pool = h2_stream_detach_pool(stream);
/* this may be called while the session has already freed
- * some internal structures. */
+ * some internal structures or even when the mplx is locked. */
if (session->mplx) {
h2_mplx_stream_done(session->mplx, stream->id, stream->rst_error);
- if (session->last_stream == stream) {
- session->last_stream = NULL;
- }
}
+ if (session->last_stream == stream) {
+ session->last_stream = NULL;
+ }
if (session->streams) {
- h2_stream_set_remove(session->streams, stream->id);
+ h2_ihash_remove(session->streams, stream->id);
}
h2_stream_destroy(stream);
@@ -1468,83 +1457,6 @@ apr_status_t h2_session_stream_destroy(h
return APR_SUCCESS;
}
-static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
-{
- char scratch[128];
- size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
-
- switch (frame->hd.type) {
- case NGHTTP2_DATA: {
- return apr_snprintf(buffer, maxlen,
- "DATA[length=%d, flags=%d, stream=%d, padlen=%d]",
- (int)frame->hd.length, frame->hd.flags,
- frame->hd.stream_id, (int)frame->data.padlen);
- }
- case NGHTTP2_HEADERS: {
- return apr_snprintf(buffer, maxlen,
- "HEADERS[length=%d, hend=%d, stream=%d, eos=%d]",
- (int)frame->hd.length,
- !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
- frame->hd.stream_id,
- !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
- }
- case NGHTTP2_PRIORITY: {
- return apr_snprintf(buffer, maxlen,
- "PRIORITY[length=%d, flags=%d, stream=%d]",
- (int)frame->hd.length,
- frame->hd.flags, frame->hd.stream_id);
- }
- case NGHTTP2_RST_STREAM: {
- return apr_snprintf(buffer, maxlen,
- "RST_STREAM[length=%d, flags=%d, stream=%d]",
- (int)frame->hd.length,
- frame->hd.flags, frame->hd.stream_id);
- }
- case NGHTTP2_SETTINGS: {
- if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
- return apr_snprintf(buffer, maxlen,
- "SETTINGS[ack=1, stream=%d]",
- frame->hd.stream_id);
- }
- return apr_snprintf(buffer, maxlen,
- "SETTINGS[length=%d, stream=%d]",
- (int)frame->hd.length, frame->hd.stream_id);
- }
- case NGHTTP2_PUSH_PROMISE: {
- return apr_snprintf(buffer, maxlen,
- "PUSH_PROMISE[length=%d, hend=%d, stream=%d]",
- (int)frame->hd.length,
- !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
- frame->hd.stream_id);
- }
- case NGHTTP2_PING: {
- return apr_snprintf(buffer, maxlen,
- "PING[length=%d, ack=%d, stream=%d]",
- (int)frame->hd.length,
- frame->hd.flags&NGHTTP2_FLAG_ACK,
- frame->hd.stream_id);
- }
- case NGHTTP2_GOAWAY: {
- size_t len = (frame->goaway.opaque_data_len < s_len)?
- frame->goaway.opaque_data_len : s_len-1;
- memcpy(scratch, frame->goaway.opaque_data, len);
- scratch[len+1] = '\0';
- return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s']",
- frame->goaway.error_code, scratch);
- }
- case NGHTTP2_WINDOW_UPDATE: {
- return apr_snprintf(buffer, maxlen,
- "WINDOW_UPDATE[length=%d, stream=%d]",
- (int)frame->hd.length, frame->hd.stream_id);
- }
- default:
- return apr_snprintf(buffer, maxlen,
- "type=%d[length=%d, flags=%d, stream=%d]",
- frame->hd.type, (int)frame->hd.length,
- frame->hd.flags, frame->hd.stream_id);
- }
-}
-
int h2_session_push_enabled(h2_session *session)
{
/* iff we can and they can */
@@ -1555,7 +1467,21 @@ int h2_session_push_enabled(h2_session *
static apr_status_t h2_session_send(h2_session *session)
{
- int rv = nghttp2_session_send(session->ngh2);
+ apr_interval_time_t saved_timeout;
+ int rv;
+ apr_socket_t *socket;
+
+ socket = ap_get_conn_socket(session->c);
+ if (socket) {
+ apr_socket_timeout_get(socket, &saved_timeout);
+ apr_socket_timeout_set(socket, session->s->timeout);
+ }
+
+ rv = nghttp2_session_send(session->ngh2);
+
+ if (socket) {
+ apr_socket_timeout_set(socket, saved_timeout);
+ }
if (rv != 0) {
if (nghttp2_is_fatal(rv)) {
dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, rv, nghttp2_strerror(rv));
@@ -1594,13 +1520,13 @@ static apr_status_t h2_session_receive(v
return APR_SUCCESS;
}
-static apr_status_t h2_session_read(h2_session *session, int block, int loops)
+static apr_status_t h2_session_read(h2_session *session, int block)
{
apr_status_t status, rstatus = APR_EAGAIN;
conn_rec *c = session->c;
- int i;
+ apr_off_t read_start = session->io.bytes_read;
- for (i = 0; i < loops; ++i) {
+ while (1) {
/* H2_IN filter handles all incoming data against the session.
* We just pull at the filter chain to make it happen */
status = ap_get_brigade(c->input_filters,
@@ -1625,7 +1551,7 @@ static apr_status_t h2_session_read(h2_s
case APR_TIMEUP:
return status;
default:
- if (!i) {
+ if (session->io.bytes_read == read_start) {
/* first attempt failed */
if (APR_STATUS_IS_ETIMEDOUT(status)
|| APR_STATUS_IS_ECONNABORTED(status)
@@ -1652,16 +1578,56 @@ static apr_status_t h2_session_read(h2_s
if (!is_accepting_streams(session)) {
break;
}
+ if ((session->io.bytes_read - read_start) > (64*1024)) {
+ /* read enough in one go, give write a chance */
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, c,
+ "h2_session(%ld): read 64k, returning", session->id);
+ break;
+ }
}
return rstatus;
}
+static int unsubmitted_iter(void *ctx, void *val)
+{
+ h2_stream *stream = val;
+ if (h2_stream_needs_submit(stream)) {
+ *((int *)ctx) = 1;
+ return 0;
+ }
+ return 1;
+}
+
+static int has_unsubmitted_streams(h2_session *session)
+{
+ int has_unsubmitted = 0;
+ h2_ihash_iter(session->streams, unsubmitted_iter, &has_unsubmitted);
+ return has_unsubmitted;
+}
+
+static int suspended_iter(void *ctx, void *val)
+{
+ h2_stream *stream = val;
+ if (h2_stream_is_suspended(stream)) {
+ *((int *)ctx) = 1;
+ return 0;
+ }
+ return 1;
+}
+
+static int has_suspended_streams(h2_session *session)
+{
+ int has_suspended = 0;
+ h2_ihash_iter(session->streams, suspended_iter, &has_suspended);
+ return has_suspended;
+}
+
static apr_status_t h2_session_submit(h2_session *session)
{
apr_status_t status = APR_EAGAIN;
h2_stream *stream;
- if (h2_stream_set_has_unsubmitted(session->streams)) {
+ if (has_unsubmitted_streams(session)) {
/* If we have responses ready, submit them now. */
while ((stream = h2_mplx_next_submit(session->mplx, session->streams))) {
status = submit_response(session, stream);
@@ -1784,7 +1750,7 @@ static void h2_session_ev_conn_error(h2_
default:
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
"h2_session(%ld): conn error -> shutdown", session->id);
- h2_session_shutdown(session, arg, msg);
+ h2_session_shutdown(session, arg, msg, 0);
break;
}
}
@@ -1801,7 +1767,7 @@ static void h2_session_ev_proto_error(h2
default:
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
"h2_session(%ld): proto error -> shutdown", session->id);
- h2_session_shutdown(session, arg, msg);
+ h2_session_shutdown(session, arg, msg, 0);
break;
}
}
@@ -1813,7 +1779,7 @@ static void h2_session_ev_conn_timeout(h
transit(session, "conn timeout", H2_SESSION_ST_DONE);
break;
default:
- h2_session_shutdown(session, arg, msg);
+ h2_session_shutdown(session, arg, msg, 1);
transit(session, "conn timeout", H2_SESSION_ST_DONE);
break;
}
@@ -1823,22 +1789,37 @@ static void h2_session_ev_no_io(h2_sessi
{
switch (session->state) {
case H2_SESSION_ST_BUSY:
+ case H2_SESSION_ST_LOCAL_SHUTDOWN:
+ case H2_SESSION_ST_REMOTE_SHUTDOWN:
/* nothing for input and output to do. If we remain
* in this state, we go into a tight loop and suck up
* CPU cycles. Ideally, we'd like to do a blocking read, but that
* is not possible if we have scheduled tasks and wait
* for them to produce something. */
- if (h2_stream_set_is_empty(session->streams)) {
- /* When we have no streams, no task event are possible,
- * switch to blocking reads */
- transit(session, "no io", H2_SESSION_ST_IDLE);
+ if (h2_ihash_is_empty(session->streams)) {
+ if (!is_accepting_streams(session)) {
+ /* We are no longer accepting new streams and have
+ * finished processing existing ones. Time to leave. */
+ h2_session_shutdown(session, arg, msg, 0);
+ transit(session, "no io", H2_SESSION_ST_DONE);
+ }
+ else {
+ /* When we have no streams, no task event are possible,
+ * switch to blocking reads */
+ transit(session, "no io", H2_SESSION_ST_IDLE);
+ session->idle_until = (session->requests_received?
+ session->s->keep_alive_timeout :
+ session->s->timeout) + apr_time_now();
+ }
}
- else if (!h2_stream_set_has_unsubmitted(session->streams)
- && !h2_stream_set_has_suspended(session->streams)) {
+ else if (!has_unsubmitted_streams(session)
+ && !has_suspended_streams(session)) {
/* none of our streams is waiting for a response or
* new output data from task processing,
- * switch to blocking reads. */
+ * switch to blocking reads. We are probably waiting on
+ * window updates. */
transit(session, "no io", H2_SESSION_ST_IDLE);
+ session->idle_until = apr_time_now() + session->s->timeout;
}
else {
/* Unable to do blocking reads, as we wait on events from
@@ -1853,11 +1834,11 @@ static void h2_session_ev_no_io(h2_sessi
}
}
-static void h2_session_ev_wait_timeout(h2_session *session, int arg, const char *msg)
+static void h2_session_ev_stream_ready(h2_session *session, int arg, const char *msg)
{
switch (session->state) {
case H2_SESSION_ST_WAIT:
- transit(session, "wait timeout", H2_SESSION_ST_BUSY);
+ transit(session, "stream ready", H2_SESSION_ST_BUSY);
break;
default:
/* nop */
@@ -1865,39 +1846,54 @@ static void h2_session_ev_wait_timeout(h
}
}
-static void h2_session_ev_stream_ready(h2_session *session, int arg, const char *msg)
+static void h2_session_ev_data_read(h2_session *session, int arg, const char *msg)
{
switch (session->state) {
+ case H2_SESSION_ST_IDLE:
case H2_SESSION_ST_WAIT:
- transit(session, "stream ready", H2_SESSION_ST_BUSY);
+ transit(session, "data read", H2_SESSION_ST_BUSY);
break;
+ /* fall through */
default:
/* nop */
break;
}
}
-static void h2_session_ev_data_read(h2_session *session, int arg, const char *msg)
+static void h2_session_ev_ngh2_done(h2_session *session, int arg, const char *msg)
{
switch (session->state) {
- case H2_SESSION_ST_IDLE:
- transit(session, "data read", H2_SESSION_ST_BUSY);
+ case H2_SESSION_ST_DONE:
+ /* nop */
break;
- /* fall through */
default:
+ transit(session, "nghttp2 done", H2_SESSION_ST_DONE);
+ break;
+ }
+}
+
+static void h2_session_ev_mpm_stopping(h2_session *session, int arg, const char *msg)
+{
+ switch (session->state) {
+ case H2_SESSION_ST_DONE:
+ case H2_SESSION_ST_LOCAL_SHUTDOWN:
/* nop */
break;
+ default:
+ h2_session_shutdown(session, arg, msg, 0);
+ break;
}
}
-static void h2_session_ev_ngh2_done(h2_session *session, int arg, const char *msg)
+static void h2_session_ev_pre_close(h2_session *session, int arg, const char *msg)
{
switch (session->state) {
case H2_SESSION_ST_DONE:
+ case H2_SESSION_ST_LOCAL_SHUTDOWN:
/* nop */
break;
default:
- transit(session, "nghttp2 done", H2_SESSION_ST_DONE);
+ h2_session_shutdown(session, arg, msg, 1);
break;
}
}
@@ -1927,9 +1923,6 @@ static void dispatch_event(h2_session *s
case H2_SESSION_EV_NO_IO:
h2_session_ev_no_io(session, arg, msg);
break;
- case H2_SESSION_EV_WAIT_TIMEOUT:
- h2_session_ev_wait_timeout(session, arg, msg);
- break;
case H2_SESSION_EV_STREAM_READY:
h2_session_ev_stream_ready(session, arg, msg);
break;
@@ -1939,6 +1932,12 @@ static void dispatch_event(h2_session *s
case H2_SESSION_EV_NGH2_DONE:
h2_session_ev_ngh2_done(session, arg, msg);
break;
+ case H2_SESSION_EV_MPM_STOPPING:
+ h2_session_ev_mpm_stopping(session, arg, msg);
+ break;
+ case H2_SESSION_EV_PRE_CLOSE:
+ h2_session_ev_pre_close(session, arg, msg);
+ break;
default:
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
"h2_session(%ld): unknown event %d",
@@ -1953,28 +1952,55 @@ static void dispatch_event(h2_session *s
static const int MAX_WAIT_MICROS = 200 * 1000;
+static void update_child_status(h2_session *session, int status, const char *msg)
+{
+ apr_snprintf(session->status, sizeof(session->status),
+ "%s, streams: %d/%d/%d/%d/%d (open/recv/resp/push/rst)",
+ msg? msg : "-",
+ (int)h2_ihash_count(session->streams),
+ (int)session->requests_received,
+ (int)session->responses_submitted,
+ (int)session->pushes_submitted,
+ (int)session->pushes_reset + session->streams_reset);
+ ap_update_child_status_descr(session->c->sbh, status, session->status);
+}
+
apr_status_t h2_session_process(h2_session *session, int async)
{
apr_status_t status = APR_SUCCESS;
conn_rec *c = session->c;
- int rv, have_written, have_read;
+ int rv, have_written, have_read, mpm_state, no_streams;
ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
"h2_session(%ld): process start, async=%d", session->id, async);
+ if (c->cs) {
+ c->cs->state = CONN_STATE_WRITE_COMPLETION;
+ }
+
while (1) {
have_read = have_written = 0;
+ if (!ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
+ if (mpm_state == AP_MPMQ_STOPPING) {
+ dispatch_event(session, H2_SESSION_EV_MPM_STOPPING, 0, NULL);
+ break;
+ }
+ }
+
+ session->status[0] = '\0';
+
switch (session->state) {
case H2_SESSION_ST_INIT:
+ ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c);
if (!h2_is_acceptable_connection(c, 1)) {
- h2_session_shutdown(session, NGHTTP2_INADEQUATE_SECURITY, NULL);
+ update_child_status(session, SERVER_BUSY_READ, "inadequate security");
+ h2_session_shutdown(session, NGHTTP2_INADEQUATE_SECURITY, NULL, 1);
}
else {
- ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
+ update_child_status(session, SERVER_BUSY_READ, "init");
status = h2_session_start(session, &rv);
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
- APLOGNO(03079)
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, APLOGNO(03079)
"h2_session(%ld): started on %s:%d", session->id,
session->s->server_hostname,
c->local_addr->port);
@@ -1986,32 +2012,80 @@ apr_status_t h2_session_process(h2_sessi
break;
case H2_SESSION_ST_IDLE:
- h2_filter_cin_timeout_set(session->cin, session->keepalive_secs);
- ap_update_child_status(c->sbh, SERVER_BUSY_KEEPALIVE, NULL);
- status = h2_session_read(session, 1, 10);
- if (status == APR_SUCCESS) {
- have_read = 1;
- dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
- }
- else if (status == APR_EAGAIN) {
- /* nothing to read */
- }
- else if (APR_STATUS_IS_TIMEUP(status)) {
- dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
- break;
+ no_streams = h2_ihash_is_empty(session->streams);
+ update_child_status(session, (no_streams? SERVER_BUSY_KEEPALIVE
+ : SERVER_BUSY_READ), "idle");
+ if (async && no_streams && !session->r && session->requests_received) {
+ ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
+ "h2_session(%ld): async idle, nonblock read", session->id);
+ /* We do not return to the async mpm immediately, since under
+ * load, mpms show the tendency to throw keep_alive connections
+ * away very rapidly.
+ * So, if we are still processing streams, we wait for the
+ * normal timeout first and, on timeout, close.
+ * If we have no streams, we still wait a short amount of
+ * time here for the next frame to arrive, before handing
+ * it to keep_alive processing of the mpm.
+ */
+ status = h2_session_read(session, 0);
+
+ if (status == APR_SUCCESS) {
+ have_read = 1;
+ dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
+ }
+ else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) {
+ if (apr_time_now() > session->idle_until) {
+ dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
+ }
+ else {
+ status = APR_EAGAIN;
+ goto out;
+ }
+ }
+ else {
+ ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, c,
+ "h2_session(%ld): idle, no data, error",
+ session->id);
+ dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, "timeout");
+ }
}
else {
- dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
+ /* We wait in smaller increments, using a 1 second timeout.
+ * That gives us the chance to check for MPMQ_STOPPING often.
+ */
+ status = h2_mplx_idle(session->mplx);
+ if (status != APR_SUCCESS) {
+ dispatch_event(session, H2_SESSION_EV_CONN_ERROR,
+ H2_ERR_ENHANCE_YOUR_CALM, "less is more");
+ }
+ h2_filter_cin_timeout_set(session->cin, apr_time_from_sec(1));
+ status = h2_session_read(session, 1);
+ if (status == APR_SUCCESS) {
+ have_read = 1;
+ dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
+ }
+ else if (status == APR_EAGAIN) {
+ /* nothing to read */
+ }
+ else if (APR_STATUS_IS_TIMEUP(status)) {
+ if (apr_time_now() > session->idle_until) {
+ dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, "timeout");
+ }
+ /* continue reading handling */
+ }
+ else {
+ dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, "error");
+ }
}
+
break;
case H2_SESSION_ST_BUSY:
case H2_SESSION_ST_LOCAL_SHUTDOWN:
case H2_SESSION_ST_REMOTE_SHUTDOWN:
if (nghttp2_session_want_read(session->ngh2)) {
- ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
- h2_filter_cin_timeout_set(session->cin, session->timeout_secs);
- status = h2_session_read(session, 0, 10);
+ h2_filter_cin_timeout_set(session->cin, session->s->timeout);
+ status = h2_session_read(session, 0);
if (status == APR_SUCCESS) {
have_read = 1;
dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
@@ -2028,7 +2102,7 @@ apr_status_t h2_session_process(h2_sessi
}
}
- if (!h2_stream_set_is_empty(session->streams)) {
+ if (!h2_ihash_is_empty(session->streams)) {
/* resume any streams for which data is available again */
h2_session_resume_streams_with_data(session);
/* Submit any responses/push_promises that are ready */
@@ -2050,7 +2124,8 @@ apr_status_t h2_session_process(h2_sessi
}
}
- 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) {
have_written = 1;
@@ -2063,39 +2138,55 @@ apr_status_t h2_session_process(h2_sessi
}
if (have_read || have_written) {
- session->wait_us = 0;
+ if (session->wait_us) {
+ session->wait_us = 0;
+ update_child_status(session, SERVER_BUSY_READ, "busy");
+ }
}
- else {
+ else if (!nghttp2_session_want_write(session->ngh2)) {
dispatch_event(session, H2_SESSION_EV_NO_IO, 0, NULL);
}
break;
case H2_SESSION_ST_WAIT:
- session->wait_us = H2MAX(session->wait_us, 10);
+ if (session->wait_us <= 0) {
+ session->wait_us = 10;
+ session->start_wait = apr_time_now();
+ update_child_status(session, SERVER_BUSY_READ, "wait");
+ }
+ else if ((apr_time_now() - session->start_wait) >= session->s->timeout) {
+ /* waited long enough */
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_TIMEUP, c,
+ "h2_session: wait for data");
+ dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
+ }
+ else {
+ /* repeating, increase timer for graceful backoff */
+ session->wait_us = H2MIN(session->wait_us*2, MAX_WAIT_MICROS);
+ }
+
if (APLOGctrace1(c)) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
"h2_session: wait for data, %ld micros",
(long)session->wait_us);
}
-
- ap_log_cerror( APLOG_MARK, APLOG_TRACE2, status, c,
- "h2_session(%ld): process -> trywait", session->id);
status = h2_mplx_out_trywait(session->mplx, session->wait_us,
session->iowait);
if (status == APR_SUCCESS) {
- dispatch_event(session, H2_SESSION_EV_STREAM_READY, 0, NULL);
+ session->wait_us = 0;
+ dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
}
else if (status == APR_TIMEUP) {
- /* nothing, increase timer for graceful backup */
- session->wait_us = H2MIN(session->wait_us*2, MAX_WAIT_MICROS);
- dispatch_event(session, H2_SESSION_EV_WAIT_TIMEOUT, 0, NULL);
+ /* go back to checking all inputs again */
+ transit(session, "wait cycle", H2_SESSION_ST_BUSY);
}
else {
- h2_session_shutdown(session, H2_ERR_INTERNAL_ERROR, "cond wait error");
+ h2_session_shutdown(session, H2_ERR_INTERNAL_ERROR, "cond wait error", 0);
}
break;
case H2_SESSION_ST_DONE:
+ update_child_status(session, SERVER_CLOSING, "done");
status = APR_EOF;
goto out;
@@ -2107,19 +2198,16 @@ apr_status_t h2_session_process(h2_sessi
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",
@@ -2136,10 +2224,17 @@ out:
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);
}
}
return status;
}
+
+apr_status_t h2_session_pre_close(h2_session *session, int async)
+{
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
+ "h2_session(%ld): pre_close", session->id);
+ dispatch_event(session, H2_SESSION_EV_PRE_CLOSE, 0, "timeout");
+ return APR_SUCCESS;
+}
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_session.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_session.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_session.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_session.h Wed Mar 2 11:21:28 2016
@@ -37,11 +37,14 @@
*
*/
+#include "h2.h"
+
struct apr_thread_mutext_t;
struct apr_thread_cond_t;
struct h2_ctx;
struct h2_config;
struct h2_filter_cin;
+struct h2_ihash_t;
struct h2_mplx;
struct h2_priority;
struct h2_push;
@@ -55,16 +58,6 @@ struct h2_workers;
struct nghttp2_session;
typedef enum {
- H2_SESSION_ST_INIT, /* send initial SETTINGS, etc. */
- H2_SESSION_ST_DONE, /* finished, connection close */
- H2_SESSION_ST_IDLE, /* nothing to write, expecting data inc */
- H2_SESSION_ST_BUSY, /* read/write without stop */
- H2_SESSION_ST_WAIT, /* waiting for tasks reporting back */
- H2_SESSION_ST_LOCAL_SHUTDOWN, /* we announced GOAWAY */
- H2_SESSION_ST_REMOTE_SHUTDOWN, /* client announced GOAWAY */
-} h2_session_state;
-
-typedef enum {
H2_SESSION_EV_INIT, /* session was initialized */
H2_SESSION_EV_LOCAL_GOAWAY, /* we send a GOAWAY */
H2_SESSION_EV_REMOTE_GOAWAY, /* remote send us a GOAWAY */
@@ -72,10 +65,11 @@ typedef enum {
H2_SESSION_EV_PROTO_ERROR, /* protocol error */
H2_SESSION_EV_CONN_TIMEOUT, /* connection timeout */
H2_SESSION_EV_NO_IO, /* nothing has been read or written */
- H2_SESSION_EV_WAIT_TIMEOUT, /* timeout waiting for tasks */
H2_SESSION_EV_STREAM_READY, /* stream signalled availability of headers/data */
H2_SESSION_EV_DATA_READ, /* connection data has been read */
H2_SESSION_EV_NGH2_DONE, /* nghttp2 wants neither read nor write anything */
+ H2_SESSION_EV_MPM_STOPPING, /* the process is stopping */
+ H2_SESSION_EV_PRE_CLOSE, /* connection will close after this */
} h2_session_event_t;
typedef struct h2_session {
@@ -90,6 +84,7 @@ typedef struct h2_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. */
@@ -111,8 +106,8 @@ typedef struct h2_session {
apr_size_t max_stream_count; /* max number of open streams */
apr_size_t max_stream_mem; /* max buffer memory for a single stream */
- int timeout_secs; /* connection timeout (seconds) */
- int keepalive_secs; /* connection idle timeout (seconds) */
+ apr_time_t start_wait; /* Time we started waiting for sth. to happen */
+ apr_time_t idle_until; /* Time we shut down due to sheer boredom */
apr_pool_t *pool; /* pool to use in session handling */
apr_bucket_brigade *bbtmp; /* brigade for keeping temporary data */
@@ -124,7 +119,7 @@ typedef struct h2_session {
struct h2_mplx *mplx; /* multiplexer for stream data */
struct h2_stream *last_stream; /* last stream worked with */
- struct h2_stream_set *streams; /* streams handled by this session */
+ struct h2_ihash_t *streams; /* streams handled by this session */
apr_pool_t *spare; /* spare stream pool */
@@ -132,6 +127,8 @@ typedef struct h2_session {
struct h2_workers *workers; /* for executing stream tasks */
struct h2_push_diary *push_diary; /* remember pushes, avoid duplicates */
+
+ char status[64]; /* status message for scoreboard */
} h2_session;
@@ -166,6 +163,11 @@ h2_session *h2_session_rcreate(request_r
apr_status_t h2_session_process(h2_session *session, int async);
/**
+ * Last chance to do anything before the connection is closed.
+ */
+apr_status_t h2_session_pre_close(h2_session *session, int async);
+
+/**
* Cleanup the session and all objects it still contains. This will not
* destroy h2_task instances that have not finished yet.
* @param session the session to destroy
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_stream.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_stream.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_stream.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_stream.c Wed Mar 2 11:21:28 2016
@@ -120,7 +120,7 @@ static int close_output(h2_stream *strea
return 1;
}
-static int input_open(h2_stream *stream)
+static int input_open(const h2_stream *stream)
{
switch (stream->state) {
case H2_STREAM_ST_OPEN:
@@ -158,7 +158,8 @@ h2_stream *h2_stream_open(int id, apr_po
{
h2_stream *stream = h2_stream_create(id, pool, session);
set_state(stream, H2_STREAM_ST_OPEN);
- stream->request = h2_request_create(id, pool, session->config);
+ stream->request = h2_request_create(id, pool,
+ h2_config_geti(session->config, H2_CONF_SER_HEADERS));
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03082)
"h2_stream(%ld-%d): opened", session->id, stream->id);
@@ -168,11 +169,6 @@ h2_stream *h2_stream_open(int id, apr_po
apr_status_t h2_stream_destroy(h2_stream *stream)
{
AP_DEBUG_ASSERT(stream);
- if (stream->request) {
- h2_request_destroy(stream->request);
- stream->request = NULL;
- }
-
if (stream->pool) {
apr_pool_destroy(stream->pool);
}
@@ -242,6 +238,9 @@ apr_status_t h2_stream_set_request(h2_st
}
set_state(stream, H2_STREAM_ST_OPEN);
status = h2_request_rwrite(stream->request, r);
+ stream->request->serialize = h2_config_geti(h2_config_rget(r),
+ H2_CONF_SER_HEADERS);
+
return status;
}
@@ -324,7 +323,7 @@ apr_status_t h2_stream_schedule(h2_strea
return status;
}
-int h2_stream_is_scheduled(h2_stream *stream)
+int h2_stream_is_scheduled(const h2_stream *stream)
{
return stream->scheduled;
}
@@ -431,7 +430,7 @@ void h2_stream_set_suspended(h2_stream *
stream->session->id, stream->id, stream->suspended);
}
-int h2_stream_is_suspended(h2_stream *stream)
+int h2_stream_is_suspended(const h2_stream *stream)
{
AP_DEBUG_ASSERT(stream);
return stream->suspended;
@@ -475,12 +474,12 @@ apr_status_t h2_stream_read_to(h2_stream
return stream->sos->read_to(stream->sos, bb, plen, peos);
}
-int h2_stream_input_is_open(h2_stream *stream)
+int h2_stream_input_is_open(const h2_stream *stream)
{
return input_open(stream);
}
-int h2_stream_needs_submit(h2_stream *stream)
+int h2_stream_needs_submit(const h2_stream *stream)
{
switch (stream->state) {
case H2_STREAM_ST_OPEN:
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_stream.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_stream.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_stream.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_stream.h Wed Mar 2 11:21:28 2016
@@ -16,6 +16,8 @@
#ifndef __mod_h2__h2_stream__
#define __mod_h2__h2_stream__
+#include "h2.h"
+
/**
* A HTTP/2 stream, e.g. a client request+response in HTTP/1.1 terms.
*
@@ -30,16 +32,6 @@
*/
#include "h2_io.h"
-typedef enum {
- H2_STREAM_ST_IDLE,
- H2_STREAM_ST_OPEN,
- H2_STREAM_ST_RESV_LOCAL,
- H2_STREAM_ST_RESV_REMOTE,
- H2_STREAM_ST_CLOSED_INPUT,
- H2_STREAM_ST_CLOSED_OUTPUT,
- H2_STREAM_ST_CLOSED,
-} h2_stream_state_t;
-
struct h2_mplx;
struct h2_priority;
struct h2_request;
@@ -191,7 +183,7 @@ apr_status_t h2_stream_schedule(h2_strea
* @param stream the stream to check on
* @return != 0 iff stream has been scheduled
*/
-int h2_stream_is_scheduled(h2_stream *stream);
+int h2_stream_is_scheduled(const h2_stream *stream);
struct h2_response *h2_stream_get_response(h2_stream *stream);
@@ -278,21 +270,21 @@ void h2_stream_set_suspended(h2_stream *
* @param stream the stream to check
* @return != 0 iff stream is suspended.
*/
-int h2_stream_is_suspended(h2_stream *stream);
+int h2_stream_is_suspended(const h2_stream *stream);
/**
* Check if the stream has open input.
* @param stream the stream to check
* @return != 0 iff stream has open input.
*/
-int h2_stream_input_is_open(h2_stream *stream);
+int h2_stream_input_is_open(const h2_stream *stream);
/**
* Check if the stream has not submitted a response or RST yet.
* @param stream the stream to check
* @return != 0 iff stream has not submitted a response or RST.
*/
-int h2_stream_needs_submit(h2_stream *stream);
+int h2_stream_needs_submit(const h2_stream *stream);
/**
* Submit any server push promises on this stream and schedule
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_task.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_task.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_task.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_task.c Wed Mar 2 11:21:28 2016
@@ -35,6 +35,7 @@
#include "h2_private.h"
#include "h2_conn.h"
#include "h2_config.h"
+#include "h2_ctx.h"
#include "h2_from_h1.h"
#include "h2_h2.h"
#include "h2_mplx.h"
@@ -85,6 +86,27 @@ static apr_status_t h2_filter_read_respo
return h2_from_h1_read_response(task->output->from_h1, f, bb);
}
+static apr_status_t h2_response_freeze_filter(ap_filter_t* f,
+ apr_bucket_brigade* bb)
+{
+ h2_task *task = f->ctx;
+ AP_DEBUG_ASSERT(task);
+
+ if (task->frozen) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,
+ "h2_response_freeze_filter, saving");
+ return ap_save_brigade(f, &task->frozen_out, &bb, task->c->pool);
+ }
+
+ if (APR_BRIGADE_EMPTY(bb)) {
+ return APR_SUCCESS;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,
+ "h2_response_freeze_filter, passing");
+ return ap_pass_brigade(f->next, bb);
+}
+
/*******************************************************************************
* Register various hooks
*/
@@ -92,6 +114,9 @@ static const char *const mod_ssl[]
static int h2_task_pre_conn(conn_rec* c, void *arg);
static int h2_task_process_conn(conn_rec* c);
+APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_in) *h2_task_logio_add_bytes_in;
+APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *h2_task_logio_add_bytes_out;
+
void h2_task_register_hooks(void)
{
/* This hook runs on new connections before mod_ssl has a say.
@@ -116,6 +141,17 @@ void h2_task_register_hooks(void)
NULL, AP_FTYPE_PROTOCOL);
ap_register_output_filter("H2_TRAILERS", h2_response_trailers_filter,
NULL, AP_FTYPE_PROTOCOL);
+ ap_register_output_filter("H2_RESPONSE_FREEZE", h2_response_freeze_filter,
+ NULL, AP_FTYPE_RESOURCE);
+}
+
+/* post config init */
+apr_status_t h2_task_init(apr_pool_t *pool, server_rec *s)
+{
+ h2_task_logio_add_bytes_in = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_in);
+ h2_task_logio_add_bytes_out = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_out);
+
+ return APR_SUCCESS;
}
static int h2_task_pre_conn(conn_rec* c, void *arg)
@@ -143,60 +179,80 @@ static int h2_task_pre_conn(conn_rec* c,
}
h2_task *h2_task_create(long session_id, const h2_request *req,
- apr_pool_t *pool, h2_mplx *mplx)
+ conn_rec *c, h2_mplx *mplx)
{
- h2_task *task = apr_pcalloc(pool, sizeof(h2_task));
+ h2_task *task = apr_pcalloc(c->pool, sizeof(h2_task));
if (task == NULL) {
- ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool,
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, c,
APLOGNO(02941) "h2_task(%ld-%d): create stream task",
session_id, req->id);
h2_mplx_out_close(mplx, req->id, NULL);
return NULL;
}
- task->id = apr_psprintf(pool, "%ld-%d", session_id, req->id);
+ task->id = apr_psprintf(c->pool, "%ld-%d", session_id, req->id);
task->stream_id = req->id;
+ task->c = c;
task->mplx = mplx;
task->request = req;
task->input_eos = !req->body;
- task->ser_headers = h2_config_geti(req->config, H2_CONF_SER_HEADERS);
+ task->ser_headers = req->serialize;
+
+ h2_ctx_create_for(c, task);
return task;
}
-apr_status_t h2_task_do(h2_task *task, conn_rec *c, apr_thread_cond_t *cond,
- apr_socket_t *socket)
+apr_status_t h2_task_do(h2_task *task, apr_thread_cond_t *cond)
{
+ apr_status_t status;
+
AP_DEBUG_ASSERT(task);
task->io = cond;
- task->input = h2_task_input_create(task, c->pool, c->bucket_alloc);
- task->output = h2_task_output_create(task, c->pool);
-
- ap_process_connection(c, socket);
+ task->input = h2_task_input_create(task, task->c);
+ task->output = h2_task_output_create(task, task->c);
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
- "h2_task(%s): processing done", task->id);
+ ap_process_connection(task->c, ap_get_conn_socket(task->c));
- h2_task_input_destroy(task->input);
- h2_task_output_close(task->output);
- h2_task_output_destroy(task->output);
- task->io = NULL;
+ if (task->frozen) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c,
+ "h2_task(%s): process_conn returned frozen task",
+ task->id);
+ /* cleanup delayed */
+ status = APR_EAGAIN;
+ }
+ else {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c,
+ "h2_task(%s): processing done", task->id);
+ status = APR_SUCCESS;
+ }
- return APR_SUCCESS;
+ return status;
}
-static apr_status_t h2_task_process_request(const h2_request *req, conn_rec *c)
+static apr_status_t h2_task_process_request(h2_task *task, conn_rec *c)
{
- request_rec *r;
+ const h2_request *req = task->request;
conn_state_t *cs = c->cs;
+ request_rec *r;
r = h2_request_create_rec(req, c);
if (r && (r->status == HTTP_OK)) {
ap_update_child_status(c->sbh, SERVER_BUSY_READ, r);
- if (cs)
+ if (cs) {
cs->state = CONN_STATE_HANDLER;
+ }
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+ "h2_task(%s): start process_request", task->id);
ap_process_request(r);
+ if (task->frozen) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+ "h2_task(%s): process_request frozen", task->id);
+ }
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+ "h2_task(%s): process_request done", task->id);
+
/* After the call to ap_process_request, the
* request pool will have been deleted. We set
* r=NULL here to ensure that any dereference
@@ -204,11 +260,10 @@ static apr_status_t h2_task_process_requ
* will result in a segfault immediately instead
* of nondeterministic failures later.
*/
- if (cs)
+ if (cs)
cs->state = CONN_STATE_WRITE_COMPLETION;
r = NULL;
}
- ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, NULL);
c->sbh = NULL;
return APR_SUCCESS;
@@ -227,7 +282,7 @@ static int h2_task_process_conn(conn_rec
if (!ctx->task->ser_headers) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
"h2_h2, processing request directly");
- h2_task_process_request(ctx->task->request, c);
+ h2_task_process_request(ctx->task, c);
return DONE;
}
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
@@ -235,3 +290,28 @@ static int h2_task_process_conn(conn_rec
}
return DECLINED;
}
+
+apr_status_t h2_task_freeze(h2_task *task, request_rec *r)
+{
+ if (!task->frozen) {
+ conn_rec *c = task->c;
+
+ task->frozen = 1;
+ task->frozen_out = apr_brigade_create(c->pool, c->bucket_alloc);
+ ap_add_output_filter("H2_RESPONSE_FREEZE", task, r, r->connection);
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+ "h2_task(%s), frozen", task->id);
+ }
+ return APR_SUCCESS;
+}
+
+apr_status_t h2_task_thaw(h2_task *task)
+{
+ if (task->frozen) {
+ task->frozen = 0;
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c,
+ "h2_task(%s), thawed", task->id);
+ }
+ return APR_SUCCESS;
+}
+
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_task.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_task.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_task.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_task.h Wed Mar 2 11:21:28 2016
@@ -16,6 +16,8 @@
#ifndef __mod_h2__h2_task__
#define __mod_h2__h2_task__
+#include <http_core.h>
+
/**
* A h2_task fakes a HTTP/1.1 request from the data in a HTTP/2 stream
* (HEADER+CONT.+DATA) the module recieves.
@@ -48,24 +50,37 @@ typedef struct h2_task h2_task;
struct h2_task {
const char *id;
int stream_id;
+ conn_rec *c;
struct h2_mplx *mplx;
const struct h2_request *request;
unsigned int filters_set : 1;
unsigned int input_eos : 1;
unsigned int ser_headers : 1;
+ unsigned int frozen : 1;
struct h2_task_input *input;
struct h2_task_output *output;
struct apr_thread_cond_t *io; /* used to wait for events on */
+
+ apr_bucket_brigade *frozen_out;
};
h2_task *h2_task_create(long session_id, const struct h2_request *req,
- apr_pool_t *pool, struct h2_mplx *mplx);
+ conn_rec *c, struct h2_mplx *mplx);
-apr_status_t h2_task_do(h2_task *task, conn_rec *c,
- struct apr_thread_cond_t *cond, apr_socket_t *socket);
+apr_status_t h2_task_do(h2_task *task, struct apr_thread_cond_t *cond);
void h2_task_register_hooks(void);
+/*
+ * One time, post config intialization.
+ */
+apr_status_t h2_task_init(apr_pool_t *pool, server_rec *s);
+
+extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_in) *h2_task_logio_add_bytes_in;
+extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *h2_task_logio_add_bytes_out;
+
+apr_status_t h2_task_freeze(h2_task *task, request_rec *r);
+apr_status_t h2_task_thaw(h2_task *task);
#endif /* defined(__mod_h2__h2_task__) */
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.c Wed Mar 2 11:21:28 2016
@@ -43,29 +43,30 @@ static int ser_header(void *ctx, const c
return 1;
}
-h2_task_input *h2_task_input_create(h2_task *task, apr_pool_t *pool,
- apr_bucket_alloc_t *bucket_alloc)
+h2_task_input *h2_task_input_create(h2_task *task, conn_rec *c)
{
- h2_task_input *input = apr_pcalloc(pool, sizeof(h2_task_input));
+ h2_task_input *input = apr_pcalloc(c->pool, sizeof(h2_task_input));
if (input) {
+ input->c = c;
input->task = task;
input->bb = NULL;
+ input->block = APR_BLOCK_READ;
if (task->ser_headers) {
- ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool,
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
"h2_task_input(%s): serialize request %s %s",
task->id, task->request->method, task->request->path);
- input->bb = apr_brigade_create(pool, bucket_alloc);
+ input->bb = apr_brigade_create(c->pool, c->bucket_alloc);
apr_brigade_printf(input->bb, NULL, NULL, "%s %s HTTP/1.1\r\n",
task->request->method, task->request->path);
apr_table_do(ser_header, input, task->request->headers, NULL);
apr_brigade_puts(input->bb, NULL, NULL, "\r\n");
if (input->task->input_eos) {
- APR_BRIGADE_INSERT_TAIL(input->bb, apr_bucket_eos_create(bucket_alloc));
+ APR_BRIGADE_INSERT_TAIL(input->bb, apr_bucket_eos_create(c->bucket_alloc));
}
}
else if (!input->task->input_eos) {
- input->bb = apr_brigade_create(pool, bucket_alloc);
+ input->bb = apr_brigade_create(c->pool, c->bucket_alloc);
}
else {
/* We do not serialize and have eos already, no need to
@@ -75,9 +76,9 @@ h2_task_input *h2_task_input_create(h2_t
return input;
}
-void h2_task_input_destroy(h2_task_input *input)
+void h2_task_input_block_set(h2_task_input *input, apr_read_type_e block)
{
- input->bb = NULL;
+ input->block = block;
}
apr_status_t h2_task_input_read(h2_task_input *input,
@@ -120,7 +121,7 @@ apr_status_t h2_task_input_read(h2_task_
return APR_EOF;
}
- while ((bblen == 0) || (mode == AP_MODE_READBYTES && bblen < readbytes)) {
+ while (bblen == 0) {
/* Get more data for our stream from mplx.
*/
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
@@ -129,30 +130,38 @@ apr_status_t h2_task_input_read(h2_task_
input->task->id, block,
(long)readbytes, (long)bblen);
- /* Although we sometimes get called with APR_NONBLOCK_READs,
- we seem to fill our buffer blocking. Otherwise we get EAGAIN,
- return that to our caller and everyone throws up their hands,
- never calling us again. */
- status = h2_mplx_in_read(input->task->mplx, APR_BLOCK_READ,
+ /* Override the block mode we get called with depending on the input's
+ * setting.
+ */
+ status = h2_mplx_in_read(input->task->mplx, block,
input->task->stream_id, input->bb,
f->r? f->r->trailers_in : NULL,
input->task->io);
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
"h2_task_input(%s): mplx in read returned",
input->task->id);
- if (status != APR_SUCCESS) {
+ if (APR_STATUS_IS_EAGAIN(status)
+ && (mode == AP_MODE_GETLINE || block == APR_BLOCK_READ)) {
+ /* chunked input handling does not seem to like it if we
+ * return with APR_EAGAIN from a GETLINE read...
+ * upload 100k test on test-ser.example.org hangs */
+ status = APR_SUCCESS;
+ }
+ else if (status != APR_SUCCESS) {
return status;
}
+
status = apr_brigade_length(input->bb, 1, &bblen);
if (status != APR_SUCCESS) {
return status;
}
- if ((bblen == 0) && (block == APR_NONBLOCK_READ)) {
- return h2_util_has_eos(input->bb, -1)? APR_EOF : APR_EAGAIN;
- }
+
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
"h2_task_input(%s): mplx in read, %ld bytes in brigade",
input->task->id, (long)bblen);
+ if (h2_task_logio_add_bytes_in) {
+ h2_task_logio_add_bytes_in(f->c, bblen);
+ }
}
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.h Wed Mar 2 11:21:28 2016
@@ -26,15 +26,14 @@ struct h2_task;
typedef struct h2_task_input h2_task_input;
struct h2_task_input {
+ conn_rec *c;
struct h2_task *task;
apr_bucket_brigade *bb;
+ apr_read_type_e block;
};
-h2_task_input *h2_task_input_create(struct h2_task *task, apr_pool_t *pool,
- apr_bucket_alloc_t *bucket_alloc);
-
-void h2_task_input_destroy(h2_task_input *input);
+h2_task_input *h2_task_input_create(struct h2_task *task, conn_rec *c);
apr_status_t h2_task_input_read(h2_task_input *input,
ap_filter_t* filter,
@@ -43,4 +42,6 @@ apr_status_t h2_task_input_read(h2_task_
apr_read_type_e block,
apr_off_t readbytes);
+void h2_task_input_block_set(h2_task_input *input, apr_read_type_e block);
+
#endif /* defined(__mod_h2__h2_task_input__) */
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.c Wed Mar 2 11:21:28 2016
@@ -34,14 +34,14 @@
#include "h2_util.h"
-h2_task_output *h2_task_output_create(h2_task *task, apr_pool_t *pool)
+h2_task_output *h2_task_output_create(h2_task *task, conn_rec *c)
{
- h2_task_output *output = apr_pcalloc(pool, sizeof(h2_task_output));
-
+ h2_task_output *output = apr_pcalloc(c->pool, sizeof(h2_task_output));
if (output) {
+ output->c = c;
output->task = task;
output->state = H2_TASK_OUT_INIT;
- output->from_h1 = h2_from_h1_create(task->stream_id, pool);
+ output->from_h1 = h2_from_h1_create(task->stream_id, c->pool);
if (!output->from_h1) {
return NULL;
}
@@ -49,16 +49,25 @@ h2_task_output *h2_task_output_create(h2
return output;
}
-void h2_task_output_destroy(h2_task_output *output)
+static apr_table_t *get_trailers(h2_task_output *output)
{
- if (output->from_h1) {
- h2_from_h1_destroy(output->from_h1);
- output->from_h1 = NULL;
+ if (!output->trailers_passed) {
+ h2_response *response = h2_from_h1_get_response(output->from_h1);
+ if (response && response->trailers) {
+ output->trailers_passed = 1;
+ if (h2_task_logio_add_bytes_out) {
+ /* counter trailers as if we'd do a HTTP/1.1 serialization */
+ h2_task_logio_add_bytes_out(output->c,
+ h2_util_table_bytes(response->trailers, 3)+1);
+ }
+ return response->trailers;
+ }
}
+ return NULL;
}
static apr_status_t open_if_needed(h2_task_output *output, ap_filter_t *f,
- apr_bucket_brigade *bb)
+ apr_bucket_brigade *bb, const char *caller)
{
if (output->state == H2_TASK_OUT_INIT) {
h2_response *response;
@@ -67,12 +76,12 @@ static apr_status_t open_if_needed(h2_ta
if (!response) {
if (f) {
/* This happens currently when ap_die(status, r) is invoked
- * by a read request filter.
- */
+ * by a read request filter. */
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO(03204)
- "h2_task_output(%s): write without response "
+ "h2_task_output(%s): write without response by %s "
"for %s %s %s",
- output->task->id, output->task->request->method,
+ output->task->id, caller,
+ output->task->request->method,
output->task->request->authority,
output->task->request->path);
f->c->aborted = 1;
@@ -83,40 +92,41 @@ static apr_status_t open_if_needed(h2_ta
return APR_ECONNABORTED;
}
- output->trailers_passed = !!response->trailers;
+ if (h2_task_logio_add_bytes_out) {
+ /* counter headers as if we'd do a HTTP/1.1 serialization */
+ /* TODO: counter a virtual status line? */
+ apr_off_t bytes_written;
+ apr_brigade_length(bb, 0, &bytes_written);
+ bytes_written += h2_util_table_bytes(response->headers, 3)+1;
+ h2_task_logio_add_bytes_out(f->c, bytes_written);
+ }
+ get_trailers(output);
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO(03348)
+ "h2_task_output(%s): open as needed %s %s %s",
+ output->task->id, output->task->request->method,
+ output->task->request->authority,
+ output->task->request->path);
return h2_mplx_out_open(output->task->mplx, output->task->stream_id,
response, f, bb, output->task->io);
}
return APR_EOF;
}
-static apr_table_t *get_trailers(h2_task_output *output)
-{
- if (!output->trailers_passed) {
- h2_response *response = h2_from_h1_get_response(output->from_h1);
- if (response && response->trailers) {
- output->trailers_passed = 1;
- return response->trailers;
- }
- }
- return NULL;
-}
-
void h2_task_output_close(h2_task_output *output)
{
- open_if_needed(output, NULL, NULL);
+ open_if_needed(output, NULL, NULL, "close");
if (output->state != H2_TASK_OUT_DONE) {
+ if (output->task->frozen_out
+ && !APR_BRIGADE_EMPTY(output->task->frozen_out)) {
+ h2_mplx_out_write(output->task->mplx, output->task->stream_id,
+ NULL, output->task->frozen_out, NULL, NULL);
+ }
h2_mplx_out_close(output->task->mplx, output->task->stream_id,
get_trailers(output));
output->state = H2_TASK_OUT_DONE;
}
}
-int h2_task_output_has_started(h2_task_output *output)
-{
- return output->state >= H2_TASK_OUT_STARTED;
-}
-
/* Bring the data from the brigade (which represents the result of the
* request_rec out filter chain) into the h2_mplx for further sending
* on the master connection.
@@ -132,7 +142,14 @@ apr_status_t h2_task_output_write(h2_tas
return APR_SUCCESS;
}
- status = open_if_needed(output, f, bb);
+ if (output->task->frozen) {
+ h2_util_bb_log(output->c, output->task->stream_id, APLOG_TRACE2,
+ "frozen task output write", bb);
+ return ap_save_brigade(f, &output->task->frozen_out, &bb,
+ output->c->pool);
+ }
+
+ status = open_if_needed(output, f, bb, "write");
if (status != APR_EOF) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
"h2_task_output(%s): opened and passed brigade",
@@ -142,6 +159,11 @@ apr_status_t h2_task_output_write(h2_tas
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
"h2_task_output(%s): write brigade", output->task->id);
+ if (h2_task_logio_add_bytes_out) {
+ apr_off_t bytes_written;
+ apr_brigade_length(bb, 0, &bytes_written);
+ h2_task_logio_add_bytes_out(f->c, bytes_written);
+ }
return h2_mplx_out_write(output->task->mplx, output->task->stream_id,
f, bb, get_trailers(output), output->task->io);
}
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.h Wed Mar 2 11:21:28 2016
@@ -35,15 +35,14 @@ typedef enum {
typedef struct h2_task_output h2_task_output;
struct h2_task_output {
+ conn_rec *c;
struct h2_task *task;
h2_task_output_state_t state;
struct h2_from_h1 *from_h1;
unsigned int trailers_passed : 1;
};
-h2_task_output *h2_task_output_create(struct h2_task *task, apr_pool_t *pool);
-
-void h2_task_output_destroy(h2_task_output *output);
+h2_task_output *h2_task_output_create(struct h2_task *task, conn_rec *c);
apr_status_t h2_task_output_write(h2_task_output *output,
ap_filter_t* filter,
@@ -51,6 +50,7 @@ apr_status_t h2_task_output_write(h2_tas
void h2_task_output_close(h2_task_output *output);
-int h2_task_output_has_started(h2_task_output *output);
+apr_status_t h2_task_output_freeze(h2_task_output *output);
+apr_status_t h2_task_output_thaw(h2_task_output *output);
#endif /* defined(__mod_h2__h2_task_output__) */
Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_util.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_util.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_util.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_util.c Wed Mar 2 11:21:28 2016
@@ -28,6 +28,36 @@
#include "h2_request.h"
#include "h2_util.h"
+/* h2_log2(n) iff n is a power of 2 */
+unsigned char h2_log2(apr_uint32_t n)
+{
+ int lz = 0;
+ if (!n) {
+ return 0;
+ }
+ if (!(n & 0xffff0000u)) {
+ lz += 16;
+ n = (n << 16);
+ }
+ if (!(n & 0xff000000u)) {
+ lz += 8;
+ n = (n << 8);
+ }
+ if (!(n & 0xf0000000u)) {
+ lz += 4;
+ n = (n << 4);
+ }
+ if (!(n & 0xc0000000u)) {
+ lz += 2;
+ n = (n << 2);
+ }
+ if (!(n & 0x80000000u)) {
+ lz += 1;
+ }
+
+ return 31 - lz;
+}
+
size_t h2_util_hex_dump(char *buffer, size_t maxlen,
const char *data, size_t datalen)
{
@@ -230,6 +260,111 @@ const char *h2_util_first_token_match(ap
return NULL;
}
+
+/*******************************************************************************
+ * ihash - hash for structs with int identifier
+ ******************************************************************************/
+struct h2_ihash_t {
+ apr_hash_t *hash;
+ size_t ioff;
+};
+
+static unsigned int ihash(const char *key, apr_ssize_t *klen)
+{
+ return (unsigned int)(*((int*)key));
+}
+
+h2_ihash_t *h2_ihash_create(apr_pool_t *pool, size_t offset_of_int)
+{
+ h2_ihash_t *ih = apr_pcalloc(pool, sizeof(h2_ihash_t));
+ ih->hash = apr_hash_make_custom(pool, ihash);
+ ih->ioff = offset_of_int;
+ return ih;
+}
+
+size_t h2_ihash_count(h2_ihash_t *ih)
+{
+ return apr_hash_count(ih->hash);
+}
+
+int h2_ihash_is_empty(h2_ihash_t *ih)
+{
+ return apr_hash_count(ih->hash) == 0;
+}
+
+void *h2_ihash_get(h2_ihash_t *ih, int id)
+{
+ return apr_hash_get(ih->hash, &id, sizeof(id));
+}
+
+typedef struct {
+ h2_ihash_iter_t *iter;
+ void *ctx;
+} iter_ctx;
+
+static int ihash_iter(void *ctx, const void *key, apr_ssize_t klen,
+ const void *val)
+{
+ iter_ctx *ictx = ctx;
+ return ictx->iter(ictx->ctx, (void*)val); /* why is this passed const?*/
+}
+
+void h2_ihash_iter(h2_ihash_t *ih, h2_ihash_iter_t *fn, void *ctx)
+{
+ iter_ctx ictx;
+ ictx.iter = fn;
+ ictx.ctx = ctx;
+ apr_hash_do(ihash_iter, &ictx, ih->hash);
+}
+
+void h2_ihash_add(h2_ihash_t *ih, void *val)
+{
+ apr_hash_set(ih->hash, ((char *)val + ih->ioff), sizeof(int), val);
+}
+
+void h2_ihash_remove(h2_ihash_t *ih, int id)
+{
+ apr_hash_set(ih->hash, &id, sizeof(id), NULL);
+}
+
+void h2_ihash_clear(h2_ihash_t *ih)
+{
+ apr_hash_clear(ih->hash);
+}
+
+/*******************************************************************************
+ * h2_util for apt_table_t
+ ******************************************************************************/
+
+typedef struct {
+ apr_size_t bytes;
+ apr_size_t pair_extra;
+} table_bytes_ctx;
+
+static int count_bytes(void *x, const char *key, const char *value)
+{
+ table_bytes_ctx *ctx = x;
+ if (key) {
+ ctx->bytes += strlen(key);
+ }
+ if (value) {
+ ctx->bytes += strlen(value);
+ }
+ ctx->bytes += ctx->pair_extra;
+ return 1;
+}
+
+apr_size_t h2_util_table_bytes(apr_table_t *t, apr_size_t pair_extra)
+{
+ table_bytes_ctx ctx;
+
+ ctx.bytes = 0;
+ ctx.pair_extra = pair_extra;
+ apr_table_do(count_bytes, &ctx, t, NULL);
+ return ctx.bytes;
+}
+
+
/*******************************************************************************
* h2_util for bucket brigades
******************************************************************************/
@@ -970,6 +1105,9 @@ static literal IgnoredResponseTrailers[]
H2_DEF_LITERAL("www-authenticate"),
H2_DEF_LITERAL("proxy-authenticate"),
};
+static literal IgnoredProxyRespHds[] = {
+ H2_DEF_LITERAL("alt-svc"),
+};
static int ignore_header(const literal *lits, size_t llen,
const char *name, size_t nlen)
@@ -1002,12 +1140,125 @@ int h2_res_ignore_trailer(const char *na
return ignore_header(H2_LIT_ARGS(IgnoredResponseTrailers), name, len);
}
-void h2_req_strip_ignored_header(apr_table_t *headers)
+int h2_proxy_res_ignore_header(const char *name, size_t len)
{
- int i;
- for (i = 0; i < H2_ALEN(IgnoredRequestHeaders); ++i) {
- apr_table_unset(headers, IgnoredRequestHeaders[i].name);
+ return (h2_req_ignore_header(name, len)
+ || ignore_header(H2_LIT_ARGS(IgnoredProxyRespHds), name, len));
+}
+
+
+/*******************************************************************************
+ * frame logging
+ ******************************************************************************/
+
+int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
+{
+ char scratch[128];
+ size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
+
+ switch (frame->hd.type) {
+ case NGHTTP2_DATA: {
+ return apr_snprintf(buffer, maxlen,
+ "DATA[length=%d, flags=%d, stream=%d, padlen=%d]",
+ (int)frame->hd.length, frame->hd.flags,
+ frame->hd.stream_id, (int)frame->data.padlen);
+ }
+ case NGHTTP2_HEADERS: {
+ return apr_snprintf(buffer, maxlen,
+ "HEADERS[length=%d, hend=%d, stream=%d, eos=%d]",
+ (int)frame->hd.length,
+ !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
+ frame->hd.stream_id,
+ !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
+ }
+ case NGHTTP2_PRIORITY: {
+ return apr_snprintf(buffer, maxlen,
+ "PRIORITY[length=%d, flags=%d, stream=%d]",
+ (int)frame->hd.length,
+ frame->hd.flags, frame->hd.stream_id);
+ }
+ case NGHTTP2_RST_STREAM: {
+ return apr_snprintf(buffer, maxlen,
+ "RST_STREAM[length=%d, flags=%d, stream=%d]",
+ (int)frame->hd.length,
+ frame->hd.flags, frame->hd.stream_id);
+ }
+ case NGHTTP2_SETTINGS: {
+ if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
+ return apr_snprintf(buffer, maxlen,
+ "SETTINGS[ack=1, stream=%d]",
+ frame->hd.stream_id);
+ }
+ return apr_snprintf(buffer, maxlen,
+ "SETTINGS[length=%d, stream=%d]",
+ (int)frame->hd.length, frame->hd.stream_id);
+ }
+ case NGHTTP2_PUSH_PROMISE: {
+ return apr_snprintf(buffer, maxlen,
+ "PUSH_PROMISE[length=%d, hend=%d, stream=%d]",
+ (int)frame->hd.length,
+ !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
+ frame->hd.stream_id);
+ }
+ case NGHTTP2_PING: {
+ return apr_snprintf(buffer, maxlen,
+ "PING[length=%d, ack=%d, stream=%d]",
+ (int)frame->hd.length,
+ frame->hd.flags&NGHTTP2_FLAG_ACK,
+ frame->hd.stream_id);
+ }
+ case NGHTTP2_GOAWAY: {
+ size_t len = (frame->goaway.opaque_data_len < s_len)?
+ frame->goaway.opaque_data_len : s_len-1;
+ memcpy(scratch, frame->goaway.opaque_data, len);
+ scratch[len] = '\0';
+ return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s']",
+ frame->goaway.error_code, scratch);
+ }
+ case NGHTTP2_WINDOW_UPDATE: {
+ return apr_snprintf(buffer, maxlen,
+ "WINDOW_UPDATE[stream=%d, incr=%d]",
+ frame->hd.stream_id,
+ frame->window_update.window_size_increment);
+ }
+ default:
+ return apr_snprintf(buffer, maxlen,
+ "type=%d[length=%d, flags=%d, stream=%d]",
+ frame->hd.type, (int)frame->hd.length,
+ frame->hd.flags, frame->hd.stream_id);
}
}
+/*******************************************************************************
+ * push policy
+ ******************************************************************************/
+void h2_push_policy_determine(struct h2_request *req, apr_pool_t *p, int push_enabled)
+{
+ h2_push_policy policy = H2_PUSH_NONE;
+ if (push_enabled) {
+ const char *val = apr_table_get(req->headers, "accept-push-policy");
+ if (val) {
+ if (ap_find_token(p, val, "fast-load")) {
+ policy = H2_PUSH_FAST_LOAD;
+ }
+ else if (ap_find_token(p, val, "head")) {
+ policy = H2_PUSH_HEAD;
+ }
+ else if (ap_find_token(p, val, "default")) {
+ policy = H2_PUSH_DEFAULT;
+ }
+ else if (ap_find_token(p, val, "none")) {
+ policy = H2_PUSH_NONE;
+ }
+ else {
+ /* nothing known found in this header, go by default */
+ policy = H2_PUSH_DEFAULT;
+ }
+ }
+ else {
+ policy = H2_PUSH_DEFAULT;
+ }
+ }
+ req->push_policy = policy;
+}