Author: rhuijben Date: Sun Nov 1 12:18:54 2015 New Revision: 1711737 URL: http://svn.apache.org/viewvc?rev=1711737&view=rev Log: In the http2 stream handler: hook up the standard request response handlers.
This makes the serf_get program fully capable of sending out http/2 requests, that don't contain a request body. Note that for testing you need either OpenSSL 1.0.2+ or a server capable of h2direct. * protocols/http2_protocol.c (http2_process): Fix some stream status checks. Put more likely values first. (http2_protocol_write): Avoid duplicated code. Update request numbers. * protocols/http2_stream.c (serf_http2__stream_setup_request): Set status to halfclosed, because we sent HTTP2_FLAG_END_STREAM. (stream_setup_response): New function. (serf_http2__stream_handle_hpack, serf_http2__stream_handle_data): Use stream_setup_response to setup response. (serf_http2__stream_processor): Let request read the data until it is no longer interested. Modified: serf/trunk/protocols/http2_protocol.c serf/trunk/protocols/http2_stream.c Modified: serf/trunk/protocols/http2_protocol.c URL: http://svn.apache.org/viewvc/serf/trunk/protocols/http2_protocol.c?rev=1711737&r1=1711736&r2=1711737&view=diff ============================================================================== --- serf/trunk/protocols/http2_protocol.c (original) +++ serf/trunk/protocols/http2_protocol.c Sun Nov 1 12:18:54 2015 @@ -961,17 +961,17 @@ http2_process(serf_http2_protocol_t *h2) } break; case HTTP2_FRAME_TYPE_HEADERS: - if (stream->status != H2S_IDLE - && stream->status != H2S_RESERVED_LOCAL - && stream->status != H2S_OPEN - && stream->status != H2S_HALFCLOSED_REMOTE) + 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_REMOTE) + && stream->status != H2S_HALFCLOSED_LOCAL) { reset_reason = SERF_ERROR_HTTP2_STREAM_CLOSED; } @@ -1352,18 +1352,16 @@ http2_protocol_write(serf_connection_t * if (request) { - /* Yuck.. there must be easier ways to do this, but I don't - want to change outgoing.c all the time just yet. */ conn->unwritten_reqs = request->next; if (conn->unwritten_reqs_tail == request) conn->unwritten_reqs = conn->unwritten_reqs_tail = NULL; request->next = NULL; - if (conn->written_reqs_tail) - conn->written_reqs_tail->next = request; - else - conn->written_reqs = conn->written_reqs_tail = request; + serf__link_requests(&conn->written_reqs, &conn->written_reqs_tail, + request); + conn->nr_of_written_reqs++; + conn->nr_of_written_reqs--; status = setup_for_http2(ctx, request); if (status) Modified: serf/trunk/protocols/http2_stream.c URL: http://svn.apache.org/viewvc/serf/trunk/protocols/http2_stream.c?rev=1711737&r1=1711736&r2=1711737&view=diff ============================================================================== --- serf/trunk/protocols/http2_stream.c (original) +++ serf/trunk/protocols/http2_stream.c Sun Nov 1 12:18:54 2015 @@ -126,7 +126,7 @@ serf_http2__stream_setup_request(serf_ht serf_http2__enqueue_frame(stream->h2, hpack, TRUE); - stream->status = H2S_OPEN; /* Headers sent */ + stream->status = H2S_HALFCLOSED_LOCAL; /* Headers sent */ return APR_SUCCESS; } @@ -165,6 +165,34 @@ stream_response_eof(void *baton, } } +void +stream_setup_response(serf_http2_stream_t *stream, + serf_config_t *config) +{ + serf_request_t *request; + serf_bucket_t *agg; + + agg = serf_bucket_aggregate_create(stream->alloc); + serf_bucket_aggregate_hold_open(agg, stream_response_eof, stream); + + serf_bucket_set_config(agg, config); + + request = stream->data->request; + + if (!request) + return; + + if (! request->resp_bkt) + { + apr_pool_t *scratch_pool = request->respool; /* ### Pass scratch pool */ + + request->resp_bkt = request->acceptor(request, agg, request->acceptor_baton, + scratch_pool); + } + + stream->data->response_agg = agg; +} + serf_bucket_t * serf_http2__stream_handle_hpack(serf_http2_stream_t *stream, serf_bucket_t *bucket, @@ -176,12 +204,7 @@ serf_http2__stream_handle_hpack(serf_htt serf_bucket_alloc_t *allocator) { if (!stream->data->response_agg) - { - stream->data->response_agg = serf_bucket_aggregate_create(stream->alloc); - serf_bucket_aggregate_hold_open(stream->data->response_agg, - stream_response_eof, stream); - serf_bucket_set_config(stream->data->response_agg, config); - } + stream_setup_response(stream, config); bucket = serf__bucket_hpack_decode_create(bucket, NULL, NULL, max_entry_size, hpack_tbl, allocator); @@ -208,13 +231,7 @@ serf_http2__stream_handle_data(serf_http serf_bucket_alloc_t *allocator) { if (!stream->data->response_agg) - { - stream->data->response_agg = serf_bucket_aggregate_create(stream->alloc); - serf_bucket_aggregate_hold_open(stream->data->response_agg, - stream_response_eof, stream); - - serf_bucket_set_config(stream->data->response_agg, config); - } + stream_setup_response(stream, config); serf_bucket_aggregate_append(stream->data->response_agg, bucket); @@ -236,9 +253,76 @@ serf_http2__stream_processor(void *baton { serf_http2_stream_t *stream = baton; apr_status_t status = APR_SUCCESS; + serf_request_t *request = stream->data->request; - if (!stream->data->response_agg) - return APR_EAGAIN; + SERF_H2_assert(stream->data->response_agg != NULL); + + if (request) + { + SERF_H2_assert(request->resp_bkt != NULL); + + status = stream->data->request->handler(request, request->resp_bkt, + request->handler_baton, + request->respool); + + if (! APR_STATUS_IS_EOF(status) + && !SERF_BUCKET_READ_ERROR(status)) + return status; + + /* Ok, the request thinks is done, let's handle the bookkeeping, + to remove it from the outstanding requests */ + { + serf_connection_t *conn = serf_request_get_conn(request); + serf_request_t **rq = &conn->written_reqs; + serf_request_t *last = NULL; + + while (*rq && (*rq != request)) + { + last = *rq; + rq = &last->next; + } + + if (*rq) + { + (*rq) = request->next; + + if (conn->written_reqs_tail == request) + conn->written_reqs_tail = last; + + conn->nr_of_written_reqs--; + } + + serf__destroy_request(request); + stream->data->request = NULL; + } + + if (SERF_BUCKET_READ_ERROR(status)) + { + if (stream->status != H2S_CLOSED) + { + /* Tell the other side that we are no longer interested + to receive more data */ + serf_http2__stream_reset(stream, status, TRUE); + } + + return status; + } + + SERF_H2_assert(APR_STATUS_IS_EOF(status)); + + /* Even though the request reported that it is done, we might not + have read all the data that we should (*cough* padding *cough*), + or perhaps an invalid 'Content-Length' value; maybe both. + + This may even handle not-interested - return EOF cases, but that + would have broken the pipeline for HTTP/1.1. + */ + + /* ### For now, fall through and eat whatever is left. + Usually this is 0 bytes */ + + status = APR_SUCCESS; + } /* ### TODO: Delegate to request */ while (!status) @@ -251,9 +335,11 @@ serf_http2__stream_processor(void *baton if (!SERF_BUCKET_READ_ERROR(status)) { +#if 0 if (len > 0) { - char *printable = serf_bstrmemdup(bucket->allocator, data, len); + serf_bucket_alloc_t *alloc = stream->data->response_agg->allocator; + char *printable = serf_bstrmemdup(alloc, data, len); char *c; for (c = printable; *c; c++) @@ -269,10 +355,21 @@ serf_http2__stream_processor(void *baton fputs(printable, stdout); #endif - serf_bucket_mem_free(bucket->allocator, printable); + serf_bucket_mem_free(alloc, printable); } +#endif } } + if (APR_STATUS_IS_EOF(status) + && stream->status == H2S_CLOSED || stream->status == H2S_HALFCLOSED_REMOTE) + { + /* If there was a request, it is already gone, so we can now safely + destroy our aggregate which may include everything upto the http2 + frames */ + serf_bucket_destroy(stream->data->response_agg); + stream->data->response_agg = NULL; + } + return status; }