You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2016/02/08 17:53:46 UTC
svn commit: r1729209 [2/2] - in /httpd/httpd/trunk: ./ modules/http2/
Modified: httpd/httpd/trunk/modules/http2/h2_util.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_util.c?rev=1729209&r1=1729208&r2=1729209&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_util.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_util.c Mon Feb 8 16:53:45 2016
@@ -1004,6 +1004,9 @@ static literal IgnoredResponseTrailers[]
H2_DEF_LITERAL("www-authenticate"),
H2_DEF_LITERAL("proxy-authenticate"),
};
+static literal IgnoredProxyRespHds[] = {
+ H2_DEF_LITERAL("alt-svc"),
+};
static int ignore_header(const literal *lits, size_t llen,
const char *name, size_t nlen)
@@ -1036,12 +1039,125 @@ int h2_res_ignore_trailer(const char *na
return ignore_header(H2_LIT_ARGS(IgnoredResponseTrailers), name, len);
}
-void h2_req_strip_ignored_header(apr_table_t *headers)
+int h2_proxy_res_ignore_header(const char *name, size_t len)
{
- int i;
- for (i = 0; i < H2_ALEN(IgnoredRequestHeaders); ++i) {
- apr_table_unset(headers, IgnoredRequestHeaders[i].name);
+ return (h2_req_ignore_header(name, len)
+ || ignore_header(H2_LIT_ARGS(IgnoredProxyRespHds), name, len));
+}
+
+
+/*******************************************************************************
+ * frame logging
+ ******************************************************************************/
+
+int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
+{
+ char scratch[128];
+ size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
+
+ switch (frame->hd.type) {
+ case NGHTTP2_DATA: {
+ return apr_snprintf(buffer, maxlen,
+ "DATA[length=%d, flags=%d, stream=%d, padlen=%d]",
+ (int)frame->hd.length, frame->hd.flags,
+ frame->hd.stream_id, (int)frame->data.padlen);
+ }
+ case NGHTTP2_HEADERS: {
+ return apr_snprintf(buffer, maxlen,
+ "HEADERS[length=%d, hend=%d, stream=%d, eos=%d]",
+ (int)frame->hd.length,
+ !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
+ frame->hd.stream_id,
+ !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
+ }
+ case NGHTTP2_PRIORITY: {
+ return apr_snprintf(buffer, maxlen,
+ "PRIORITY[length=%d, flags=%d, stream=%d]",
+ (int)frame->hd.length,
+ frame->hd.flags, frame->hd.stream_id);
+ }
+ case NGHTTP2_RST_STREAM: {
+ return apr_snprintf(buffer, maxlen,
+ "RST_STREAM[length=%d, flags=%d, stream=%d]",
+ (int)frame->hd.length,
+ frame->hd.flags, frame->hd.stream_id);
+ }
+ case NGHTTP2_SETTINGS: {
+ if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
+ return apr_snprintf(buffer, maxlen,
+ "SETTINGS[ack=1, stream=%d]",
+ frame->hd.stream_id);
+ }
+ return apr_snprintf(buffer, maxlen,
+ "SETTINGS[length=%d, stream=%d]",
+ (int)frame->hd.length, frame->hd.stream_id);
+ }
+ case NGHTTP2_PUSH_PROMISE: {
+ return apr_snprintf(buffer, maxlen,
+ "PUSH_PROMISE[length=%d, hend=%d, stream=%d]",
+ (int)frame->hd.length,
+ !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
+ frame->hd.stream_id);
+ }
+ case NGHTTP2_PING: {
+ return apr_snprintf(buffer, maxlen,
+ "PING[length=%d, ack=%d, stream=%d]",
+ (int)frame->hd.length,
+ frame->hd.flags&NGHTTP2_FLAG_ACK,
+ frame->hd.stream_id);
+ }
+ case NGHTTP2_GOAWAY: {
+ size_t len = (frame->goaway.opaque_data_len < s_len)?
+ frame->goaway.opaque_data_len : s_len-1;
+ memcpy(scratch, frame->goaway.opaque_data, len);
+ scratch[len+1] = '\0';
+ return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s']",
+ frame->goaway.error_code, scratch);
+ }
+ case NGHTTP2_WINDOW_UPDATE: {
+ return apr_snprintf(buffer, maxlen,
+ "WINDOW_UPDATE[stream=%d, incr=%d]",
+ frame->hd.stream_id,
+ frame->window_update.window_size_increment);
+ }
+ default:
+ return apr_snprintf(buffer, maxlen,
+ "type=%d[length=%d, flags=%d, stream=%d]",
+ frame->hd.type, (int)frame->hd.length,
+ frame->hd.flags, frame->hd.stream_id);
}
}
+/*******************************************************************************
+ * push policy
+ ******************************************************************************/
+void h2_push_policy_determine(struct h2_request *req, apr_pool_t *p, int push_enabled)
+{
+ h2_push_policy policy = H2_PUSH_NONE;
+ if (push_enabled) {
+ const char *val = apr_table_get(req->headers, "accept-push-policy");
+ if (val) {
+ if (ap_find_token(p, val, "fast-load")) {
+ policy = H2_PUSH_FAST_LOAD;
+ }
+ else if (ap_find_token(p, val, "head")) {
+ policy = H2_PUSH_HEAD;
+ }
+ else if (ap_find_token(p, val, "default")) {
+ policy = H2_PUSH_DEFAULT;
+ }
+ else if (ap_find_token(p, val, "none")) {
+ policy = H2_PUSH_NONE;
+ }
+ else {
+ /* nothing known found in this header, go by default */
+ policy = H2_PUSH_DEFAULT;
+ }
+ }
+ else {
+ policy = H2_PUSH_DEFAULT;
+ }
+ }
+ req->push_policy = policy;
+}
Modified: httpd/httpd/trunk/modules/http2/h2_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_util.h?rev=1729209&r1=1729208&r2=1729209&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_util.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_util.h Mon Feb 8 16:53:45 2016
@@ -28,6 +28,8 @@ size_t h2_util_header_print(char *buffer
void h2_util_camel_case_header(char *s, size_t len);
+int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen);
+
/**
* Count the bytes that all key/value pairs in a table have
* in length (exlucding terminating 0s), plus additional extra per pair.
@@ -40,8 +42,8 @@ apr_size_t h2_util_table_bytes(apr_table
int h2_req_ignore_header(const char *name, size_t len);
int h2_req_ignore_trailer(const char *name, size_t len);
-void h2_req_strip_ignored_header(apr_table_t *headers);
int h2_res_ignore_trailer(const char *name, size_t len);
+int h2_proxy_res_ignore_header(const char *name, size_t len);
/**
* Return != 0 iff the string s contains the token, as specified in
@@ -190,4 +192,15 @@ apr_status_t h2_transfer_brigade(apr_buc
apr_off_t *plen,
int *peos);
+/**
+ * Set the push policy for the given request. Takes request headers into
+ * account, see draft https://tools.ietf.org/html/draft-ruellan-http-accept-push-policy-00
+ * for details.
+ *
+ * @param req the request to determine the policy for
+ * @param p the pool to use
+ * @param push_enabled if HTTP/2 server push is generally enabled for this request
+ */
+void h2_push_policy_determine(struct h2_request *req, apr_pool_t *p, int push_enabled);
+
#endif /* defined(__mod_h2__h2_util__) */
Modified: httpd/httpd/trunk/modules/http2/mod_http2.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_http2.c?rev=1729209&r1=1729208&r2=1729209&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_http2.c (original)
+++ httpd/httpd/trunk/modules/http2/mod_http2.c Mon Feb 8 16:53:45 2016
@@ -72,7 +72,7 @@ static int h2_post_config(apr_pool_t *p,
apr_pool_t *ptemp, server_rec *s)
{
void *data = NULL;
- const char *mod_h2_init_key = "mod_h2_init_counter";
+ const char *mod_h2_init_key = "mod_http2_init_counter";
nghttp2_info *ngh2;
apr_status_t status;
(void)plog;(void)ptemp;
Modified: httpd/httpd/trunk/modules/http2/mod_http2.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_http2.h?rev=1729209&r1=1729208&r2=1729209&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_http2.h (original)
+++ httpd/httpd/trunk/modules/http2/mod_http2.h Mon Feb 8 16:53:45 2016
@@ -13,8 +13,8 @@
* limitations under the License.
*/
-#ifndef mod_http2_mod_http2_h
-#define mod_http2_mod_http2_h
+#ifndef __MOD_HTTP2_H__
+#define __MOD_HTTP2_H__
/** The http2_var_lookup() optional function retrieves HTTP2 environment
* variables. */
Added: httpd/httpd/trunk/modules/http2/mod_proxy_http2.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_proxy_http2.c?rev=1729209&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_proxy_http2.c (added)
+++ httpd/httpd/trunk/modules/http2/mod_proxy_http2.c Mon Feb 8 16:53:45 2016
@@ -0,0 +1,395 @@
+/* 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 <nghttp2/nghttp2.h>
+
+#include <httpd.h>
+#include <mod_proxy.h>
+
+
+#include "mod_proxy_http2.h"
+#include "h2_request.h"
+#include "h2_util.h"
+#include "h2_version.h"
+#include "h2_proxy_session.h"
+
+static void register_hook(apr_pool_t *p);
+
+AP_DECLARE_MODULE(proxy_http2) = {
+ STANDARD20_MODULE_STUFF,
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ NULL, /* command apr_table_t */
+ register_hook /* register hooks */
+};
+
+static int h2_proxy_post_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ void *data = NULL;
+ const char *init_key = "mod_proxy_http2_init_counter";
+ nghttp2_info *ngh2;
+ apr_status_t status = APR_SUCCESS;
+ (void)plog;(void)ptemp;
+
+ apr_pool_userdata_get(&data, init_key, s->process->pool);
+ if ( data == NULL ) {
+ apr_pool_userdata_set((const void *)1, init_key,
+ apr_pool_cleanup_null, s->process->pool);
+ return APR_SUCCESS;
+ }
+
+ ngh2 = nghttp2_version(0);
+ ap_log_error( APLOG_MARK, APLOG_INFO, 0, s, APLOGNO()
+ "mod_proxy_http2 (v%s, nghttp2 %s), initializing...",
+ MOD_HTTP2_VERSION, ngh2? ngh2->version_str : "unknown");
+
+ return status;
+}
+
+/**
+ * canonicalize the url into the request, if it is meant for us.
+ * slightly modified copy from mod_http
+ */
+static int proxy_http2_canon(request_rec *r, char *url)
+{
+ char *host, *path, sport[7];
+ char *search = NULL;
+ const char *err;
+ const char *scheme;
+ const char *http_scheme;
+ apr_port_t port, def_port;
+
+ /* ap_port_of_scheme() */
+ if (ap_casecmpstrn(url, "h2c:", 4) == 0) {
+ url += 4;
+ scheme = "h2c";
+ http_scheme = "http";
+ }
+ else if (ap_casecmpstrn(url, "h2:", 3) == 0) {
+ url += 3;
+ scheme = "h2";
+ http_scheme = "https";
+ }
+ else {
+ return DECLINED;
+ }
+ port = def_port = ap_proxy_port_of_scheme(http_scheme);
+
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+ "HTTP2: canonicalising URL %s", url);
+
+ /* do syntatic check.
+ * We break the URL into host, port, path, search
+ */
+ err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
+ if (err) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO()
+ "error parsing URL %s: %s", url, err);
+ return HTTP_BAD_REQUEST;
+ }
+
+ /*
+ * now parse path/search args, according to rfc1738:
+ * process the path.
+ *
+ * In a reverse proxy, our URL has been processed, so canonicalise
+ * unless proxy-nocanon is set to say it's raw
+ * In a forward proxy, we have and MUST NOT MANGLE the original.
+ */
+ switch (r->proxyreq) {
+ default: /* wtf are we doing here? */
+ case PROXYREQ_REVERSE:
+ if (apr_table_get(r->notes, "proxy-nocanon")) {
+ path = url; /* this is the raw path */
+ }
+ else {
+ path = ap_proxy_canonenc(r->pool, url, strlen(url),
+ enc_path, 0, r->proxyreq);
+ search = r->args;
+ }
+ break;
+ case PROXYREQ_PROXY:
+ path = url;
+ break;
+ }
+
+ if (path == NULL) {
+ return HTTP_BAD_REQUEST;
+ }
+
+ if (port != def_port) {
+ apr_snprintf(sport, sizeof(sport), ":%d", port);
+ }
+ else {
+ sport[0] = '\0';
+ }
+
+ if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
+ host = apr_pstrcat(r->pool, "[", host, "]", NULL);
+ }
+ r->filename = apr_pstrcat(r->pool, "proxy:", scheme, "://", host, sport,
+ "/", path, (search) ? "?" : "", (search) ? search : "", NULL);
+ return OK;
+}
+
+static apr_status_t proxy_http2_cleanup(const char *scheme, request_rec *r,
+ proxy_conn_rec *backend)
+{
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "cleanup, releasing connection");
+ ap_proxy_release_connection(scheme, backend, r->server);
+ return OK;
+}
+
+static
+int proxy_http2_process_stream(apr_pool_t *p, const char *url, request_rec *r,
+ proxy_conn_rec **pp_conn, proxy_worker *worker,
+ proxy_server_conf *conf, char *server_portstr,
+ int flushall)
+{
+ int rv = APR_ENOTIMPL;
+ proxy_conn_rec *p_conn = *pp_conn;
+ h2_proxy_session *session;
+ h2_proxy_stream *stream;
+
+ session = h2_proxy_session_setup(r, *pp_conn, conf);
+ if (!session) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, p_conn->connection,
+ "session unavailable");
+ return HTTP_SERVICE_UNAVAILABLE;
+ }
+
+ /* TODO
+ * - enter http2 client processing loop:
+ * - send any input in datasource callback from r->input_filters
+ * - await response HEADERs
+ * - send any DATA to r->output_filters
+ * - on stream close, check for missing response
+ * - on certain errors, mark connection for close
+ */
+ rv = h2_proxy_session_open_stream(session, url, r, &stream);
+ if (rv == OK) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+ "process stream(%d): %s %s%s, original: %s",
+ stream->id, stream->req->method,
+ stream->req->authority, stream->req->path,
+ r->the_request);
+ rv = h2_proxy_stream_process(stream);
+ }
+
+ if (rv != OK) {
+ conn_rec *c = r->connection;
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO()
+ "pass request body failed to %pI (%s) from %s (%s)",
+ p_conn->addr, p_conn->hostname ? p_conn->hostname: "",
+ c->client_ip, c->remote_host ? c->remote_host: "");
+ }
+
+ return rv;
+}
+
+static int proxy_http2_handler(request_rec *r,
+ proxy_worker *worker,
+ proxy_server_conf *conf,
+ char *url,
+ const char *proxyname,
+ apr_port_t proxyport)
+{
+ const char *proxy_function;
+ proxy_conn_rec *backend;
+ char *locurl = url, *u, *firsturl;
+ apr_size_t slen;
+ int is_ssl = 0, retry = 0;
+ int flushall = 0;
+ int status;
+ char server_portstr[32];
+ conn_rec *c = r->connection;
+ apr_pool_t *p = r->pool;
+ apr_uri_t *uri = apr_palloc(p, sizeof(*uri));
+ const char *ssl_hostname;
+
+ /* find the scheme */
+ if ((url[0] != 'h' && url[0] != 'H') || url[1] != '2') {
+ return DECLINED;
+ }
+ u = strchr(url, ':');
+ if (u == NULL || u[1] != '/' || u[2] != '/' || u[3] == '\0') {
+ return DECLINED;
+ }
+ slen = (u - url);
+ switch(slen) {
+ case 2:
+ proxy_function = "H2";
+ is_ssl = 1;
+ break;
+ case 3:
+ if (url[2] != 'c' && url[2] != 'C') {
+ return DECLINED;
+ }
+ proxy_function = "H2C";
+ break;
+ default:
+ return DECLINED;
+ }
+
+ if (apr_table_get(r->subprocess_env, "proxy-flushall")) {
+ flushall = 1;
+ }
+
+ /* scheme says, this is for us. */
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "H2: serving URL %s", url);
+
+ /* Get a proxy_conn_rec from the worker, might be a new one, might
+ * be one still open from another request, or it might fail if the
+ * worker is stopped or in error. */
+ if ((status = ap_proxy_acquire_connection(proxy_function, &backend,
+ worker, r->server)) != OK) {
+ goto cleanup;
+ }
+
+ backend->is_ssl = is_ssl;
+ if (is_ssl) {
+ /* If there is still some data on an existing ssl connection, now
+ * would be a good timne to get rid of it. */
+ ap_proxy_ssl_connection_cleanup(backend, r);
+ }
+
+ while (retry < 2) {
+ conn_rec *backconn;
+
+ /* Step One: Determine the URL to connect to (might be a proxy),
+ * initialize the backend accordingly and determine the server
+ * port string we can expect in responses. */
+ if ((status = ap_proxy_determine_connection(p, r, conf, worker, backend,
+ uri, &locurl, proxyname,
+ proxyport, server_portstr,
+ sizeof(server_portstr))) != OK) {
+ break;
+ }
+
+ if (!ssl_hostname && backend->ssl_hostname) {
+ /* When reusing connections and finding sockets closed, the proxy
+ * framework loses the ssl_hostname setting. This is vital for us,
+ * so we save it once it is known. */
+ ssl_hostname = apr_pstrdup(r->pool, backend->ssl_hostname);
+ }
+
+ if (!retry) {
+ firsturl = locurl;
+ /* http does a prefetch here, so that it immediately can start sending
+ * when the backend connection comes online. This minimizes the risk of
+ * reusing a connection only to experience a keepalive close.
+ */
+ }
+ else {
+ /* On a retry, we'd expect to see the same url again */
+ AP_DEBUG_ASSERT(strcmp(firsturl, locurl) == 0);
+ }
+
+ /* Step Two: Make the Connection (or check that an already existing
+ * socket is still usable). On success, we have a socket connected to
+ * backend->hostname. */
+ if (ap_proxy_connect_backend(proxy_function, backend, worker, r->server)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO()
+ "H2: failed to make connection to backend: %s",
+ backend->hostname);
+ status = HTTP_SERVICE_UNAVAILABLE;
+ break;
+ }
+
+ /* Step Three: Create conn_rec for the socket we have open now. */
+ backconn = backend->connection;
+ if (!backconn) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO()
+ "setup new connection: is_ssl=%d %s %s %s, was %s",
+ backend->is_ssl,
+ backend->ssl_hostname, r->hostname, backend->hostname,
+ ssl_hostname);
+ if ((status = ap_proxy_connection_create(proxy_function, backend,
+ c, r->server)) != OK) {
+ break;
+ }
+ backconn = backend->connection;
+
+ /*
+ * On SSL connections set a note on the connection what CN is
+ * requested, such that mod_ssl can check if it is requested to do
+ * so.
+ */
+ if (ssl_hostname) {
+ apr_table_setn(backend->connection->notes,
+ "proxy-request-hostname", ssl_hostname);
+ }
+
+ if (backend->is_ssl) {
+ apr_table_setn(backend->connection->notes,
+ "proxy-request-alpn-protos", "h2");
+ }
+
+ /* Step Three-and-a-Half: See if the socket is still connected (if
+ * desired). Note: Since ap_proxy_connect_backend just above does
+ * the same check (unconditionally), this step is not required when
+ * backend's socket/connection is reused (ie. no Step Three).
+ */
+ if (worker->s->ping_timeout_set && worker->s->ping_timeout < 0 &&
+ !ap_proxy_is_socket_connected(backend->sock)) {
+ backend->close = 1;
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, status, r, APLOGNO()
+ "socket check failed to %pI (%s)",
+ worker->cp->addr, worker->s->hostname);
+ retry++;
+ continue;
+ }
+ }
+
+ /* Step Four: Send the Request in a new HTTP/2 stream and
+ * loop until we got the response or encounter errors.
+ */
+ if ((status = proxy_http2_process_stream(p, url, r, &backend, worker,
+ conf, server_portstr,
+ flushall)) != OK) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO()
+ "H2: failed to process request: %s",
+ r->the_request);
+ backend->close = 1;
+ if (backend) {
+ proxy_run_detach_backend(r, backend);
+ }
+ }
+ break;
+ }
+
+ /* clean up before return */
+cleanup:
+ if (backend) {
+ if (status != OK) {
+ backend->close = 1;
+ }
+ proxy_http2_cleanup(proxy_function, r, backend);
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_TRACE1, status, r, "leaving handler");
+ return status;
+}
+
+static void register_hook(apr_pool_t *p)
+{
+ ap_hook_post_config(h2_proxy_post_config, NULL, NULL, APR_HOOK_MIDDLE);
+
+ proxy_hook_scheme_handler(proxy_http2_handler, NULL, NULL, APR_HOOK_FIRST);
+ proxy_hook_canon_handler(proxy_http2_canon, NULL, NULL, APR_HOOK_FIRST);
+}
+
Added: httpd/httpd/trunk/modules/http2/mod_proxy_http2.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_proxy_http2.h?rev=1729209&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_proxy_http2.h (added)
+++ httpd/httpd/trunk/modules/http2/mod_proxy_http2.h Mon Feb 8 16:53:45 2016
@@ -0,0 +1,20 @@
+/* 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_PROXY_HTTP2_H__
+#define __MOD_PROXY_HTTP2_H__
+
+
+#endif