patch attached
On Tue, Nov 6, 2012 at 10:45 PM, Lieven Govaerts <l...@mobsol.be> wrote: > Hi, > > On Tue, Nov 6, 2012 at 6:13 PM, Lieven Govaerts <l...@mobsol.be> wrote: >> Hi, >> >> On Tue, Nov 6, 2012 at 4:50 PM, Lieven Govaerts <l...@mobsol.be> wrote: >>> Ben, >>> >>> On Tue, Nov 6, 2012 at 4:09 PM, Ben Reser <b...@reser.org> wrote: >>>> I worked with Philip today and was able to reproduce the exact problem >>>> he's been seeing. I ended up having to get his full httpd.conf to >>>> figure it out.. >>>> >>>> Ultimately the problem proved to be that he had this directive: >>>> Timeout 3 >>>> >>>> Which would mean if we don't tend a connection for 3 seconds Apache >>>> will close it. Serf should be able to deal with the connection being >>>> closed. >>>> >> >> okay, so with the Timeout directive added I can reproduce this issue. >> >> What I see is that the server closes the connection in the middle of >> sending a response to the client. It doesn't even finalize the >> response first. >> So ra_serf is reading the data from the response bucket, but gets an >> APR_EOF when it needs more data than specified in the Content-Length >> header of the response. >> >> What is the expected behavior here, let serf close down the connection >> and try the request again on a new connection? > > IMHO, the reasonable thing to do here, is to report the error > correctly to the user, so that we don't corrupt the working copy. > This way the user can retry the transaction. > > For response bodies that are encoded (chunked or gzip/deflated), serf > will already return an error status to svn/ra_serf. The one case where > serf silently returns APR_EOF is when no encoding is used so the > Content-Length header is set. > Attached patch to serf trunk will calculate the actually read bytes > from the response body, and ensures an error is returned to svn when > this is less than what's expected. The patch is not final, I probably > want to use another error code. > > Philip or Ben, can you test that with this patch svn will always stop > with a communication error? > > Lieven
Index: buckets/response_buckets.c =================================================================== --- buckets/response_buckets.c (revision 1674) +++ buckets/response_buckets.c (working copy) @@ -41,6 +41,12 @@ typedef struct { int chunked; /* Do we need to read trailers? */ int head_req; /* Was this a HEAD request? */ + + int check_length; /* Indicates we need calculate # of bytes in the + body and return error if it's too short. */ + apr_size_t exp_body_len; /* expected size of body, 0 if not known. */ + apr_size_t actual_body_len; /* Keep track of actual bytes read from the body + to see if we received the full response. */ } response_context_t; @@ -57,6 +63,9 @@ serf_bucket_t *serf_bucket_response_create( ctx->state = STATE_STATUS_LINE; ctx->chunked = 0; ctx->head_req = 0; + ctx->check_length = 0; + ctx->exp_body_len = 0; + ctx->actual_body_len = 0; serf_linebuf_init(&ctx->linebuf); @@ -251,6 +260,8 @@ static apr_status_t run_machine(serf_bucket_t *bkt } ctx->body = serf_bucket_limit_create(ctx->body, length, bkt->allocator); + ctx->check_length = 1; + ctx->exp_body_len = length; } else { v = serf_bucket_headers_get(ctx->headers, "Transfer-Encoding"); @@ -268,6 +279,8 @@ static apr_status_t run_machine(serf_bucket_t *bkt } v = serf_bucket_headers_get(ctx->headers, "Content-Encoding"); if (v) { + ctx->check_length = 0; /* deflate bucket will check this. */ + /* Need to handle multiple content-encoding. */ if (v && strcasecmp("gzip", v) == 0) { ctx->body = @@ -385,6 +398,12 @@ static apr_status_t serf_response_read(serf_bucket } rv = serf_bucket_read(ctx->body, requested, data, len); + if (SERF_BUCKET_READ_ERROR(rv)) + return rv; + + if (ctx->check_length) + ctx->actual_body_len += *len; + if (APR_STATUS_IS_EOF(rv)) { if (ctx->chunked) { ctx->state = STATE_TRAILERS; @@ -392,6 +411,8 @@ static apr_status_t serf_response_read(serf_bucket rv = APR_SUCCESS; } else { + if (ctx->check_length && ctx->actual_body_len < ctx->exp_body_len) + return SERF_ERROR_BAD_HTTP_RESPONSE; ctx->state = STATE_DONE; } }