You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by jo...@apache.org on 2016/11/14 10:26:34 UTC
svn commit: r1769588 [14/17] - in
/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat: ./ docs/conf/
docs/manual/ docs/manual/howto/ docs/manual/mod/ docs/manual/platform/
docs/manual/programs/ docs/manual/rewrite/ include/ modules/ modules/aaa/
modules/a...
Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_request.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_request.c?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_request.c (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_request.c Mon Nov 14 10:26:31 2016
@@ -30,27 +30,44 @@
#include <scoreboard.h>
#include "h2_private.h"
+#include "h2_config.h"
#include "h2_push.h"
#include "h2_request.h"
#include "h2_util.h"
-static apr_status_t inspect_clen(h2_request *req, const char *s)
+typedef struct {
+ apr_table_t *headers;
+ apr_pool_t *pool;
+} h1_ctx;
+
+static int set_h1_header(void *ctx, const char *key, const char *value)
{
- char *end;
- req->content_length = apr_strtoi64(s, &end, 10);
- return (s == end)? APR_EINVAL : APR_SUCCESS;
+ h1_ctx *x = ctx;
+ size_t klen = strlen(key);
+ if (!h2_req_ignore_header(key, klen)) {
+ h2_headers_add_h1(x->headers, x->pool, key, klen, value, strlen(value));
+ }
+ return 1;
}
-apr_status_t h2_request_rwrite(h2_request *req, apr_pool_t *pool,
- request_rec *r)
+apr_status_t h2_request_rcreate(h2_request **preq, apr_pool_t *pool,
+ request_rec *r)
{
- apr_status_t status;
- const char *scheme, *authority;
+ h2_request *req;
+ const char *scheme, *authority, *path;
+ h1_ctx x;
+ *preq = NULL;
scheme = apr_pstrdup(pool, r->parsed_uri.scheme? r->parsed_uri.scheme
: ap_http_scheme(r));
authority = apr_pstrdup(pool, r->hostname);
+ path = apr_uri_unparse(pool, &r->parsed_uri, APR_URI_UNP_OMITSITEPART);
+
+ if (!r->method || !scheme || !r->hostname || !path) {
+ return APR_EINVAL;
+ }
+
if (!ap_strchr_c(authority, ':') && r->server && r->server->port) {
apr_port_t defport = apr_uri_port_of_scheme(scheme);
if (defport != r->server->port) {
@@ -60,11 +77,23 @@ apr_status_t h2_request_rwrite(h2_reques
}
}
- status = h2_req_make(req, pool, apr_pstrdup(pool, r->method), scheme,
- authority, apr_uri_unparse(pool, &r->parsed_uri,
- APR_URI_UNP_OMITSITEPART),
- r->headers_in);
- return status;
+ req = apr_pcalloc(pool, sizeof(*req));
+ req->method = apr_pstrdup(pool, r->method);
+ req->scheme = scheme;
+ req->authority = authority;
+ req->path = path;
+ req->headers = apr_table_make(pool, 10);
+ if (r->server) {
+ req->serialize = h2_config_geti(h2_config_sget(r->server),
+ H2_CONF_SER_HEADERS);
+ }
+
+ x.pool = pool;
+ x.headers = req->headers;
+ apr_table_do(set_h1_header, &x, r->headers_in, NULL);
+
+ *preq = req;
+ return APR_SUCCESS;
}
apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool,
@@ -82,8 +111,7 @@ apr_status_t h2_request_add_header(h2_re
if (!apr_is_empty_table(req->headers)) {
ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool,
APLOGNO(02917)
- "h2_request(%d): pseudo header after request start",
- req->id);
+ "h2_request: pseudo header after request start");
return APR_EGENERAL;
}
@@ -109,8 +137,8 @@ apr_status_t h2_request_add_header(h2_re
strncpy(buffer, name, (nlen > 31)? 31 : nlen);
ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, pool,
APLOGNO(02954)
- "h2_request(%d): ignoring unknown pseudo header %s",
- req->id, buffer);
+ "h2_request: ignoring unknown pseudo header %s",
+ buffer);
}
}
else {
@@ -121,16 +149,10 @@ apr_status_t h2_request_add_header(h2_re
return status;
}
-apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool,
- int eos, int push)
+apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos)
{
const char *s;
- if (req->eoh) {
- /* already done */
- return APR_SUCCESS;
- }
-
/* rfc7540, ch. 8.1.2.3:
* - if we have :authority, it overrides any Host header
* - :authority MUST be ommited when converting h1->h2, so we
@@ -147,18 +169,8 @@ apr_status_t h2_request_end_headers(h2_r
}
s = apr_table_get(req->headers, "Content-Length");
- if (s) {
- if (inspect_clen(req, s) != APR_SUCCESS) {
- ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool,
- APLOGNO(02959)
- "h2_request(%d): content-length value not parsed: %s",
- req->id, s);
- return APR_EINVAL;
- }
- }
- else {
+ if (!s) {
/* no content-length given */
- req->content_length = -1;
if (!eos) {
/* We have not seen a content-length and have no eos,
* simulate a chunked encoding for our HTTP/1.1 infrastructure,
@@ -168,68 +180,16 @@ apr_status_t h2_request_end_headers(h2_r
apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
}
else if (apr_table_get(req->headers, "Content-Type")) {
- /* If we have a content-type, but already see eos, no more
+ /* If we have a content-type, but already seen eos, no more
* data will come. Signal a zero content length explicitly.
*/
apr_table_setn(req->headers, "Content-Length", "0");
}
}
- req->eoh = 1;
- h2_push_policy_determine(req, pool, push);
-
- /* In the presence of trailers, force behaviour of chunked encoding */
- s = apr_table_get(req->headers, "Trailer");
- if (s && s[0]) {
- req->trailers = apr_table_make(pool, 5);
- if (!req->chunked) {
- req->chunked = 1;
- apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
- }
- }
-
- return APR_SUCCESS;
-}
-
-static apr_status_t add_h1_trailer(h2_request *req, apr_pool_t *pool,
- const char *name, size_t nlen,
- const char *value, size_t vlen)
-{
- char *hname, *hvalue;
-
- if (h2_req_ignore_trailer(name, nlen)) {
- return APR_SUCCESS;
- }
-
- hname = apr_pstrndup(pool, name, nlen);
- hvalue = apr_pstrndup(pool, value, vlen);
- h2_util_camel_case_header(hname, nlen);
-
- apr_table_mergen(req->trailers, hname, hvalue);
-
return APR_SUCCESS;
}
-
-apr_status_t h2_request_add_trailer(h2_request *req, apr_pool_t *pool,
- const char *name, size_t nlen,
- const char *value, size_t vlen)
-{
- if (!req->trailers) {
- ap_log_perror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, pool, APLOGNO(03059)
- "h2_request(%d): unanounced trailers",
- req->id);
- return APR_EINVAL;
- }
- if (nlen == 0 || name[0] == ':') {
- ap_log_perror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, pool, APLOGNO(03060)
- "h2_request(%d): pseudo header in trailer",
- req->id);
- return APR_EINVAL;
- }
- return add_h1_trailer(req, pool, name, nlen, value, vlen);
-}
-
h2_request *h2_request_clone(apr_pool_t *p, const h2_request *src)
{
h2_request *dst = apr_pmemdup(p, src, sizeof(*dst));
@@ -238,25 +198,24 @@ h2_request *h2_request_clone(apr_pool_t
dst->authority = apr_pstrdup(p, src->authority);
dst->path = apr_pstrdup(p, src->path);
dst->headers = apr_table_clone(p, src->headers);
- if (src->trailers) {
- dst->trailers = apr_table_clone(p, src->trailers);
- }
return dst;
}
-request_rec *h2_request_create_rec(const h2_request *req, conn_rec *conn)
+request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
{
- request_rec *r;
- apr_pool_t *p;
int access_status = HTTP_OK;
-
- apr_pool_create(&p, conn->pool);
+ const char *rpath;
+ apr_pool_t *p;
+ request_rec *r;
+ const char *s;
+
+ apr_pool_create(&p, c->pool);
apr_pool_tag(p, "request");
r = apr_pcalloc(p, sizeof(request_rec));
- AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)conn);
+ AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)c);
r->pool = p;
- r->connection = conn;
- r->server = conn->base_server;
+ r->connection = c;
+ r->server = c->base_server;
r->user = NULL;
r->ap_auth_type = NULL;
@@ -274,9 +233,9 @@ request_rec *h2_request_create_rec(const
r->request_config = ap_create_request_config(r->pool);
/* Must be set before we run create request hook */
- r->proto_output_filters = conn->output_filters;
+ r->proto_output_filters = c->output_filters;
r->output_filters = r->proto_output_filters;
- r->proto_input_filters = conn->input_filters;
+ r->proto_input_filters = c->input_filters;
r->input_filters = r->proto_input_filters;
ap_run_create_request(r);
r->per_dir_config = r->server->lookup_defaults;
@@ -295,10 +254,10 @@ request_rec *h2_request_create_rec(const
*/
r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
- r->useragent_addr = conn->client_addr;
- r->useragent_ip = conn->client_ip;
+ r->useragent_addr = c->client_addr;
+ r->useragent_ip = c->client_ip;
- ap_run_pre_read_request(r, conn);
+ ap_run_pre_read_request(r, c);
/* Time to populate r with the data we have. */
r->request_time = req->request_time;
@@ -309,12 +268,13 @@ request_rec *h2_request_create_rec(const
r->header_only = 1;
}
- ap_parse_uri(r, req->path);
- r->protocol = "HTTP/2.0";
+ rpath = (req->path ? req->path : "");
+ ap_parse_uri(r, rpath);
+ r->protocol = (char*)"HTTP/2.0";
r->proto_num = HTTP_VERSION(2, 0);
r->the_request = apr_psprintf(r->pool, "%s %s %s",
- r->method, req->path, r->protocol);
+ r->method, rpath, r->protocol);
/* update what we think the virtual host is based on the headers we've
* now read. may update status.
@@ -327,6 +287,17 @@ request_rec *h2_request_create_rec(const
/* we may have switched to another server */
r->per_dir_config = r->server->lookup_defaults;
+ s = apr_table_get(r->headers_in, "Expect");
+ if (s && s[0]) {
+ if (ap_cstr_casecmp(s, "100-continue") == 0) {
+ r->expecting_100 = 1;
+ }
+ else {
+ r->status = HTTP_EXPECTATION_FAILED;
+ ap_send_error_response(r, 0);
+ }
+ }
+
/*
* Add the HTTP_IN filter here to ensure that ap_discard_request_body
* called by ap_die and by ap_send_error_response works correctly on
@@ -341,16 +312,16 @@ request_rec *h2_request_create_rec(const
/* Request check post hooks failed. An example of this would be a
* request for a vhost where h2 is disabled --> 421.
*/
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, conn, APLOGNO()
- "h2_request(%d): access_status=%d, request_create failed",
- req->id, access_status);
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03367)
+ "h2_request: access_status=%d, request_create failed",
+ access_status);
ap_die(access_status, r);
- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
+ ap_update_child_status(c->sbh, SERVER_BUSY_LOG, r);
ap_run_log_transaction(r);
r = NULL;
goto traceout;
}
-
+
AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method,
(char *)r->uri, (char *)r->server->defn_name,
r->status);
Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_request.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_request.h?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_request.h (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_request.h Mon Nov 14 10:26:31 2016
@@ -18,8 +18,8 @@
#include "h2.h"
-apr_status_t h2_request_rwrite(h2_request *req, apr_pool_t *pool,
- request_rec *r);
+apr_status_t h2_request_rcreate(h2_request **preq, apr_pool_t *pool,
+ request_rec *r);
apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool,
const char *name, size_t nlen,
@@ -29,8 +29,7 @@ apr_status_t h2_request_add_trailer(h2_r
const char *name, size_t nlen,
const char *value, size_t vlen);
-apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool,
- int eos, int push);
+apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos);
h2_request *h2_request_clone(apr_pool_t *p, const h2_request *src);
Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_session.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_session.c?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_session.c (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_session.c Mon Nov 14 10:26:31 2016
@@ -38,9 +38,8 @@
#include "h2_mplx.h"
#include "h2_push.h"
#include "h2_request.h"
-#include "h2_response.h"
+#include "h2_headers.h"
#include "h2_stream.h"
-#include "h2_from_h1.h"
#include "h2_task.h"
#include "h2_session.h"
#include "h2_util.h"
@@ -75,7 +74,6 @@ static apr_status_t h2_session_receive(v
const char *data, apr_size_t len,
apr_size_t *readlen);
-static int is_accepting_streams(h2_session *session);
static void dispatch_event(h2_session *session, h2_session_event_t ev,
int err, const char *msg);
@@ -84,7 +82,6 @@ apr_status_t h2_session_stream_done(h2_s
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
"h2_stream(%ld-%d): EOS bucket cleanup -> done",
session->id, stream->id);
- h2_ihash_remove(session->streams, stream->id);
h2_mplx_stream_done(session->mplx, stream);
dispatch_event(session, H2_SESSION_EV_STREAM_DONE, 0, NULL);
@@ -96,10 +93,9 @@ typedef struct stream_sel_ctx {
h2_stream *candidate;
} stream_sel_ctx;
-static int find_cleanup_stream(void *udata, void *sdata)
+static int find_cleanup_stream(h2_stream *stream, void *ictx)
{
- stream_sel_ctx *ctx = udata;
- h2_stream *stream = sdata;
+ stream_sel_ctx *ctx = ictx;
if (H2_STREAM_CLIENT_INITIATED(stream->id)) {
if (!ctx->session->local.accepting
&& stream->id > ctx->session->local.accepted_max) {
@@ -123,7 +119,7 @@ static void cleanup_streams(h2_session *
ctx.session = session;
ctx.candidate = NULL;
while (1) {
- h2_ihash_iter(session->streams, find_cleanup_stream, &ctx);
+ h2_mplx_stream_do(session->mplx, find_cleanup_stream, &ctx);
if (ctx.candidate) {
h2_session_stream_done(session, ctx.candidate);
ctx.candidate = NULL;
@@ -144,9 +140,12 @@ h2_stream *h2_session_open_stream(h2_ses
apr_pool_tag(stream_pool, "h2_stream");
stream = h2_stream_open(stream_id, stream_pool, session,
- initiated_on, req);
+ initiated_on);
nghttp2_session_set_stream_user_data(session->ngh2, stream_id, stream);
- h2_ihash_add(session->streams, stream);
+
+ if (req) {
+ h2_stream_set_request(stream, req);
+ }
if (H2_STREAM_CLIENT_INITIATED(stream_id)) {
if (stream_id > session->remote.emitted_max) {
@@ -285,11 +284,6 @@ static int on_data_chunk_recv_cb(nghttp2
int rv;
(void)flags;
- if (!is_accepting_streams(session)) {
- /* ignore */
- return 0;
- }
-
stream = get_stream(session, stream_id);
if (!stream) {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03064)
@@ -398,11 +392,6 @@ static int on_header_cb(nghttp2_session
apr_status_t status;
(void)flags;
- if (!is_accepting_streams(session)) {
- /* just ignore */
- return 0;
- }
-
stream = get_stream(session, frame->hd.stream_id);
if (!stream) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
@@ -414,7 +403,7 @@ static int on_header_cb(nghttp2_session
status = h2_stream_add_header(stream, (const char *)name, namelen,
(const char *)value, valuelen);
- if (status != APR_SUCCESS && !stream->response) {
+ if (status != APR_SUCCESS && !h2_stream_is_ready(stream)) {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
return 0;
@@ -511,7 +500,7 @@ static int on_frame_recv_cb(nghttp2_sess
session->id, (int)frame->hd.stream_id,
(int)frame->rst_stream.error_code);
stream = get_stream(session, frame->hd.stream_id);
- if (stream && stream->request && stream->request->initiated_on) {
+ if (stream && stream->initiated_on) {
++session->pushes_reset;
}
else {
@@ -519,9 +508,16 @@ static int on_frame_recv_cb(nghttp2_sess
}
break;
case NGHTTP2_GOAWAY:
- session->remote.accepted_max = frame->goaway.last_stream_id;
- session->remote.error = frame->goaway.error_code;
- dispatch_event(session, H2_SESSION_EV_REMOTE_GOAWAY, 0, NULL);
+ if (frame->goaway.error_code == 0
+ && frame->goaway.last_stream_id == ((1u << 31) - 1)) {
+ /* shutdown notice. Should not come from a client... */
+ session->remote.accepting = 0;
+ }
+ else {
+ session->remote.accepted_max = frame->goaway.last_stream_id;
+ dispatch_event(session, H2_SESSION_EV_REMOTE_GOAWAY,
+ frame->goaway.error_code, NULL);
+ }
break;
default:
if (APLOGctrace2(session->c)) {
@@ -608,6 +604,7 @@ static int on_send_data_cb(nghttp2_sessi
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c,
"h2_stream(%ld-%d): send_data_cb, reading stream",
session->id, (int)stream_id);
+ apr_brigade_cleanup(session->bbtmp);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
else if (len != length) {
@@ -615,6 +612,7 @@ static int on_send_data_cb(nghttp2_sessi
"h2_stream(%ld-%d): send_data_cb, wanted %ld bytes, "
"got %ld from stream",
session->id, (int)stream_id, (long)length, (long)len);
+ apr_brigade_cleanup(session->bbtmp);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
@@ -625,10 +623,11 @@ static int on_send_data_cb(nghttp2_sessi
}
status = h2_conn_io_pass(&session->io, session->bbtmp);
-
apr_brigade_cleanup(session->bbtmp);
+
if (status == APR_SUCCESS) {
- stream->data_frames_sent++;
+ stream->out_data_frames++;
+ stream->out_data_octets += length;
return 0;
}
else {
@@ -658,6 +657,27 @@ static int on_frame_send_cb(nghttp2_sess
return 0;
}
+#ifdef H2_NG2_INVALID_HEADER_CB
+static int on_invalid_header_cb(nghttp2_session *ngh2,
+ const nghttp2_frame *frame,
+ const uint8_t *name, size_t namelen,
+ const uint8_t *value, size_t valuelen,
+ uint8_t flags, void *user_data)
+{
+ h2_session *session = user_data;
+ if (APLOGcdebug(session->c)) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03456)
+ "h2_session(%ld-%d): denying stream with invalid header "
+ "'%s: %s'", session->id, (int)frame->hd.stream_id,
+ apr_pstrndup(session->pool, (const char *)name, namelen),
+ apr_pstrndup(session->pool, (const char *)value, valuelen));
+ }
+ return nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE,
+ frame->hd.stream_id,
+ NGHTTP2_PROTOCOL_ERROR);
+}
+#endif
+
#define NGH2_SET_CALLBACK(callbacks, name, fn)\
nghttp2_session_callbacks_set_##name##_callback(callbacks, fn)
@@ -680,15 +700,16 @@ static apr_status_t init_callbacks(conn_
NGH2_SET_CALLBACK(*pcb, on_header, on_header_cb);
NGH2_SET_CALLBACK(*pcb, send_data, on_send_data_cb);
NGH2_SET_CALLBACK(*pcb, on_frame_send, on_frame_send_cb);
-
+#ifdef H2_NG2_INVALID_HEADER_CB
+ NGH2_SET_CALLBACK(*pcb, on_invalid_header, on_invalid_header_cb);
+#endif
return APR_SUCCESS;
}
static void h2_session_destroy(h2_session *session)
{
- AP_DEBUG_ASSERT(session);
+ ap_assert(session);
- h2_ihash_clear(session->streams);
if (session->mplx) {
h2_mplx_set_consumed_cb(session->mplx, NULL, NULL);
h2_mplx_release_and_join(session->mplx, session->iowait);
@@ -714,12 +735,35 @@ static void h2_session_destroy(h2_sessio
}
}
+static apr_status_t h2_session_shutdown_notice(h2_session *session)
+{
+ apr_status_t status;
+
+ ap_assert(session);
+ if (!session->local.accepting) {
+ return APR_SUCCESS;
+ }
+
+ nghttp2_submit_shutdown_notice(session->ngh2);
+ session->local.accepting = 0;
+ status = nghttp2_session_send(session->ngh2);
+ if (status == APR_SUCCESS) {
+ status = h2_conn_io_flush(&session->io);
+ }
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03457)
+ "session(%ld): sent shutdown notice", session->id);
+ return status;
+}
+
static apr_status_t h2_session_shutdown(h2_session *session, int error,
const char *msg, int force_close)
{
apr_status_t status = APR_SUCCESS;
- AP_DEBUG_ASSERT(session);
+ ap_assert(session);
+ if (session->local.shutdown) {
+ return APR_SUCCESS;
+ }
if (!msg && error) {
msg = nghttp2_strerror(error);
}
@@ -743,6 +787,8 @@ static apr_status_t h2_session_shutdown(
nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE,
session->local.accepted_max,
error, (uint8_t*)msg, msg? strlen(msg):0);
+ session->local.accepting = 0;
+ session->local.shutdown = 1;
status = nghttp2_session_send(session->ngh2);
if (status == APR_SUCCESS) {
status = h2_conn_io_flush(&session->io);
@@ -753,6 +799,7 @@ static apr_status_t h2_session_shutdown(
dispatch_event(session, H2_SESSION_EV_LOCAL_GOAWAY, error, msg);
if (force_close) {
+ apr_brigade_cleanup(session->bbtmp);
h2_mplx_abort(session->mplx);
}
@@ -772,8 +819,7 @@ static apr_status_t session_pool_cleanup
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
"session(%ld): pool_cleanup", session->id);
- if (session->state != H2_SESSION_ST_DONE
- && session->state != H2_SESSION_ST_LOCAL_SHUTDOWN) {
+ if (session->state != H2_SESSION_ST_DONE) {
/* Not good. The connection is being torn down and we have
* not sent a goaway. This is considered a protocol error and
* the client has to assume that any streams "in flight" may have
@@ -877,8 +923,6 @@ static h2_session *h2_session_create_int
return NULL;
}
- session->streams = h2_ihash_create(session->pool,
- offsetof(h2_stream, id));
session->mplx = h2_mplx_create(c, session->pool, session->config,
session->s->timeout, workers);
@@ -984,7 +1028,7 @@ static apr_status_t h2_session_start(h2_
size_t slen;
int win_size;
- AP_DEBUG_ASSERT(session);
+ ap_assert(session);
/* Start the conversation by submitting our SETTINGS frame */
*rv = 0;
if (session->r) {
@@ -1031,7 +1075,7 @@ static apr_status_t h2_session_start(h2_
return status;
}
- status = h2_stream_set_request(stream, session->r);
+ status = h2_stream_set_request_rec(stream, session->r);
if (status != APR_SUCCESS) {
return status;
}
@@ -1087,6 +1131,10 @@ static apr_status_t h2_session_start(h2_
return status;
}
+static apr_status_t on_stream_headers(h2_session *session, h2_stream *stream,
+ h2_headers *headers, apr_off_t len,
+ int eos);
+
static ssize_t stream_data_cb(nghttp2_session *ng2s,
int32_t stream_id,
uint8_t *buf,
@@ -1100,7 +1148,7 @@ static ssize_t stream_data_cb(nghttp2_se
int eos = 0;
apr_status_t status;
h2_stream *stream;
- AP_DEBUG_ASSERT(session);
+ ap_assert(session);
/* The session wants to send more DATA for the stream. We need
* to find out how much of the requested length we can send without
@@ -1120,10 +1168,8 @@ static ssize_t stream_data_cb(nghttp2_se
session->id, (int)stream_id);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
-
- AP_DEBUG_ASSERT(!h2_stream_is_suspended(stream));
-
- status = h2_stream_out_prepare(stream, &nread, &eos);
+
+ status = h2_stream_out_prepare(stream, &nread, &eos, NULL);
if (nread) {
*data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
}
@@ -1142,7 +1188,6 @@ static ssize_t stream_data_cb(nghttp2_se
* it. Remember at our h2_stream that we need to do this.
*/
nread = 0;
- h2_mplx_suspend_stream(session->mplx, stream->id);
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03071)
"h2_stream(%ld-%d): suspending",
session->id, (int)stream_id);
@@ -1157,34 +1202,11 @@ static ssize_t stream_data_cb(nghttp2_se
}
if (eos) {
- apr_table_t *trailers = h2_stream_get_trailers(stream);
- if (trailers && !apr_is_empty_table(trailers)) {
- h2_ngheader *nh;
- int rv;
-
- nh = h2_util_ngheader_make(stream->pool, trailers);
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03072)
- "h2_stream(%ld-%d): submit %d trailers",
- session->id, (int)stream_id,(int) nh->nvlen);
- rv = nghttp2_submit_trailer(ng2s, stream->id, nh->nv, nh->nvlen);
- if (rv < 0) {
- nread = rv;
- }
- *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
- }
-
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
}
-
return (ssize_t)nread;
}
-typedef struct {
- nghttp2_nv *nv;
- size_t nvlen;
- size_t offset;
-} nvctx_t;
-
struct h2_stream *h2_session_push(h2_session *session, h2_stream *is,
h2_push *push)
{
@@ -1260,8 +1282,9 @@ apr_status_t h2_session_set_prio(h2_sess
s_parent = nghttp2_stream_get_parent(s);
if (s_parent) {
nghttp2_priority_spec ps;
- int id_parent, id_grandpa, w_parent, w, rv = 0;
- char *ptype = "AFTER";
+ int id_parent, id_grandpa, w_parent, w;
+ int rv = 0;
+ const char *ptype = "AFTER";
h2_dependency dep = prio->dependency;
id_parent = nghttp2_stream_get_stream_id(s_parent);
@@ -1379,63 +1402,50 @@ static apr_status_t h2_session_send(h2_s
}
/**
- * A stream was resumed as new output data arrived.
- */
-static apr_status_t on_stream_resume(void *ctx, int stream_id)
-{
- h2_session *session = ctx;
- h2_stream *stream = get_stream(session, stream_id);
- apr_status_t status = APR_SUCCESS;
-
- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
- "h2_stream(%ld-%d): on_resume", session->id, stream_id);
- if (stream) {
- int rv = nghttp2_session_resume_data(session->ngh2, stream_id);
- session->have_written = 1;
- ap_log_cerror(APLOG_MARK, nghttp2_is_fatal(rv)?
- APLOG_ERR : APLOG_DEBUG, 0, session->c,
- APLOGNO(02936)
- "h2_stream(%ld-%d): resuming %s",
- session->id, stream->id, rv? nghttp2_strerror(rv) : "");
- }
- return status;
-}
-
-/**
- * A response for the stream is ready.
+ * headers for the stream are ready.
*/
-static apr_status_t on_stream_response(void *ctx, int stream_id)
+static apr_status_t on_stream_headers(h2_session *session, h2_stream *stream,
+ h2_headers *headers, apr_off_t len,
+ int eos)
{
- h2_session *session = ctx;
- h2_stream *stream = get_stream(session, stream_id);
apr_status_t status = APR_SUCCESS;
- h2_response *response;
int rv = 0;
- AP_DEBUG_ASSERT(session);
+ ap_assert(session);
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
- "h2_stream(%ld-%d): on_response", session->id, stream_id);
- if (!stream) {
- return APR_NOTFOUND;
- }
-
- response = h2_stream_get_response(stream);
- AP_DEBUG_ASSERT(response || stream->rst_error);
-
- if (stream->submitted) {
- rv = NGHTTP2_PROTOCOL_ERROR;
+ "h2_stream(%ld-%d): on_headers", session->id, stream->id);
+ if (headers->status < 100) {
+ int err = H2_STREAM_RST(stream, headers->status);
+ rv = nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE,
+ stream->id, err);
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+ "h2_stream(%ld-%d): unpexected header status %d, stream rst",
+ session->id, stream->id, headers->status);
+ goto leave;
+ }
+ else if (stream->has_response) {
+ h2_ngheader *nh;
+
+ nh = h2_util_ngheader_make(stream->pool, headers->headers);
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03072)
+ "h2_stream(%ld-%d): submit %d trailers",
+ session->id, (int)stream->id,(int) nh->nvlen);
+ rv = nghttp2_submit_trailer(session->ngh2, stream->id, nh->nv, nh->nvlen);
+ goto leave;
}
- else if (response && response->headers) {
+ else {
nghttp2_data_provider provider, *pprovider = NULL;
h2_ngheader *ngh;
+ apr_table_t *hout;
const h2_priority *prio;
+ const char *note;
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03073)
"h2_stream(%ld-%d): submit response %d, REMOTE_WINDOW_SIZE=%u",
- session->id, stream->id, response->http_status,
+ session->id, stream->id, headers->status,
(unsigned int)nghttp2_session_get_stream_remote_window_size(session->ngh2, stream->id));
- if (response->content_length != 0) {
+ if (!eos || len > 0) {
memset(&provider, 0, sizeof(provider));
provider.source.fd = stream->id;
provider.read_callback = stream_data_cb;
@@ -1457,45 +1467,48 @@ static apr_status_t on_stream_response(v
* as the client, having this resource in its cache, might
* also have the pushed ones as well.
*/
- if (stream->request && !stream->request->initiated_on
- && H2_HTTP_2XX(response->http_status)
+ if (!stream->initiated_on
+ && h2_headers_are_response(headers)
+ && H2_HTTP_2XX(headers->status)
&& h2_session_push_enabled(session)) {
- h2_stream_submit_pushes(stream);
+ h2_stream_submit_pushes(stream, headers);
}
- prio = h2_stream_get_priority(stream);
+ prio = h2_stream_get_priority(stream, headers);
if (prio) {
h2_session_set_prio(session, stream, prio);
- /* no showstopper if that fails for some reason */
}
- ngh = h2_util_ngheader_make_res(stream->pool, response->http_status,
- response->headers);
- rv = nghttp2_submit_response(session->ngh2, response->stream_id,
+ hout = headers->headers;
+ note = apr_table_get(headers->notes, H2_FILTER_DEBUG_NOTE);
+ if (note && !strcmp("on", note)) {
+ int32_t connFlowIn, connFlowOut;
+
+ connFlowIn = nghttp2_session_get_effective_local_window_size(session->ngh2);
+ connFlowOut = nghttp2_session_get_remote_window_size(session->ngh2);
+ hout = apr_table_clone(stream->pool, hout);
+ apr_table_setn(hout, "conn-flow-in",
+ apr_itoa(stream->pool, connFlowIn));
+ apr_table_setn(hout, "conn-flow-out",
+ apr_itoa(stream->pool, connFlowOut));
+ }
+
+ ngh = h2_util_ngheader_make_res(stream->pool, headers->status, hout);
+ rv = nghttp2_submit_response(session->ngh2, stream->id,
ngh->nv, ngh->nvlen, pprovider);
- }
- else {
- int err = H2_STREAM_RST(stream, H2_ERR_PROTOCOL_ERROR);
+ stream->has_response = h2_headers_are_response(headers);
+ session->have_written = 1;
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03074)
- "h2_stream(%ld-%d): RST_STREAM, err=%d",
- session->id, stream->id, err);
-
- rv = nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE,
- stream->id, err);
- }
-
- stream->submitted = 1;
- session->have_written = 1;
-
- if (stream->request && stream->request->initiated_on) {
- ++session->pushes_submitted;
- }
- else {
- ++session->responses_submitted;
+ if (stream->initiated_on) {
+ ++session->pushes_submitted;
+ }
+ else {
+ ++session->responses_submitted;
+ }
}
+leave:
if (nghttp2_is_fatal(rv)) {
status = APR_EGENERAL;
dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, rv, nghttp2_strerror(rv));
@@ -1520,6 +1533,56 @@ static apr_status_t on_stream_response(v
return status;
}
+/**
+ * A stream was resumed as new response/output data arrived.
+ */
+static apr_status_t on_stream_resume(void *ctx, h2_stream *stream)
+{
+ h2_session *session = ctx;
+ apr_status_t status = APR_EAGAIN;
+ int rv;
+ apr_off_t len = 0;
+ int eos = 0;
+ h2_headers *headers;
+
+ ap_assert(stream);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
+ "h2_stream(%ld-%d): on_resume", session->id, stream->id);
+
+send_headers:
+ headers = NULL;
+ status = h2_stream_out_prepare(stream, &len, &eos, &headers);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, session->c,
+ "h2_stream(%ld-%d): prepared len=%ld, eos=%d",
+ session->id, stream->id, (long)len, eos);
+ if (headers) {
+ status = on_stream_headers(session, stream, headers, len, eos);
+ if (status != APR_SUCCESS || stream->rst_error) {
+ return status;
+ }
+ goto send_headers;
+ }
+ else if (status != APR_EAGAIN) {
+ if (!stream->has_response) {
+ int err = H2_STREAM_RST(stream, H2_ERR_PROTOCOL_ERROR);
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03466)
+ "h2_stream(%ld-%d): no response, RST_STREAM, err=%d",
+ session->id, stream->id, err);
+ nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE,
+ stream->id, err);
+ return APR_SUCCESS;
+ }
+ rv = nghttp2_session_resume_data(session->ngh2, stream->id);
+ session->have_written = 1;
+ ap_log_cerror(APLOG_MARK, nghttp2_is_fatal(rv)?
+ APLOG_ERR : APLOG_DEBUG, 0, session->c,
+ APLOGNO(02936)
+ "h2_stream(%ld-%d): resuming %s",
+ session->id, stream->id, rv? nghttp2_strerror(rv) : "");
+ }
+ return status;
+}
+
static apr_status_t h2_session_receive(void *ctx, const char *data,
apr_size_t len, apr_size_t *readlen)
{
@@ -1533,7 +1596,7 @@ static apr_status_t h2_session_receive(v
n = nghttp2_session_mem_recv(session->ngh2, (const uint8_t *)data, len);
if (n < 0) {
if (nghttp2_is_fatal((int)n)) {
- dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, (int)n, nghttp2_strerror(n));
+ dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, (int)n, nghttp2_strerror((int)n));
return APR_EGENERAL;
}
}
@@ -1566,7 +1629,7 @@ static apr_status_t h2_session_read(h2_s
/* successful read, reset our idle timers */
rstatus = APR_SUCCESS;
if (block) {
- /* successfull blocked read, try unblocked to
+ /* successful blocked read, try unblocked to
* get more. */
block = 0;
}
@@ -1600,9 +1663,6 @@ static apr_status_t h2_session_read(h2_s
* status. */
return rstatus;
}
- 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,
@@ -1613,48 +1673,12 @@ static apr_status_t h2_session_read(h2_s
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 const char *StateNames[] = {
"INIT", /* H2_SESSION_ST_INIT */
"DONE", /* H2_SESSION_ST_DONE */
"IDLE", /* H2_SESSION_ST_IDLE */
"BUSY", /* H2_SESSION_ST_BUSY */
"WAIT", /* H2_SESSION_ST_WAIT */
- "LSHUTDOWN", /* H2_SESSION_ST_LOCAL_SHUTDOWN */
- "RSHUTDOWN", /* H2_SESSION_ST_REMOTE_SHUTDOWN */
};
static const char *state_name(h2_session_state state)
@@ -1665,18 +1689,6 @@ static const char *state_name(h2_session
return StateNames[state];
}
-static int is_accepting_streams(h2_session *session)
-{
- switch (session->state) {
- case H2_SESSION_ST_IDLE:
- case H2_SESSION_ST_BUSY:
- case H2_SESSION_ST_WAIT:
- return 1;
- default:
- return 0;
- }
-}
-
static void update_child_status(h2_session *session, int status, const char *msg)
{
/* Assume that we also change code/msg when something really happened and
@@ -1698,7 +1710,12 @@ static void update_child_status(h2_sessi
static void transit(h2_session *session, const char *action, h2_session_state nstate)
{
if (session->state != nstate) {
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03078)
+ int loglvl = APLOG_DEBUG;
+ if ((session->state == H2_SESSION_ST_BUSY && nstate == H2_SESSION_ST_WAIT)
+ || (session->state == H2_SESSION_ST_WAIT && nstate == H2_SESSION_ST_BUSY)){
+ loglvl = APLOG_TRACE1;
+ }
+ ap_log_cerror(APLOG_MARK, loglvl, 0, session->c, APLOGNO(03078)
"h2_session(%ld): transit [%s] -- %s --> [%s]", session->id,
state_name(session->state), action, state_name(nstate));
session->state = nstate;
@@ -1708,12 +1725,6 @@ static void transit(h2_session *session,
SERVER_BUSY_KEEPALIVE
: SERVER_BUSY_READ), "idle");
break;
- case H2_SESSION_ST_REMOTE_SHUTDOWN:
- update_child_status(session, SERVER_CLOSING, "remote goaway");
- break;
- case H2_SESSION_ST_LOCAL_SHUTDOWN:
- update_child_status(session, SERVER_CLOSING, "local goaway");
- break;
case H2_SESSION_ST_DONE:
update_child_status(session, SERVER_CLOSING, "done");
break;
@@ -1738,39 +1749,22 @@ static void h2_session_ev_init(h2_sessio
static void h2_session_ev_local_goaway(h2_session *session, int arg, const char *msg)
{
- session->local.accepting = 0;
cleanup_streams(session);
- switch (session->state) {
- case H2_SESSION_ST_LOCAL_SHUTDOWN:
- /* already did that? */
- break;
- case H2_SESSION_ST_IDLE:
- case H2_SESSION_ST_REMOTE_SHUTDOWN:
- /* all done */
- transit(session, "local goaway", H2_SESSION_ST_DONE);
- break;
- default:
- transit(session, "local goaway", H2_SESSION_ST_LOCAL_SHUTDOWN);
- break;
+ if (!session->remote.shutdown) {
+ update_child_status(session, SERVER_CLOSING, "local goaway");
}
+ transit(session, "local goaway", H2_SESSION_ST_DONE);
}
static void h2_session_ev_remote_goaway(h2_session *session, int arg, const char *msg)
{
- session->remote.accepting = 0;
- cleanup_streams(session);
- switch (session->state) {
- case H2_SESSION_ST_REMOTE_SHUTDOWN:
- /* already received that? */
- break;
- case H2_SESSION_ST_IDLE:
- case H2_SESSION_ST_LOCAL_SHUTDOWN:
- /* all done */
- transit(session, "remote goaway", H2_SESSION_ST_DONE);
- break;
- default:
- transit(session, "remote goaway", H2_SESSION_ST_REMOTE_SHUTDOWN);
- break;
+ if (!session->remote.shutdown) {
+ session->remote.error = arg;
+ session->remote.accepting = 0;
+ session->remote.shutdown = 1;
+ cleanup_streams(session);
+ update_child_status(session, SERVER_CLOSING, "remote goaway");
+ transit(session, "remote goaway", H2_SESSION_ST_DONE);
}
}
@@ -1779,7 +1773,6 @@ static void h2_session_ev_conn_error(h2_
switch (session->state) {
case H2_SESSION_ST_INIT:
case H2_SESSION_ST_DONE:
- case H2_SESSION_ST_LOCAL_SHUTDOWN:
/* just leave */
transit(session, "conn error", H2_SESSION_ST_DONE);
break;
@@ -1794,31 +1787,18 @@ static void h2_session_ev_conn_error(h2_
static void h2_session_ev_proto_error(h2_session *session, int arg, const char *msg)
{
- switch (session->state) {
- case H2_SESSION_ST_DONE:
- case H2_SESSION_ST_LOCAL_SHUTDOWN:
- /* just leave */
- transit(session, "proto error", H2_SESSION_ST_DONE);
- break;
-
- default:
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03402)
- "h2_session(%ld): proto error -> shutdown", session->id);
- h2_session_shutdown(session, arg, msg, 0);
- break;
+ if (!session->local.shutdown) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03402)
+ "h2_session(%ld): proto error -> shutdown", session->id);
+ h2_session_shutdown(session, arg, msg, 0);
}
}
static void h2_session_ev_conn_timeout(h2_session *session, int arg, const char *msg)
{
- switch (session->state) {
- case H2_SESSION_ST_LOCAL_SHUTDOWN:
- transit(session, "conn timeout", H2_SESSION_ST_DONE);
- break;
- default:
- h2_session_shutdown(session, arg, msg, 1);
- transit(session, "conn timeout", H2_SESSION_ST_DONE);
- break;
+ transit(session, msg, H2_SESSION_ST_DONE);
+ if (!session->local.shutdown) {
+ h2_session_shutdown(session, arg, msg, 1);
}
}
@@ -1826,8 +1806,6 @@ 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 to READ, nothing to WRITE on the master connection.
* Possible causes:
* - we wait for the client to send us sth
@@ -1837,9 +1815,9 @@ static void h2_session_ev_no_io(h2_sessi
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
"h2_session(%ld): NO_IO event, %d streams open",
session->id, session->open_streams);
+ h2_conn_io_flush(&session->io);
if (session->open_streams > 0) {
- if (has_unsubmitted_streams(session)
- || has_suspended_streams(session)) {
+ if (h2_mplx_awaits_data(session->mplx)) {
/* waiting for at least one stream to produce data */
transit(session, "no io", H2_SESSION_ST_WAIT);
}
@@ -1860,7 +1838,7 @@ static void h2_session_ev_no_io(h2_sessi
}
}
}
- else if (is_accepting_streams(session)) {
+ else if (session->local.accepting) {
/* When we have no streams, but accept new, switch to idle */
apr_time_t now = apr_time_now();
transit(session, "no io (keepalive)", H2_SESSION_ST_IDLE);
@@ -1923,26 +1901,17 @@ static void h2_session_ev_mpm_stopping(h
{
switch (session->state) {
case H2_SESSION_ST_DONE:
- case H2_SESSION_ST_LOCAL_SHUTDOWN:
/* nop */
break;
default:
- h2_session_shutdown(session, arg, msg, 0);
+ h2_session_shutdown_notice(session);
break;
}
}
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:
- h2_session_shutdown(session, arg, msg, 1);
- break;
- }
+ h2_session_shutdown(session, arg, msg, 1);
}
static void h2_session_ev_stream_open(h2_session *session, int arg, const char *msg)
@@ -2030,6 +1999,7 @@ static void dispatch_event(h2_session *s
}
if (session->state == H2_SESSION_ST_DONE) {
+ apr_brigade_cleanup(session->bbtmp);
h2_mplx_abort(session->mplx);
}
}
@@ -2052,14 +2022,14 @@ apr_status_t h2_session_process(h2_sessi
c->cs->state = CONN_STATE_WRITE_COMPLETION;
}
- while (1) {
+ while (session->state != H2_SESSION_ST_DONE) {
trace = APLOGctrace3(c);
session->have_read = session->have_written = 0;
- if (!ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
+ if (session->local.accepting
+ && !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;
}
}
@@ -2175,12 +2145,18 @@ apr_status_t h2_session_process(h2_sessi
}
/* continue reading handling */
}
+ else if (APR_STATUS_IS_ECONNABORTED(status)
+ || APR_STATUS_IS_ECONNRESET(status)
+ || APR_STATUS_IS_EOF(status)
+ || APR_STATUS_IS_EBADF(status)) {
+ ap_log_cerror( APLOG_MARK, APLOG_TRACE3, status, c,
+ "h2_session(%ld): input gone", session->id);
+ dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
+ }
else {
- if (trace) {
- ap_log_cerror( APLOG_MARK, APLOG_TRACE3, status, c,
- "h2_session(%ld): idle(1 sec timeout) "
- "read failed", session->id);
- }
+ ap_log_cerror( APLOG_MARK, APLOG_TRACE3, status, c,
+ "h2_session(%ld): idle(1 sec timeout) "
+ "read failed", session->id);
dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, "error");
}
}
@@ -2188,8 +2164,6 @@ apr_status_t h2_session_process(h2_sessi
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(session->c->sbh, SERVER_BUSY_READ, NULL);
h2_filter_cin_timeout_set(session->cin, session->s->timeout);
@@ -2213,7 +2187,6 @@ apr_status_t h2_session_process(h2_sessi
/* trigger window updates, stream resumes and submits */
status = h2_mplx_dispatch_master_events(session->mplx,
on_stream_resume,
- on_stream_response,
session);
if (status != APR_SUCCESS) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, c,
@@ -2272,7 +2245,7 @@ apr_status_t h2_session_process(h2_sessi
else if (APR_STATUS_IS_TIMEUP(status)) {
/* go back to checking all inputs again */
transit(session, "wait cycle", session->local.accepting?
- H2_SESSION_ST_BUSY : H2_SESSION_ST_LOCAL_SHUTDOWN);
+ H2_SESSION_ST_BUSY : H2_SESSION_ST_DONE);
}
else if (APR_STATUS_IS_ECONNRESET(status)
|| APR_STATUS_IS_ECONNABORTED(status)) {
@@ -2288,10 +2261,6 @@ apr_status_t h2_session_process(h2_sessi
}
break;
- case H2_SESSION_ST_DONE:
- status = APR_EOF;
- goto out;
-
default:
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
APLOGNO(03080)
@@ -2321,11 +2290,12 @@ out:
&& (APR_STATUS_IS_EOF(status)
|| APR_STATUS_IS_ECONNRESET(status)
|| APR_STATUS_IS_ECONNABORTED(status))) {
- dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
- }
+ dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
+ }
- status = (session->state == H2_SESSION_ST_DONE)? APR_EOF : APR_SUCCESS;
+ status = APR_SUCCESS;
if (session->state == H2_SESSION_ST_DONE) {
+ status = APR_EOF;
if (!session->eoc_written) {
session->eoc_written = 1;
h2_conn_io_write_eoc(&session->io, session);
@@ -2339,6 +2309,7 @@ apr_status_t h2_session_pre_close(h2_ses
{
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");
+ dispatch_event(session, H2_SESSION_EV_PRE_CLOSE, 0,
+ (session->state == H2_SESSION_ST_IDLE)? "timeout" : NULL);
return APR_SUCCESS;
}
Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_session.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_session.h?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_session.h (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_session.h Mon Nov 14 10:26:31 2016
@@ -31,7 +31,7 @@
* New incoming HEADER frames are converted into a h2_stream+h2_task instance
* that both represent a HTTP/2 stream, but may have separate lifetimes. This
* allows h2_task to be scheduled in other threads without semaphores
- * all over the place. It allows task memory to be freed independant of
+ * all over the place. It allows task memory to be freed independent of
* session lifetime and sessions may close down while tasks are still running.
*
*
@@ -49,7 +49,6 @@ struct h2_mplx;
struct h2_priority;
struct h2_push;
struct h2_push_diary;
-struct h2_response;
struct h2_session;
struct h2_stream;
struct h2_task;
@@ -87,7 +86,6 @@ typedef struct h2_session {
struct h2_workers *workers; /* for executing stream tasks */
struct h2_filter_cin *cin; /* connection input filter context */
h2_conn_io io; /* io on httpd conn filters */
- struct h2_ihash_t *streams; /* streams handled by this session */
struct nghttp2_session *ngh2; /* the nghttp2 session (internal use) */
h2_session_state state; /* state session is in */
@@ -100,7 +98,7 @@ typedef struct h2_session {
unsigned int flush : 1; /* flushing output necessary */
unsigned int have_read : 1; /* session has read client data */
unsigned int have_written : 1; /* session did write data to client */
- apr_interval_time_t wait_us; /* timout during BUSY_WAIT state, micro secs */
+ apr_interval_time_t wait_us; /* timeout during BUSY_WAIT state, micro secs */
struct h2_push_diary *push_diary; /* remember pushes, avoid duplicates */
@@ -156,7 +154,7 @@ h2_session *h2_session_rcreate(request_r
/**
* Process the given HTTP/2 session until it is ended or a fatal
- * error occured.
+ * error occurred.
*
* @param session the sessionm to process
*/
@@ -175,7 +173,7 @@ apr_status_t h2_session_pre_close(h2_ses
void h2_session_eoc_callback(h2_session *session);
/**
- * Called when a serious error occured and the session needs to terminate
+ * Called when a serious error occurred and the session needs to terminate
* without further connection io.
* @param session the session to abort
* @param reason the apache status that caused the abort
@@ -187,11 +185,6 @@ void h2_session_abort(h2_session *sessio
*/
void h2_session_close(h2_session *session);
-/* Start submitting the response to a stream request. This is possible
- * once we have all the response headers. */
-apr_status_t h2_session_handle_response(h2_session *session,
- struct h2_stream *stream);
-
/**
* Create and register a new stream under the given id.
*
Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_stream.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_stream.c?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_stream.c (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_stream.c Mon Nov 14 10:26:31 2016
@@ -16,6 +16,8 @@
#include <assert.h>
#include <stddef.h>
+#include <apr_strings.h>
+
#include <httpd.h>
#include <http_core.h>
#include <http_connection.h>
@@ -29,11 +31,10 @@
#include "h2_conn.h"
#include "h2_config.h"
#include "h2_h2.h"
-#include "h2_filter.h"
#include "h2_mplx.h"
#include "h2_push.h"
#include "h2_request.h"
-#include "h2_response.h"
+#include "h2_headers.h"
#include "h2_session.h"
#include "h2_stream.h"
#include "h2_task.h"
@@ -53,7 +54,7 @@ static int state_transition[][7] = {
/*CL*/{ 1, 1, 0, 0, 1, 1, 1 },
};
-static void H2_STREAM_OUT_LOG(int lvl, h2_stream *s, char *tag)
+static void H2_STREAM_OUT_LOG(int lvl, h2_stream *s, const char *tag)
{
if (APLOG_C_IS_LEVEL(s->session->c, lvl)) {
conn_rec *c = s->session->c;
@@ -61,9 +62,9 @@ static void H2_STREAM_OUT_LOG(int lvl, h
const char *line = "(null)";
apr_size_t len, bmax = sizeof(buffer)/sizeof(buffer[0]);
- len = h2_util_bb_print(buffer, bmax, tag, "", s->buffer);
- ap_log_cerror(APLOG_MARK, lvl, 0, c, "bb_dump(%ld-%d): %s",
- c->id, s->id, len? buffer : line);
+ len = h2_util_bb_print(buffer, bmax, tag, "", s->out_buffer);
+ ap_log_cerror(APLOG_MARK, lvl, 0, c, "bb_dump(%s): %s",
+ c->log_id, len? buffer : line);
}
}
@@ -150,15 +151,29 @@ static int output_open(h2_stream *stream
}
}
+static void prep_output(h2_stream *stream) {
+ conn_rec *c = stream->session->c;
+ if (!stream->out_buffer) {
+ stream->out_buffer = apr_brigade_create(stream->pool, c->bucket_alloc);
+ }
+}
+
+static void prepend_response(h2_stream *stream, h2_headers *response)
+{
+ conn_rec *c = stream->session->c;
+ apr_bucket *b;
+
+ prep_output(stream);
+ b = h2_bucket_headers_create(c->bucket_alloc, response);
+ APR_BRIGADE_INSERT_HEAD(stream->out_buffer, b);
+}
+
static apr_status_t stream_pool_cleanup(void *ctx)
{
h2_stream *stream = ctx;
apr_status_t status;
- if (stream->input) {
- h2_beam_destroy(stream->input);
- stream->input = NULL;
- }
+ ap_assert(stream->can_be_cleaned);
if (stream->files) {
apr_file_t *file;
int i;
@@ -175,29 +190,21 @@ static apr_status_t stream_pool_cleanup(
}
h2_stream *h2_stream_open(int id, apr_pool_t *pool, h2_session *session,
- int initiated_on, const h2_request *creq)
+ int initiated_on)
{
- h2_request *req;
h2_stream *stream = apr_pcalloc(pool, sizeof(h2_stream));
- stream->id = id;
- stream->state = H2_STREAM_ST_IDLE;
- stream->pool = pool;
- stream->session = session;
- set_state(stream, H2_STREAM_ST_OPEN);
-
- if (creq) {
- /* take it into out pool and assure correct id's */
- req = h2_request_clone(pool, creq);
- req->id = id;
- req->initiated_on = initiated_on;
- }
- else {
- req = h2_req_create(id, pool,
- h2_config_geti(session->config, H2_CONF_SER_HEADERS));
- }
- stream->request = req;
+ stream->id = id;
+ stream->initiated_on = initiated_on;
+ stream->created = apr_time_now();
+ stream->state = H2_STREAM_ST_IDLE;
+ stream->pool = pool;
+ stream->session = session;
+
+ h2_beam_create(&stream->input, pool, id, "input", H2_BEAM_OWNER_SEND, 0);
+ h2_beam_create(&stream->output, pool, id, "output", H2_BEAM_OWNER_RECV, 0);
+ set_state(stream, H2_STREAM_ST_OPEN);
apr_pool_cleanup_register(pool, stream, stream_pool_cleanup,
apr_pool_cleanup_null);
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03082)
@@ -207,31 +214,35 @@ h2_stream *h2_stream_open(int id, apr_po
void h2_stream_cleanup(h2_stream *stream)
{
- AP_DEBUG_ASSERT(stream);
- if (stream->buffer) {
- apr_brigade_cleanup(stream->buffer);
- }
- if (stream->input) {
- apr_status_t status;
- status = h2_beam_shutdown(stream->input, APR_NONBLOCK_READ, 1);
- if (status == APR_EAGAIN) {
- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c,
- "h2_stream(%ld-%d): wait on input shutdown",
- stream->session->id, stream->id);
- status = h2_beam_shutdown(stream->input, APR_BLOCK_READ, 1);
- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, stream->session->c,
- "h2_stream(%ld-%d): input shutdown returned",
- stream->session->id, stream->id);
- }
+ apr_status_t status;
+
+ ap_assert(stream);
+ if (stream->out_buffer) {
+ /* remove any left over output buckets that may still have
+ * references into request pools */
+ apr_brigade_cleanup(stream->out_buffer);
+ }
+ h2_beam_abort(stream->input);
+ status = h2_beam_wait_empty(stream->input, APR_NONBLOCK_READ);
+ if (status == APR_EAGAIN) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c,
+ "h2_stream(%ld-%d): wait on input drain",
+ stream->session->id, stream->id);
+ status = h2_beam_wait_empty(stream->input, APR_BLOCK_READ);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, stream->session->c,
+ "h2_stream(%ld-%d): input drain returned",
+ stream->session->id, stream->id);
}
}
void h2_stream_destroy(h2_stream *stream)
{
- AP_DEBUG_ASSERT(stream);
+ ap_assert(stream);
+ ap_assert(!h2_mplx_stream_get(stream->session->mplx, stream->id));
ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, stream->session->c,
"h2_stream(%ld-%d): destroy",
stream->session->id, stream->id);
+ stream->can_be_cleaned = 1;
if (stream->pool) {
apr_pool_destroy(stream->pool);
}
@@ -255,42 +266,75 @@ void h2_stream_rst(h2_stream *stream, in
stream->rst_error = error_code;
close_input(stream);
close_output(stream);
+ if (stream->out_buffer) {
+ apr_brigade_cleanup(stream->out_buffer);
+ }
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
"h2_stream(%ld-%d): reset, error=%d",
stream->session->id, stream->id, error_code);
}
-struct h2_response *h2_stream_get_response(h2_stream *stream)
-{
- return stream->response;
-}
-
-apr_status_t h2_stream_set_request(h2_stream *stream, request_rec *r)
+apr_status_t h2_stream_set_request_rec(h2_stream *stream, request_rec *r)
{
+ h2_request *req;
apr_status_t status;
- AP_DEBUG_ASSERT(stream);
+
+ ap_assert(stream->request == NULL);
+ ap_assert(stream->rtmp == NULL);
if (stream->rst_error) {
return APR_ECONNRESET;
}
- set_state(stream, H2_STREAM_ST_OPEN);
- status = h2_request_rwrite(stream->request, stream->pool, r);
- stream->request->serialize = h2_config_geti(h2_config_sget(r->server),
- H2_CONF_SER_HEADERS);
+ status = h2_request_rcreate(&req, stream->pool, r);
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(03058)
- "h2_request(%d): rwrite %s host=%s://%s%s",
- stream->request->id, stream->request->method,
- stream->request->scheme, stream->request->authority,
- stream->request->path);
-
+ "h2_request(%d): set_request_rec %s host=%s://%s%s",
+ stream->id, req->method, req->scheme, req->authority,
+ req->path);
+ stream->rtmp = req;
return status;
}
+apr_status_t h2_stream_set_request(h2_stream *stream, const h2_request *r)
+{
+ ap_assert(stream->request == NULL);
+ ap_assert(stream->rtmp == NULL);
+ stream->rtmp = h2_request_clone(stream->pool, r);
+ return APR_SUCCESS;
+}
+
+static apr_status_t add_trailer(h2_stream *stream,
+ const char *name, size_t nlen,
+ const char *value, size_t vlen)
+{
+ conn_rec *c = stream->session->c;
+ char *hname, *hvalue;
+
+ if (nlen == 0 || name[0] == ':') {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, c, APLOGNO(03060)
+ "h2_request(%ld-%d): pseudo header in trailer",
+ c->id, stream->id);
+ return APR_EINVAL;
+ }
+ if (h2_req_ignore_trailer(name, nlen)) {
+ return APR_SUCCESS;
+ }
+ if (!stream->trailers) {
+ stream->trailers = apr_table_make(stream->pool, 5);
+ }
+ hname = apr_pstrndup(stream->pool, name, nlen);
+ hvalue = apr_pstrndup(stream->pool, value, vlen);
+ h2_util_camel_case_header(hname, nlen);
+ apr_table_mergen(stream->trailers, hname, hvalue);
+
+ return APR_SUCCESS;
+}
+
apr_status_t h2_stream_add_header(h2_stream *stream,
const char *name, size_t nlen,
const char *value, size_t vlen)
{
- AP_DEBUG_ASSERT(stream);
- if (!stream->response) {
+ ap_assert(stream);
+
+ if (!stream->has_response) {
if (name[0] == ':') {
if ((vlen) > stream->session->s->limit_req_line) {
/* pseudo header: approximation of request line size check */
@@ -325,14 +369,17 @@ apr_status_t h2_stream_add_header(h2_str
}
if (h2_stream_is_scheduled(stream)) {
- return h2_request_add_trailer(stream->request, stream->pool,
- name, nlen, value, vlen);
+ return add_trailer(stream, name, nlen, value, vlen);
}
else {
- if (!input_open(stream)) {
+ if (!stream->rtmp) {
+ stream->rtmp = h2_req_create(stream->id, stream->pool,
+ NULL, NULL, NULL, NULL, NULL, 0);
+ }
+ if (stream->state != H2_STREAM_ST_OPEN) {
return APR_ECONNRESET;
}
- return h2_request_add_header(stream->request, stream->pool,
+ return h2_request_add_header(stream->rtmp, stream->pool,
name, nlen, value, vlen);
}
}
@@ -340,52 +387,57 @@ apr_status_t h2_stream_add_header(h2_str
apr_status_t h2_stream_schedule(h2_stream *stream, int eos, int push_enabled,
h2_stream_pri_cmp *cmp, void *ctx)
{
- apr_status_t status;
- AP_DEBUG_ASSERT(stream);
- AP_DEBUG_ASSERT(stream->session);
- AP_DEBUG_ASSERT(stream->session->mplx);
-
- if (!output_open(stream)) {
- return APR_ECONNRESET;
- }
- if (stream->scheduled) {
- return APR_EINVAL;
- }
- if (eos) {
- close_input(stream);
- }
-
- if (stream->response) {
- /* already have a resonse, probably a HTTP error code */
- return h2_mplx_process(stream->session->mplx, stream, cmp, ctx);
- }
-
- /* Seeing the end-of-headers, we have everything we need to
- * start processing it.
- */
- status = h2_request_end_headers(stream->request, stream->pool,
- eos, push_enabled);
- if (status == APR_SUCCESS) {
- stream->request->body = !eos;
- stream->scheduled = 1;
- stream->input_remaining = stream->request->content_length;
-
- status = h2_mplx_process(stream->session->mplx, stream, cmp, ctx);
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
- "h2_stream(%ld-%d): scheduled %s %s://%s%s",
- stream->session->id, stream->id,
- stream->request->method, stream->request->scheme,
- stream->request->authority, stream->request->path);
- }
- else {
- h2_stream_rst(stream, H2_ERR_INTERNAL_ERROR);
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c,
- "h2_stream(%ld-%d): RST=2 (internal err) %s %s://%s%s",
- stream->session->id, stream->id,
- stream->request->method, stream->request->scheme,
- stream->request->authority, stream->request->path);
+ apr_status_t status = APR_EINVAL;
+ ap_assert(stream);
+ ap_assert(stream->session);
+ ap_assert(stream->session->mplx);
+
+ if (!stream->scheduled) {
+ if (eos) {
+ close_input(stream);
+ }
+
+ if (h2_stream_is_ready(stream)) {
+ /* already have a resonse, probably a HTTP error code */
+ return h2_mplx_process(stream->session->mplx, stream, cmp, ctx);
+ }
+ else if (!stream->request && stream->rtmp) {
+ /* This is the common case: a h2_request was being assembled, now
+ * it gets finalized and checked for completness */
+ status = h2_request_end_headers(stream->rtmp, stream->pool, eos);
+ if (status == APR_SUCCESS) {
+ stream->rtmp->serialize = h2_config_geti(stream->session->config,
+ H2_CONF_SER_HEADERS);
+
+ stream->request = stream->rtmp;
+ stream->rtmp = NULL;
+ stream->scheduled = 1;
+ stream->push_policy = h2_push_policy_determine(stream->request->headers,
+ stream->pool, push_enabled);
+
+
+ status = h2_mplx_process(stream->session->mplx, stream, cmp, ctx);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
+ "h2_stream(%ld-%d): scheduled %s %s://%s%s "
+ "chunked=%d",
+ stream->session->id, stream->id,
+ stream->request->method, stream->request->scheme,
+ stream->request->authority, stream->request->path,
+ stream->request->chunked);
+ return status;
+ }
+ }
+ else {
+ status = APR_ECONNRESET;
+ }
}
+ h2_stream_rst(stream, H2_ERR_INTERNAL_ERROR);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c,
+ "h2_stream(%ld-%d): RST=2 (internal err) %s %s://%s%s",
+ stream->session->id, stream->id,
+ stream->request->method, stream->request->scheme,
+ stream->request->authority, stream->request->path);
return status;
}
@@ -396,20 +448,31 @@ int h2_stream_is_scheduled(const h2_stre
apr_status_t h2_stream_close_input(h2_stream *stream)
{
- apr_status_t status = APR_SUCCESS;
-
- AP_DEBUG_ASSERT(stream);
+ conn_rec *c = stream->session->c;
+ apr_status_t status;
+ apr_bucket_brigade *tmp;
+ apr_bucket *b;
+
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
"h2_stream(%ld-%d): closing input",
stream->session->id, stream->id);
-
if (stream->rst_error) {
return APR_ECONNRESET;
}
- if (close_input(stream) && stream->input) {
- status = h2_beam_close(stream->input);
+ tmp = apr_brigade_create(stream->pool, c->bucket_alloc);
+ if (stream->trailers && !apr_is_empty_table(stream->trailers)) {
+ h2_headers *r = h2_headers_create(HTTP_OK, stream->trailers,
+ NULL, stream->pool);
+ b = h2_bucket_headers_create(c->bucket_alloc, r);
+ APR_BRIGADE_INSERT_TAIL(tmp, b);
+ stream->trailers = NULL;
}
+
+ b = apr_bucket_eos_create(c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(tmp, b);
+ status = h2_beam_send(stream->input, tmp, APR_BLOCK_READ);
+ apr_brigade_destroy(tmp);
return status;
}
@@ -418,68 +481,39 @@ apr_status_t h2_stream_write_data(h2_str
{
conn_rec *c = stream->session->c;
apr_status_t status = APR_SUCCESS;
+ apr_bucket_brigade *tmp;
- AP_DEBUG_ASSERT(stream);
+ ap_assert(stream);
if (!stream->input) {
return APR_EOF;
}
- if (input_closed(stream) || !stream->request->eoh) {
+ if (input_closed(stream) || !stream->request) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
"h2_stream(%ld-%d): writing denied, closed=%d, eoh=%d",
stream->session->id, stream->id, input_closed(stream),
- stream->request->eoh);
+ stream->request != NULL);
return APR_EINVAL;
}
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
"h2_stream(%ld-%d): add %ld input bytes",
stream->session->id, stream->id, (long)len);
-
- if (!stream->request->chunked) {
- stream->input_remaining -= len;
- if (stream->input_remaining < 0) {
- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c,
- APLOGNO(02961)
- "h2_stream(%ld-%d): got %ld more content bytes than announced "
- "in content-length header: %ld",
- stream->session->id, stream->id,
- (long)stream->request->content_length,
- -(long)stream->input_remaining);
- h2_stream_rst(stream, H2_ERR_PROTOCOL_ERROR);
- return APR_ECONNABORTED;
- }
- }
- if (!stream->tmp) {
- stream->tmp = apr_brigade_create(stream->pool, c->bucket_alloc);
- }
- apr_brigade_write(stream->tmp, NULL, NULL, data, len);
+ tmp = apr_brigade_create(stream->pool, c->bucket_alloc);
+ apr_brigade_write(tmp, NULL, NULL, data, len);
+ status = h2_beam_send(stream->input, tmp, APR_BLOCK_READ);
+ apr_brigade_destroy(tmp);
+
+ stream->in_data_frames++;
+ stream->in_data_octets += len;
+
if (eos) {
- APR_BRIGADE_INSERT_TAIL(stream->tmp,
- apr_bucket_eos_create(c->bucket_alloc));
- close_input(stream);
+ return h2_stream_close_input(stream);
}
- status = h2_beam_send(stream->input, stream->tmp, APR_BLOCK_READ);
- apr_brigade_cleanup(stream->tmp);
return status;
}
-void h2_stream_set_suspended(h2_stream *stream, int suspended)
-{
- AP_DEBUG_ASSERT(stream);
- stream->suspended = !!suspended;
- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, stream->session->c,
- "h2_stream(%ld-%d): suspended=%d",
- stream->session->id, stream->id, stream->suspended);
-}
-
-int h2_stream_is_suspended(const h2_stream *stream)
-{
- AP_DEBUG_ASSERT(stream);
- return stream->suspended;
-}
-
static apr_status_t fill_buffer(h2_stream *stream, apr_size_t amount)
{
conn_rec *c = stream->session->c;
@@ -489,9 +523,12 @@ static apr_status_t fill_buffer(h2_strea
if (!stream->output) {
return APR_EOF;
}
- status = h2_beam_receive(stream->output, stream->buffer,
+ status = h2_beam_receive(stream->output, stream->out_buffer,
APR_NONBLOCK_READ, amount);
- /* The buckets we reveive are using the stream->buffer pool as
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, stream->session->c,
+ "h2_stream(%ld-%d): beam_received",
+ stream->session->id, stream->id);
+ /* The buckets we reveive are using the stream->out_buffer pool as
* lifetime which is exactly what we want since this is stream->pool.
*
* However: when we send these buckets down the core output filters, the
@@ -502,8 +539,8 @@ static apr_status_t fill_buffer(h2_strea
* file. Any split off buckets we sent afterwards will result in a
* APR_EBADF.
*/
- for (b = APR_BRIGADE_FIRST(stream->buffer);
- b != APR_BRIGADE_SENTINEL(stream->buffer);
+ for (b = APR_BRIGADE_FIRST(stream->out_buffer);
+ b != APR_BRIGADE_SENTINEL(stream->out_buffer);
b = APR_BUCKET_NEXT(b)) {
if (APR_BUCKET_IS_FILE(b)) {
apr_bucket_file *f = (apr_bucket_file *)b->data;
@@ -521,77 +558,76 @@ static apr_status_t fill_buffer(h2_strea
return status;
}
-apr_status_t h2_stream_set_response(h2_stream *stream, h2_response *response,
- h2_bucket_beam *output)
+apr_status_t h2_stream_set_error(h2_stream *stream, int http_status)
{
- apr_status_t status = APR_SUCCESS;
- conn_rec *c = stream->session->c;
-
- if (!output_open(stream)) {
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
- "h2_stream(%ld-%d): output closed",
- stream->session->id, stream->id);
- return APR_ECONNRESET;
- }
+ h2_headers *response;
- stream->response = response;
- stream->output = output;
- stream->buffer = apr_brigade_create(stream->pool, c->bucket_alloc);
-
- h2_stream_filter(stream);
- if (stream->output) {
- status = fill_buffer(stream, 0);
+ if (h2_stream_is_ready(stream)) {
+ return APR_EINVAL;
}
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c,
- "h2_stream(%ld-%d): set_response(%d)",
- stream->session->id, stream->id,
- stream->response->http_status);
- return status;
+ if (stream->rtmp) {
+ stream->request = stream->rtmp;
+ stream->rtmp = NULL;
+ }
+ response = h2_headers_die(http_status, stream->request, stream->pool);
+ prepend_response(stream, response);
+ h2_beam_close(stream->output);
+ return APR_SUCCESS;
}
-apr_status_t h2_stream_set_error(h2_stream *stream, int http_status)
+static apr_bucket *get_first_headers_bucket(apr_bucket_brigade *bb)
{
- h2_response *response;
-
- if (stream->submitted) {
- return APR_EINVAL;
+ if (bb) {
+ apr_bucket *b = APR_BRIGADE_FIRST(bb);
+ while (b != APR_BRIGADE_SENTINEL(bb)) {
+ if (H2_BUCKET_IS_HEADERS(b)) {
+ return b;
+ }
+ b = APR_BUCKET_NEXT(b);
+ }
}
- response = h2_response_die(stream->id, http_status, stream->request,
- stream->pool);
- return h2_stream_set_response(stream, response, NULL);
+ return NULL;
}
-static const apr_size_t DATA_CHUNK_SIZE = ((16*1024) - 100 - 9);
-
-apr_status_t h2_stream_out_prepare(h2_stream *stream,
- apr_off_t *plen, int *peos)
+apr_status_t h2_stream_out_prepare(h2_stream *stream, apr_off_t *plen,
+ int *peos, h2_headers **presponse)
{
conn_rec *c = stream->session->c;
apr_status_t status = APR_SUCCESS;
apr_off_t requested;
+ apr_bucket *b, *e;
+ if (presponse) {
+ *presponse = NULL;
+ }
+
if (stream->rst_error) {
*plen = 0;
*peos = 1;
return APR_ECONNRESET;
}
+
+ if (!output_open(stream)) {
+ return APR_ECONNRESET;
+ }
+ prep_output(stream);
if (*plen > 0) {
- requested = H2MIN(*plen, DATA_CHUNK_SIZE);
+ requested = H2MIN(*plen, H2_DATA_CHUNK_SIZE);
}
else {
- requested = DATA_CHUNK_SIZE;
+ requested = H2_DATA_CHUNK_SIZE;
}
*plen = requested;
H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "h2_stream_out_prepare_pre");
- h2_util_bb_avail(stream->buffer, plen, peos);
+ h2_util_bb_avail(stream->out_buffer, plen, peos);
if (!*peos && *plen < requested) {
/* try to get more data */
- status = fill_buffer(stream, (requested - *plen) + DATA_CHUNK_SIZE);
+ status = fill_buffer(stream, (requested - *plen) + H2_DATA_CHUNK_SIZE);
if (APR_STATUS_IS_EOF(status)) {
apr_bucket *eos = apr_bucket_eos_create(c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(stream->buffer, eos);
+ APR_BRIGADE_INSERT_TAIL(stream->out_buffer, eos);
status = APR_SUCCESS;
}
else if (status == APR_EAGAIN) {
@@ -599,20 +635,70 @@ apr_status_t h2_stream_out_prepare(h2_st
status = APR_SUCCESS;
}
*plen = requested;
- h2_util_bb_avail(stream->buffer, plen, peos);
+ h2_util_bb_avail(stream->out_buffer, plen, peos);
}
H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "h2_stream_out_prepare_post");
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c,
- "h2_stream(%ld-%d): prepare, len=%ld eos=%d, trailers=%s",
- c->id, stream->id, (long)*plen, *peos,
- (stream->response && stream->response->trailers)?
- "yes" : "no");
- if (!*peos && !*plen && status == APR_SUCCESS) {
- return APR_EAGAIN;
+
+ b = APR_BRIGADE_FIRST(stream->out_buffer);
+ while (b != APR_BRIGADE_SENTINEL(stream->out_buffer)) {
+ e = APR_BUCKET_NEXT(b);
+ if (APR_BUCKET_IS_FLUSH(b)
+ || (!APR_BUCKET_IS_METADATA(b) && b->length == 0)) {
+ APR_BUCKET_REMOVE(b);
+ apr_bucket_destroy(b);
+ }
+ else {
+ break;
+ }
+ b = e;
}
+
+ b = get_first_headers_bucket(stream->out_buffer);
+ if (b) {
+ /* there are HEADERS to submit */
+ *peos = 0;
+ *plen = 0;
+ if (b == APR_BRIGADE_FIRST(stream->out_buffer)) {
+ if (presponse) {
+ *presponse = h2_bucket_headers_get(b);
+ APR_BUCKET_REMOVE(b);
+ apr_bucket_destroy(b);
+ status = APR_SUCCESS;
+ }
+ else {
+ /* someone needs to retrieve the response first */
+ h2_mplx_keep_active(stream->session->mplx, stream->id);
+ status = APR_EAGAIN;
+ }
+ }
+ else {
+ apr_bucket *e = APR_BRIGADE_FIRST(stream->out_buffer);
+ while (e != APR_BRIGADE_SENTINEL(stream->out_buffer)) {
+ if (e == b) {
+ break;
+ }
+ else if (e->length != (apr_size_t)-1) {
+ *plen += e->length;
+ }
+ e = APR_BUCKET_NEXT(e);
+ }
+ }
+ }
+
+ if (!*peos && !*plen && status == APR_SUCCESS
+ && (!presponse || !*presponse)) {
+ status = APR_EAGAIN;
+ }
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c,
+ "h2_stream(%ld-%d): prepare, len=%ld eos=%d",
+ c->id, stream->id, (long)*plen, *peos);
return status;
}
+static int is_not_headers(apr_bucket *b)
+{
+ return !H2_BUCKET_IS_HEADERS(b);
+}
apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb,
apr_off_t *plen, int *peos)
@@ -623,7 +709,7 @@ apr_status_t h2_stream_read_to(h2_stream
if (stream->rst_error) {
return APR_ECONNRESET;
}
- status = h2_append_brigade(bb, stream->buffer, plen, peos);
+ status = h2_append_brigade(bb, stream->out_buffer, plen, peos, is_not_headers);
if (status == APR_SUCCESS && !*peos && !*plen) {
status = APR_EAGAIN;
}
@@ -639,27 +725,13 @@ int h2_stream_input_is_open(const h2_str
return input_open(stream);
}
-int h2_stream_needs_submit(const h2_stream *stream)
-{
- switch (stream->state) {
- case H2_STREAM_ST_OPEN:
- case H2_STREAM_ST_CLOSED_INPUT:
- case H2_STREAM_ST_CLOSED_OUTPUT:
- case H2_STREAM_ST_CLOSED:
- return !stream->submitted;
- default:
- return 0;
- }
-}
-
-apr_status_t h2_stream_submit_pushes(h2_stream *stream)
+apr_status_t h2_stream_submit_pushes(h2_stream *stream, h2_headers *response)
{
apr_status_t status = APR_SUCCESS;
apr_array_header_t *pushes;
int i;
- pushes = h2_push_collect_update(stream, stream->request,
- h2_stream_get_response(stream));
+ pushes = h2_push_collect_update(stream, stream->request, response);
if (pushes && !apr_is_empty_array(pushes)) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
"h2_stream(%ld-%d): found %d push candidates",
@@ -678,14 +750,13 @@ apr_status_t h2_stream_submit_pushes(h2_
apr_table_t *h2_stream_get_trailers(h2_stream *stream)
{
- return stream->response? stream->response->trailers : NULL;
+ return NULL;
}
-const h2_priority *h2_stream_get_priority(h2_stream *stream)
+const h2_priority *h2_stream_get_priority(h2_stream *stream,
+ h2_headers *response)
{
- h2_response *response = h2_stream_get_response(stream);
-
- if (response && stream->request && stream->request->initiated_on) {
+ if (response && stream->initiated_on) {
const char *ctype = apr_table_get(response->headers, "content-type");
if (ctype) {
/* FIXME: Not good enough, config needs to come from request->server */
@@ -695,3 +766,38 @@ const h2_priority *h2_stream_get_priorit
return NULL;
}
+const char *h2_stream_state_str(h2_stream *stream)
+{
+ switch (stream->state) {
+ case H2_STREAM_ST_IDLE:
+ return "IDLE";
+ case H2_STREAM_ST_OPEN:
+ return "OPEN";
+ case H2_STREAM_ST_RESV_LOCAL:
+ return "RESERVED_LOCAL";
+ case H2_STREAM_ST_RESV_REMOTE:
+ return "RESERVED_REMOTE";
+ case H2_STREAM_ST_CLOSED_INPUT:
+ return "HALF_CLOSED_REMOTE";
+ case H2_STREAM_ST_CLOSED_OUTPUT:
+ return "HALF_CLOSED_LOCAL";
+ case H2_STREAM_ST_CLOSED:
+ return "CLOSED";
+ default:
+ return "UNKNOWN";
+
+ }
+}
+
+int h2_stream_is_ready(h2_stream *stream)
+{
+ if (stream->has_response) {
+ return 1;
+ }
+ else if (stream->out_buffer && get_first_headers_bucket(stream->out_buffer)) {
+ return 1;
+ }
+ return 0;
+}
+
+