Author: rhuijben Date: Mon Nov 16 12:44:14 2015 New Revision: 1714582 URL: http://svn.apache.org/viewvc?rev=1714582&view=rev Log: On incoming connections: implement switching to the http2 protocol. This adds the protocol switching to the connection and http2 protocol, but doesn't add request routing for http2 on the server side yet.
Add a test that excercises this code, but currently expect a failure to handle the actual request. * incoming.c (client_connected): Make the peeking optional. (perform_peek_protocol): Use a slightly larger prefix. Switch protocol. (read_from_client): Do the right thing if protocol was switched. (serf__client_flush): Rename to... (serf__incoming_client_flush): ... this and make available within serf. (write_to_client): Update caller. (hangup_client, serf_incoming_set_framing_type): New function. (serf__process_client): Delegate work to protocol specific callbacks. (serf_incoming_create2): Initialize protocol support. * protocols/http2_protocol.c (http2_protocol_read, http2_protocol_write, http2_protocol_hangup, http2_protocol_teardown): Rename to... (http2_outgoing_read, http2_outgoing_write, http2_outgoing_hangup, http2_outgoing_teardown): ... this. (http2_incoming_read, http2_incoming_write, http2_incoming_hangup, http2_incoming_teardown): New prototypes. (serf_http2_protocol_t): Extend a bit. (http2_protocol_cleanup): Use protocol as baton. (serf__http2_protocol_init): Tweak init. (serf__http2_protocol_init_server): New function. (serf_http2__enqueue_frame): Support both client and server. Simplify assuming improved flush handling. (http2_process): Use h2 stream instead of only that in conn. (http2_protocol_read, http2_protocol_write, http2_protocol_hangup, http2_protocol_teardown): Rename to... (http2_outgoing_read, http2_outgoing_write, http2_outgoing_hangup, http2_outgoing_teardown): ... this. (http2_incoming_read, http2_incoming_write, http2_incoming_hangup, http2_incoming_teardown): New functions. * serf.h (serf_incoming_set_framing_type): New function. * serf_private.h (serf_incoming_t): Tweak varname to match outgoing name. Add callbacks. (serf__incoming_client_flush, serf__http2_protocol_init_server): New function. * test/test_server.c (connection_setup_http2, test_listen_http2): New function. Modified: serf/trunk/incoming.c serf/trunk/protocols/http2_protocol.c serf/trunk/serf.h serf/trunk/serf_private.h serf/trunk/test/test_server.c Modified: serf/trunk/incoming.c URL: http://svn.apache.org/viewvc/serf/trunk/incoming.c?rev=1714582&r1=1714581&r2=1714582&view=diff ============================================================================== --- serf/trunk/incoming.c (original) +++ serf/trunk/incoming.c Mon Nov 16 12:44:14 2015 @@ -86,12 +86,15 @@ static apr_status_t client_connected(ser serf_bucket_aggregate_append(client->ostream_head, ostream); - client->proto_peek_bkt = serf_bucket_aggregate_create(client->allocator); - - serf_bucket_aggregate_append( - client->proto_peek_bkt, - serf_bucket_barrier_create(client->stream, - client->allocator)); + if (client->framing_type == SERF_CONNECTION_FRAMING_TYPE_NONE) { + client->proto_peek_bkt = serf_bucket_aggregate_create( + client->allocator); + + serf_bucket_aggregate_append( + client->proto_peek_bkt, + serf_bucket_barrier_create(client->stream, + client->allocator)); + } return status; } @@ -161,8 +164,8 @@ apr_status_t serf_incoming_response_crea apr_status_t perform_peek_protocol(serf_incoming_t *client) { - const char h2prefix[] = "PRI * HTTP/2.0\r\n"; - const apr_size_t h2prefixlen = 16; + const char h2prefix[] = "PRI * HTTP/2.0\r\n\r\n"; + const apr_size_t h2prefixlen = sizeof(h2prefix) - 1; const char *data; apr_size_t len; @@ -183,6 +186,8 @@ apr_status_t perform_peek_protocol(serf_ if (len && memcmp(data, h2prefix, len) != 0) { /* This is not HTTP/2 */ + serf_incoming_set_framing_type(client, + SERF_CONNECTION_FRAMING_TYPE_HTTP1); /* Easy out */ serf_bucket_destroy(client->proto_peek_bkt); @@ -192,7 +197,8 @@ apr_status_t perform_peek_protocol(serf_ } else if (len == h2prefixlen) { /* We have HTTP/2 */ - client->framing = SERF_CONNECTION_FRAMING_TYPE_HTTP2; + serf_incoming_set_framing_type(client, + SERF_CONNECTION_FRAMING_TYPE_HTTP2); serf_bucket_destroy(client->proto_peek_bkt); client->proto_peek_bkt = NULL; @@ -218,6 +224,8 @@ apr_status_t perform_peek_protocol(serf_ if (len && memcmp(data, h2prefix, len)) { /* This is not HTTP/2 */ + serf_incoming_set_framing_type(client, + SERF_CONNECTION_FRAMING_TYPE_HTTP1); /* Put data ahead of other data and do the usual thing */ serf_bucket_aggregate_prepend(client->proto_peek_bkt, @@ -230,7 +238,8 @@ apr_status_t perform_peek_protocol(serf_ } else if (len == h2prefixlen) { /* We have HTTP/2 */ - client->framing = SERF_CONNECTION_FRAMING_TYPE_HTTP2; + serf_incoming_set_framing_type(client, + SERF_CONNECTION_FRAMING_TYPE_HTTP2); /* Put data ahead of other data and do the usual thing */ serf_bucket_aggregate_prepend(client->proto_peek_bkt, @@ -256,6 +265,10 @@ static apr_status_t read_from_client(ser status = perform_peek_protocol(client); if (status) return status; + + /* Did we switch protocol? */ + if (client->perform_read != read_from_client) + return client->perform_read(client); } do { @@ -386,8 +399,8 @@ static apr_status_t no_more_writes(serf_ return APR_SUCCESS; } -static apr_status_t serf__client_flush(serf_incoming_t *client, - bool pump) +apr_status_t serf__incoming_client_flush(serf_incoming_t *client, + bool pump) { apr_status_t status = APR_SUCCESS; apr_status_t read_status = APR_SUCCESS; @@ -464,7 +477,7 @@ static apr_status_t write_to_client(serf { apr_status_t status; - status = serf__client_flush(client, true); + status = serf__incoming_client_flush(client, true); if (APR_STATUS_IS_EAGAIN(status)) return APR_SUCCESS; @@ -478,6 +491,46 @@ static apr_status_t write_to_client(serf return APR_SUCCESS; } +static apr_status_t hangup_client(serf_incoming_t *client) +{ + return APR_ECONNRESET; +} + + +void serf_incoming_set_framing_type( + serf_incoming_t *client, + serf_connection_framing_type_t framing_type) +{ + client->framing_type = framing_type; + + if (client->skt) { + client->dirty_conn = true; + client->ctx->dirty_pollset = true; + client->stop_writing = 0; + + /* Close down existing protocol */ + if (client->protocol_baton && client->perform_teardown) { + client->perform_teardown(client); + client->protocol_baton = NULL; + } + + /* Reset to default */ + client->perform_read = read_from_client; + client->perform_write = write_to_client; + client->perform_hangup = hangup_client; + client->perform_teardown = NULL; + + switch (framing_type) { + case SERF_CONNECTION_FRAMING_TYPE_HTTP2: + serf__http2_protocol_init_server(client); + break; + default: + break; + } + } +} + + apr_status_t serf__process_client(serf_incoming_t *client, apr_int16_t events) { apr_status_t rv; @@ -491,14 +544,17 @@ apr_status_t serf__process_client(serf_i } if ((events & APR_POLLIN) != 0) { - rv = read_from_client(client); + rv = client->perform_read(client); if (rv) { return rv; } } if ((events & APR_POLLHUP) != 0) { - return APR_ECONNRESET; + rv = client->perform_hangup(client); + if (rv) { + return rv; + } } if ((events & APR_POLLERR) != 0) { @@ -506,7 +562,7 @@ apr_status_t serf__process_client(serf_i } if ((events & APR_POLLOUT) != 0) { - rv = write_to_client(client); + rv = client->perform_write(client); if (rv) { return rv; } @@ -591,6 +647,8 @@ apr_status_t serf_incoming_create2( ic->dirty_conn = false; ic->wait_for_connect = true; ic->vec_len = 0; + /* Detect HTTP 1 or 2 via peek operation */ + ic->framing_type = SERF_CONNECTION_FRAMING_TYPE_NONE; ic->setup = setup; ic->setup_baton = setup_baton; @@ -604,6 +662,10 @@ apr_status_t serf_incoming_create2( ic->ssltunnel_ostream = NULL; ic->protocol_baton = NULL; + ic->perform_read = read_from_client; + ic->perform_write = write_to_client; + ic->perform_hangup = hangup_client; + ic->perform_teardown = NULL; ic->current_request = NULL; ic->desc.desc_type = APR_POLL_SOCKET; Modified: serf/trunk/protocols/http2_protocol.c URL: http://svn.apache.org/viewvc/serf/trunk/protocols/http2_protocol.c?rev=1714582&r1=1714581&r2=1714582&view=diff ============================================================================== --- serf/trunk/protocols/http2_protocol.c (original) +++ serf/trunk/protocols/http2_protocol.c Mon Nov 16 12:44:14 2015 @@ -32,16 +32,28 @@ #include "protocols/http2_protocol.h" static apr_status_t -http2_protocol_read(serf_connection_t *conn); +http2_outgoing_read(serf_connection_t *conn); static apr_status_t -http2_protocol_write(serf_connection_t *conn); +http2_outgoing_write(serf_connection_t *conn); static apr_status_t -http2_protocol_hangup(serf_connection_t *conn); +http2_outgoing_hangup(serf_connection_t *conn); static void -http2_protocol_teardown(serf_connection_t *conn); +http2_outgoing_teardown(serf_connection_t *conn); + +static apr_status_t +http2_incoming_read(serf_incoming_t *client); + +static apr_status_t +http2_incoming_write(serf_incoming_t *client); + +static apr_status_t +http2_incoming_hangup(serf_incoming_t *client); + +static void +http2_incoming_teardown(serf_incoming_t *conn); static apr_status_t http2_process(serf_http2_protocol_t *h2); @@ -105,14 +117,16 @@ serf_bucket_create_numberv(serf_bucket_a struct serf_http2_protocol_t { apr_pool_t *pool; - serf_connection_t *conn; - serf_bucket_t *ostream; + serf_connection_t *conn; /* Either CONN or CLIENT is set */ + serf_incoming_t *client; + serf_bucket_t *stream, *ostream; serf_bucket_alloc_t *allocator; serf_http2_processor_t processor; void *processor_baton; serf_bucket_t *read_frame; /* Frame currently being read */ - int in_frame; + bool in_frame; + apr_size_t prefix_left; serf_hpack_table_t *hpack_tbl; serf_config_t *config; @@ -125,7 +139,7 @@ struct serf_http2_protocol_t apr_uint32_t lr_max_concurrent; apr_uint32_t lr_hpack_table_size; apr_int32_t lr_next_streamid; - char lr_push_enabled; + bool lr_push_enabled; /* Remote -> Local. Settings set by us. Acknowledged by other side */ apr_uint32_t rl_default_window; @@ -135,13 +149,13 @@ struct serf_http2_protocol_t apr_uint32_t rl_max_concurrent; apr_uint32_t rl_hpack_table_size; apr_int32_t rl_next_streamid; - char rl_push_enabled; + bool rl_push_enabled; serf_http2_stream_t *first; serf_http2_stream_t *last; int setting_acks; - int enforce_flow_control; + bool enforce_flow_control; serf_bucket_t *continuation_bucket; apr_int32_t continuation_streamid; @@ -157,8 +171,9 @@ http2_bucket_processor(void *baton, static apr_status_t http2_protocol_cleanup(void *state) { - serf_connection_t *conn = state; - serf_http2_protocol_t *h2 = conn->protocol_baton; + serf_http2_protocol_t *h2 = state; + serf_connection_t *conn = h2->conn; + serf_incoming_t *client = h2->client; serf_http2_stream_t *stream, *next; /* First clean out all streams */ @@ -183,7 +198,7 @@ http2_protocol_cleanup(void *state) h2->processor = NULL; h2->processor_baton = NULL; - + } /* Else: The processor (probably a stream) needs to handle this. It usually does that @@ -197,7 +212,11 @@ http2_protocol_cleanup(void *state) } h2->in_frame = FALSE; - conn->protocol_baton = NULL; + if (conn) + conn->protocol_baton = NULL; + if (client) + client->protocol_baton = NULL; + return APR_SUCCESS; } @@ -206,13 +225,14 @@ void serf__http2_protocol_init(serf_conn serf_http2_protocol_t *h2; apr_pool_t *protocol_pool; serf_bucket_t *tmp; - const int WE_ARE_CLIENT = 1; + const bool WE_ARE_CLIENT = true; apr_pool_create(&protocol_pool, conn->pool); h2 = apr_pcalloc(protocol_pool, sizeof(*h2)); h2->pool = protocol_pool; h2->conn = conn; + h2->stream = conn->stream; h2->ostream = conn->ostream_tail; h2->allocator = conn->allocator; h2->config = conn->config; @@ -247,13 +267,13 @@ void serf__http2_protocol_init(serf_conn HTTP2_DEFAULT_HPACK_TABLE_SIZE, protocol_pool); - apr_pool_cleanup_register(protocol_pool, conn, http2_protocol_cleanup, + apr_pool_cleanup_register(protocol_pool, h2, http2_protocol_cleanup, apr_pool_cleanup_null); - conn->perform_read = http2_protocol_read; - conn->perform_write = http2_protocol_write; - conn->perform_hangup = http2_protocol_hangup; - conn->perform_teardown = http2_protocol_teardown; + conn->perform_read = http2_outgoing_read; + conn->perform_write = http2_outgoing_write; + conn->perform_hangup = http2_outgoing_hangup; + conn->perform_teardown = http2_outgoing_teardown; conn->protocol_baton = h2; /* Disable HTTP/1.1 guessing that affects writability */ @@ -261,8 +281,7 @@ void serf__http2_protocol_init(serf_conn conn->max_outstanding_requests = 0; /* Send the HTTP/2 Connection Preface */ - tmp = SERF_BUCKET_SIMPLE_STRING("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", - conn->allocator); + tmp = SERF_BUCKET_SIMPLE_STRING(HTTP2_CONNECTION_PREFIX, h2->allocator); serf_bucket_aggregate_append(h2->ostream, tmp); /* And now a settings frame and a huge window */ @@ -272,7 +291,7 @@ void serf__http2_protocol_init(serf_conn tmp = serf__bucket_http2_frame_create(NULL, HTTP2_FRAME_TYPE_SETTINGS, 0, NULL, NULL, NULL, /* stream: 0 */ h2->lr_max_framesize, - conn->allocator); + h2->allocator); serf_http2__enqueue_frame(h2, tmp, FALSE); @@ -282,13 +301,95 @@ void serf__http2_protocol_init(serf_conn HTTP2_FRAME_TYPE_WINDOW_UPDATE, 0, NULL, NULL, NULL, /* stream: 0 */ h2->lr_max_framesize, - conn->allocator); + h2->allocator); serf_http2__enqueue_frame(h2, tmp, FALSE); h2->rl_window += 0x40000000; /* And update our own administration */ } } +void serf__http2_protocol_init_server(serf_incoming_t *client) +{ + serf_http2_protocol_t *h2; + apr_pool_t *protocol_pool; + serf_bucket_t *tmp; + const int WE_ARE_CLIENT = false; + + apr_pool_create(&protocol_pool, client->pool); + + h2 = apr_pcalloc(protocol_pool, sizeof(*h2)); + h2->pool = protocol_pool; + h2->client = client; + h2->stream = client->stream; + h2->ostream = client->ostream_tail; + h2->allocator = client->allocator; + h2->config = client->config; + + h2->prefix_left = sizeof(HTTP2_CONNECTION_PREFIX) - 1; + + /* Defaults until negotiated */ + h2->rl_default_window = HTTP2_DEFAULT_WINDOW_SIZE; + h2->rl_window = HTTP2_DEFAULT_WINDOW_SIZE; + h2->rl_next_streamid = WE_ARE_CLIENT ? 2 : 1; + h2->rl_max_framesize = HTTP2_DEFAULT_MAX_FRAMESIZE; + h2->rl_max_headersize = APR_UINT32_MAX; + h2->rl_max_concurrent = HTTP2_DEFAULT_MAX_CONCURRENT; + h2->rl_hpack_table_size = HTTP2_DEFAULT_HPACK_TABLE_SIZE; + h2->rl_push_enabled = TRUE; + + h2->lr_default_window = HTTP2_DEFAULT_WINDOW_SIZE; + h2->lr_window = HTTP2_DEFAULT_WINDOW_SIZE; + h2->lr_next_streamid = WE_ARE_CLIENT ? 1 : 2; + h2->lr_max_framesize = HTTP2_DEFAULT_MAX_FRAMESIZE; + h2->lr_max_headersize = APR_UINT32_MAX; + h2->lr_max_concurrent = HTTP2_DEFAULT_MAX_CONCURRENT; + h2->lr_hpack_table_size = HTTP2_DEFAULT_HPACK_TABLE_SIZE; + h2->lr_push_enabled = TRUE; + + h2->setting_acks = 0; + h2->enforce_flow_control = TRUE; + h2->continuation_bucket = NULL; + h2->continuation_streamid = 0; + + h2->first = h2->last = NULL; + + h2->hpack_tbl = serf__hpack_table_create(TRUE, + HTTP2_DEFAULT_HPACK_TABLE_SIZE, + protocol_pool); + + apr_pool_cleanup_register(protocol_pool, h2, http2_protocol_cleanup, + apr_pool_cleanup_null); + + client->perform_read = http2_incoming_read; + client->perform_write = http2_incoming_write; + client->perform_hangup = http2_incoming_hangup; + client->perform_teardown = http2_incoming_teardown; + client->protocol_baton = h2; + + /* Send a settings frame and a huge window */ + { + serf_bucket_t *window_size; + + tmp = serf__bucket_http2_frame_create(NULL, HTTP2_FRAME_TYPE_SETTINGS, 0, + NULL, NULL, NULL, /* stream: 0 */ + h2->lr_max_framesize, + h2->allocator); + + serf_http2__enqueue_frame(h2, tmp, FALSE); + + /* Add 1GB to the current window. */ + window_size = serf_bucket_create_numberv(h2->allocator, "4", 0x40000000); + tmp = serf__bucket_http2_frame_create(window_size, + HTTP2_FRAME_TYPE_WINDOW_UPDATE, 0, + NULL, NULL, NULL, /* stream: 0 */ + h2->lr_max_framesize, + h2->allocator); + serf_http2__enqueue_frame(h2, tmp, FALSE); + + h2->rl_window += 0x40000000; /* And update our own administration */ + } +} + /* Creates a HTTP/2 request from a serf request */ static apr_status_t enqueue_http2_request(serf_http2_protocol_t *h2) @@ -321,7 +422,9 @@ serf_http2__enqueue_frame(serf_http2_pro { apr_status_t status; - if (!pump && !h2->conn->dirty_conn) + if (!pump + && !((h2->conn && h2->conn->dirty_conn) + || (h2->client && h2->client->dirty_conn))) { const char *data; apr_size_t len; @@ -333,7 +436,7 @@ serf_http2__enqueue_frame(serf_http2_pro if (SERF_BUCKET_READ_ERROR(status)) { serf_bucket_destroy(frame); - return status; + return status; } if (len == 0) @@ -349,22 +452,24 @@ serf_http2__enqueue_frame(serf_http2_pro return APR_SUCCESS; /* Flush final output buffer (after ssl, etc.) */ - status = serf__connection_flush(h2->conn, FALSE); - if (SERF_BUCKET_READ_ERROR(status)) - return status; - - /* Write new data to output buffer if necessary and - flush again */ - if (!status) + if (h2->conn) status = serf__connection_flush(h2->conn, TRUE); + else + status = serf__incoming_client_flush(h2->client, TRUE); if (APR_STATUS_IS_EAGAIN(status)) - { - h2->conn->dirty_conn = TRUE; - h2->conn->ctx->dirty_pollset = TRUE; - } - else if (SERF_BUCKET_READ_ERROR(status)) - return status; + return APR_SUCCESS; + else if (status) + return status; + + if (h2->conn) { + h2->conn->dirty_conn = true; + h2->conn->ctx->dirty_pollset = true; + } + else { + h2->client->dirty_conn = true; + h2->client->ctx->dirty_pollset = true; + } return APR_SUCCESS; } @@ -960,7 +1065,7 @@ http2_process(serf_http2_protocol_t *h2) SERF_H2_assert(!h2->in_frame); body = serf__bucket_http2_unframe_create( - h2->conn->stream, + h2->stream, h2->rl_max_framesize, h2->allocator); @@ -1459,7 +1564,7 @@ http2_process(serf_http2_protocol_t *h2) } static apr_status_t -http2_protocol_read(serf_connection_t *conn) +http2_outgoing_read(serf_connection_t *conn) { apr_status_t status; @@ -1492,7 +1597,7 @@ http2_protocol_read(serf_connection_t *c } static apr_status_t -http2_protocol_write(serf_connection_t *conn) +http2_outgoing_write(serf_connection_t *conn) { serf_http2_protocol_t *h2 = conn->protocol_baton; apr_status_t status; @@ -1520,7 +1625,7 @@ http2_protocol_write(serf_connection_t * } static apr_status_t -http2_protocol_hangup(serf_connection_t *conn) +http2_outgoing_hangup(serf_connection_t *conn) { /* serf_http2_protocol_t *ctx = conn->protocol_baton; */ @@ -1528,7 +1633,7 @@ http2_protocol_hangup(serf_connection_t } static void -http2_protocol_teardown(serf_connection_t *conn) +http2_outgoing_teardown(serf_connection_t *conn) { serf_http2_protocol_t *ctx = conn->protocol_baton; @@ -1536,6 +1641,121 @@ http2_protocol_teardown(serf_connection_ conn->protocol_baton = NULL; } +static apr_status_t +http2_incoming_read(serf_incoming_t *client) +{ + apr_status_t status; + serf_http2_protocol_t *h2 = client->protocol_baton; + + /* If the stop_writing flag was set on the connection, reset it now because + there is some data to read. */ + if (client->stop_writing) + { + client->stop_writing = 0; + client->dirty_conn = 1; + client->ctx->dirty_pollset = 1; + } + + if (h2->prefix_left) { + serf_bucket_t *stream; + + if (client->proto_peek_bkt) + stream = client->proto_peek_bkt; + else + stream = client->stream; + + do { + const char *data; + apr_size_t len; + + status = serf_bucket_read(stream, h2->prefix_left, + &data, &len); + + if (!SERF_BUCKET_READ_ERROR(status)) { + if (len && memcmp(data, + HTTP2_CONNECTION_PREFIX - h2->prefix_left - 1 + + sizeof(HTTP2_CONNECTION_PREFIX), + len) != 0) + { + return SERF_ERROR_HTTP2_PROTOCOL_ERROR; + } + h2->prefix_left -= len; + } + } while (status == APR_SUCCESS && h2->prefix_left); + + if (!h2->prefix_left && client->proto_peek_bkt) { + /* Peek buffer is now empty. Use actual stream */ + serf_bucket_destroy(client->proto_peek_bkt); + client->proto_peek_bkt = NULL; + + h2->stream = client->stream; + } + + if (APR_STATUS_IS_EAGAIN(status) || status == SERF_ERROR_WAIT_CONN) + { + return APR_SUCCESS; + } + else if (status) { + return status; + } + } + + status = http2_process(h2); + + if (!status) + return APR_SUCCESS; + else if (APR_STATUS_IS_EOF(status)) + { + /* TODO: Teardown connection, reset if necessary, etc. */ + return status; + } + else if (APR_STATUS_IS_EAGAIN(status) + || status == SERF_ERROR_WAIT_CONN) + { + /* Update pollset, etc. etc. */ + return APR_SUCCESS; + } + else + return status; +} + +static apr_status_t +http2_incoming_write(serf_incoming_t *client) +{ + /* serf_http2_protocol_t *h2 = client->protocol_baton; */ + apr_status_t status; + + status = serf__incoming_client_flush(client, TRUE); + + if (APR_STATUS_IS_EAGAIN(status)) + return APR_SUCCESS; + else if (status) + return status; + + /* Probably nothing to write. Connection will check new requests */ + client->dirty_conn = true; + client->ctx->dirty_pollset = true; + + return APR_SUCCESS; +} + +static apr_status_t +http2_incoming_hangup(serf_incoming_t *client) +{ + /* serf_http2_protocol_t *ctx = conn->protocol_baton; */ + + return APR_EGENERAL; +} + +static void +http2_incoming_teardown(serf_incoming_t *client) +{ + serf_http2_protocol_t *ctx = client->protocol_baton; + + apr_pool_destroy(ctx->pool); + client->protocol_baton = NULL; +} + void serf_http2__allocate_stream_id(void *baton, apr_int32_t *streamid) Modified: serf/trunk/serf.h URL: http://svn.apache.org/viewvc/serf/trunk/serf.h?rev=1714582&r1=1714581&r2=1714582&view=diff ============================================================================== --- serf/trunk/serf.h (original) +++ serf/trunk/serf.h Mon Nov 16 12:44:14 2015 @@ -627,6 +627,13 @@ void serf_connection_set_framing_type( serf_connection_framing_type_t framing_type); /** + * @since New in 1.4. + */ +void serf_incoming_set_framing_type( + serf_incoming_t *client, + serf_connection_framing_type_t framing_type); + +/** * Setup the @a request for delivery on its connection. * * Right before this is invoked, @a pool will be built within the Modified: serf/trunk/serf_private.h URL: http://svn.apache.org/viewvc/serf/trunk/serf_private.h?rev=1714582&r1=1714581&r2=1714582&view=diff ============================================================================== --- serf/trunk/serf_private.h (original) +++ serf/trunk/serf_private.h Mon Nov 16 12:44:14 2015 @@ -368,13 +368,21 @@ struct serf_incoming_t { serf_incoming_closed_t closed; void *closed_baton; - serf_connection_framing_type_t framing; + serf_connection_framing_type_t framing_type; bool dirty_conn; bool wait_for_connect; bool hit_eof; bool stop_writing; + /* Event callbacks, called from serf__process_client() to do the actual + processing. */ + apr_status_t(*perform_read)(serf_incoming_t *client); + apr_status_t(*perform_write)(serf_incoming_t *client); + apr_status_t(*perform_hangup)(serf_incoming_t *client); + + /* Cleanup of protocol handling */ + void(*perform_teardown)(serf_incoming_t *conn); void *protocol_baton; /* A bucket wrapped around our socket (for reading responses). */ @@ -629,6 +637,7 @@ void serf__context_progress_delta(void * apr_status_t serf__process_client(serf_incoming_t *l, apr_int16_t events); apr_status_t serf__process_listener(serf_listener_t *l); apr_status_t serf__incoming_update_pollset(serf_incoming_t *incoming); +apr_status_t serf__incoming_client_flush(serf_incoming_t *client, bool pump); /* from outgoing.c */ apr_status_t serf__open_connections(serf_context_t *ctx); @@ -666,6 +675,7 @@ serf_bucket_t *serf__bucket_log_wrapper_ /* From http2_protocol.c: Initializes http2 state on connection */ void serf__http2_protocol_init(serf_connection_t *conn); +void serf__http2_protocol_init_server(serf_incoming_t *client); typedef struct serf_hpack_table_t serf_hpack_table_t; Modified: serf/trunk/test/test_server.c URL: http://svn.apache.org/viewvc/serf/trunk/test/test_server.c?rev=1714582&r1=1714581&r2=1714582&view=diff ============================================================================== --- serf/trunk/test/test_server.c (original) +++ serf/trunk/test/test_server.c Mon Nov 16 12:44:14 2015 @@ -183,6 +183,22 @@ run_client_server_loop(test_baton_t *tb, return APR_SUCCESS; } +static apr_status_t connection_setup_http2(apr_socket_t *skt, + serf_bucket_t **read_bkt, + serf_bucket_t **write_bkt, + void *setup_baton, + apr_pool_t *pool) +{ + test_baton_t *tb = setup_baton; + + /* Would be nice to be able to call default_http_conn_setup */ + *read_bkt = serf_bucket_socket_create(skt, tb->bkt_alloc); + + serf_connection_set_framing_type(tb->connection, + SERF_CONNECTION_FRAMING_TYPE_HTTP2); + + return APR_SUCCESS; +} void test_listen_http(CuTest *tc) { test_baton_t *tb = tc->testBaton; @@ -203,6 +219,27 @@ void test_listen_http(CuTest *tc) CuAssertIntEquals(tc, APR_SUCCESS, status); } +void test_listen_http2(CuTest *tc) +{ + test_baton_t *tb = tc->testBaton; + apr_status_t status; + handler_baton_t handler_ctx[2]; + const int num_requests = sizeof(handler_ctx) / sizeof(handler_ctx[0]); + + setup_test_server(tb); + + status = setup_test_client_context(tb, connection_setup_http2, + tb->pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + create_new_request(tb, &handler_ctx[0], "GET", "/", 1); + create_new_request(tb, &handler_ctx[1], "GET", "/", 2); + + status = run_client_server_loop(tb, num_requests, + handler_ctx, tb->pool); + CuAssertIntEquals(tc, SERF_ERROR_HTTP2_PROTOCOL_ERROR, status); +} + /*****************************************************************************/ CuSuite *test_server(void) { @@ -211,6 +248,7 @@ CuSuite *test_server(void) CuSuiteSetSetupTeardownCallbacks(suite, test_setup, test_teardown); SUITE_ADD_TEST(suite, test_listen_http); + SUITE_ADD_TEST(suite, test_listen_http2); return suite; }