h2_conn.lo dnl
h2_conn_io.lo dnl
h2_ctx.lo dnl
+h2_filter.lo dnl
h2_from_h1.lo dnl
h2_h2.lo dnl
h2_io.lo dnl
#include "h2_private.h"
#include "h2_config.h"
#include "h2_ctx.h"
+#include "h2_filter.h"
#include "h2_mplx.h"
#include "h2_session.h"
#include "h2_stream.h"
const 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 maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);
int max_threads_per_child = 0;
int idle_secs = 0;
int i;
workers = h2_workers_create(s, pool, minw, maxw);
idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
h2_workers_set_max_idle_secs(workers, idle_secs);
-
+
+ ap_register_input_filter("H2_IN", h2_filter_core_input,
+ NULL, AP_FTYPE_CONNECTION);
+
return status;
}
apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
{
h2_session *session;
+ h2_filter_core_in *in;
if (!workers) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911)
}
h2_ctx_session_set(ctx, session);
+
+ in = apr_pcalloc(session->pool, sizeof(*in));
+ in->session = session;
+ ap_add_input_filter("H2_IN", in, r, c);
+
ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c);
return APR_SUCCESS;
h2_session *session;
session = h2_ctx_session_get(ctx);
+ if (session->c->cs) {
+ session->c->cs->sense = CONN_SENSE_DEFAULT;
+ }
status = h2_session_process(session, async);
+ if (session->c->cs) {
+ session->c->cs->state = CONN_STATE_WRITE_COMPLETION;
+ }
+
if (status == APR_EOF) {
ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
"h2_session(%ld): done", session->id);
/* hereafter session will be gone */
}
else {
- session->c->data_in_input_filters = 0;
session->c->keepalive = AP_CONN_KEEPALIVE;
}
- if (session->c->cs) {
- session->c->cs->state = CONN_STATE_WRITE_COMPLETION;
- }
-
return DONE;
}
break;
}
- /* TODO: we simulate that we had already a request on this connection.
- * This keeps the mod_ssl SNI vs. Host name matcher from answering
- * 400 Bad Request
- * when names do not match. We prefer a predictable 421 status.
- */
+ /* Simulate that we had already a request on this connection. */
task->c->keepalives = 1;
return APR_SUCCESS;
#include "h2_config.h"
#include "h2_conn_io.h"
#include "h2_h2.h"
+#include "h2_session.h"
#include "h2_util.h"
#define TLS_DATA_MAX (16*1024)
apr_pool_t *pool)
{
io->connection = c;
- io->input = apr_brigade_create(pool, c->bucket_alloc);
io->output = apr_brigade_create(pool, c->bucket_alloc);
io->buflen = 0;
io->is_tls = h2_h2_is_tls(c);
return io->bufsize > 0;
}
-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) {
- apr_size_t consumed = 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);
- }
-
- 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);
- apr_brigade_cleanup(io->input);
- if (status != APR_SUCCESS || done) {
- return status;
- }
- }
-
- /* 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);
- }
-
- /* TODO: replace this with a connection filter itself, so that we
- * no longer need to transfer incoming buckets to our own brigade.
- */
- status = ap_get_brigade(io->connection->input_filters,
- io->input, AP_MODE_READBYTES,
- block, 64 * 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 pass_out(apr_bucket_brigade *bb, void *ctx)
{
h2_conn_io *io = (h2_conn_io*)ctx;
#define __mod_h2__h2_conn_io__
struct h2_config;
+struct h2_session;
/* 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
*/
typedef struct {
conn_rec *connection;
- apr_bucket_brigade *input;
apr_bucket_brigade *output;
int is_tls;
int h2_conn_io_is_buffered(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);
--- /dev/null
+/* 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_log.h>
+#include <http_connection.h>
+#include <scoreboard.h>
+
+#include "h2_private.h"
+#include "h2_session.h"
+#include "h2_conn_io.h"
+#include "h2_util.h"
+
+#include "h2_filter.h"
+
+
+static apr_status_t consume_brigade(h2_filter_core_in *in,
+ apr_bucket_brigade *bb,
+ apr_read_type_e block)
+{
+ apr_status_t status = APR_SUCCESS;
+ apr_size_t readlen = 0;
+
+ while (status == APR_SUCCESS && !APR_BRIGADE_EMPTY(bb)) {
+
+ apr_bucket* bucket = APR_BRIGADE_FIRST(bb);
+ 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) {
+ apr_size_t consumed = 0;
+
+ status = h2_session_receive(in->session, bucket_data,
+ bucket_length, &consumed);
+ 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_filter_core_input(ap_filter_t* f,
+ apr_bucket_brigade* brigade,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t readbytes)
+{
+ h2_filter_core_in *in = f->ctx;
+ apr_status_t status = APR_SUCCESS;
+
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, f->c,
+ "core_input: read, block=%d, mode=%d, readbytes=%ld",
+ block, mode, (long)readbytes);
+
+ if (mode == AP_MODE_INIT || mode == AP_MODE_SPECULATIVE) {
+ return ap_get_brigade(f->next, brigade, mode, block, readbytes);
+ }
+
+ if (mode != AP_MODE_READBYTES) {
+ return (block == APR_BLOCK_READ)? APR_SUCCESS : APR_EAGAIN;
+ }
+
+ if (!f->bb) {
+ f->bb = apr_brigade_create(in->session->pool, f->c->bucket_alloc);
+ }
+
+ if (APR_BRIGADE_EMPTY(f->bb)) {
+ /* 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(f->c->sbh,
+ SERVER_BUSY_KEEPALIVE, f->c);
+ }
+ else {
+ ap_update_child_status(f->c->sbh, SERVER_BUSY_READ, NULL);
+ }
+
+ status = ap_get_brigade(f->next, f->bb, AP_MODE_READBYTES,
+ block, readbytes);
+ }
+
+ switch (status) {
+ case APR_SUCCESS:
+ return consume_brigade(in, f->bb, block);
+ case APR_EOF:
+ case APR_EAGAIN:
+ break;
+ default:
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, f->c,
+ "h2_conn_io: error reading");
+ break;
+ }
+ return status;
+}
--- /dev/null
+/* 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_filter__
+#define __mod_h2__h2_filter__
+
+struct h2_session;
+
+typedef struct {
+ struct h2_session *session;
+} h2_filter_core_in;
+
+apr_status_t h2_filter_core_input(ap_filter_t* filter,
+ apr_bucket_brigade* brigade,
+ ap_input_mode_t mode,
+ apr_read_type_e block,
+ apr_off_t readbytes);
+
+
+#endif /* __mod_h2__h2_filter__ */
/*******************************************************************************
* Hooks for processing incoming connections:
- * - pre_conn_before_tls switches SSL off for stream connections
* - process_conn take over connection in case of h2
*/
static int h2_h2_process_conn(conn_rec* c);
int h2_allows_h2_direct(conn_rec *c)
{
const h2_config *cfg = h2_config_get(c);
+ int is_tls = h2_h2_is_tls(c);
+ const char *needed_protocol = is_tls? "h2" : "h2c";
int h2_direct = h2_config_geti(cfg, H2_CONF_DIRECT);
if (h2_direct < 0) {
- if (h2_h2_is_tls(c)) {
- /* disabled by default on TLS */
- h2_direct = 0;
- }
- else {
- /* enabled if "Protocols h2c" is configured */
- h2_direct = ap_is_allowed_protocol(c, NULL, NULL, "h2c");
- }
+ h2_direct = is_tls? 0 : 1;
}
- return !!h2_direct;
+ return (h2_direct
+ && ap_is_allowed_protocol(c, NULL, NULL, needed_protocol));
}
int h2_allows_h2_upgrade(conn_rec *c)
return DECLINED;
}
- if (!ctx && c->keepalives == 0
- && !strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c))
- && h2_allows_h2_direct(c)
- && h2_is_acceptable_connection(c, 1)) {
- /* Fresh connection still is on http/1.1 and H2Direct is enabled.
- * Otherwise connection is in a fully acceptable state.
- * -> peek at the first 24 incoming bytes
- */
- apr_bucket_brigade *temp;
- char *s = NULL;
- apr_size_t slen;
+ if (!ctx && c->keepalives == 0) {
+ const char *proto = ap_get_protocol(c);
- temp = apr_brigade_create(c->pool, c->bucket_alloc);
- status = ap_get_brigade(c->input_filters, temp,
- AP_MODE_SPECULATIVE, APR_BLOCK_READ, 24);
-
- if (status != APR_SUCCESS) {
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
- "h2_h2, error reading 24 bytes speculative");
- apr_brigade_destroy(temp);
- return DECLINED;
+ if (APLOGctrace1(c)) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn, "
+ "new connection using protocol '%s', direct=%d, "
+ "tls acceptable=%d", proto, h2_allows_h2_direct(c),
+ h2_is_acceptable_connection(c, 1));
}
- apr_brigade_pflatten(temp, &s, &slen, c->pool);
- if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) {
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
- "h2_h2, direct mode detected");
- if (!ctx) {
- ctx = h2_ctx_get(c, 1);
+ if (!strcmp(AP_PROTOCOL_HTTP1, proto)
+ && h2_allows_h2_direct(c)
+ && h2_is_acceptable_connection(c, 1)) {
+ /* Fresh connection still is on http/1.1 and H2Direct is enabled.
+ * Otherwise connection is in a fully acceptable state.
+ * -> peek at the first 24 incoming bytes
+ */
+ apr_bucket_brigade *temp;
+ char *s = NULL;
+ apr_size_t slen;
+
+ temp = apr_brigade_create(c->pool, c->bucket_alloc);
+ status = ap_get_brigade(c->input_filters, temp,
+ AP_MODE_SPECULATIVE, APR_BLOCK_READ, 24);
+
+ if (status != APR_SUCCESS) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
+ "h2_h2, error reading 24 bytes speculative");
+ apr_brigade_destroy(temp);
+ return DECLINED;
}
- h2_ctx_protocol_set(ctx, h2_h2_is_tls(c)? "h2" : "h2c");
- }
- else {
- ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
- "h2_h2, not detected in %d bytes: %s",
- (int)slen, s);
+
+ apr_brigade_pflatten(temp, &s, &slen, c->pool);
+ if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+ "h2_h2, direct mode detected");
+ if (!ctx) {
+ ctx = h2_ctx_get(c, 1);
+ }
+ h2_ctx_protocol_set(ctx, h2_h2_is_tls(c)? "h2" : "h2c");
+ }
+ else {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+ "h2_h2, not detected in %d bytes: %s",
+ (int)slen, s);
+ }
+
+ apr_brigade_destroy(temp);
}
-
- apr_brigade_destroy(temp);
}
if (ctx) {
return session->last_stream;
}
-/* h2_io_on_read_cb implementation that offers the data read
- * directly to the session for consumption.
- */
-static apr_status_t session_receive(const char *data, apr_size_t len,
- apr_size_t *readlen, int *done,
- void *puser)
-{
- h2_session *session = (h2_session *)puser;
- AP_DEBUG_ASSERT(session);
- if (len > 0) {
- ssize_t n = nghttp2_session_mem_recv(session->ngh2,
- (const uint8_t *)data, len);
- if (n < 0) {
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL,
- session->c,
- "h2_session: nghttp2_session_mem_recv error %d",
- (int)n);
- if (nghttp2_is_fatal((int)n)) {
- *done = 1;
- h2_session_abort_int(session, (int)n);
- return APR_EGENERAL;
- }
- }
- else {
- *readlen = n;
- }
- }
- return APR_SUCCESS;
-}
-
apr_status_t h2_session_close(h2_session *session)
{
AP_DEBUG_ASSERT(session);
* to find out how much of the requested length we can send without
* blocking.
* Indicate EOS when we encounter it or DEFERRED if the stream
- * should be suspended.
- * TODO: for handling of TRAILERS, the EOF indication needs
- * to be aware of that.
+ * should be suspended. Beware of trailers.
*/
(void)ng2s;
return APR_SUCCESS;
}
+apr_status_t h2_session_receive(h2_session *session,
+ const char *data, apr_size_t len,
+ apr_size_t *readlen)
+{
+ if (len > 0) {
+ ssize_t n = nghttp2_session_mem_recv(session->ngh2,
+ (const uint8_t *)data, len);
+ if (n < 0) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL,
+ session->c,
+ "h2_session: nghttp2_session_mem_recv error %d",
+ (int)n);
+ if (nghttp2_is_fatal((int)n)) {
+ h2_session_abort(session, 0, (int)n);
+ return APR_EGENERAL;
+ }
+ }
+ else {
+ *readlen = n;
+ }
+ }
+ return APR_SUCCESS;
+}
+
+
+
static apr_status_t h2_session_read(h2_session *session, int block)
{
- apr_status_t status;
- status = h2_conn_io_read(&session->io,
- block? APR_BLOCK_READ : APR_NONBLOCK_READ,
- session_receive, session);
- switch (status) {
- case APR_SUCCESS:
- /* successful read, reset our idle timers */
- session->wait_micros = 0;
- break;
- case APR_EAGAIN:
- break;
- default:
- if (APR_STATUS_IS_ETIMEDOUT(status)
- || APR_STATUS_IS_ECONNABORTED(status)
- || APR_STATUS_IS_ECONNRESET(status)
- || APR_STATUS_IS_EOF(status)
- || APR_STATUS_IS_EBADF(status)) {
- /* common status for a client that has left */
- ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
- "h2_session(%ld): terminating",
- session->id);
- /* Stolen from mod_reqtimeout to speed up lingering when
- * a read timeout happened.
- */
- apr_table_setn(session->c->notes, "short-lingering-close", "1");
- }
- else {
- /* uncommon status, log on INFO so that we see this */
- ap_log_cerror( APLOG_MARK, APLOG_INFO, status, session->c,
- APLOGNO(02950)
- "h2_session(%ld): error reading, terminating",
- session->id);
- }
- h2_session_abort(session, status, 0);
- break;
+ while (1) {
+ apr_status_t status;
+
+ /* H2_IN filter handles all incoming data against the session.
+ * We just pull at the filter chain to make it happen */
+ status = ap_get_brigade(session->c->input_filters,
+ session->bbtmp, AP_MODE_READBYTES,
+ block? APR_BLOCK_READ : APR_NONBLOCK_READ,
+ APR_BUCKET_BUFF_SIZE);
+ /* get rid of any possible data we do not expect to get */
+ apr_brigade_cleanup(session->bbtmp);
+
+ switch (status) {
+ case APR_SUCCESS:
+ /* successful read, reset our idle timers */
+ session->wait_micros = 0;
+ if (block) {
+ return APR_SUCCESS;
+ }
+ break;
+ case APR_EAGAIN:
+ return status;
+ default:
+ if (APR_STATUS_IS_ETIMEDOUT(status)
+ || APR_STATUS_IS_ECONNABORTED(status)
+ || APR_STATUS_IS_ECONNRESET(status)
+ || APR_STATUS_IS_EOF(status)
+ || APR_STATUS_IS_EBADF(status)) {
+ /* common status for a client that has left */
+ ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
+ "h2_session(%ld): terminating",
+ session->id);
+ /* Stolen from mod_reqtimeout to speed up lingering when
+ * a read timeout happened.
+ */
+ apr_table_setn(session->c->notes, "short-lingering-close", "1");
+ }
+ else {
+ /* uncommon status, log on INFO so that we see this */
+ ap_log_cerror( APLOG_MARK, APLOG_INFO, status, session->c,
+ APLOGNO(02950)
+ "h2_session(%ld): error reading, terminating",
+ session->id);
+ }
+ h2_session_abort(session, status, 0);
+ return status;
+ }
}
- return status;
}
static apr_status_t h2_session_submit(h2_session *session)
}
got_streams = !h2_stream_set_is_empty(session->streams);
+
+ /* If we want client data, see if some is there. */
+ if (nghttp2_session_want_read(session->ngh2)) {
+ int idle = (session->frames_received > 2 && !got_streams);
+ int may_block = ((session->frames_received <= 1)
+ || idle
+ || (!h2_stream_set_has_unsubmitted(session->streams)
+ && !h2_stream_set_has_suspended(session->streams)));
+
+ status = h2_session_read(session, may_block && !async);
+
+ ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, session->c,
+ "h2_session(%ld): process -> read", session->id);
+ if (status == APR_SUCCESS) {
+ have_read = 1;
+ got_streams = !h2_stream_set_is_empty(session->streams);
+ if (session->reprioritize) {
+ ap_log_cerror( APLOG_MARK, APLOG_TRACE2, status, session->c,
+ "h2_session(%ld): process -> reprioritize", session->id);
+ h2_mplx_reprioritize(session->mplx, stream_pri_cmp, session);
+ session->reprioritize = 0;
+ }
+ }
+ else if (status == APR_EAGAIN) {
+ /* FIXME: disabling this as event currently discards
+ * connections on load when we return. */
+ if (async && may_block && 0) {
+ /* There is nothing to read and we are in a state where we
+ * have nothing to write until new input comes. Return to
+ * our caller so that the MPM may schedule us again when
+ * read seems possible.
+ */
+ ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, session->c,
+ "h2_session(%ld): process -> BLOCK_READ, "
+ "frames_received=%d, got_streams=%d, "
+ "have_written=%d",
+ session->id, (int)session->frames_received,
+ (int)got_streams, (int)have_written);
+ h2_conn_io_flush(&session->io);
+ if (session->c->cs) {
+ session->c->cs->sense = CONN_SENSE_WANT_READ;
+ }
+ return APR_SUCCESS;
+ }
+ }
+ else {
+ ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
+ "h2_session(%ld): failed read", session->id);
+ return status;
+ }
+ }
+
if (got_streams) {
ap_log_cerror( APLOG_MARK, APLOG_TRACE2, status, session->c,
"h2_session(%ld): process -> check resume", session->id);
}
}
- /* If we want client data, see if some is there. */
- if (nghttp2_session_want_read(session->ngh2)) {
- int idle = (session->frames_received > 2 && !got_streams);
- int may_block = ((session->frames_received <= 1)
- || (idle && !async)
- || (!have_written && !h2_stream_set_has_unsubmitted(session->streams)
- && !h2_stream_set_has_suspended(session->streams)));
- status = h2_session_read(session, may_block);
-
- ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, session->c,
- "h2_session(%ld): process -> read", session->id);
- if (status == APR_SUCCESS) {
- have_read = 1;
- got_streams = !h2_stream_set_is_empty(session->streams);
- if (session->reprioritize) {
- ap_log_cerror( APLOG_MARK, APLOG_TRACE2, status, session->c,
- "h2_session(%ld): process -> reprioritize", session->id);
- h2_mplx_reprioritize(session->mplx, stream_pri_cmp, session);
- session->reprioritize = 0;
- }
- }
- else if (status == APR_EAGAIN && idle && async) {
- /* There is nothing to read and we are in a state where we
- * have nothing to write until new input comes. Return to
- * our caller so that the MPM may schedule us again when
- * read seems possible.
- */
- ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, session->c,
- "h2_session(%ld): process -> BLOCK_READ, "
- "frames_received=%d, got_streams=%d, "
- "have_written=%d",
- session->id, (int)session->frames_received,
- (int)got_streams, (int)have_written);
- if (have_written) {
- h2_conn_io_flush(&session->io);
- }
- if (session->c->cs) {
- session->c->cs->state = CONN_STATE_WRITE_COMPLETION;
- session->c->cs->sense = (have_written?
- CONN_SENSE_DEFAULT
- : CONN_SENSE_WANT_READ);
- }
- return APR_SUCCESS;
- }
- }
-
if (!have_read && !have_written) {
if (session->wait_micros == 0) {
session->wait_micros = 10;
* of 'h2c', NULL otherwise */
server_rec *s; /* server/vhost we're starting on */
const struct h2_config *config; /* Relevant config for this session */
+
int started;
int aborted; /* this session is being aborted */
int reprioritize; /* scheduled streams priority needs to
struct apr_thread_cond_t *iowait; /* our cond when trywaiting for data */
h2_conn_io io; /* io on httpd conn filters */
+
struct h2_mplx *mplx; /* multiplexer for stream data */
struct h2_stream *last_stream; /* last stream worked with */
h2_session *h2_session_rcreate(request_rec *r, struct h2_ctx *ctx,
struct h2_workers *workers);
+/**
+ * Recieve len bytes of raw HTTP/2 input data. Return the amount
+ * consumed and if the session is done.
+ */
+apr_status_t h2_session_receive(h2_session *session,
+ const char *data, apr_size_t len,
+ apr_size_t *readlen);
+
/**
* Process the given HTTP/2 session until it is ended or a fatal
* error occured.
# End Source File
# Begin Source File
+SOURCE=./h2_filter.c
+# End Source File
+# Begin Source File
+
SOURCE=./h2_from_h1.c
# End Source File
# Begin Source File