diff options
author | Daniel Stenberg <daniel@haxx.se> | 2010-08-25 13:42:14 +0200 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2010-08-25 13:42:14 +0200 |
commit | 6b6a3bcb61f2d8f4e80a1e2a5bc62e78904256ed (patch) | |
tree | 210ebaa16337c5758c0162fc56381a55abe58e27 /lib | |
parent | 0cbdcd07a8b176376cf8b57ba89b8717cdd40d5b (diff) |
http: handle trailer headers in all chunked responses
HTTP allows that a server sends trailing headers after all the chunks
have been sent WITHOUT signalling their presence in the first response
headers. The "Trailer:" header is only a SHOULD there and as we need to
handle the situation even without that header I made libcurl ignore
Trailer: completely.
Test case 1116 was added to verify this and to make sure we handle more
than one trailer header properly.
Reported by: Patrick McManus
Bug: http://curl.haxx.se/bug/view.cgi?id=3052450
Diffstat (limited to 'lib')
-rw-r--r-- | lib/http.c | 14 | ||||
-rw-r--r-- | lib/http_chunks.c | 151 | ||||
-rw-r--r-- | lib/url.c | 4 | ||||
-rw-r--r-- | lib/urldata.h | 3 |
4 files changed, 69 insertions, 103 deletions
diff --git a/lib/http.c b/lib/http.c index 2d0e78af3..3d6f977ea 100644 --- a/lib/http.c +++ b/lib/http.c @@ -3647,20 +3647,6 @@ CURLcode Curl_http_readwrite_headers(struct SessionHandle *data, /* init our chunky engine */ Curl_httpchunk_init(conn); } - - else if(checkprefix("Trailer:", k->p) || - checkprefix("Trailers:", k->p)) { - /* - * This test helps Curl_httpchunk_read() to determine to look - * for well formed trailers after the zero chunksize record. In - * this case a CRLF is required after the zero chunksize record - * when no trailers are sent, or after the last trailer record. - * - * It seems both Trailer: and Trailers: occur in the wild. - */ - k->trailerhdrpresent = TRUE; - } - else if(checkprefix("Content-Encoding:", k->p) && data->set.str[STRING_ENCODING]) { /* diff --git a/lib/http_chunks.c b/lib/http_chunks.c index a66f87210..0d41979af 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -184,22 +184,8 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, if(*datap == 0x0a) { /* we're now expecting data to come, unless size was zero! */ if(0 == ch->datasize) { - if(k->trailerhdrpresent!=TRUE) { - /* No Trailer: header found - revert to original Curl processing */ - ch->state = CHUNK_STOPCR; - - /* We need to increment the datap here since we bypass the - increment below with the immediate break */ - length--; - datap++; - - /* This is the final byte, continue to read the final CRLF */ - break; - } - else { - ch->state = CHUNK_TRAILER; /* attempt to read trailers */ - conn->trlPos=0; - } + ch->state = CHUNK_TRAILER; /* now check for trailers */ + conn->trlPos=0; } else { ch->state = CHUNK_DATA; @@ -280,9 +266,9 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, datap++; length--; } - else { + else return CHUNKE_BAD_CHUNK; - } + break; case CHUNK_POSTLF: @@ -295,80 +281,32 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, datap++; length--; } - else { + else return CHUNKE_BAD_CHUNK; - } break; case CHUNK_TRAILER: - /* conn->trailer is assumed to be freed in url.c on a - connection basis */ - if(conn->trlPos >= conn->trlMax) { - /* in this logic we always allocate one byte more than trlMax - contains, just because CHUNK_TRAILER_POSTCR will append two bytes - so we need to make sure we have room for an extra byte */ - char *ptr; - if(conn->trlMax) { - conn->trlMax *= 2; - ptr = realloc(conn->trailer, conn->trlMax + 1); - } - else { - conn->trlMax=128; - ptr = malloc(conn->trlMax + 1); - } - if(!ptr) - return CHUNKE_OUT_OF_MEMORY; - conn->trailer = ptr; - } - conn->trailer[conn->trlPos++]=*datap; - - if(*datap == 0x0d) - ch->state = CHUNK_TRAILER_CR; - else { - datap++; - length--; - } - break; - - case CHUNK_TRAILER_CR: if(*datap == 0x0d) { - ch->state = CHUNK_TRAILER_POSTCR; - datap++; - length--; - } - else - return CHUNKE_BAD_CHUNK; - break; - - case CHUNK_TRAILER_POSTCR: - if(*datap == 0x0a) { - conn->trailer[conn->trlPos++]=0x0a; - conn->trailer[conn->trlPos]=0; - if(conn->trlPos==2) { - ch->state = CHUNK_STOP; - length--; + /* this is the end of a trailer, but if the trailer was zero bytes + there was no trailer and we move on */ - /* - * Note that this case skips over the final STOP states since we've - * already read the final CRLF and need to return - */ + if(conn->trlPos) { + /* we allocate trailer with 3 bytes extra room to fit this */ + conn->trailer[conn->trlPos++]=0x0d; + conn->trailer[conn->trlPos++]=0x0a; + conn->trailer[conn->trlPos]=0; - ch->dataleft = length; - - return CHUNKE_STOP; /* return stop */ - } - else { #ifdef CURL_DOES_CONVERSIONS /* Convert to host encoding before calling Curl_client_write */ result = Curl_convert_from_network(conn->data, conn->trailer, conn->trlPos); - if(result != CURLE_OK) { + if(result != CURLE_OK) /* Curl_convert_from_network calls failf if unsuccessful */ /* Treat it as a bad chunk */ - return(CHUNKE_BAD_CHUNK); - } + return CHUNKE_BAD_CHUNK; + #endif /* CURL_DOES_CONVERSIONS */ if(!data->set.http_te_skip) { result = Curl_client_write(conn, CLIENTWRITE_HEADER, @@ -376,9 +314,44 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, if(result) return CHUNKE_WRITE_ERROR; } + conn->trlPos=0; + ch->state = CHUNK_TRAILER_CR; } - ch->state = CHUNK_TRAILER; - conn->trlPos=0; + else { + /* no trailer, we're on the final CRLF pair */ + ch->state = CHUNK_TRAILER_POSTCR; + break; /* don't advance the pointer */ + } + } + else { + /* conn->trailer is assumed to be freed in url.c on a + connection basis */ + if(conn->trlPos >= conn->trlMax) { + /* we always allocate three extra bytes, just because when the full + header has been received we append CRLF\0 */ + char *ptr; + if(conn->trlMax) { + conn->trlMax *= 2; + ptr = realloc(conn->trailer, conn->trlMax + 3); + } + else { + conn->trlMax=128; + ptr = malloc(conn->trlMax + 3); + } + if(!ptr) + return CHUNKE_OUT_OF_MEMORY; + conn->trailer = ptr; + } + fprintf(stderr, "MOO: %c\n", *datap); + conn->trailer[conn->trlPos++]=*datap; + } + datap++; + length--; + break; + + case CHUNK_TRAILER_CR: + if(*datap == 0x0a) { + ch->state = CHUNK_TRAILER_POSTCR; datap++; length--; } @@ -386,6 +359,20 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, return CHUNKE_BAD_CHUNK; break; + case CHUNK_TRAILER_POSTCR: + /* We enter this state when a CR should arrive so we expect to + have to first pass a CR before we wait for LF */ + if(*datap != 0x0d) { + /* not a CR then it must be another header in the trailer */ + ch->state = CHUNK_TRAILER; + break; + } + datap++; + length--; + /* now wait for the final LF */ + ch->state = CHUNK_STOP; + break; + case CHUNK_STOPCR: /* Read the final CRLF that ends all chunk bodies */ @@ -394,9 +381,8 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, datap++; length--; } - else { + else return CHUNKE_BAD_CHUNK; - } break; case CHUNK_STOP: @@ -409,9 +395,8 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn, ch->dataleft = length; return CHUNKE_STOP; /* return stop */ } - else { + else return CHUNKE_BAD_CHUNK; - } default: return CHUNKE_STATE_ERROR; @@ -5300,10 +5300,8 @@ static CURLcode do_init(struct connectdata *conn) static void do_complete(struct connectdata *conn) { conn->data->req.chunk=FALSE; - conn->data->req.trailerhdrpresent=FALSE; - conn->data->req.maxfd = (conn->sockfd>conn->writesockfd? - conn->sockfd:conn->writesockfd)+1; + conn->sockfd:conn->writesockfd)+1; } CURLcode Curl_do(struct connectdata **connp, bool *done) diff --git a/lib/urldata.h b/lib/urldata.h index de9acf8bf..940cb3551 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -588,9 +588,6 @@ struct SingleRequest { bool forbidchunk; /* used only to explicitly forbid chunk-upload for specific upload buffers. See readmoredata() in http.c for details. */ - bool trailerhdrpresent; /* Set when Trailer: header found in HTTP response. - Required to determine whether to look for trailers - in case of Transfer-Encoding: chunking */ }; /* |