[prev in list] [next in list] [prev in thread] [next in thread]
List: apache-cvs
Subject: svn commit: r1688474 [4/21] - in /httpd/httpd/trunk/modules/http2: ./ m4/ mod-h2.xcodeproj/ mod-h2.x
From: jim () apache ! org
Date: 2015-06-30 15:26:19
Message-ID: 20150630152622.2A09CAC08DF () hades ! apache ! org
[Download RAW message or body]
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_alpn.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_alpn.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_alpn.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_alpn.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,295 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <apr_strings.h>
+#include <apr_optional.h>
+#include <apr_optional_hooks.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_connection.h>
+#include <http_protocol.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+
+#include "h2_config.h"
+#include "h2_ctx.h"
+#include "h2_conn.h"
+#include "h2_h2.h"
+#include "h2_alpn.h"
+
+/*******************************************************************************
+ * SSL var lookup
+ */
+APR_DECLARE_OPTIONAL_FN(char *, ssl_var_lookup,
+ (apr_pool_t *, server_rec *,
+ conn_rec *, request_rec *,
+ char *));
+static char *(*opt_ssl_var_lookup)(apr_pool_t *, server_rec *,
+ conn_rec *, request_rec *,
+ char *);
+
+/*******************************************************************************
+ * NPN callbacks and registry, deprecated
+ */
+typedef int (*ssl_npn_advertise_protos)(conn_rec *connection,
+ apr_array_header_t *protos);
+
+typedef int (*ssl_npn_proto_negotiated)(conn_rec *connection,
+ const char *proto_name, apr_size_t proto_name_len);
+
+APR_DECLARE_OPTIONAL_FN(int, modssl_register_npn,
+ (conn_rec *conn,
+ ssl_npn_advertise_protos advertisefn,
+ ssl_npn_proto_negotiated negotiatedfn));
+
+static int (*opt_ssl_register_npn)(conn_rec*,
+ ssl_npn_advertise_protos,
+ ssl_npn_proto_negotiated);
+
+/*******************************************************************************
+ * ALPN callbacks and registry
+ */
+typedef int (*ssl_alpn_propose_protos)(conn_rec *connection,
+ apr_array_header_t *client_protos, apr_array_header_t *protos);
+
+typedef int (*ssl_alpn_proto_negotiated)(conn_rec *connection,
+ const char *proto_name, apr_size_t proto_name_len);
+
+APR_DECLARE_OPTIONAL_FN(int, modssl_register_alpn,
+ (conn_rec *conn,
+ ssl_alpn_propose_protos proposefn,
+ ssl_alpn_proto_negotiated negotiatedfn));
+
+static int (*opt_ssl_register_alpn)(conn_rec*,
+ ssl_alpn_propose_protos,
+ ssl_alpn_proto_negotiated);
+
+/*******************************************************************************
+ * Hooks for processing incoming connections:
+ * - pre_conn_after_tls registers for ALPN handling
+ */
+static int h2_alpn_pre_conn(conn_rec* c, void *arg);
+
+/*******************************************************************************
+ * Once per lifetime init, retrieve optional functions
+ */
+apr_status_t h2_alpn_init(apr_pool_t *pool, server_rec *s)
+{
+ (void)pool;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "h2_alpn init");
+ opt_ssl_register_npn = APR_RETRIEVE_OPTIONAL_FN(modssl_register_npn);
+ opt_ssl_register_alpn = APR_RETRIEVE_OPTIONAL_FN(modssl_register_alpn);
+ opt_ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
+
+ if (!opt_ssl_register_alpn && !opt_ssl_register_npn) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
+ "mod_ssl does not offer ALPN or NPN registration");
+ }
+ return APR_SUCCESS;
+}
+
+/*******************************************************************************
+ * Register various hooks
+ */
+static const char *const mod_ssl[] = { "mod_ssl.c", NULL};
+static const char *const mod_core[] = { "core.c", NULL};
+
+static void check_sni_host(conn_rec *c)
+{
+ /* If we have not done so already, ask the connection for the
+ * hostname send to us via SNI. This information is later used
+ * to retrieve the correct server settings for this connection.
+ */
+ h2_ctx *ctx = h2_ctx_get(c);
+ if (opt_ssl_var_lookup && !ctx->hostname) {
+ const char *p = opt_ssl_var_lookup(c->pool, c->base_server, c,
+ NULL, (char*)"SSL_TLS_SNI");
+ if (p && *p) {
+ ctx->hostname = apr_pstrdup(c->pool, p);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+ "h2_h2, connection, SNI %s",
+ ctx->hostname? ctx->hostname : "NULL");
+ }
+ }
+}
+
+void h2_alpn_register_hooks(void)
+{
+ /* This hook runs on new connection after mod_ssl, but before the core
+ * httpd. Its purpose is to register, if TLS is used, the ALPN callbacks
+ * that enable us to chose "h2" as next procotol if the client supports it.
+ */
+ ap_hook_pre_connection(h2_alpn_pre_conn,
+ mod_ssl, mod_core, APR_HOOK_LAST);
+
+}
+
+static int h2_util_array_index(apr_array_header_t *array, const char *s)
+{
+ for (int i = 0; i < array->nelts; i++) {
+ const char *p = APR_ARRAY_IDX(array, i, const char*);
+ if (!strcmp(p, s)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int h2_npn_advertise(conn_rec *c, apr_array_header_t *protos)
+{
+ h2_config *cfg;
+
+ check_sni_host(c);
+ cfg = h2_config_get(c);
+ if (!h2_config_geti(cfg, H2_CONF_ENABLED)) {
+ return DECLINED;
+ }
+
+ for (apr_size_t i = 0; i < h2_alpn_protos_len; ++i) {
+ const char *proto = h2_alpn_protos[i];
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+ "NPN proposing %s from client selection", proto);
+ APR_ARRAY_PUSH(protos, const char*) = proto;
+ }
+ return OK;
+}
+
+static int h2_negotiated(conn_rec *c, const char *via,
+ const char *proto_name,
+ apr_size_t proto_name_len)
+{
+ h2_ctx *ctx = h2_ctx_get(c);
+
+ if (h2_ctx_is_task(ctx) ) {
+ return DECLINED;
+ }
+
+ if (h2_ctx_pnego_is_done(ctx)) {
+ /* called twice? refraing from overriding existing selection.
+ * NPN is fading...
+ */
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+ "protocol negotiated via %s called, but already set",
+ via);
+ return DECLINED;
+ }
+
+ if (APLOGctrace1(c)) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+ "protocol negotiated via %s is %s", via,
+ apr_pstrndup(c->pool, proto_name, proto_name_len));
+ }
+
+ for (apr_size_t i = 0; i < h2_alpn_protos_len; ++i) {
+ const char *proto = h2_alpn_protos[i];
+ if (proto_name_len == strlen(proto)
+ && strncmp(proto, proto_name, proto_name_len) == 0) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+ "protocol set via %s to %s", via, proto);
+ h2_ctx_pnego_set_done(ctx, proto);
+ break;
+ }
+ }
+ return OK;
+}
+
+static int h2_npn_negotiated(conn_rec *c,
+ const char *proto_name,
+ apr_size_t proto_name_len)
+{
+ return h2_negotiated(c, "NPN", proto_name, proto_name_len);
+}
+
+static int h2_alpn_propose(conn_rec *c,
+ apr_array_header_t *client_protos,
+ apr_array_header_t *protos)
+{
+ h2_config *cfg;
+
+ check_sni_host(c);
+ cfg = h2_config_get(c);
+ if (!h2_config_geti(cfg, H2_CONF_ENABLED)) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+ "ALPN propose, h2 disabled for config %s", cfg->name);
+ return DECLINED;
+ }
+
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+ "ALPN propose for config %s", cfg->name);
+ /* */
+ for (apr_size_t i = 0; i < h2_alpn_protos_len; ++i) {
+ const char *proto = h2_alpn_protos[i];
+ if (h2_util_array_index(client_protos, proto) >= 0) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+ "ALPN proposing %s", proto);
+ APR_ARRAY_PUSH(protos, const char*) = proto;
+ return OK; /* propose only one, the first match from our list */
+ }
+ }
+ return OK;
+}
+
+static int h2_alpn_negotiated(conn_rec *c,
+ const char *proto_name,
+ apr_size_t proto_name_len)
+{
+ return h2_negotiated(c, "ALPN", proto_name, proto_name_len);
+}
+
+
+
+int h2_alpn_pre_conn(conn_rec* c, void *arg)
+{
+ (void)arg;
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+ "h2_h2, pre_connection, start");
+
+ h2_ctx *ctx = h2_ctx_get(c);
+ if (h2_ctx_is_task(ctx)) {
+ /* our stream pseudo connection */
+ return DECLINED;
+ }
+
+ if (h2_h2_is_tls(c)) {
+ /* Brand new TLS connection: Does mod_ssl offer ALPN/NPN support?
+ * If so, register at all present, clients may use either/or.
+ */
+ if (opt_ssl_register_alpn == NULL && opt_ssl_register_npn == NULL) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+ "h2_h2, pre_connection, no ALPN/NPN "
+ "support in mod_ssl");
+ return DECLINED;
+ }
+
+ if (opt_ssl_register_alpn) {
+ opt_ssl_register_alpn(c, h2_alpn_propose, h2_alpn_negotiated);
+ }
+ if (opt_ssl_register_npn) {
+ opt_ssl_register_npn(c, h2_npn_advertise, h2_npn_negotiated);
+ }
+
+ h2_ctx_pnego_set_started(ctx);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+ "h2_alpn, pre_connection, ALPN callback registered");
+ }
+
+ return DECLINED;
+}
+
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_alpn.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_alpn.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_alpn.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_alpn.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,29 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_alpn__
+#define __mod_h2__h2_alpn__
+
+/*
+ * One time, post config intialization.
+ */
+apr_status_t h2_alpn_init(apr_pool_t *pool, server_rec *s);
+
+/* Register apache hooks for ALPN protocol
+ */
+void h2_alpn_register_hooks(void);
+
+
+#endif /* defined(__mod_h2__h2_h2__) */
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_alt_svc.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_alt_svc.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_alt_svc.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_alt_svc.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,122 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <apr_strings.h>
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_alt_svc.h"
+#include "h2_ctx.h"
+#include "h2_config.h"
+#include "h2_h2.h"
+#include "h2_util.h"
+
+static int h2_alt_svc_request_handler(request_rec *r);
+
+void h2_alt_svc_register_hooks(void)
+{
+ ap_hook_handler(h2_alt_svc_request_handler, NULL, NULL, APR_HOOK_LAST);
+}
+
+/**
+ * Parse an Alt-Svc specifier as described in "HTTP Alternative Services"
+ * (https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-04)
+ * with the following changes:
+ * - do not percent encode token values
+ * - do not use quotation marks
+ */
+h2_alt_svc *h2_alt_svc_parse(const char *s, apr_pool_t *pool) {
+ const char *sep = strchr(s, '=');
+ if (sep) {
+ const char *alpn = apr_pstrndup(pool, s, sep - s);
+ const char *host = NULL;
+ int port = 0;
+ s = sep + 1;
+ sep = strchr(s, ':'); /* mandatory : */
+ if (sep) {
+ if (sep != s) { /* optional host */
+ host = apr_pstrndup(pool, s, sep - s);
+ }
+ s = sep + 1;
+ if (*s) { /* must be a port number */
+ port = (int)apr_atoi64(s);
+ if (port > 0 && port < (0x1 << 16)) {
+ h2_alt_svc *as = apr_pcalloc(pool, sizeof(*as));
+ as->alpn = alpn;
+ as->host = host;
+ as->port = port;
+ return as;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+#define h2_alt_svc_IDX(list, i) ((h2_alt_svc**)(list)->elts)[i]
+
+static int h2_alt_svc_request_handler(request_rec *r)
+{
+ h2_ctx *ctx = h2_ctx_rget(r);
+
+ if (h2_ctx_is_active(ctx) || h2_ctx_is_task(ctx)) {
+ return DECLINED;
+ }
+
+ h2_config *cfg = h2_config_rget(r);
+ if (r->hostname && cfg && cfg->alt_svcs && cfg->alt_svcs->nelts > 0) {
+ const char *alt_svc_used = apr_table_get(r->headers_in, "Alt-Svc-Used");
+ if (!alt_svc_used /*|| (alt_svc_used[0] == '0')*/) {
+ /* We have alt-svcs defined and client is not already using
+ * one, announce the services that were configured and match.
+ * The security of this connection determines if we allow
+ * other host names or ports only.
+ */
+ const char *alt_svc = "";
+ const char *svc_ma = "";
+ int secure = h2_h2_is_tls(r->connection);
+ int ma = h2_config_geti(cfg, H2_CONF_ALT_SVC_MAX_AGE);
+ if (ma >= 0) {
+ svc_ma = apr_psprintf(r->pool, "; ma=%d", ma);
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "h2_alt_svc: announce %s for %s:%d",
+ (secure? "secure" : "insecure"),
+ r->hostname, (int)r->server->port);
+ for (int i = 0; i < cfg->alt_svcs->nelts; ++i) {
+ h2_alt_svc *as = h2_alt_svc_IDX(cfg->alt_svcs, i);
+ const char *ahost = as->host;
+ if (ahost && !apr_strnatcasecmp(ahost, r->hostname)) {
+ ahost = NULL;
+ }
+ if (secure || !ahost) {
+ alt_svc = apr_psprintf(r->pool, "%s%s%s=\"%s:%d\"%s",
+ alt_svc,
+ (*alt_svc? ", " : ""), as->alpn,
+ ahost? ahost : "", as->port,
+ svc_ma);
+ }
+ }
+ if (*alt_svc) {
+ apr_table_set(r->headers_out, "Alt-Svc", alt_svc);
+ }
+ }
+ }
+
+ return DECLINED;
+}
+
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_alt_svc.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_alt_svc.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_alt_svc.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_alt_svc.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,39 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_alt_svc__
+#define __mod_h2__h2_alt_svc__
+
+typedef struct h2_alt_svc h2_alt_svc;
+
+struct h2_alt_svc {
+ const char *alpn;
+ const char *host;
+ int port;
+};
+
+void h2_alt_svc_register_hooks(void);
+
+/**
+ * Parse an Alt-Svc specifier as described in "HTTP Alternative Services"
+ * (https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-04)
+ * with the following changes:
+ * - do not percent encode token values
+ * - do not use quotation marks
+ */
+h2_alt_svc *h2_alt_svc_parse(const char *s, apr_pool_t *pool);
+
+
+#endif /* defined(__mod_h2__h2_alt_svc__) */
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_config.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_config.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_config.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_config.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,384 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_log.h>
+#include <http_vhost.h>
+
+#include <apr_strings.h>
+
+#include "h2_alt_svc.h"
+#include "h2_ctx.h"
+#include "h2_conn.h"
+#include "h2_config.h"
+#include "h2_private.h"
+
+#define DEF_VAL (-1)
+
+#define H2_CONFIG_GET(a, b, n) \
+ (((a)->n == DEF_VAL)? (b) : (a))->n
+
+static h2_config defconf = {
+ "default",
+ 0, /* enabled */
+ 100, /* max_streams */
+ 16 * 1024, /* max_hl_size */
+ 64 * 1024, /* window_size */
+ -1, /* min workers */
+ -1, /* max workers */
+ 10 * 60, /* max workers idle secs */
+ 64 * 1024, /* stream max mem size */
+ NULL, /* no alt-svcs */
+ -1, /* alt-svc max age */
+ 0, /* serialize headers */
+ 1, /* hack mpm event */
+ 1, /* h2 direct mode */
+};
+
+static void *h2_config_create(apr_pool_t *pool,
+ const char *prefix, const char *x)
+{
+ h2_config *conf = (h2_config *)apr_pcalloc(pool, sizeof(h2_config));
+
+ const char *s = x? x : "unknown";
+ char *name = (char *)apr_pcalloc(pool, strlen(prefix) + strlen(s) + 20);
+ strcpy(name, prefix);
+ strcat(name, "[");
+ strcat(name, s);
+ strcat(name, "]");
+
+ conf->name = name;
+ conf->h2_enabled = DEF_VAL;
+ conf->h2_max_streams = DEF_VAL;
+ conf->h2_max_hl_size = DEF_VAL;
+ conf->h2_window_size = DEF_VAL;
+ conf->min_workers = DEF_VAL;
+ conf->max_workers = DEF_VAL;
+ conf->max_worker_idle_secs = DEF_VAL;
+ conf->stream_max_mem_size = DEF_VAL;
+ conf->alt_svc_max_age = DEF_VAL;
+ conf->serialize_headers = DEF_VAL;
+ conf->hack_mpm_event = DEF_VAL;
+ conf->h2_direct = DEF_VAL;
+ return conf;
+}
+
+void *h2_config_create_svr(apr_pool_t *pool, server_rec *s)
+{
+ return h2_config_create(pool, "srv", s->defn_name);
+}
+
+void *h2_config_create_dir(apr_pool_t *pool, char *x)
+{
+ return h2_config_create(pool, "dir", x);
+}
+
+void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv)
+{
+ h2_config *base = (h2_config *)basev;
+ h2_config *add = (h2_config *)addv;
+ h2_config *n = (h2_config *)apr_pcalloc(pool, sizeof(h2_config));
+
+ char *name = (char *)apr_pcalloc(pool,
+ 20 + strlen(add->name) + strlen(base->name));
+ strcpy(name, "merged[");
+ strcat(name, add->name);
+ strcat(name, ", ");
+ strcat(name, base->name);
+ strcat(name, "]");
+ n->name = name;
+
+ n->h2_enabled = H2_CONFIG_GET(add, base, h2_enabled);
+ n->h2_max_streams = H2_CONFIG_GET(add, base, h2_max_streams);
+ n->h2_max_hl_size = H2_CONFIG_GET(add, base, h2_max_hl_size);
+ n->h2_window_size = H2_CONFIG_GET(add, base, h2_window_size);
+ n->min_workers = H2_CONFIG_GET(add, base, min_workers);
+ n->max_workers = H2_CONFIG_GET(add, base, max_workers);
+ n->max_worker_idle_secs = H2_CONFIG_GET(add, base, max_worker_idle_secs);
+ n->stream_max_mem_size = H2_CONFIG_GET(add, base, stream_max_mem_size);
+ n->alt_svcs = add->alt_svcs? add->alt_svcs : base->alt_svcs;
+ n->alt_svc_max_age = H2_CONFIG_GET(add, base, alt_svc_max_age);
+ n->serialize_headers = H2_CONFIG_GET(add, base, serialize_headers);
+ n->hack_mpm_event = H2_CONFIG_GET(add, base, hack_mpm_event);
+ n->h2_direct = H2_CONFIG_GET(add, base, h2_direct);
+
+ return n;
+}
+
+int h2_config_geti(h2_config *conf, h2_config_var_t var)
+{
+ switch(var) {
+ case H2_CONF_ENABLED:
+ return H2_CONFIG_GET(conf, &defconf, h2_enabled);
+ case H2_CONF_MAX_STREAMS:
+ return H2_CONFIG_GET(conf, &defconf, h2_max_streams);
+ case H2_CONF_MAX_HL_SIZE:
+ return H2_CONFIG_GET(conf, &defconf, h2_max_hl_size);
+ case H2_CONF_WIN_SIZE:
+ return H2_CONFIG_GET(conf, &defconf, h2_window_size);
+ case H2_CONF_MIN_WORKERS:
+ return H2_CONFIG_GET(conf, &defconf, min_workers);
+ case H2_CONF_MAX_WORKERS:
+ return H2_CONFIG_GET(conf, &defconf, max_workers);
+ case H2_CONF_MAX_WORKER_IDLE_SECS:
+ return H2_CONFIG_GET(conf, &defconf, max_worker_idle_secs);
+ case H2_CONF_STREAM_MAX_MEM:
+ return H2_CONFIG_GET(conf, &defconf, stream_max_mem_size);
+ case H2_CONF_ALT_SVC_MAX_AGE:
+ return H2_CONFIG_GET(conf, &defconf, alt_svc_max_age);
+ case H2_CONF_SER_HEADERS:
+ return H2_CONFIG_GET(conf, &defconf, serialize_headers);
+ case H2_CONF_HACK_MPM_EVENT:
+ return H2_CONFIG_GET(conf, &defconf, hack_mpm_event);
+ case H2_CONF_DIRECT:
+ return H2_CONFIG_GET(conf, &defconf, h2_direct);
+ default:
+ return DEF_VAL;
+ }
+}
+
+h2_config *h2_config_sget(server_rec *s)
+{
+ h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config,
+ &h2_module);
+ AP_DEBUG_ASSERT(cfg);
+ return cfg;
+}
+
+
+static const char *h2_conf_set_engine(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ h2_config *cfg = h2_config_sget(parms->server);
+ if (!strcasecmp(value, "On")) {
+ cfg->h2_enabled = 1;
+ return NULL;
+ }
+ else if (!strcasecmp(value, "Off")) {
+ cfg->h2_enabled = 0;
+ return NULL;
+ }
+
+ (void)arg;
+ return "value must be On or Off";
+}
+
+static const char *h2_conf_set_max_streams(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ h2_config *cfg = h2_config_sget(parms->server);
+ cfg->h2_max_streams = (int)apr_atoi64(value);
+ (void)arg;
+ return NULL;
+}
+
+static const char *h2_conf_set_window_size(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ h2_config *cfg = h2_config_sget(parms->server);
+ cfg->h2_window_size = (int)apr_atoi64(value);
+ (void)arg;
+ return NULL;
+}
+
+static const char *h2_conf_set_max_hl_size(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ h2_config *cfg = h2_config_sget(parms->server);
+ cfg->h2_max_hl_size = (int)apr_atoi64(value);
+ (void)arg;
+ return NULL;
+}
+
+static const char *h2_conf_set_min_workers(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ h2_config *cfg = h2_config_sget(parms->server);
+ cfg->min_workers = (int)apr_atoi64(value);
+ (void)arg;
+ return NULL;
+}
+
+static const char *h2_conf_set_max_workers(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ h2_config *cfg = h2_config_sget(parms->server);
+ cfg->max_workers = (int)apr_atoi64(value);
+ (void)arg;
+ return NULL;
+}
+
+static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ h2_config *cfg = h2_config_sget(parms->server);
+ cfg->max_worker_idle_secs = (int)apr_atoi64(value);
+ (void)arg;
+ return NULL;
+}
+
+static const char *h2_conf_set_stream_max_mem_size(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ h2_config *cfg = h2_config_sget(parms->server);
+
+
+ cfg->stream_max_mem_size = (int)apr_atoi64(value);
+ (void)arg;
+ return NULL;
+}
+
+static const char *h2_add_alt_svc(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ if (value && strlen(value)) {
+ h2_config *cfg = h2_config_sget(parms->server);
+ h2_alt_svc *as = h2_alt_svc_parse(value, parms->pool);
+ if (!as) {
+ return "unable to parse alt-svc specifier";
+ }
+ if (!cfg->alt_svcs) {
+ cfg->alt_svcs = apr_array_make(parms->pool, 5, sizeof(h2_alt_svc*));
+ }
+ APR_ARRAY_PUSH(cfg->alt_svcs, h2_alt_svc*) = as;
+ }
+ (void)arg;
+ return NULL;
+}
+
+static const char *h2_conf_set_alt_svc_max_age(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ h2_config *cfg = h2_config_sget(parms->server);
+ cfg->alt_svc_max_age = (int)apr_atoi64(value);
+ (void)arg;
+ return NULL;
+}
+
+static const char *h2_conf_set_serialize_headers(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ h2_config *cfg = h2_config_sget(parms->server);
+ cfg->serialize_headers = !apr_strnatcasecmp(value, "On");
+ (void)arg;
+ return NULL;
+}
+
+static const char *h2_conf_set_hack_mpm_event(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ h2_config *cfg = h2_config_sget(parms->server);
+ cfg->hack_mpm_event = !apr_strnatcasecmp(value, "On");
+ (void)arg;
+ return NULL;
+}
+
+static const char *h2_conf_set_direct(cmd_parms *parms,
+ void *arg, const char *value)
+{
+ h2_config *cfg = h2_config_sget(parms->server);
+ cfg->h2_direct = !apr_strnatcasecmp(value, "On");
+ (void)arg;
+ return NULL;
+}
+
+#pragma GCC diagnostic ignored "-Wmissing-braces"
+const command_rec h2_cmds[] = {
+ AP_INIT_TAKE1("H2Engine", h2_conf_set_engine, NULL,
+ RSRC_CONF, "on to enable HTTP/2 protocol handling"),
+ AP_INIT_TAKE1("H2MaxSessionStreams", h2_conf_set_max_streams, NULL,
+ RSRC_CONF, "maximum number of open streams per session"),
+ AP_INIT_TAKE1("H2InitialWindowSize", h2_conf_set_window_size, NULL,
+ RSRC_CONF, "initial window size on client DATA"),
+ AP_INIT_TAKE1("H2MaxHeaderListSize", h2_conf_set_max_hl_size, NULL,
+ RSRC_CONF, "maximum acceptable size of request headers"),
+ AP_INIT_TAKE1("H2MinWorkers", h2_conf_set_min_workers, NULL,
+ RSRC_CONF, "minimum number of worker threads per child"),
+ AP_INIT_TAKE1("H2MaxWorkers", h2_conf_set_max_workers, NULL,
+ RSRC_CONF, "maximum number of worker threads per child"),
+ AP_INIT_TAKE1("H2MaxWorkerIdleSeconds", h2_conf_set_max_worker_idle_secs, NULL,
+ RSRC_CONF, "maximum number of idle seconds before a worker shuts \
down"), + AP_INIT_TAKE1("H2StreamMaxMemSize", h2_conf_set_stream_max_mem_size, \
NULL, + RSRC_CONF, "maximum number of bytes buffered in memory for a \
stream"), + AP_INIT_TAKE1("H2AltSvc", h2_add_alt_svc, NULL,
+ RSRC_CONF, "adds an Alt-Svc for this server"),
+ AP_INIT_TAKE1("H2AltSvcMaxAge", h2_conf_set_alt_svc_max_age, NULL,
+ RSRC_CONF, "set the maximum age (in seconds) that client can rely \
on alt-svc information"), + AP_INIT_TAKE1("H2SerializeHeaders", \
h2_conf_set_serialize_headers, NULL, + RSRC_CONF, "on to enable \
header serialization for compatibility"), + AP_INIT_TAKE1("H2HackMpmEvent", \
h2_conf_set_hack_mpm_event, NULL, + RSRC_CONF, "on to enable a hack \
that makes mpm_event working with mod_h2"), + AP_INIT_TAKE1("H2Direct", \
h2_conf_set_direct, NULL, + RSRC_CONF, "on to enable direct HTTP/2 \
mode on non-TLS"), + { NULL, NULL, NULL, 0, 0, NULL }
+};
+
+
+h2_config *h2_config_rget(request_rec *r)
+{
+ h2_config *cfg = (h2_config *)ap_get_module_config(r->per_dir_config,
+ &h2_module);
+ return cfg? cfg : h2_config_sget(r->server);
+}
+
+h2_config *h2_config_get(conn_rec *c)
+{
+ h2_ctx *ctx = h2_ctx_get(c);
+ if (ctx->config) {
+ return ctx->config;
+ }
+ if (!ctx->server && ctx->hostname) {
+ /* We have a host agreed upon via TLS SNI, but no request yet.
+ * The sni host was accepted and therefore does match a server record
+ * (vhost) for it. But we need to know which one.
+ * Normally, it is enough to be set on the initial request on a
+ * connection, but we need it earlier. Simulate a request and call
+ * the vhost matching stuff.
+ */
+ apr_uri_t uri;
+ memset(&uri, 0, sizeof(uri));
+ uri.scheme = (char*)"https";
+ uri.hostinfo = (char*)ctx->hostname;
+ uri.hostname = (char*)ctx->hostname;
+ uri.port_str = (char*)"";
+ uri.port = c->local_addr->port;
+ uri.path = (char*)"/";
+
+ request_rec r;
+ memset(&r, 0, sizeof(r));
+ r.uri = (char*)"/";
+ r.connection = c;
+ r.pool = c->pool;
+ r.hostname = ctx->hostname;
+ r.headers_in = apr_table_make(c->pool, 1);
+ r.parsed_uri = uri;
+ r.status = HTTP_OK;
+ r.server = r.connection->base_server;
+ ap_update_vhost_from_headers(&r);
+ ctx->server = r.server;
+ }
+
+ if (ctx->server) {
+ ctx->config = h2_config_sget(ctx->server);
+ return ctx->config;
+ }
+
+ return h2_config_sget(c->base_server);
+}
+
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_config.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_config.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_config.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_config.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,78 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_config_h__
+#define __mod_h2__h2_config_h__
+
+#undef PACKAGE_VERSION
+#undef PACKAGE_TARNAME
+#undef PACKAGE_STRING
+#undef PACKAGE_NAME
+#undef PACKAGE_BUGREPORT
+#include "config.h"
+
+typedef enum {
+ H2_CONF_ENABLED,
+ H2_CONF_MAX_STREAMS,
+ H2_CONF_MAX_HL_SIZE,
+ H2_CONF_WIN_SIZE,
+ H2_CONF_MIN_WORKERS,
+ H2_CONF_MAX_WORKERS,
+ H2_CONF_MAX_WORKER_IDLE_SECS,
+ H2_CONF_STREAM_MAX_MEM,
+ H2_CONF_ALT_SVCS,
+ H2_CONF_ALT_SVC_MAX_AGE,
+ H2_CONF_SER_HEADERS,
+ H2_CONF_HACK_MPM_EVENT,
+ H2_CONF_DIRECT,
+} h2_config_var_t;
+
+/* Apache httpd module configuration for h2. */
+typedef struct h2_config {
+ const char *name;
+ int h2_enabled; /* if mod_h2 is active at all here */
+ int h2_max_streams; /* max concurrent # streams (http2) */
+ int h2_max_hl_size; /* max header list size (http2) */
+ int h2_window_size; /* stream window size (http2) */
+ int min_workers; /* min # of worker threads/child */
+ int max_workers; /* max # of worker threads/child */
+ int max_worker_idle_secs; /* max # of idle seconds for worker */
+ int stream_max_mem_size; /* max # bytes held in memory/stream */
+ apr_array_header_t *alt_svcs; /* h2_alt_svc specs for this server */
+ int alt_svc_max_age; /* seconds clients can rely on alt-svc info*/
+ int serialize_headers; /* Use serialized HTTP/1.1 headers for
+ processing, better compatibility */
+ int hack_mpm_event; /* If mpm_event is detected, perform a hack
+ on stream connections to make it work */
+ int h2_direct; /* if mod_h2 is active on non-TLS directly */
+} h2_config;
+
+
+void *h2_config_create_dir(apr_pool_t *pool, char *x);
+void *h2_config_create_svr(apr_pool_t *pool, server_rec *s);
+void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv);
+
+apr_status_t h2_config_apply_header(h2_config *config, request_rec *r);
+
+extern const command_rec h2_cmds[];
+
+h2_config *h2_config_get(conn_rec *c);
+h2_config *h2_config_sget(server_rec *s);
+h2_config *h2_config_rget(request_rec *r);
+
+int h2_config_geti(h2_config *conf, h2_config_var_t var);
+
+#endif /* __mod_h2__h2_config_h__ */
+
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_conn.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_conn.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_conn.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_conn.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,543 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <ap_mpm.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+#include <http_log.h>
+#include <http_connection.h>
+#include <http_protocol.h>
+#include <http_request.h>
+
+#include "h2_private.h"
+#include "h2_config.h"
+#include "h2_ctx.h"
+#include "h2_mplx.h"
+#include "h2_session.h"
+#include "h2_stream.h"
+#include "h2_stream_set.h"
+#include "h2_task.h"
+#include "h2_worker.h"
+#include "h2_workers.h"
+#include "h2_conn.h"
+
+static struct h2_workers *workers;
+
+static apr_status_t h2_session_process(h2_session *session);
+
+static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
+static module *mpm_module;
+static module *ssl_module;
+static int checked;
+
+static void check_modules()
+{
+ if (!checked) {
+ for (int i = 0; ap_loaded_modules[i]; ++i) {
+ module *m = ap_loaded_modules[i];
+ if (!strcmp("event.c", m->name)) {
+ mpm_type = H2_MPM_EVENT;
+ mpm_module = m;
+ }
+ else if (!strcmp("worker.c", m->name)) {
+ mpm_type = H2_MPM_WORKER;
+ mpm_module = m;
+ }
+ else if (!strcmp("prefork.c", m->name)) {
+ mpm_type = H2_MPM_PREFORK;
+ mpm_module = m;
+ }
+ else if (!strcmp("mod_ssl.c", m->name)) {
+ ssl_module = m;
+ }
+ }
+ checked = 1;
+ }
+}
+
+apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
+{
+ h2_config *config = h2_config_sget(s);
+ apr_status_t status = APR_SUCCESS;
+ int minw = h2_config_geti(config, H2_CONF_MIN_WORKERS);
+ int maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);
+
+ int max_threads_per_child = 0;
+ ap_mpm_query(AP_MPMQ_MAX_THREADS, &max_threads_per_child);
+ int threads_limit = 0;
+ ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &threads_limit);
+
+ if (minw <= 0) {
+ minw = max_threads_per_child;
+ }
+ if (maxw <= 0) {
+ maxw = threads_limit;
+ if (maxw < minw) {
+ maxw = minw;
+ }
+ }
+
+ for (int i = 0; ap_loaded_modules[i]; ++i) {
+ module *m = ap_loaded_modules[i];
+ if (!strcmp("event.c", m->name)) {
+ mpm_type = H2_MPM_EVENT;
+ mpm_module = m;
+ }
+ else if (!strcmp("worker.c", m->name)) {
+ mpm_type = H2_MPM_WORKER;
+ mpm_module = m;
+ }
+ else if (!strcmp("prefork.c", m->name)) {
+ mpm_type = H2_MPM_PREFORK;
+ mpm_module = m;
+ /* prefork reports 1 thread per child, also as max */
+ if (maxw == 1) {
+ maxw = 8; /* number of cores maybe? */
+ }
+ }
+ else if (!strcmp("mod_ssl.c", m->name)) {
+ ssl_module = m;
+ }
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "h2_workers: min=%d max=%d, mthrpchild=%d, thr_limit=%d",
+ minw, maxw, max_threads_per_child, threads_limit);
+
+ workers = h2_workers_create(s, pool, minw, maxw);
+ int idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
+ h2_workers_set_max_idle_secs(workers, idle_secs);
+
+ return status;
+}
+
+h2_mpm_type_t h2_conn_mpm_type(void) {
+ check_modules();
+ return mpm_type;
+}
+
+module *h2_conn_mpm_module(void) {
+ check_modules();
+ return mpm_module;
+}
+
+apr_status_t h2_conn_rprocess(request_rec *r)
+{
+ h2_config *config = h2_config_rget(r);
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "h2_conn_process start");
+ if (!workers) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "workers not initialized");
+ return APR_EGENERAL;
+ }
+
+ h2_session *session = h2_session_rcreate(r, config, workers);
+ if (!session) {
+ return APR_EGENERAL;
+ }
+
+ return h2_session_process(session);
+}
+
+apr_status_t h2_conn_main(conn_rec *c)
+{
+ h2_config *config = h2_config_get(c);
+
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "h2_conn_main start");
+ if (!workers) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, "workers not initialized");
+ return APR_EGENERAL;
+ }
+
+ h2_session *session = h2_session_create(c, config, workers);
+ if (!session) {
+ return APR_EGENERAL;
+ }
+
+ return h2_session_process(session);
+}
+
+apr_status_t h2_session_process(h2_session *session)
+{
+ apr_status_t status = APR_SUCCESS;
+ int rv = 0;
+
+ /* Start talking to the client. Apart from protocol meta data,
+ * we mainly will see new http/2 streams opened by the client, which
+ * basically are http requests we need to dispatch.
+ *
+ * There will be bursts of new streams, to be served concurrently,
+ * followed by long pauses of no activity.
+ *
+ * Since the purpose of http/2 is to allow siumultaneous streams, we
+ * need to dispatch the handling of each stream into a separate worker
+ * thread, keeping this thread open for sending responses back as
+ * soon as they arrive.
+ * At the same time, we need to continue reading new frames from
+ * our client, which may be meta (WINDOWS_UPDATEs, PING, SETTINGS) or
+ * new streams.
+ *
+ * As long as we have streams open in this session, we cannot really rest
+ * since there are two conditions to wait on: 1. new data from the client,
+ * 2. new data from the open streams to send back.
+ *
+ * Only when we have no more streams open, can we do a blocking read
+ * on our connection.
+ *
+ * TODO: implement graceful GO_AWAY after configurable idle time
+ */
+
+ ap_update_child_status_from_conn(session->c->sbh, SERVER_BUSY_READ,
+ session->c);
+
+ if (APLOGctrace2(session->c)) {
+ ap_filter_t *filter = session->c->input_filters;
+ while (filter) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
+ "h2_conn(%ld), has connection filter %s",
+ session->id, filter->frec->name);
+ filter = filter->next;
+ }
+ }
+
+ status = h2_session_start(session, &rv);
+
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
+ "h2_session(%ld): starting on %s:%d", session->id,
+ session->c->base_server->defn_name,
+ session->c->local_addr->port);
+ if (status != APR_SUCCESS) {
+ h2_session_abort(session, status, rv);
+ h2_session_destroy(session);
+ return status;
+ }
+
+ apr_interval_time_t wait_micros = 0;
+ static const int MAX_WAIT_MICROS = 200 * 1000;
+
+ while (!h2_session_is_done(session)) {
+ int have_written = 0;
+ int have_read = 0;
+
+ status = h2_session_write(session, wait_micros);
+ if (status == APR_SUCCESS) {
+ have_written = 1;
+ wait_micros = 0;
+ }
+ else if (status == APR_EAGAIN) {
+ /* nop */
+ }
+ else if (status == APR_TIMEUP) {
+ wait_micros *= 2;
+ if (wait_micros > MAX_WAIT_MICROS) {
+ wait_micros = MAX_WAIT_MICROS;
+ }
+ }
+ else {
+ ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
+ "h2_session(%ld): writing, terminating",
+ session->id);
+ h2_session_abort(session, status, 0);
+ break;
+ }
+
+ /* We would like to do blocking reads as often as possible as they
+ * are more efficient in regard to server resources.
+ * We can do them under the following circumstances:
+ * - we have no open streams and therefore have nothing to write
+ * - we have just started the session and are waiting for the first
+ * two frames to come in. There will always be at least 2 frames as
+ * * h2 will send SETTINGS and SETTINGS-ACK
+ * * h2c will count the header settings as one frame and we
+ * submit our settings and need the ACK.
+ */
+ int got_streams = !h2_stream_set_is_empty(session->streams);
+ status = h2_session_read(session,
+ (!got_streams
+ || session->frames_received <= 1)?
+ APR_BLOCK_READ : APR_NONBLOCK_READ);
+ switch (status) {
+ case APR_SUCCESS:
+ /* successful read, reset our idle timers */
+ have_read = 1;
+ wait_micros = 0;
+ break;
+ case APR_EAGAIN:
+ break;
+ case APR_EBADF:
+ case APR_EOF:
+ case APR_ECONNABORTED:
+ case ECONNRESET:
+ ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
+ "h2_session(%ld): reading",
+ session->id);
+ h2_session_abort(session, status, 0);
+ break;
+ default:
+ ap_log_cerror( APLOG_MARK, APLOG_WARNING, status, session->c,
+ "h2_session(%ld): error reading, terminating",
+ session->id);
+ h2_session_abort(session, status, 0);
+ break;
+ }
+
+ if (!have_read && !have_written) {
+ /* Nothing to read or write, we may have sessions, but
+ * the have no data yet ready to be delivered. Slowly
+ * back off to give others a chance to do their work.
+ */
+ if (wait_micros == 0) {
+ wait_micros = 10;
+ }
+ }
+ }
+
+ ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
+ "h2_session(%ld): done", session->id);
+
+ ap_update_child_status_from_conn(session->c->sbh, SERVER_CLOSING,
+ session->c);
+
+ h2_session_close(session);
+ h2_session_destroy(session);
+
+ return DONE;
+}
+
+
+static void fix_event_conn(conn_rec *c, conn_rec *master);
+
+conn_rec *h2_conn_create(conn_rec *master, apr_pool_t *pool)
+{
+ apr_socket_t *socket;
+
+ AP_DEBUG_ASSERT(master);
+
+ /* CAVEAT: it seems necessary to setup the conn_rec in the master
+ * connection thread. Other attempts crashed.
+ * HOWEVER: we setup the connection using the pools and other items
+ * from the master connection, since we do not want to allocate
+ * lots of resources here.
+ * Lets allocated pools and everything else when we actually start
+ * working on this new connection.
+ */
+ /* Not sure about the scoreboard handle. Reusing the one from the main
+ * connection could make sense, is not really correct, but we cannot
+ * easily create new handles for our worker threads either.
+ * TODO
+ */
+ socket = ap_get_module_config(master->conn_config, &core_module);
+ conn_rec *c = ap_run_create_connection(pool, master->base_server,
+ socket,
+ master->id^((long)pool),
+ master->sbh,
+ master->bucket_alloc);
+ if (c == NULL) {
+ ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool,
+ "h2_task: creating conn");
+ return NULL;
+ }
+ return c;
+}
+
+apr_status_t h2_conn_prep(h2_task_env *env, conn_rec *master, h2_worker *worker)
+{
+ h2_config *cfg = h2_config_get(master);
+
+ ap_log_perror(APLOG_MARK, APLOG_TRACE3, 0, env->pool,
+ "h2_conn(%ld): created from master", master->id);
+
+ /* Ok, we are just about to start processing the connection and
+ * the worker is calling us to setup all necessary resources.
+ * We can borrow some from the worker itself and some we do as
+ * sub-resources from it, so that we get a nice reuse of
+ * pools.
+ */
+ env->c.pool = env->pool;
+ env->c.bucket_alloc = h2_worker_get_bucket_alloc(worker);
+ env->c.current_thread = h2_worker_get_thread(worker);
+
+ env->c.conn_config = ap_create_conn_config(env->pool);
+ env->c.notes = apr_table_make(env->pool, 5);
+
+ ap_set_module_config(env->c.conn_config, &core_module,
+ h2_worker_get_socket(worker));
+
+ if (ssl_module) {
+ /* See #19, there is a range of SSL variables to be gotten from
+ * the main connection that should be available in request handlers
+ */
+ void *sslcfg = ap_get_module_config(master->conn_config, ssl_module);
+ if (sslcfg) {
+ ap_set_module_config(env->c.conn_config, ssl_module, sslcfg);
+ }
+ }
+
+ /* This works for mpm_worker so far. Other mpm modules have
+ * different needs, unfortunately. The most interesting one
+ * being mpm_event...
+ */
+ switch (h2_conn_mpm_type()) {
+ case H2_MPM_WORKER:
+ /* all fine */
+ break;
+ case H2_MPM_EVENT:
+ if (h2_config_geti(cfg, H2_CONF_HACK_MPM_EVENT)) {
+ fix_event_conn(&env->c, master);
+ }
+ break;
+ default:
+ /* fingers crossed */
+ break;
+ }
+
+ return APR_SUCCESS;
+}
+
+apr_status_t h2_conn_setup(struct h2_task_env *env, struct h2_worker *worker)
+{
+ return h2_conn_prep(env, env->mplx->c, worker);
+}
+
+apr_status_t h2_conn_init(struct h2_task_env *env, struct h2_worker *worker)
+{
+ conn_rec *master = env->mplx->c;
+ h2_config *cfg = h2_config_get(master);
+
+ apr_socket_t *socket = ap_get_module_config(master->conn_config,
+ &core_module);
+ conn_rec *c = ap_run_create_connection(env->pool, master->base_server,
+ socket,
+ master->id^((long)env->pool),
+ master->sbh,
+ master->bucket_alloc);
+ if (c == NULL) {
+ ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, env->pool,
+ "h2_task: creating conn");
+ return APR_ENOMEM;
+ }
+
+ env->c = *c;
+ env->c.bucket_alloc = h2_worker_get_bucket_alloc(worker);
+ env->c.current_thread = h2_worker_get_thread(worker);
+
+ ap_set_module_config(env->c.conn_config, &core_module, socket);
+ if (ssl_module) {
+ /* See #19, there is a range of SSL variables to be gotten from
+ * the main connection that should be available in request handlers
+ */
+ void *sslcfg = ap_get_module_config(master->conn_config, ssl_module);
+ if (sslcfg) {
+ ap_set_module_config(env->c.conn_config, ssl_module, sslcfg);
+ }
+ }
+
+ /* This works for mpm_worker so far. Other mpm modules have
+ * different needs, unfortunately. The most interesting one
+ * being mpm_event...
+ */
+ switch (h2_conn_mpm_type()) {
+ case H2_MPM_WORKER:
+ /* all fine */
+ break;
+ case H2_MPM_EVENT:
+ if (h2_config_geti(cfg, H2_CONF_HACK_MPM_EVENT)) {
+ fix_event_conn(&env->c, master);
+ }
+ break;
+ default:
+ /* fingers crossed */
+ break;
+ }
+
+ return APR_SUCCESS;
+}
+
+apr_status_t h2_conn_post(conn_rec *c, h2_worker *worker)
+{
+ (void)worker;
+
+ /* be sure no one messes with this any more */
+ memset(c, 0, sizeof(*c));
+ return APR_SUCCESS;
+}
+
+apr_status_t h2_conn_process(conn_rec *c, apr_socket_t *socket)
+{
+ AP_DEBUG_ASSERT(c);
+
+ c->clogging_input_filters = 1;
+ ap_process_connection(c, socket);
+
+ return APR_SUCCESS;
+}
+
+/* This is an internal mpm event.c struct which is disguised
+ * as a conn_state_t so that mpm_event can have special connection
+ * state information without changing the struct seen on the outside.
+ *
+ * For our task connections we need to create a new beast of this type
+ * and fill it with enough meaningful things that mpm_event reads and
+ * starts processing out task request.
+ */
+typedef struct event_conn_state_t event_conn_state_t;
+struct event_conn_state_t {
+ /** APR_RING of expiration timeouts */
+ APR_RING_ENTRY(event_conn_state_t) timeout_list;
+ /** the expiration time of the next keepalive timeout */
+ apr_time_t expiration_time;
+ /** connection record this struct refers to */
+ conn_rec *c;
+ /** request record (if any) this struct refers to */
+ request_rec *r;
+ /** is the current conn_rec suspended? (disassociated with
+ * a particular MPM thread; for suspend_/resume_connection
+ * hooks)
+ */
+ int suspended;
+ /** memory pool to allocate from */
+ apr_pool_t *p;
+ /** bucket allocator */
+ apr_bucket_alloc_t *bucket_alloc;
+ /** poll file descriptor information */
+ apr_pollfd_t pfd;
+ /** public parts of the connection state */
+ conn_state_t pub;
+};
+APR_RING_HEAD(timeout_head_t, event_conn_state_t);
+
+static void fix_event_conn(conn_rec *c, conn_rec *master)
+{
+ event_conn_state_t *master_cs = ap_get_module_config(master->conn_config,
+ h2_conn_mpm_module());
+ event_conn_state_t *cs = apr_pcalloc(c->pool, sizeof(event_conn_state_t));
+ cs->bucket_alloc = apr_bucket_alloc_create(c->pool);
+
+ ap_set_module_config(c->conn_config, h2_conn_mpm_module(), cs);
+
+ cs->c = c;
+ cs->r = NULL;
+ cs->p = master_cs->p;
+ cs->pfd = master_cs->pfd;
+ cs->pub = master_cs->pub;
+ cs->pub.state = CONN_STATE_READ_REQUEST_LINE;
+
+ c->cs = &(cs->pub);
+}
+
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_conn.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_conn.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_conn.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_conn.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,70 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_conn__
+#define __mod_h2__h2_conn__
+
+struct h2_task;
+struct h2_task_env;
+struct h2_worker;
+
+/* Process the connection that is now starting the HTTP/2
+ * conversation. Return when the HTTP/2 session is done
+ * and the connection will close.
+ */
+apr_status_t h2_conn_main(conn_rec *c);
+
+/* Process the request that has been upgraded to a HTTP/2
+ * conversation. Return when the HTTP/2 session is done
+ * and the connection will close.
+ */
+apr_status_t h2_conn_rprocess(request_rec *r);
+
+/* Initialize this child process for h2 connection work,
+ * to be called once during child init before multi processing
+ * starts.
+ */
+apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s);
+
+
+typedef enum {
+ H2_MPM_UNKNOWN,
+ H2_MPM_WORKER,
+ H2_MPM_EVENT,
+ H2_MPM_PREFORK,
+} h2_mpm_type_t;
+
+h2_mpm_type_t h2_conn_mpm_type();
+module *h2_conn_mpm_module();
+
+/* Returns the type of MPM module detected */
+h2_mpm_type_t h2_conn_mpm_type(void);
+
+/* Gives the detected module itself or NULL if unknown */
+module *h2_conn_mpm_module(void);
+
+
+conn_rec *h2_conn_create(conn_rec *master, apr_pool_t *stream_pool);
+
+apr_status_t h2_conn_init(struct h2_task_env *env, struct h2_worker *worker);
+
+apr_status_t h2_conn_setup(struct h2_task_env *env, struct h2_worker *worker);
+apr_status_t h2_conn_prep(struct h2_task_env *env, conn_rec *master,
+ struct h2_worker *worker);
+apr_status_t h2_conn_post(conn_rec *c, struct h2_worker *worker);
+
+apr_status_t h2_conn_process(conn_rec *c, apr_socket_t *socket);
+
+#endif /* defined(__mod_h2__h2_conn__) */
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_conn_io.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_conn_io.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_conn_io.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_conn_io.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,285 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <ap_mpm.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_connection.h>
+
+#include "h2_private.h"
+#include "h2_conn_io.h"
+#include "h2_h2.h"
+#include "h2_util.h"
+
+/* If we write directly to our brigade or use a char buffer to collect
+ * out data.
+ */
+
+#define H2_CONN_IO_BUF_SIZE (64 * 1024)
+#define H2_CONN_IO_SSL_WRITE_SIZE (16 * 1024)
+
+
+apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c)
+{
+ io->connection = c;
+ io->input = apr_brigade_create(c->pool, c->bucket_alloc);
+ io->output = apr_brigade_create(c->pool, c->bucket_alloc);
+ io->buffer_output = h2_h2_is_tls(c);
+ io->buflen = 0;
+
+ if (io->buffer_output) {
+ io->bufsize = H2_CONN_IO_BUF_SIZE;
+ io->buffer = apr_pcalloc(c->pool, io->bufsize);
+ }
+ else {
+ io->bufsize = 0;
+ }
+
+ return APR_SUCCESS;
+}
+
+void h2_conn_io_destroy(h2_conn_io *io)
+{
+ io->input = NULL;
+ io->output = NULL;
+}
+
+static apr_status_t h2_conn_io_bucket_read(h2_conn_io *io,
+ apr_read_type_e block,
+ h2_conn_io_on_read_cb on_read_cb,
+ void *puser, int *pdone)
+{
+ apr_status_t status = APR_SUCCESS;
+ apr_size_t readlen = 0;
+ *pdone = 0;
+
+ while (status == APR_SUCCESS && !*pdone
+ && !APR_BRIGADE_EMPTY(io->input)) {
+
+ apr_bucket* bucket = APR_BRIGADE_FIRST(io->input);
+ if (APR_BUCKET_IS_METADATA(bucket)) {
+ /* we do nothing regarding any meta here */
+ }
+ else {
+ const char *bucket_data = NULL;
+ apr_size_t bucket_length = 0;
+ status = apr_bucket_read(bucket, &bucket_data,
+ &bucket_length, block);
+
+ if (status == APR_SUCCESS && bucket_length > 0) {
+ if (APLOGctrace2(io->connection)) {
+ char buffer[32];
+ h2_util_hex_dump(buffer, sizeof(buffer)/sizeof(buffer[0]),
+ bucket_data, bucket_length);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, io->connection,
+ "h2_conn_io(%ld): read %d bytes: %s",
+ io->connection->id, (int)bucket_length, buffer);
+ }
+
+ if (bucket_length > 0) {
+ apr_size_t consumed = 0;
+ status = on_read_cb(bucket_data, bucket_length,
+ &consumed, pdone, puser);
+ if (status == APR_SUCCESS && bucket_length > consumed) {
+ /* We have data left in the bucket. Split it. */
+ status = apr_bucket_split(bucket, consumed);
+ }
+ readlen += consumed;
+ }
+ }
+ }
+ apr_bucket_delete(bucket);
+ }
+ if (readlen == 0 && status == APR_SUCCESS && block == APR_NONBLOCK_READ) {
+ return APR_EAGAIN;
+ }
+ return status;
+}
+
+apr_status_t h2_conn_io_read(h2_conn_io *io,
+ apr_read_type_e block,
+ h2_conn_io_on_read_cb on_read_cb,
+ void *puser)
+{
+ apr_status_t status;
+ int done = 0;
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, io->connection,
+ "h2_conn_io: try read, block=%d", block);
+
+ if (!APR_BRIGADE_EMPTY(io->input)) {
+ /* Seems something is left from a previous read, lets
+ * satisfy our caller with the data we already have. */
+ status = h2_conn_io_bucket_read(io, block, on_read_cb, puser, &done);
+ if (status != APR_SUCCESS || done) {
+ return status;
+ }
+ apr_brigade_cleanup(io->input);
+ }
+
+ /* We only do a blocking read when we have no streams to process. So,
+ * in httpd scoreboard lingo, we are in a KEEPALIVE connection state.
+ * When reading non-blocking, we do have streams to process and update
+ * child with NULL request. That way, any current request information
+ * in the scoreboard is preserved.
+ */
+ if (block == APR_BLOCK_READ) {
+ ap_update_child_status_from_conn(io->connection->sbh,
+ SERVER_BUSY_KEEPALIVE,
+ io->connection);
+ }
+ else {
+ ap_update_child_status(io->connection->sbh, SERVER_BUSY_READ, NULL);
+ }
+
+ status = ap_get_brigade(io->connection->input_filters,
+ io->input, AP_MODE_READBYTES,
+ block, 16 * 4096);
+ switch (status) {
+ case APR_SUCCESS:
+ return h2_conn_io_bucket_read(io, block, on_read_cb, puser, &done);
+ case APR_EOF:
+ case APR_EAGAIN:
+ break;
+ default:
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, io->connection,
+ "h2_conn_io: error reading");
+ break;
+ }
+ return status;
+}
+
+static apr_status_t flush_out(apr_bucket_brigade *bb, void *ctx)
+{
+ h2_conn_io *io = (h2_conn_io*)ctx;
+
+ ap_update_child_status(io->connection->sbh, SERVER_BUSY_WRITE, NULL);
+
+ apr_status_t status = ap_pass_brigade(io->connection->output_filters, bb);
+ apr_brigade_cleanup(bb);
+ return status;
+}
+
+static apr_status_t bucketeer_buffer(h2_conn_io *io) {
+ const char *data = io->buffer;
+ apr_size_t remaining = io->buflen;
+ int bcount = (int)(remaining / H2_CONN_IO_SSL_WRITE_SIZE);
+ apr_bucket *b;
+
+ for (int i = 0; i < bcount; ++i) {
+ b = apr_bucket_transient_create(data, H2_CONN_IO_SSL_WRITE_SIZE,
+ io->output->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(io->output, b);
+ data += H2_CONN_IO_SSL_WRITE_SIZE;
+ remaining -= H2_CONN_IO_SSL_WRITE_SIZE;
+ }
+
+ if (remaining > 0) {
+ b = apr_bucket_transient_create(data, remaining,
+ io->output->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(io->output, b);
+ }
+ return APR_SUCCESS;
+}
+
+apr_status_t h2_conn_io_write(h2_conn_io *io,
+ const char *buf, size_t length)
+{
+ apr_status_t status = APR_SUCCESS;
+ io->unflushed = 1;
+
+ if (io->buffer_output) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, io->connection,
+ "h2_conn_io: buffering %ld bytes", (long)length);
+ while (length > 0 && (status == APR_SUCCESS)) {
+ apr_size_t avail = io->bufsize - io->buflen;
+ if (avail <= 0) {
+ bucketeer_buffer(io);
+ status = flush_out(io->output, io);
+ io->buflen = 0;
+ }
+ else if (length > avail) {
+ memcpy(io->buffer + io->buflen, buf, avail);
+ io->buflen += avail;
+ length -= avail;
+ buf += avail;
+ }
+ else {
+ memcpy(io->buffer + io->buflen, buf, length);
+ io->buflen += length;
+ length = 0;
+ break;
+ }
+ }
+
+ }
+ else {
+ status = apr_brigade_write(io->output, flush_out, io, buf, length);
+ if (status == APR_SUCCESS
+ || APR_STATUS_IS_ECONNABORTED(status)
+ || APR_STATUS_IS_EPIPE(status)) {
+ /* These are all fine and no reason for concern. Everything else
+ * is interesting. */
+ status = APR_SUCCESS;
+ }
+ else {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, io->connection,
+ "h2_conn_io: write error");
+ }
+ }
+
+ return status;
+}
+
+
+apr_status_t h2_conn_io_flush(h2_conn_io *io)
+{
+ if (io->unflushed) {
+ if (io->buflen > 0) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, io->connection,
+ "h2_conn_io: flush, flushing %ld bytes", \
(long)io->buflen); + apr_bucket *b = \
apr_bucket_transient_create(io->buffer, io->buflen, + \
io->output->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(io->output, b);
+ io->buflen = 0;
+ }
+ /* Append flush.
+ */
+ APR_BRIGADE_INSERT_TAIL(io->output,
+ apr_bucket_flush_create(io->output->bucket_alloc));
+
+ /* Send it out through installed filters (TLS) to the client */
+ apr_status_t status = flush_out(io->output, io);
+
+ if (status == APR_SUCCESS
+ || APR_STATUS_IS_ECONNABORTED(status)
+ || APR_STATUS_IS_EPIPE(status)) {
+ /* These are all fine and no reason for concern. Everything else
+ * is interesting. */
+ io->unflushed = 0;
+ }
+ else {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, io->connection,
+ "h2_conn_io: flush error");
+ }
+
+ return status;
+ }
+ return APR_SUCCESS;
+}
+
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_conn_io.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_conn_io.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_conn_io.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_conn_io.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,55 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_conn_io__
+#define __mod_h2__h2_conn_io__
+
+/* h2_io is the basic handler of a httpd connection. It keeps two brigades,
+ * one for input, one for output and works with the installed connection
+ * filters.
+ * The read is done via a callback function, so that input can be processed
+ * directly without copying.
+ */
+typedef struct {
+ conn_rec *connection;
+ apr_bucket_brigade *input;
+ apr_bucket_brigade *output;
+ int buffer_output;
+
+ char *buffer;
+ apr_size_t buflen;
+ apr_size_t bufsize;
+ int unflushed;
+} h2_conn_io;
+
+apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c);
+void h2_conn_io_destroy(h2_conn_io *io);
+
+typedef apr_status_t (*h2_conn_io_on_read_cb)(const char *data, apr_size_t len,
+ apr_size_t *readlen, int *done,
+ void *puser);
+
+apr_status_t h2_conn_io_read(h2_conn_io *io,
+ apr_read_type_e block,
+ h2_conn_io_on_read_cb on_read_cb,
+ void *puser);
+
+apr_status_t h2_conn_io_write(h2_conn_io *io,
+ const char *buf,
+ size_t length);
+
+apr_status_t h2_conn_io_flush(h2_conn_io *io);
+
+#endif /* defined(__mod_h2__h2_conn_io__) */
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_ctx.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_ctx.c?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_ctx.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_ctx.c Tue Jun 30 15:26:16 2015
@@ -0,0 +1,100 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_config.h>
+
+#include "h2_private.h"
+#include "h2_task.h"
+#include "h2_ctx.h"
+#include "h2_private.h"
+
+static h2_ctx *h2_ctx_create(conn_rec *c)
+{
+ h2_ctx *ctx = apr_pcalloc(c->pool, sizeof(h2_ctx));
+ AP_DEBUG_ASSERT(ctx);
+ ctx->pnego_state = H2_PNEGO_NONE;
+ ap_set_module_config(c->conn_config, &h2_module, ctx);
+ return ctx;
+}
+
+h2_ctx *h2_ctx_create_for(conn_rec *c, h2_task_env *env)
+{
+ h2_ctx *ctx = h2_ctx_create(c);
+ if (ctx) {
+ ctx->task_env = env;
+ }
+ return ctx;
+}
+
+h2_ctx *h2_ctx_get(conn_rec *c)
+{
+ h2_ctx *ctx = (h2_ctx*)ap_get_module_config(c->conn_config, &h2_module);
+ if (ctx == NULL) {
+ ctx = h2_ctx_create(c);
+ }
+ return ctx;
+}
+
+h2_ctx *h2_ctx_rget(request_rec *r)
+{
+ return h2_ctx_get(r->connection);
+}
+
+const char *h2_ctx_pnego_get(h2_ctx *ctx)
+{
+ return ctx? ctx->protocol : NULL;
+}
+
+void h2_ctx_pnego_set_started(h2_ctx *ctx)
+{
+ ctx->pnego_state = H2_PNEGO_STARTED;
+}
+
+h2_ctx *h2_ctx_pnego_set_done(h2_ctx *ctx, const char *proto)
+{
+ ctx->protocol = proto;
+ ctx->pnego_state = H2_PNEGO_DONE;
+ ctx->is_h2 = (proto != NULL);
+ return ctx;
+}
+
+int h2_ctx_is_task(h2_ctx *ctx)
+{
+ return ctx && !!ctx->task_env;
+}
+
+int h2_ctx_pnego_is_ongoing(h2_ctx *ctx)
+{
+ return ctx && (ctx->pnego_state == H2_PNEGO_STARTED);
+}
+
+int h2_ctx_pnego_is_done(h2_ctx *ctx)
+{
+ return ctx && (ctx->pnego_state == H2_PNEGO_DONE);
+}
+
+int h2_ctx_is_active(h2_ctx *ctx)
+{
+ return ctx && ctx->is_h2;
+}
+
+struct h2_task_env *h2_ctx_get_task(h2_ctx *ctx)
+{
+ return ctx->task_env;
+}
Added: httpd/httpd/trunk/modules/http2/mod_h2/h2_ctx.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_h2/h2_ctx.h?rev=1688474&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_h2/h2_ctx.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_h2/h2_ctx.h Tue Jun 30 15:26:16 2015
@@ -0,0 +1,74 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_ctx__
+#define __mod_h2__h2_ctx__
+
+struct h2_task_env;
+struct h2_config;
+
+typedef enum {
+ H2_PNEGO_NONE,
+ H2_PNEGO_STARTED,
+ H2_PNEGO_DONE,
+} h2_pnego_state_t;
+
+/**
+ * The h2 module context associated with a connection.
+ *
+ * It keeps track of the different types of connections:
+ * - those from clients that use HTTP/2 protocol
+ * - those from clients that do not use HTTP/2
+ * - those created by ourself to perform work on HTTP/2 streams
+ */
+typedef struct h2_ctx {
+ int is_h2; /* h2 engine is used */
+ h2_pnego_state_t pnego_state; /* protocol negotiation state */
+ const char *protocol; /* the protocol negotiated */
+ struct h2_task_env *task_env; /* the h2_task environment or NULL */
+ const char *hostname; /* hostname negotiated via SNI, optional */
+ server_rec *server; /* httpd server config selected. */
+ struct h2_config *config; /* effective config in this context */
+} h2_ctx;
+
+h2_ctx *h2_ctx_get(conn_rec *c);
+h2_ctx *h2_ctx_rget(request_rec *r);
+h2_ctx *h2_ctx_create_for(conn_rec *c, struct h2_task_env *env);
+
+
+void h2_ctx_pnego_set_started(h2_ctx *ctx);
+h2_ctx *h2_ctx_pnego_set_done(h2_ctx *ctx, const char *proto);
+/**
+ * Returns != 0 iff protocol negitiation did happen, not matter
+ * what the outcome was.
+ */
+int h2_ctx_pnego_is_done(h2_ctx *ctx);
+/**
+ * Returns != 0 iff protocol negotiation has started but is not
+ * done yet.
+ */
+int h2_ctx_pnego_is_ongoing(h2_ctx *ctx);
+
+/**
+ * Get the h2 protocol negotiated for this connection, or NULL.
+ */
+const char *h2_ctx_pnego_get(h2_ctx *ctx);
+
+int h2_ctx_is_task(h2_ctx *ctx);
+int h2_ctx_is_active(h2_ctx *ctx);
+
+struct h2_task_env *h2_ctx_get_task(h2_ctx *ctx);
+
+#endif /* defined(__mod_h2__h2_ctx__) */
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic