Modified: serf/trunk/protocols/http2_protocol.c URL: http://svn.apache.org/viewvc/serf/trunk/protocols/http2_protocol.c?rev=1714622&r1=1714621&r2=1714622&view=diff ============================================================================== --- serf/trunk/protocols/http2_protocol.c (original) +++ serf/trunk/protocols/http2_protocol.c Mon Nov 16 16:27:04 2015 @@ -59,106 +59,107 @@ static apr_status_t http2_process(serf_http2_protocol_t *h2); static serf_bucket_t * -serf_bucket_create_numberv(serf_bucket_alloc_t *allocator, const char *format, ...) +serf_bucket_create_numberv(serf_bucket_alloc_t *allocator, const char *format, + ...) { - va_list argp; - const char *c; - char *buffer; - apr_size_t sz = 0; - unsigned char *r; - - va_start(argp, format); - - for (c = format; *c; c++) - { - SERF_H2_assert(*c >= '1' && *c <= '4'); - - if (*c >= '1' && *c <= '4') - sz += (*c - '0'); - } - - buffer = serf_bucket_mem_alloc(allocator, sz); - r = (void*)buffer; - for (c = format; *c; c++) - { - apr_uint32_t tmp; - - switch (*c) - { - case '1': - *r++ = va_arg(argp, int) & 0xFF; - break; - case '2': - tmp = va_arg(argp, int); - *r++ = (tmp >> 8) & 0xFF; - *r++ = tmp & 0xFF; - break; - case '3': - tmp = va_arg(argp, apr_uint32_t); - *r++ = (tmp >> 16) & 0xFF; - *r++ = (tmp >> 8) & 0xFF; - *r++ = tmp & 0xFF; - break; - case '4': - tmp = va_arg(argp, apr_uint32_t); - *r++ = (tmp >> 24) & 0xFF; - *r++ = (tmp >> 16) & 0xFF; - *r++ = (tmp >> 8) & 0xFF; - *r++ = tmp & 0xFF; - break; - } + va_list argp; + const char *c; + char *buffer; + apr_size_t sz = 0; + unsigned char *r; + + va_start(argp, format); + + for (c = format; *c; c++) + { + SERF_H2_assert(*c >= '1' && *c <= '4'); + + if (*c >= '1' && *c <= '4') + sz += (*c - '0'); + } + + buffer = serf_bucket_mem_alloc(allocator, sz); + r = (void*)buffer; + for (c = format; *c; c++) + { + apr_uint32_t tmp; + + switch (*c) + { + case '1': + *r++ = va_arg(argp, int) & 0xFF; + break; + case '2': + tmp = va_arg(argp, int); + *r++ = (tmp >> 8) & 0xFF; + *r++ = tmp & 0xFF; + break; + case '3': + tmp = va_arg(argp, apr_uint32_t); + *r++ = (tmp >> 16) & 0xFF; + *r++ = (tmp >> 8) & 0xFF; + *r++ = tmp & 0xFF; + break; + case '4': + tmp = va_arg(argp, apr_uint32_t); + *r++ = (tmp >> 24) & 0xFF; + *r++ = (tmp >> 16) & 0xFF; + *r++ = (tmp >> 8) & 0xFF; + *r++ = tmp & 0xFF; + break; + } } - va_end(argp); + va_end(argp); - return serf_bucket_simple_own_create(buffer, sz, allocator); + return serf_bucket_simple_own_create(buffer, sz, allocator); } struct serf_http2_protocol_t { - apr_pool_t *pool; - 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 */ - bool in_frame; - apr_size_t prefix_left; - - serf_hpack_table_t *hpack_tbl; - serf_config_t *config; - - /* Local -> Remote. Settings provided by other side */ - apr_uint32_t lr_default_window; - apr_uint32_t lr_window; - apr_uint32_t lr_max_framesize; - apr_uint32_t lr_max_headersize; - apr_uint32_t lr_max_concurrent; - apr_uint32_t lr_hpack_table_size; - apr_int32_t lr_next_streamid; - bool lr_push_enabled; - - /* Remote -> Local. Settings set by us. Acknowledged by other side */ - apr_uint32_t rl_default_window; - apr_uint32_t rl_window; - apr_uint32_t rl_max_framesize; - apr_uint32_t rl_max_headersize; - apr_uint32_t rl_max_concurrent; - apr_uint32_t rl_hpack_table_size; - apr_int32_t rl_next_streamid; - bool rl_push_enabled; + apr_pool_t *pool; + 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 */ + bool in_frame; + apr_size_t prefix_left; + + serf_hpack_table_t *hpack_tbl; + serf_config_t *config; + + /* Local -> Remote. Settings provided by other side */ + apr_uint32_t lr_default_window; + apr_uint32_t lr_window; + apr_uint32_t lr_max_framesize; + apr_uint32_t lr_max_headersize; + apr_uint32_t lr_max_concurrent; + apr_uint32_t lr_hpack_table_size; + apr_int32_t lr_next_streamid; + bool lr_push_enabled; + + /* Remote -> Local. Settings set by us. Acknowledged by other side */ + apr_uint32_t rl_default_window; + apr_uint32_t rl_window; + apr_uint32_t rl_max_framesize; + apr_uint32_t rl_max_headersize; + apr_uint32_t rl_max_concurrent; + apr_uint32_t rl_hpack_table_size; + apr_int32_t rl_next_streamid; + bool rl_push_enabled; - serf_http2_stream_t *first; - serf_http2_stream_t *last; + serf_http2_stream_t *first; + serf_http2_stream_t *last; - int setting_acks; - bool enforce_flow_control; + int setting_acks; + bool enforce_flow_control; - serf_bucket_t *continuation_bucket; - apr_int32_t continuation_streamid; + serf_bucket_t *continuation_bucket; + apr_int32_t continuation_streamid; }; /* Forward definition */ @@ -171,33 +172,33 @@ http2_bucket_processor(void *baton, static apr_status_t http2_protocol_cleanup(void *state) { - serf_http2_protocol_t *h2 = state; - serf_connection_t *conn = h2->conn; - serf_incoming_t *client = h2->client; - serf_http2_stream_t *stream, *next; + 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 */ - for (stream = h2->first; stream; stream = next) + /* First clean out all streams */ + for (stream = h2->first; stream; stream = next) { - next = stream->next; - serf_http2__stream_cleanup(stream); + next = stream->next; + serf_http2__stream_cleanup(stream); } - h2->first = h2->last = NULL; + h2->first = h2->last = NULL; - if (h2->processor != NULL) + if (h2->processor != NULL) { - h2->read_frame = NULL; + h2->read_frame = NULL; - if (h2->processor == http2_bucket_processor) + if (h2->processor == http2_bucket_processor) { - serf_bucket_t *payload = h2->processor_baton; + serf_bucket_t *payload = h2->processor_baton; - if (payload) - serf_bucket_destroy(payload); + if (payload) + serf_bucket_destroy(payload); - h2->processor = NULL; - h2->processor_baton = NULL; + h2->processor = NULL; + h2->processor_baton = NULL; } /* Else: The processor (probably a stream) @@ -205,107 +206,110 @@ http2_protocol_cleanup(void *state) by adding frames to an aggregate to allow reading multiple frames as a stream. */ } - else if (h2->read_frame) + else if (h2->read_frame) { - serf_bucket_destroy(h2->read_frame); - h2->read_frame = NULL; + serf_bucket_destroy(h2->read_frame); + h2->read_frame = NULL; } - h2->in_frame = FALSE; + h2->in_frame = FALSE; - if (conn) - conn->protocol_baton = NULL; - if (client) - client->protocol_baton = NULL; + if (conn) + conn->protocol_baton = NULL; + if (client) + client->protocol_baton = NULL; - return APR_SUCCESS; + return APR_SUCCESS; } void serf__http2_protocol_init(serf_connection_t *conn) { - serf_http2_protocol_t *h2; - apr_pool_t *protocol_pool; - serf_bucket_t *tmp; - 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; - - /* 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); - - 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 */ - conn->probable_keepalive_limit = 0; - conn->max_outstanding_requests = 0; - - /* Send the HTTP/2 Connection Preface */ - 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 */ - { - 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_protocol_t *h2; + apr_pool_t *protocol_pool; + serf_bucket_t *tmp; + const bool WE_ARE_CLIENT = true; - serf_http2__enqueue_frame(h2, tmp, FALSE); + apr_pool_create(&protocol_pool, conn->pool); - /* Add 1GB to the current window. */ - window_size = serf_bucket_create_numberv(conn->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 = 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; + + /* 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->rl_window += 0x40000000; /* And update our own administration */ - } + 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); + + 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 */ + conn->probable_keepalive_limit = 0; + conn->max_outstanding_requests = 0; + + /* Send the HTTP/2 Connection Preface */ + 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 */ + { + 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(conn->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 */ + } } void serf__http2_protocol_init_server(serf_incoming_t *client) @@ -370,7 +374,8 @@ void serf__http2_protocol_init_server(se { serf_bucket_t *window_size; - tmp = serf__bucket_http2_frame_create(NULL, HTTP2_FRAME_TYPE_SETTINGS, 0, + tmp = serf__bucket_http2_frame_create(NULL, HTTP2_FRAME_TYPE_SETTINGS, + 0, NULL, NULL, NULL, /* stream: 0 */ h2->lr_max_framesize, h2->allocator); @@ -378,9 +383,11 @@ void serf__http2_protocol_init_server(se serf_http2__enqueue_frame(h2, tmp, FALSE); /* Add 1GB to the current window. */ - window_size = serf_bucket_create_numberv(h2->allocator, "4", 0x40000000); + window_size = serf_bucket_create_numberv(h2->allocator, "4", + 0x40000000); tmp = serf__bucket_http2_frame_create(window_size, - HTTP2_FRAME_TYPE_WINDOW_UPDATE, 0, + HTTP2_FRAME_TYPE_WINDOW_UPDATE, + 0, NULL, NULL, NULL, /* stream: 0 */ h2->lr_max_framesize, h2->allocator); @@ -394,25 +401,25 @@ void serf__http2_protocol_init_server(se static apr_status_t enqueue_http2_request(serf_http2_protocol_t *h2) { - serf_http2_stream_t *stream; + serf_http2_stream_t *stream; - stream = serf_http2__stream_create(h2, -1, - h2->lr_default_window, - h2->rl_default_window, - h2->allocator); - - if (h2->first) - { - stream->next = h2->first; - h2->first->prev = stream; - h2->first = stream; + stream = serf_http2__stream_create(h2, -1, + h2->lr_default_window, + h2->rl_default_window, + h2->allocator); + + if (h2->first) + { + stream->next = h2->first; + h2->first->prev = stream; + h2->first = stream; } - else - h2->last = h2->first = stream; + else + h2->last = h2->first = stream; - return serf_http2__stream_setup_next_request(stream, h2->conn, - h2->lr_max_framesize, - h2->hpack_tbl); + return serf_http2__stream_setup_next_request(stream, h2->conn, + h2->lr_max_framesize, + h2->hpack_tbl); } apr_status_t @@ -420,58 +427,58 @@ serf_http2__enqueue_frame(serf_http2_pro serf_bucket_t *frame, int pump) { - apr_status_t status; - - if (!pump - && !((h2->conn && h2->conn->dirty_conn) - || (h2->client && h2->client->dirty_conn))) - { - const char *data; - apr_size_t len; + apr_status_t status; - /* Cheap check to see if we should request a write - event next time around */ - status = serf_bucket_peek(h2->ostream, &data, &len); + if (!pump + && !((h2->conn && h2->conn->dirty_conn) + || (h2->client && h2->client->dirty_conn))) + { + const char *data; + apr_size_t len; + + /* Cheap check to see if we should request a write + event next time around */ + status = serf_bucket_peek(h2->ostream, &data, &len); - if (SERF_BUCKET_READ_ERROR(status)) + if (SERF_BUCKET_READ_ERROR(status)) { - serf_bucket_destroy(frame); - return status; + serf_bucket_destroy(frame); + return status; } - if (len == 0) + if (len == 0) { - h2->conn->dirty_conn = TRUE; - h2->conn->ctx->dirty_pollset = TRUE; + h2->conn->dirty_conn = TRUE; + h2->conn->ctx->dirty_pollset = TRUE; } } - serf_bucket_aggregate_append(h2->ostream, frame); + serf_bucket_aggregate_append(h2->ostream, frame); - if (!pump) - return APR_SUCCESS; + if (!pump) + return APR_SUCCESS; - /* Flush final output buffer (after ssl, etc.) */ - 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)) - 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; - } + /* Flush final output buffer (after ssl, etc.) */ + 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)) + return APR_SUCCESS; + else if (status) + return status; - return APR_SUCCESS; + 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; } /* Implements serf_bucket_prefix_handler_t. @@ -482,18 +489,18 @@ http2_handle_priority(void *baton, const char *data, apr_size_t len) { - serf_http2_stream_t *stream = baton; + serf_http2_stream_t *stream = baton; - if (len != HTTP2_PRIORITY_DATA_SIZE) - return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; + if (len != HTTP2_PRIORITY_DATA_SIZE) + return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - if (stream == NULL) - return APR_SUCCESS; /* Nothing to record this on */ + if (stream == NULL) + return APR_SUCCESS; /* Nothing to record this on */ - /* ### TODO: Store priority information on stream */ - SERF_H2_assert(stream->h2 != NULL); + /* ### TODO: Store priority information on stream */ + SERF_H2_assert(stream->h2 != NULL); - return APR_SUCCESS; + return APR_SUCCESS; } /* Implements serf_bucket_prefix_handler_t. @@ -504,29 +511,29 @@ http2_handle_promise(void *baton, const char *data, apr_size_t len) { - serf_http2_stream_t *parent_stream = baton; - serf_http2_protocol_t *h2= parent_stream->h2; - serf_http2_stream_t *promised_stream; - apr_int32_t streamid; - const struct promise_t - { - unsigned char s3, s2, s1, s0; - } *promise; - - if (len != HTTP2_PROMISE_DATA_SIZE) - return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - - SERF_H2_assert(h2 != NULL); - - promise = (const void *)data; - - /* Highest bit is reserved */ - streamid = ((promise->s3 & 0x7F) << 24) | (promise->s2 << 16) - |(promise->s1 << 8) | promise->s0; - - if (streamid == 0 - || (streamid < h2->rl_next_streamid) - || (streamid & 0x01) != (h2->rl_next_streamid & 0x01)) + serf_http2_stream_t *parent_stream = baton; + serf_http2_protocol_t *h2 = parent_stream->h2; + serf_http2_stream_t *promised_stream; + apr_int32_t streamid; + const struct promise_t + { + unsigned char s3, s2, s1, s0; + } *promise; + + if (len != HTTP2_PROMISE_DATA_SIZE) + return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; + + SERF_H2_assert(h2 != NULL); + + promise = (const void *)data; + + /* Highest bit is reserved */ + streamid = ((promise->s3 & 0x7F) << 24) | (promise->s2 << 16) + | (promise->s1 << 8) | promise->s0; + + if (streamid == 0 + || (streamid < h2->rl_next_streamid) + || (streamid & 0x01) != (h2->rl_next_streamid & 0x01)) { /* The promised stream identifier MUST bet a valid choice for the next stream sent by the sender */ @@ -537,10 +544,10 @@ http2_handle_promise(void *baton, identifier is an identifier for a stream that is not currently in the "idle" state.*/ - return SERF_ERROR_HTTP2_PROTOCOL_ERROR; + return SERF_ERROR_HTTP2_PROTOCOL_ERROR; } - else if (parent_stream->status != H2S_OPEN - && parent_stream->status != H2S_HALFCLOSED_LOCAL) + else if (parent_stream->status != H2S_OPEN + && parent_stream->status != H2S_HALFCLOSED_LOCAL) { /* PUSH_PROMISE frames MUST only be sent on a peer-initiated stream that is in either the "open" or "half-closed (remote)" state. The stream @@ -549,19 +556,19 @@ http2_handle_promise(void *baton, 0x0, a recipient MUST respond with a connection error (Section 5.4.1) of type PROTOCOL_ERROR.*/ - return SERF_ERROR_HTTP2_PROTOCOL_ERROR; + return SERF_ERROR_HTTP2_PROTOCOL_ERROR; } - promised_stream = serf_http2__stream_get(h2, streamid, TRUE, FALSE); - if (!promised_stream || promised_stream->status != H2S_IDLE) - return SERF_ERROR_HTTP2_PROTOCOL_ERROR; + promised_stream = serf_http2__stream_get(h2, streamid, TRUE, FALSE); + if (!promised_stream || promised_stream->status != H2S_IDLE) + return SERF_ERROR_HTTP2_PROTOCOL_ERROR; - promised_stream->status = H2S_RESERVED_REMOTE; + promised_stream->status = H2S_RESERVED_REMOTE; - /* Store data to allow stream to handle the promise */ - parent_stream->new_reserved_stream = promised_stream; + /* Store data to allow stream to handle the promise */ + parent_stream->new_reserved_stream = promised_stream; - return APR_SUCCESS; + return APR_SUCCESS; } /* Implements serf_bucket_prefix_handler_t. @@ -572,17 +579,17 @@ http2_handle_frame_reset(void *baton, const char *data, apr_size_t len) { - serf_http2_stream_t *stream = baton; + serf_http2_stream_t *stream = baton; - if (len != HTTP2_RST_DATA_SIZE) - return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; + if (len != HTTP2_RST_DATA_SIZE) + return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - SERF_H2_assert(stream->h2 != NULL); + SERF_H2_assert(stream->h2 != NULL); - /* ### TODO: Handle error code, etc. */ - stream->status = H2S_CLOSED; + /* ### TODO: Handle error code, etc. */ + stream->status = H2S_CLOSED; - return APR_SUCCESS; + return APR_SUCCESS; } /* Implements serf_bucket_prefix_handler_t. @@ -593,36 +600,36 @@ http2_handle_stream_window_update(void * const char *data, apr_size_t len) { - serf_http2_stream_t *stream = baton; - apr_uint32_t value; - const struct window_update_t - { - unsigned char v3, v2, v1, v0; - } *window_update; + serf_http2_stream_t *stream = baton; + apr_uint32_t value; + const struct window_update_t + { + unsigned char v3, v2, v1, v0; + } *window_update; - if (len != HTTP2_WINDOW_UPDATE_DATA_SIZE) - return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; + if (len != HTTP2_WINDOW_UPDATE_DATA_SIZE) + return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - window_update = (const void *)data; + window_update = (const void *)data; - value = (window_update->v3 << 24) | (window_update->v2 << 16) - | (window_update->v2 << 8) | window_update->v0; + value = (window_update->v3 << 24) | (window_update->v2 << 16) + | (window_update->v2 << 8) | window_update->v0; - value &= HTTP2_WINDOW_MAX_ALLOWED; /* The highest bit is reserved */ + value &= HTTP2_WINDOW_MAX_ALLOWED; /* The highest bit is reserved */ - if (value == 0) + if (value == 0) { /* A receiver MUST treat the receipt of a WINDOW_UPDATE frame with an flow - control window increment of 0 as a stream error(Section 5.4.2) of type PROTOCOL_ERROR; errors on the connection flow - control window MUST be treated as a connection error(Section 5.4.1). */ - return SERF_ERROR_HTTP2_PROTOCOL_ERROR; + return SERF_ERROR_HTTP2_PROTOCOL_ERROR; } - stream->lr_window += value; + stream->lr_window += value; - if (stream->lr_window > HTTP2_WINDOW_MAX_ALLOWED) + if (stream->lr_window > HTTP2_WINDOW_MAX_ALLOWED) { /* A sender MUST NOT allow a flow-control window to exceed 2^31-1 octets. If a sender receives a WINDOW_UPDATE that causes a flow- @@ -631,14 +638,14 @@ http2_handle_stream_window_update(void * sends a RST_STREAM with an error code of FLOW_CONTROL_ERROR; for the connection, a GOAWAY frame with an error code of FLOW_CONTROL_ERROR is sent.*/ - return SERF_ERROR_HTTP2_FLOW_CONTROL_ERROR; + return SERF_ERROR_HTTP2_FLOW_CONTROL_ERROR; } - serf__log(LOGLVL_INFO, SERF_LOGHTTP2, stream->h2->config, - "Increasing window on frame %d with 0x%x to 0x%x\n", - stream->streamid, value, stream->lr_window); + serf__log(LOGLVL_INFO, SERF_LOGHTTP2, stream->h2->config, + "Increasing window on frame %d with 0x%x to 0x%x\n", + stream->streamid, value, stream->lr_window); - return APR_SUCCESS; + return APR_SUCCESS; } /* Implements serf_bucket_prefix_handler_t. @@ -649,37 +656,37 @@ http2_handle_connection_window_update(vo const char *data, apr_size_t len) { - serf_http2_protocol_t *h2 = baton; - apr_uint32_t value; - const struct window_update_t - { - unsigned char v3, v2, v1, v0; - } *window_update; + serf_http2_protocol_t *h2 = baton; + apr_uint32_t value; + const struct window_update_t + { + unsigned char v3, v2, v1, v0; + } *window_update; - if (len != HTTP2_WINDOW_UPDATE_DATA_SIZE) - return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; + if (len != HTTP2_WINDOW_UPDATE_DATA_SIZE) + return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - SERF_H2_assert(h2 != NULL); + SERF_H2_assert(h2 != NULL); - window_update = (const void *)data; + window_update = (const void *)data; - value = (window_update->v3 << 24) | (window_update->v2 << 16) - | (window_update->v2 << 8) | window_update->v0; + value = (window_update->v3 << 24) | (window_update->v2 << 16) + | (window_update->v2 << 8) | window_update->v0; - value &= HTTP2_WINDOW_MAX_ALLOWED; /* The highest bit is reserved */ + value &= HTTP2_WINDOW_MAX_ALLOWED; /* The highest bit is reserved */ - if (value == 0) + if (value == 0) { /* A receiver MUST treat the receipt of a WINDOW_UPDATE frame with an flow - control window increment of 0 as a stream error(Section 5.4.2) of type PROTOCOL_ERROR; errors on the connection flow - control window MUST be treated as a connection error(Section 5.4.1). */ - return SERF_ERROR_HTTP2_PROTOCOL_ERROR; + return SERF_ERROR_HTTP2_PROTOCOL_ERROR; } - h2->lr_window += value; + h2->lr_window += value; - if (h2->lr_window > HTTP2_WINDOW_MAX_ALLOWED) + if (h2->lr_window > HTTP2_WINDOW_MAX_ALLOWED) { /* A sender MUST NOT allow a flow-control window to exceed 2^31-1 octets. If a sender receives a WINDOW_UPDATE that causes a flow- @@ -688,14 +695,14 @@ http2_handle_connection_window_update(vo sends a RST_STREAM with an error code of FLOW_CONTROL_ERROR; for the connection, a GOAWAY frame with an error code of FLOW_CONTROL_ERROR is sent.*/ - return SERF_ERROR_HTTP2_FLOW_CONTROL_ERROR; + return SERF_ERROR_HTTP2_FLOW_CONTROL_ERROR; } - serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, - "Increasing window on connection with 0x%x to 0x%x\n", - value, h2->lr_window); + serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, + "Increasing window on connection with 0x%x to 0x%x\n", + value, h2->lr_window); - return APR_SUCCESS; + return APR_SUCCESS; } /* Implements serf_bucket_prefix_handler_t. @@ -706,34 +713,34 @@ http2_handle_ping(void *baton, const char *data, apr_size_t len) { - serf_http2_protocol_t *h2 = baton; - serf_bucket_t *body; - apr_status_t status; - - if (len != HTTP2_PING_DATA_SIZE) - return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - - SERF_H2_assert(h2 != NULL); - - /* Reply with a PONG (=PING + ACK) with the same data*/ - - body = serf_bucket_simple_copy_create(data, len, - h2->allocator); - - status = serf_http2__enqueue_frame( - h2, - serf__bucket_http2_frame_create(body, - HTTP2_FRAME_TYPE_PING, - HTTP2_FLAG_ACK, - NULL, NULL, NULL, - h2->lr_max_framesize, - h2->allocator), - TRUE /* pump */); + serf_http2_protocol_t *h2 = baton; + serf_bucket_t *body; + apr_status_t status; - if (SERF_BUCKET_READ_ERROR(status)) - return status; + if (len != HTTP2_PING_DATA_SIZE) + return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; + + SERF_H2_assert(h2 != NULL); + + /* Reply with a PONG (=PING + ACK) with the same data*/ + + body = serf_bucket_simple_copy_create(data, len, + h2->allocator); - return APR_SUCCESS; + status = serf_http2__enqueue_frame( + h2, + serf__bucket_http2_frame_create(body, + HTTP2_FRAME_TYPE_PING, + HTTP2_FLAG_ACK, + NULL, NULL, NULL, + h2->lr_max_framesize, + h2->allocator), + TRUE /* pump */); + + if (SERF_BUCKET_READ_ERROR(status)) + return status; + + return APR_SUCCESS; } /* Implements serf_bucket_prefix_handler_t. @@ -744,15 +751,15 @@ http2_handle_ping_ack(void *baton, const char *data, apr_size_t len) { - serf_http2_protocol_t *h2 = baton; - if (len != HTTP2_PING_DATA_SIZE) - return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; + serf_http2_protocol_t *h2 = baton; + if (len != HTTP2_PING_DATA_SIZE) + return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - SERF_H2_assert(h2 != NULL); + SERF_H2_assert(h2 != NULL); - /* Did we send a ping? */ + /* Did we send a ping? */ - return APR_SUCCESS; + return APR_SUCCESS; } /* Implements serf_bucket_prefix_handler_t. @@ -763,85 +770,85 @@ http2_handle_settings(void *baton, const char *data, apr_size_t len) { - serf_http2_protocol_t *h2 = baton; - apr_size_t i; - const struct setting_t - { - unsigned char s1, s0; - unsigned char v3, v2, v1, v0; - } *setting; - - if ((len % HTTP2_SETTING_SIZE) != 0) - return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - - /* ### TODO: Handle settings */ - setting = (const void *)data; - for (i = 0, setting = (const void *)data; - i < len; - i += sizeof(*setting), setting++) - { - apr_uint16_t id = (setting->s1 << 8) | setting->s0; - apr_uint32_t value = (setting->v3 << 24) | (setting->v2 << 16) - | (setting->v1 << 8) | setting->v0; + serf_http2_protocol_t *h2 = baton; + apr_size_t i; + const struct setting_t + { + unsigned char s1, s0; + unsigned char v3, v2, v1, v0; + } *setting; + + if ((len % HTTP2_SETTING_SIZE) != 0) + return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; + + /* ### TODO: Handle settings */ + setting = (const void *)data; + for (i = 0, setting = (const void *)data; + i < len; + i += sizeof(*setting), setting++) + { + apr_uint16_t id = (setting->s1 << 8) | setting->s0; + apr_uint32_t value = (setting->v3 << 24) | (setting->v2 << 16) + | (setting->v1 << 8) | setting->v0; - switch (id) + switch (id) { - case HTTP2_SETTING_HEADER_TABLE_SIZE: - serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, - "Setting HPACK Table size to %u\n", value); - serf__hpack_table_set_max_table_size(h2->hpack_tbl, - h2->rl_hpack_table_size, - value); - break; - case HTTP2_SETTING_ENABLE_PUSH: - serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, - "Setting Push enabled: %u\n", value); - h2->lr_push_enabled = (value != 0); - break; - case HTTP2_SETTING_MAX_CONCURRENT_STREAMS: - serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, - "Setting Max Concurrent %u\n", value); - h2->lr_max_concurrent = value; - break; - case HTTP2_SETTING_INITIAL_WINDOW_SIZE: - /* Sanitize? */ - serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, - "Setting Initial Window Size %u\n", value); - h2->lr_default_window = value; - break; - case HTTP2_SETTING_MAX_FRAME_SIZE: - /* Sanitize? */ - serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, - "Setting Max framesize %u\n", value); - h2->lr_max_framesize = value; - break; - case HTTP2_SETTING_MAX_HEADER_LIST_SIZE: - serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, - "Setting Max header list size %u\n", value); - h2->lr_max_headersize = value; - break; - default: - /* An endpoint that receives a SETTINGS frame with any unknown - or unsupported identifier MUST ignore that setting. */ - serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, - "Ignoring unknown setting %d, value %u\n", id, value); - break; + case HTTP2_SETTING_HEADER_TABLE_SIZE: + serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, + "Setting HPACK Table size to %u\n", value); + serf__hpack_table_set_max_table_size(h2->hpack_tbl, + h2->rl_hpack_table_size, + value); + break; + case HTTP2_SETTING_ENABLE_PUSH: + serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, + "Setting Push enabled: %u\n", value); + h2->lr_push_enabled = (value != 0); + break; + case HTTP2_SETTING_MAX_CONCURRENT_STREAMS: + serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, + "Setting Max Concurrent %u\n", value); + h2->lr_max_concurrent = value; + break; + case HTTP2_SETTING_INITIAL_WINDOW_SIZE: + /* Sanitize? */ + serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, + "Setting Initial Window Size %u\n", value); + h2->lr_default_window = value; + break; + case HTTP2_SETTING_MAX_FRAME_SIZE: + /* Sanitize? */ + serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, + "Setting Max framesize %u\n", value); + h2->lr_max_framesize = value; + break; + case HTTP2_SETTING_MAX_HEADER_LIST_SIZE: + serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, + "Setting Max header list size %u\n", value); + h2->lr_max_headersize = value; + break; + default: + /* An endpoint that receives a SETTINGS frame with any unknown + or unsupported identifier MUST ignore that setting. */ + serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, + "Ignoring unknown setting %d, value %u\n", id, value); + break; } } /* Always ack settings */ - serf_http2__enqueue_frame( - h2, - serf__bucket_http2_frame_create( - NULL, - HTTP2_FRAME_TYPE_SETTINGS, - HTTP2_FLAG_ACK, - NULL, NULL, NULL, - h2->lr_max_framesize, - h2->allocator), - TRUE); + serf_http2__enqueue_frame( + h2, + serf__bucket_http2_frame_create( + NULL, + HTTP2_FRAME_TYPE_SETTINGS, + HTTP2_FLAG_ACK, + NULL, NULL, NULL, + h2->lr_max_framesize, + h2->allocator), + TRUE); - return APR_SUCCESS; + return APR_SUCCESS; } /* Implements serf_bucket_prefix_handler_t. @@ -852,92 +859,92 @@ http2_handle_goaway(void *baton, const char *data, apr_size_t len) { - serf_http2_protocol_t *h2 = baton; - apr_int32_t last_streamid; - apr_uint32_t error_code; - apr_uint32_t loglevel; - const struct goaway_t - { - unsigned char s3, s2, s1, s0; - unsigned char e3, e2, e1, e0; - } *goaway; - - if (len < HTTP2_GOWAWAY_DATA_SIZE) - return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - - SERF_H2_assert(h2 != NULL); - - goaway = (const void *)data; - - last_streamid = ((goaway->s3 & 0x7F) << 24) | (goaway->s2 << 16) - | (goaway->s1 << 8) | goaway->s0; - error_code = (goaway->e3 << 24) | (goaway->e2 << 16) - | (goaway->e1 << 8) | goaway->e0; - - switch (error_code + SERF_ERROR_HTTP2_NO_ERROR) - { - case SERF_ERROR_HTTP2_PROTOCOL_ERROR: - case SERF_ERROR_HTTP2_FLOW_CONTROL_ERROR: - case SERF_ERROR_HTTP2_SETTINGS_TIMEOUT: - case SERF_ERROR_HTTP2_FRAME_SIZE_ERROR: - case SERF_ERROR_HTTP2_COMPRESSION_ERROR: - case SERF_ERROR_HTTP2_INADEQUATE_SECURITY: - loglevel = LOGLVL_ERROR; - break; - - case SERF_ERROR_HTTP2_HTTP_1_1_REQUIRED: - case SERF_ERROR_HTTP2_ENHANCE_YOUR_CALM: - loglevel = LOGLVL_WARNING; - break; - - case SERF_ERROR_HTTP2_REFUSED_STREAM: - case SERF_ERROR_HTTP2_CANCEL: - case SERF_ERROR_HTTP2_CONNECT_ERROR: - case SERF_ERROR_HTTP2_STREAM_CLOSED: - /* These errors should have been sent as a stream - error. This usually tells us that we have an http/2 - implementation on the other side that doesn't implement - full stream state handling. (See HTTP/2 RFC)*/ - loglevel = LOGLVL_ERROR; - break; - - case SERF_ERROR_HTTP2_NO_ERROR: - loglevel = LOGLVL_INFO; - break; - - case SERF_ERROR_HTTP2_INTERNAL_ERROR: - default: - loglevel = LOGLVL_WARNING; - break; - } - - if (len > HTTP2_GOWAWAY_DATA_SIZE) - { - char *goaway_text; - - /* The server produced additional information in the error frame - Usually this is some literal text explaining what went wrong. - - Copy the text to make it 0 terminated and then log it. */ - - /* If this value appears truncated, that may be caused by the - limit set in http2_process */ - - goaway_text = serf_bstrmemdup(h2->allocator, - data + HTTP2_GOWAWAY_DATA_SIZE, - len - HTTP2_GOWAWAY_DATA_SIZE); - - serf__log(loglevel, SERF_LOGHTTP2, h2->config, - "Received GOAWAY, last-stream=0x%x, error=%u: %s\n", - last_streamid, error_code, goaway_text); - - serf_bucket_mem_free(h2->allocator, goaway_text); - } - else - { - serf__log(loglevel, SERF_LOGHTTP2, h2->config, - "Received GOAWAY, last-stream=0x%x, error=%u.\n", - last_streamid, error_code); + serf_http2_protocol_t *h2 = baton; + apr_int32_t last_streamid; + apr_uint32_t error_code; + apr_uint32_t loglevel; + const struct goaway_t + { + unsigned char s3, s2, s1, s0; + unsigned char e3, e2, e1, e0; + } *goaway; + + if (len < HTTP2_GOWAWAY_DATA_SIZE) + return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; + + SERF_H2_assert(h2 != NULL); + + goaway = (const void *)data; + + last_streamid = ((goaway->s3 & 0x7F) << 24) | (goaway->s2 << 16) + | (goaway->s1 << 8) | goaway->s0; + error_code = (goaway->e3 << 24) | (goaway->e2 << 16) + | (goaway->e1 << 8) | goaway->e0; + + switch (error_code + SERF_ERROR_HTTP2_NO_ERROR) + { + case SERF_ERROR_HTTP2_PROTOCOL_ERROR: + case SERF_ERROR_HTTP2_FLOW_CONTROL_ERROR: + case SERF_ERROR_HTTP2_SETTINGS_TIMEOUT: + case SERF_ERROR_HTTP2_FRAME_SIZE_ERROR: + case SERF_ERROR_HTTP2_COMPRESSION_ERROR: + case SERF_ERROR_HTTP2_INADEQUATE_SECURITY: + loglevel = LOGLVL_ERROR; + break; + + case SERF_ERROR_HTTP2_HTTP_1_1_REQUIRED: + case SERF_ERROR_HTTP2_ENHANCE_YOUR_CALM: + loglevel = LOGLVL_WARNING; + break; + + case SERF_ERROR_HTTP2_REFUSED_STREAM: + case SERF_ERROR_HTTP2_CANCEL: + case SERF_ERROR_HTTP2_CONNECT_ERROR: + case SERF_ERROR_HTTP2_STREAM_CLOSED: + /* These errors should have been sent as a stream + error. This usually tells us that we have an http/2 + implementation on the other side that doesn't implement + full stream state handling. (See HTTP/2 RFC)*/ + loglevel = LOGLVL_ERROR; + break; + + case SERF_ERROR_HTTP2_NO_ERROR: + loglevel = LOGLVL_INFO; + break; + + case SERF_ERROR_HTTP2_INTERNAL_ERROR: + default: + loglevel = LOGLVL_WARNING; + break; + } + + if (len > HTTP2_GOWAWAY_DATA_SIZE) + { + char *goaway_text; + + /* The server produced additional information in the error frame + Usually this is some literal text explaining what went wrong. + + Copy the text to make it 0 terminated and then log it. */ + + /* If this value appears truncated, that may be caused by the + limit set in http2_process */ + + goaway_text = serf_bstrmemdup(h2->allocator, + data + HTTP2_GOWAWAY_DATA_SIZE, + len - HTTP2_GOWAWAY_DATA_SIZE); + + serf__log(loglevel, SERF_LOGHTTP2, h2->config, + "Received GOAWAY, last-stream=0x%x, error=%u: %s\n", + last_streamid, error_code, goaway_text); + + serf_bucket_mem_free(h2->allocator, goaway_text); + } + else + { + serf__log(loglevel, SERF_LOGHTTP2, h2->config, + "Received GOAWAY, last-stream=0x%x, error=%u.\n", + last_streamid, error_code); } /* ### TODO: If the error is not critical stop creating new frames @@ -947,7 +954,7 @@ http2_handle_goaway(void *baton, We may receive a new error later, signalling a more important problem */ - return APR_SUCCESS; + return APR_SUCCESS; } @@ -956,32 +963,32 @@ static apr_status_t http2_handle_continuation(void *baton, serf_bucket_t *aggregate_bucket) { - serf_http2_protocol_t *h2 = baton; - apr_status_t status; - const char *data; - apr_size_t len; + serf_http2_protocol_t *h2 = baton; + apr_status_t status; + const char *data; + apr_size_t len; - if (h2->continuation_bucket != aggregate_bucket) - return APR_EOF; /* This is all we have */ + if (h2->continuation_bucket != aggregate_bucket) + return APR_EOF; /* This is all we have */ - SERF_H2_assert(h2->read_frame == NULL); - SERF_H2_assert(h2->continuation_bucket == aggregate_bucket); + SERF_H2_assert(h2->read_frame == NULL); + SERF_H2_assert(h2->continuation_bucket == aggregate_bucket); - status = http2_process(h2); - if (status) - return status; + status = http2_process(h2); + if (status) + return status; - if (h2->continuation_bucket == aggregate_bucket) + if (h2->continuation_bucket == aggregate_bucket) { /* We expect more data in the future. Something was done in http2_process() or it didn't return APR_SUCCESS */ - return APR_SUCCESS; + return APR_SUCCESS; } /* As h2->continuation_bucket is no longer attached we don't recurse on peeking. Just check if there is more */ - return serf_bucket_peek(aggregate_bucket, &data, &len); + return serf_bucket_peek(aggregate_bucket, &data, &len); } /* Implements the serf__bucket_http2_unframe_set_eof callback */ @@ -989,15 +996,15 @@ static apr_status_t http2_end_of_frame(void *baton, serf_bucket_t *frame) { - serf_http2_protocol_t *h2 = baton; + serf_http2_protocol_t *h2 = baton; - SERF_H2_assert(h2->read_frame == frame); - h2->read_frame = NULL; - h2->in_frame = FALSE; - h2->processor = NULL; - h2->processor_baton = NULL; + SERF_H2_assert(h2->read_frame == frame); + h2->read_frame = NULL; + h2->in_frame = FALSE; + h2->processor = NULL; + h2->processor_baton = NULL; - return APR_SUCCESS; + return APR_SUCCESS; } /* Implements serf_http2_processor_t */ @@ -1006,558 +1013,583 @@ http2_bucket_processor(void *baton, serf_http2_protocol_t *h2, serf_bucket_t *frame_bucket) { - struct iovec vecs[IOV_MAX]; - int vecs_used; - serf_bucket_t *payload = baton; - apr_status_t status; + struct iovec vecs[IOV_MAX]; + int vecs_used; + serf_bucket_t *payload = baton; + apr_status_t status; - status = serf_bucket_read_iovec(payload, SERF_READ_ALL_AVAIL, IOV_MAX, - vecs, &vecs_used); + status = serf_bucket_read_iovec(payload, SERF_READ_ALL_AVAIL, IOV_MAX, + vecs, &vecs_used); - if (APR_STATUS_IS_EOF(status)) + if (APR_STATUS_IS_EOF(status)) { - SERF_H2_assert(!h2->in_frame && !h2->read_frame); - serf_bucket_destroy(payload); + SERF_H2_assert(!h2->in_frame && !h2->read_frame); + serf_bucket_destroy(payload); } - return status; + return status; } /* Processes incoming HTTP2 data */ static apr_status_t http2_process(serf_http2_protocol_t *h2) { - while (TRUE) + while (TRUE) { - apr_status_t status; - serf_bucket_t *body; + apr_status_t status; + serf_bucket_t *body; - if (h2->processor) + if (h2->processor) { - status = h2->processor(h2->processor_baton, h2, h2->read_frame); + status = h2->processor(h2->processor_baton, h2, h2->read_frame); - if (SERF_BUCKET_READ_ERROR(status)) - return status; - else if (APR_STATUS_IS_EOF(status)) + if (SERF_BUCKET_READ_ERROR(status)) + return status; + else if (APR_STATUS_IS_EOF(status)) { /* ### frame ended */ - SERF_H2_assert(h2->read_frame == NULL); - h2->processor = NULL; - h2->processor_baton = NULL; + SERF_H2_assert(h2->read_frame == NULL); + h2->processor = NULL; + h2->processor_baton = NULL; } - else if (h2->in_frame) + else if (h2->in_frame) { - if (status) - return status; - else - continue; + if (status) + return status; + else + continue; } } - else + else { - SERF_H2_assert(!h2->in_frame); + SERF_H2_assert(!h2->in_frame); } - body = h2->read_frame; + body = h2->read_frame; - if (! body) + if (!body) { - SERF_H2_assert(!h2->in_frame); + SERF_H2_assert(!h2->in_frame); - body = serf__bucket_http2_unframe_create( - h2->stream, - h2->rl_max_framesize, - h2->allocator); + body = serf__bucket_http2_unframe_create( + h2->stream, + h2->rl_max_framesize, + h2->allocator); - serf__bucket_http2_unframe_set_eof(body, - http2_end_of_frame, h2); + serf__bucket_http2_unframe_set_eof(body, + http2_end_of_frame, h2); - serf_bucket_set_config(body, h2->config); - h2->read_frame = body; + serf_bucket_set_config(body, h2->config); + h2->read_frame = body; } - if (! h2->in_frame) + if (!h2->in_frame) { - apr_int32_t sid; - unsigned char frametype; - unsigned char frameflags; - apr_size_t remaining; - serf_http2_processor_t process_handler = NULL; - void *process_baton = NULL; - serf_bucket_t *process_bucket = NULL; - serf_http2_stream_t *stream; - apr_uint32_t reset_reason; + apr_int32_t sid; + unsigned char frametype; + unsigned char frameflags; + apr_size_t remaining; + serf_http2_processor_t process_handler = NULL; + void *process_baton = NULL; + serf_bucket_t *process_bucket = NULL; + serf_http2_stream_t *stream; + apr_uint32_t reset_reason; + + status = serf__bucket_http2_unframe_read_info(body, &sid, + &frametype, + &frameflags); - status = serf__bucket_http2_unframe_read_info(body, &sid, - &frametype, &frameflags); - - if (APR_STATUS_IS_EOF(status)) + if (APR_STATUS_IS_EOF(status)) { /* Entire frame is already read (just header) */ - SERF_H2_assert(h2->read_frame == NULL); - SERF_H2_assert(! h2->in_frame); + SERF_H2_assert(h2->read_frame == NULL); + SERF_H2_assert(!h2->in_frame); } - else if (status) + else if (status) { - SERF_H2_assert(h2->read_frame != NULL); - SERF_H2_assert(! h2->in_frame); - return status; + SERF_H2_assert(h2->read_frame != NULL); + SERF_H2_assert(!h2->in_frame); + return status; } - else + else { - h2->in_frame = TRUE; - SERF_H2_assert(h2->read_frame != NULL); + h2->in_frame = TRUE; + SERF_H2_assert(h2->read_frame != NULL); } - serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, - "Reading 0x%x frame, stream=0x%x, flags=0x%x\n", - frametype, sid, frameflags); + serf__log(LOGLVL_INFO, SERF_LOGHTTP2, h2->config, + "Reading 0x%x frame, stream=0x%x, flags=0x%x\n", + frametype, sid, frameflags); - /* If status is EOF then the frame doesn't have/declare a body */ - switch (frametype) + /* If status is EOF then the frame doesn't have/declare a body */ + switch (frametype) { /* ---------------------------------------------------- */ - case HTTP2_FRAME_TYPE_DATA: - case HTTP2_FRAME_TYPE_HEADERS: - case HTTP2_FRAME_TYPE_PUSH_PROMISE: - if (h2->continuation_bucket) - { - h2->continuation_bucket = NULL; - h2->continuation_streamid = 0; - return APR_EAGAIN; - } - - stream = serf_http2__stream_get(h2, sid, TRUE, TRUE); - - if (sid == 0) - { - /* DATA, HEADERS and PUSH_PROMISE: - - These frames MUST be associated with a stream. If a - XXX frame is received whose stream identifier field is 0x0, - the recipient MUST respond with a connection error - (Section 5.4.1) of type PROTOCOL_ERROR. */ - return SERF_ERROR_HTTP2_PROTOCOL_ERROR; - } + case HTTP2_FRAME_TYPE_DATA: + case HTTP2_FRAME_TYPE_HEADERS: + case HTTP2_FRAME_TYPE_PUSH_PROMISE: + if (h2->continuation_bucket) + { + h2->continuation_bucket = NULL; + h2->continuation_streamid = 0; + return APR_EAGAIN; + } - reset_reason = 0; + stream = serf_http2__stream_get(h2, sid, TRUE, TRUE); - if (frametype == HTTP2_FRAME_TYPE_DATA) - { - /* Windowing is applied above padding! */ - remaining = (apr_size_t)serf_bucket_get_remaining(body); + if (sid == 0) + { + /* DATA, HEADERS and PUSH_PROMISE: - if (h2->rl_window < remaining) - { - if (h2->enforce_flow_control) - reset_reason = SERF_ERROR_HTTP2_FLOW_CONTROL_ERROR; + These frames MUST be associated with a stream. If a + XXX frame is received whose stream identifier field is + 0x0, the recipient MUST respond with a connection error + (Section 5.4.1) of type PROTOCOL_ERROR. */ + return SERF_ERROR_HTTP2_PROTOCOL_ERROR; + } - h2->rl_window = 0; - } - else - h2->rl_window -= remaining; + reset_reason = 0; - if (stream) - { - if (stream->rl_window < remaining) - { - if (h2->enforce_flow_control) - reset_reason = SERF_ERROR_HTTP2_FLOW_CONTROL_ERROR; + if (frametype == HTTP2_FRAME_TYPE_DATA) + { + /* Windowing is applied above padding! */ + remaining = + (apr_size_t)serf_bucket_get_remaining(body); + + if (h2->rl_window < remaining) + { + if (h2->enforce_flow_control) { + reset_reason = + SERF_ERROR_HTTP2_FLOW_CONTROL_ERROR; + } - stream->rl_window = 0; - } + h2->rl_window = 0; + } else - stream->rl_window -= remaining; - } - } - - /* DATA, HEADERS and PUSH_PROMISE can have padding */ - if (frameflags & HTTP2_FLAG_PADDED) - body = serf__bucket_http2_unpad_create(body, h2->allocator); - - /* An HEADERS frame can have an included priority 'frame' */ - if (frametype == HTTP2_FRAME_TYPE_HEADERS - && (frameflags & HTTP2_FLAG_PRIORITY)) - { - body = serf_bucket_prefix_create(body, - HTTP2_PRIORITY_DATA_SIZE, - http2_handle_priority, - stream, h2->allocator); - } - else if (frametype == HTTP2_FRAME_TYPE_PUSH_PROMISE) - { - body = serf_bucket_prefix_create(body, - HTTP2_PROMISE_DATA_SIZE, - http2_handle_promise, - stream, h2->allocator); - } - - if (!stream) - { - if (!reset_reason) - reset_reason = SERF_ERROR_HTTP2_STREAM_CLOSED; - } - else - switch (frametype) + h2->rl_window -= remaining; + + if (stream) + { + if (stream->rl_window < remaining) + { + if (h2->enforce_flow_control) { + reset_reason = + SERF_ERROR_HTTP2_FLOW_CONTROL_ERROR; + } + + stream->rl_window = 0; + } + else + stream->rl_window -= remaining; + } + } + + /* DATA, HEADERS and PUSH_PROMISE can have padding */ + if (frameflags & HTTP2_FLAG_PADDED) { + body = serf__bucket_http2_unpad_create(body, + h2->allocator); + } + + /* An HEADERS frame can have an included priority 'frame' */ + if (frametype == HTTP2_FRAME_TYPE_HEADERS + && (frameflags & HTTP2_FLAG_PRIORITY)) { - case HTTP2_FRAME_TYPE_DATA: - if (stream->status != H2S_OPEN - && stream->status != H2S_HALFCLOSED_LOCAL) - { - reset_reason = SERF_ERROR_HTTP2_STREAM_CLOSED; - } - break; - case HTTP2_FRAME_TYPE_HEADERS: - if (stream->status != H2S_OPEN - && stream->status != H2S_HALFCLOSED_LOCAL - && stream->status != H2S_IDLE - && stream->status != H2S_RESERVED_REMOTE) - { - reset_reason = SERF_ERROR_HTTP2_STREAM_CLOSED; - } - break; - case HTTP2_FRAME_TYPE_PUSH_PROMISE: - if (stream->status != H2S_OPEN - && stream->status != H2S_HALFCLOSED_LOCAL) - { - reset_reason = SERF_ERROR_HTTP2_STREAM_CLOSED; - } - break; + body = serf_bucket_prefix_create(body, + HTTP2_PRIORITY_DATA_SIZE, + http2_handle_priority, + stream, h2->allocator); + } + else if (frametype == HTTP2_FRAME_TYPE_PUSH_PROMISE) + { + body = serf_bucket_prefix_create(body, + HTTP2_PROMISE_DATA_SIZE, + http2_handle_promise, + stream, h2->allocator); } - if (reset_reason) - { - if (stream) - serf_http2__stream_reset(stream, reset_reason, TRUE); + if (!stream) + { + if (!reset_reason) + reset_reason = SERF_ERROR_HTTP2_STREAM_CLOSED; + } else - serf_http2__enqueue_stream_reset(h2, sid, reset_reason); - } + switch (frametype) + { + case HTTP2_FRAME_TYPE_DATA: + if (stream->status != H2S_OPEN + && stream->status != H2S_HALFCLOSED_LOCAL) + { + reset_reason = + SERF_ERROR_HTTP2_STREAM_CLOSED; + } + break; + case HTTP2_FRAME_TYPE_HEADERS: + if (stream->status != H2S_OPEN + && stream->status != H2S_HALFCLOSED_LOCAL + && stream->status != H2S_IDLE + && stream->status != H2S_RESERVED_REMOTE) + { + reset_reason = + SERF_ERROR_HTTP2_STREAM_CLOSED; + } + break; + case HTTP2_FRAME_TYPE_PUSH_PROMISE: + if (stream->status != H2S_OPEN + && stream->status != H2S_HALFCLOSED_LOCAL) + { + reset_reason = + SERF_ERROR_HTTP2_STREAM_CLOSED; + } + break; + } - if (frametype == HTTP2_FRAME_TYPE_HEADERS - || frametype == HTTP2_FRAME_TYPE_PUSH_PROMISE) - { - if (!(frameflags & HTTP2_FLAG_END_HEADERS)) - { - /* This header frame is *directly* followed by - continuation frames... We hide this from the - stream code, by providing an aggregate that will - read through the body of multiple frames */ - - h2->continuation_bucket = serf_bucket_aggregate_create( - h2->allocator); - h2->continuation_streamid = sid; - - serf_bucket_aggregate_append(h2->continuation_bucket, - body); - - serf_bucket_aggregate_hold_open( - h2->continuation_bucket, - http2_handle_continuation, h2); - - body = h2->continuation_bucket; - } - - if (stream && !reset_reason) - { - body = serf_http2__stream_handle_hpack( - stream, body, frametype, - (frameflags & HTTP2_FLAG_END_STREAM), - HTTP2_MAX_HEADER_ENTRYSIZE, - h2->hpack_tbl, h2->config, - h2->allocator); - } - else - { - /* Even when we don't want to process the headers we - must read them to update the HPACK state */ - body = serf__bucket_hpack_decode_create( - body, NULL, NULL, - HTTP2_MAX_HEADER_ENTRYSIZE, - h2->hpack_tbl, h2->allocator); - } - } - else if (! reset_reason) - { - /* We have a data bucket */ - body = serf_http2__stream_handle_data( - stream, body, frametype, - (frameflags & HTTP2_FLAG_END_STREAM), - h2->config, h2->allocator); - } + if (reset_reason) + { + if (stream) { + serf_http2__stream_reset(stream, reset_reason, + TRUE); + } + else { + serf_http2__enqueue_stream_reset(h2, sid, + reset_reason); + } + } - if (body) - process_bucket = body; /* We will take care of discarding */ - else - { - /* The stream wants to handle the reading itself */ - process_handler = serf_http2__stream_processor; - process_baton = stream; - } - break; + if (frametype == HTTP2_FRAME_TYPE_HEADERS + || frametype == HTTP2_FRAME_TYPE_PUSH_PROMISE) + { + if (!(frameflags & HTTP2_FLAG_END_HEADERS)) + { + /* This header frame is *directly* followed by + continuation frames... We hide this from the + stream code, by providing an aggregate that will + read through the body of multiple frames */ + + h2->continuation_bucket = + serf_bucket_aggregate_create(h2->allocator); + h2->continuation_streamid = sid; + + serf_bucket_aggregate_append( + h2->continuation_bucket, body); + + serf_bucket_aggregate_hold_open( + h2->continuation_bucket, + http2_handle_continuation, h2); + + body = h2->continuation_bucket; + } + + if (stream && !reset_reason) + { + body = serf_http2__stream_handle_hpack( + stream, body, frametype, + (frameflags & HTTP2_FLAG_END_STREAM), + HTTP2_MAX_HEADER_ENTRYSIZE, + h2->hpack_tbl, h2->config, + h2->allocator); + } + else + { + /* Even when we don't want to process the headers we + must read them to update the HPACK state */ + body = serf__bucket_hpack_decode_create( + body, NULL, NULL, + HTTP2_MAX_HEADER_ENTRYSIZE, + h2->hpack_tbl, h2->allocator); + } + } + else if (!reset_reason) + { + /* We have a data bucket */ + body = serf_http2__stream_handle_data( + stream, body, frametype, + (frameflags & HTTP2_FLAG_END_STREAM), + h2->config, h2->allocator); + } - /* ---------------------------------------------------- */ - case HTTP2_FRAME_TYPE_PRIORITY: - if (sid == 0) - { - /* The PRIORITY frame always identifies a stream. If a - PRIORITY frame is received with a stream identifier of - 0x0, the recipient MUST respond with a connection error - (Section 5.4.1) of type PROTOCOL_ERROR.*/ + if (body) { + /* We will take care of discarding */ + process_bucket = body; + } + else + { + /* The stream wants to handle the reading itself */ + process_handler = serf_http2__stream_processor; + process_baton = stream; + } + break; - return SERF_ERROR_HTTP2_PROTOCOL_ERROR; - } - else if (serf_bucket_get_remaining(body) - != HTTP2_PRIORITY_DATA_SIZE) - { - /* A PRIORITY frame with a length other than 5 octets MUST - be treated as a stream error (Section 5.4.2) of type - FRAME_SIZE_ERROR.*/ - - /* ### But we currently upgrade this to a connection error */ - return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - } + /* ---------------------------------------------------- */ + case HTTP2_FRAME_TYPE_PRIORITY: + if (sid == 0) + { + /* The PRIORITY frame always identifies a stream. If a + PRIORITY frame is received with a stream identifier + of 0x0, the recipient MUST respond with a connection + error (Section 5.4.1) of type PROTOCOL_ERROR.*/ - stream = serf_http2__stream_get(h2, sid, TRUE, TRUE); + return SERF_ERROR_HTTP2_PROTOCOL_ERROR; + } + else if (serf_bucket_get_remaining(body) + != HTTP2_PRIORITY_DATA_SIZE) + { + /* A PRIORITY frame with a length other than 5 octets + MUST be treated as a stream error (Section 5.4.2) of + type FRAME_SIZE_ERROR.*/ - if (stream) - { - body = serf_bucket_prefix_create(body, - HTTP2_PRIORITY_DATA_SIZE, - http2_handle_priority, - stream, h2->allocator); - } + /* ### But we currently upgrade this to a connection error + */ + return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; + } - /* Just reading will do the right thing now */ - process_bucket = body; - break; + stream = serf_http2__stream_get(h2, sid, TRUE, TRUE); - /* ---------------------------------------------------- */ - case HTTP2_FRAME_TYPE_RST_STREAM: - if (sid == 0) - { - /* RST_STREAM frames MUST be associated with a stream. - If a RST_STREAM frame is received with a stream - identifier of 0x0, the recipient MUST treat this as a - connection error (Section 5.4.1) of type PROTOCOL_ERROR. - */ + if (stream) + { + body = serf_bucket_prefix_create( + body, + HTTP2_PRIORITY_DATA_SIZE, + http2_handle_priority, + stream, h2->allocator); + } - return SERF_ERROR_HTTP2_PROTOCOL_ERROR; - } - else if (serf_bucket_get_remaining(body) - != HTTP2_RST_DATA_SIZE) - { - /* A RST_STREAM frame with a length other than 4 octets MUST - be treated as a connection error (Section 5.4.1) of type - FRAME_SIZE_ERROR. */ + /* Just reading will do the right thing now */ + process_bucket = body; + break; + + /* ---------------------------------------------------- */ + case HTTP2_FRAME_TYPE_RST_STREAM: + if (sid == 0) + { + /* RST_STREAM frames MUST be associated with a stream. + If a RST_STREAM frame is received with a stream + identifier of 0x0, the recipient MUST treat this as + a connection error (Section 5.4.1) of type + PROTOCOL_ERROR. + */ - return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - } + return SERF_ERROR_HTTP2_PROTOCOL_ERROR; + } + else if (serf_bucket_get_remaining(body) + != HTTP2_RST_DATA_SIZE) + { + /* A RST_STREAM frame with a length other than 4 octets + MUST be treated as a connection error (Section 5.4.1) + of type FRAME_SIZE_ERROR. */ - stream = serf_http2__stream_get(h2, sid, TRUE, TRUE); + return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; + } - if (stream) - { - body = serf_bucket_prefix_create(body, - HTTP2_FRAME_TYPE_RST_STREAM, - http2_handle_frame_reset, - stream, h2->allocator); - } + stream = serf_http2__stream_get(h2, sid, TRUE, TRUE); - /* Just reading will do the right thing now */ - process_bucket = body; - break; + if (stream) + { + body = serf_bucket_prefix_create( + body, + HTTP2_FRAME_TYPE_RST_STREAM, + http2_handle_frame_reset, + stream, h2->allocator); + } - /* ---------------------------------------------------- */ - case HTTP2_FRAME_TYPE_SETTINGS: - if (sid != 0) - { - /* SETTINGS frames always apply to a connection, never a - single stream. The stream identifier for a SETTINGS - frame MUST be zero (0x0). If an endpoint receives a - SETTINGS frame whose stream identifier field is - anything other than 0x0, the endpoint MUST respond - with a connection error (Section 5.4.1) of type - PROTOCOL_ERROR. - */ - return SERF_ERROR_HTTP2_PROTOCOL_ERROR; - } + /* Just reading will do the right thing now */ + process_bucket = body; + break; + + /* ---------------------------------------------------- */ + case HTTP2_FRAME_TYPE_SETTINGS: + if (sid != 0) + { + /* SETTINGS frames always apply to a connection, never a + single stream. The stream identifier for a SETTINGS + frame MUST be zero (0x0). If an endpoint receives a + SETTINGS frame whose stream identifier field is + anything other than 0x0, the endpoint MUST respond + with a connection error (Section 5.4.1) of type + PROTOCOL_ERROR. + */ + return SERF_ERROR_HTTP2_PROTOCOL_ERROR; + } - remaining = (apr_size_t)serf_bucket_get_remaining(body); - if (frameflags & HTTP2_FLAG_ACK) - { - if (remaining != 0) - { - /* When this bit is set, the payload of the SETTINGS - frame MUST be empty. Receipt of a SETTINGS frame - with the ACK flag set and a length field value - other than 0 MUST be treated as a connection error - (Section 5.4.1) of type FRAME_SIZE_ERROR. */ + remaining = (apr_size_t)serf_bucket_get_remaining(body); + if (frameflags & HTTP2_FLAG_ACK) + { + if (remaining != 0) + { + /* When this bit is set, the payload of the SETTINGS + frame MUST be empty. Receipt of a SETTINGS frame + with the ACK flag set and a length field value + other than 0 MUST be treated as a connection error + (Section 5.4.1) of type FRAME_SIZE_ERROR. */ + return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; + } + h2->setting_acks++; + } + else if ((remaining % HTTP2_SETTING_SIZE) != 0) + { + /* A SETTINGS frame with a length other than a multiple + of 6 octets MUST be treated as a connection error + (Section 5.4.1) of type FRAME_SIZE_ERROR. */ return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - } - h2->setting_acks++; - } - else if ((remaining % HTTP2_SETTING_SIZE) != 0) - { - /* A SETTINGS frame with a length other than a multiple of - 6 octets MUST be treated as a connection error (Section - 5.4.1) of type FRAME_SIZE_ERROR. */ - return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - } - else - { - /* Just read everything... We checked it against our - max-framesize */ - body = serf_bucket_prefix_create(body, remaining, - http2_handle_settings, h2, - h2->allocator); - } - - /* Just reading will do the right thing now */ - process_bucket = body; - break; + } + else + { + /* Just read everything... We checked it against our + max-framesize */ + body = serf_bucket_prefix_create(body, remaining, + http2_handle_settings, + h2, h2->allocator); + } - /* ---------------------------------------------------- */ - case HTTP2_FRAME_TYPE_PING: - if (sid != 0) - { - /* PING frames are not associated with any individual - stream. If a PING frame is received with a stream - identifier field value other than 0x0, the recipient - MUST respond with a connection error (Section 5.4.1) - of type PROTOCOL_ERROR.*/ - return SERF_ERROR_HTTP2_PROTOCOL_ERROR; - } - else if (serf_bucket_get_remaining(body) - != HTTP2_PING_DATA_SIZE) - { - /* Receipt of a PING frame with a length field value other - than 8 MUST be treated as a connection error (Section - 5.4.1) of type FRAME_SIZE_ERROR.. */ - return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; - } - - body = serf_bucket_prefix_create(body, HTTP2_PING_DATA_SIZE, - (frameflags & HTTP2_FLAG_ACK) - ? http2_handle_ping - : http2_handle_ping_ack, - h2, h2->allocator); + /* Just reading will do the right thing now */ + process_bucket = body; + break; + + /* ---------------------------------------------------- */ + case HTTP2_FRAME_TYPE_PING: + if (sid != 0) + { + /* PING frames are not associated with any individual + stream. If a PING frame is received with a stream + identifier field value other than 0x0, the recipient + MUST respond with a connection error (Section 5.4.1) + of type PROTOCOL_ERROR.*/ + return SERF_ERROR_HTTP2_PROTOCOL_ERROR; + } + else if (serf_bucket_get_remaining(body) + != HTTP2_PING_DATA_SIZE) + { + /* Receipt of a PING frame with a length field value + other than 8 MUST be treated as a connection error + (Section 5.4.1) of type FRAME_SIZE_ERROR.. */ + return SERF_ERROR_HTTP2_FRAME_SIZE_ERROR; + } - /* Just reading will do the right thing now */ - process_bucket = body; - break; - /* ---------------------------------------------------- */ - case HTTP2_FRAME_TYPE_GOAWAY: - if (sid != 0) - { - /* The GOAWAY frame applies to the connection, not a - specific stream. An endpoint MUST treat a GOAWAY frame - with a stream identifier other than 0x0 as a connection - error(Section 5.4.1) of type PROTOCOL_ERROR. */ - return SERF_ERROR_HTTP2_PROTOCOL_ERROR; - } + body = serf_bucket_prefix_create( + body, + HTTP2_PING_DATA_SIZE, + (frameflags & HTTP2_FLAG_ACK) + ? http2_handle_ping + : http2_handle_ping_ack, + h2, h2->allocator); + + /* Just reading will do the right thing now */ + process_bucket = body; + break; + /* ---------------------------------------------------- */ + case HTTP2_FRAME_TYPE_GOAWAY: + if (sid != 0) + { + /* The GOAWAY frame applies to the connection, not a + specific stream. An endpoint MUST treat a GOAWAY frame + with a stream identifier other than 0x0 as a + connection error (Section 5.4.1) of type + PROTOCOL_ERROR. */ + return SERF_ERROR_HTTP2_PROTOCOL_ERROR; + } - /* As the final go-away frame is best effort only we are not - checking the bodysize against HTTP2_GOWAWAY_DATA_SIZE here. - We'll see what we get in the goaway handler. - - Go away frames may contain additional opaque debug - information at the end, so instead of reading - HTTP2_GOWAWAY_DATA_SIZE bytes, we just read the whole frame. - */ - remaining = (apr_size_t)serf_bucket_get_remaining(body); - - body = serf_bucket_prefix_create(body, - MIN(remaining, - HTTP2_GOWAWAY_DATA_SIZE - + 256), - http2_handle_goaway, h2, - h2->allocator); + /* As the final go-away frame is best effort only we are not + checking the bodysize against HTTP2_GOWAWAY_DATA_SIZE + here. We'll see what we get in the goaway handler. + + Go away frames may contain additional opaque debug + information at the end, so instead of reading + HTTP2_GOWAWAY_DATA_SIZE bytes, we just read the whole frame. + */ + remaining = (apr_size_t)serf_bucket_get_remaining(body); - /* Just reading will do the right thing now */ - process_bucket = body; - break; - /* ---------------------------------------------------- */ - case HTTP2_FRAME_TYPE_WINDOW_UPDATE: - if (serf_bucket_get_remaining(body)
[... 450 lines stripped ...]