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;
}