Author: rhuijben Date: Wed Nov 25 12:27:42 2015 New Revision: 1716371 URL: http://svn.apache.org/viewvc?rev=1716371&view=rev Log: Remove a lot of bucket reading from the http2 processing by trying to read a headers bucket before reading the separate lines to fill it.
Before this patch we usually created a header bucket and then read it into another header bucket. * buckets/barrier_buckets.c (serf_barrier_read_bucket): New function. (serf_bucket_type_barrier): Resolve TODO. * buckets/hpack_buckets.c (serf_hpack_decode_read, serf_hpack_decode_peek): Remove unneeded check. (serf_hpack_decode_read_bucket): New function. * buckets/response_buckets.c (response_context_t): Introduce new substates, to allow skipping some parts of processing. (serf_bucket_response_create, serf_bucket_response_get_headers): Initialize headers on demand. (run_machine): Handle the first and last headers read operations as specific states to allow reading the headers as a bucket. Modified: serf/trunk/buckets/barrier_buckets.c serf/trunk/buckets/hpack_buckets.c serf/trunk/buckets/response_buckets.c Modified: serf/trunk/buckets/barrier_buckets.c URL: http://svn.apache.org/viewvc/serf/trunk/buckets/barrier_buckets.c?rev=1716371&r1=1716370&r2=1716371&view=diff ============================================================================== --- serf/trunk/buckets/barrier_buckets.c (original) +++ serf/trunk/buckets/barrier_buckets.c Wed Nov 25 12:27:42 2015 @@ -72,6 +72,20 @@ static apr_status_t serf_barrier_readlin return serf_bucket_readline(stream, acceptable, found, data, len); } +static serf_bucket_t *serf_barrier_read_bucket(serf_bucket_t *bucket, + const serf_bucket_type_t *type) +{ + serf_bucket_t *stream = bucket->data; + + /* If a not-NULL bucket is returned then the ownership of that bucket may + now have been transferred. (See aggregate bucket read support). + + This may affect something in our bucket, like any read does, but + we still perform our 'barrier job' of keeping the stream alive. */ + + return serf_bucket_read_bucket(stream, type); +} + static apr_status_t serf_barrier_peek(serf_bucket_t *bucket, const char **data, apr_size_t *len) @@ -118,7 +132,7 @@ const serf_bucket_type_t serf_bucket_typ serf_buckets_are_v2, serf_barrier_peek, serf_barrier_destroy, - serf_default_read_bucket, /* ### TODO? */ + serf_barrier_read_bucket, serf_barrier_get_remaining, serf_barrier_set_config, }; Modified: serf/trunk/buckets/hpack_buckets.c URL: http://svn.apache.org/viewvc/serf/trunk/buckets/hpack_buckets.c?rev=1716371&r1=1716370&r2=1716371&view=diff ============================================================================== --- serf/trunk/buckets/hpack_buckets.c (original) +++ serf/trunk/buckets/hpack_buckets.c Wed Nov 25 12:27:42 2015 @@ -1877,7 +1877,7 @@ serf_hpack_decode_read(serf_bucket_t *bu apr_status_t status; status = hpack_process(bucket); - if (status || !ctx->agg) + if (status) { *len = 0; return (status == SERF_ERROR_EMPTY_READ) ? APR_SUCCESS : status; @@ -1895,7 +1895,7 @@ serf_hpack_decode_peek(serf_bucket_t *bu apr_status_t status; status = hpack_process(bucket); - if (status || !ctx->agg) + if (status) { *len = 0; return (status == SERF_ERROR_EMPTY_READ) ? APR_SUCCESS : status; @@ -1904,6 +1904,18 @@ serf_hpack_decode_peek(serf_bucket_t *bu return serf_bucket_peek(ctx->agg, data, len); } +static serf_bucket_t * +serf_hpack_decode_read_bucket(serf_bucket_t *bucket, + const serf_bucket_type_t *type) +{ + serf_hpack_decode_ctx_t *ctx = bucket->data; + + if (!ctx->hit_eof) + return serf_default_read_bucket(bucket, type); + else + return serf_bucket_read_bucket(ctx->agg, type); +} + static apr_status_t serf_hpack_decode_set_config(serf_bucket_t *bucket, serf_config_t *config) @@ -1930,10 +1942,9 @@ static void serf_hpack_decode_destroy(serf_bucket_t *bucket) { serf_hpack_decode_ctx_t *ctx = bucket->data; - serf_bucket_destroy(ctx->stream); - if (ctx->agg) - serf_bucket_destroy(ctx->agg); + serf_bucket_destroy(ctx->stream); + serf_bucket_destroy(ctx->agg); if (ctx->method) serf_bucket_mem_free(bucket->allocator, (void*)ctx->method); @@ -1960,7 +1971,7 @@ const serf_bucket_type_t serf_bucket_typ serf_buckets_are_v2, serf_hpack_decode_peek, serf_hpack_decode_destroy, - serf_default_read_bucket, + serf_hpack_decode_read_bucket, serf_default_get_remaining, serf_hpack_decode_set_config }; Modified: serf/trunk/buckets/response_buckets.c URL: http://svn.apache.org/viewvc/serf/trunk/buckets/response_buckets.c?rev=1716371&r1=1716370&r2=1716371&view=diff ============================================================================== --- serf/trunk/buckets/response_buckets.c (original) +++ serf/trunk/buckets/response_buckets.c Wed Nov 25 12:27:42 2015 @@ -35,7 +35,9 @@ typedef struct response_context_t { enum { STATE_STATUS_LINE, /* reading status line */ STATE_NEXT_STATUS_LINE, + STATE_PRE_HEADERS, STATE_HEADERS, /* reading headers */ + STATE_PRE_BODY, STATE_BODY, /* reading body */ STATE_TRAILERS, /* reading trailers */ STATE_DONE /* we've sent EOF */ @@ -87,8 +89,8 @@ serf_bucket_t *serf_bucket_response_crea ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); ctx->stream = stream; ctx->body = NULL; - ctx->incoming_headers = serf_bucket_headers_create(allocator); - ctx->fetch_headers = ctx->incoming_headers; + ctx->incoming_headers = NULL; + ctx->fetch_headers = NULL; ctx->state = STATE_STATUS_LINE; ctx->chunked = 0; ctx->head_req = 0; @@ -118,10 +120,21 @@ void serf_bucket_response_decode_content ctx->decode_content = decode; } -serf_bucket_t *serf_bucket_response_get_headers( - serf_bucket_t *bucket) +serf_bucket_t *serf_bucket_response_get_headers(serf_bucket_t *bucket) { - return ((response_context_t *)bucket->data)->fetch_headers; + response_context_t *ctx = bucket->data; + + if (!ctx->fetch_headers) { + + if (!ctx->incoming_headers) { + ctx->incoming_headers = serf_bucket_headers_create( + bucket->allocator); + } + + ctx->fetch_headers = ctx->incoming_headers; + } + + return ctx->fetch_headers; } @@ -281,7 +294,7 @@ static apr_status_t run_machine(serf_buc } /* Okay... move on to reading the headers. */ - ctx->state = STATE_HEADERS; + ctx->state = STATE_PRE_HEADERS; } else { /* The connection closed before we could get the next @@ -293,6 +306,34 @@ static apr_status_t run_machine(serf_buc } } break; + case STATE_PRE_HEADERS: + { + serf_bucket_t *read_hdrs; + + ctx->state = STATE_HEADERS; + + /* Perhaps we can just read a headers bucket? */ + read_hdrs = serf_bucket_read_bucket(ctx->stream, + &serf_bucket_type_headers); + + if (read_hdrs) { + if (ctx->incoming_headers) + serf_bucket_destroy(ctx->incoming_headers); + + ctx->incoming_headers = read_hdrs; + + ctx->state = STATE_PRE_BODY; + } + else if (!ctx->incoming_headers) { + ctx->incoming_headers = + serf_bucket_headers_create(bkt->allocator); + } + + if (!ctx->fetch_headers) + ctx->fetch_headers = ctx->incoming_headers; + } + break; + case STATE_HEADERS: status = fetch_headers(bkt, ctx); if (SERF_BUCKET_READ_ERROR(status)) @@ -301,7 +342,15 @@ static apr_status_t run_machine(serf_buc /* If an empty line was read, then we hit the end of the headers. * Move on to the body. */ - if (ctx->linebuf.state == SERF_LINEBUF_READY && !ctx->linebuf.used) { + if (ctx->linebuf.state != SERF_LINEBUF_READY || ctx->linebuf.used) + break; + + /* Advance the state. */ + ctx->state = STATE_PRE_BODY; + /* fall through */ + + case STATE_PRE_BODY: + { const char *v; int chunked = 0; int gzip = 0; @@ -523,7 +572,7 @@ apr_status_t serf_bucket_response_status * it is quite possible to advance *and* to return APR_EAGAIN. */ status = run_machine(bkt, ctx); - if (ctx->state == STATE_HEADERS) { + if (ctx->state != STATE_STATUS_LINE) { *sline = ctx->sl; } else {