From: Hans Leidekker Subject: [2/2] winhttp: Don't assume that end of chunk means end of stream. Message-Id: <1379923420.28479.1.camel@t400> Date: Mon, 23 Sep 2013 10:03:40 +0200 --- dlls/winhttp/request.c | 199 +++++++++++++++++------------------------ dlls/winhttp/winhttp_private.h | 4 +- 2 files changed, 87 insertions(+), 116 deletions(-) diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index db8e6af..8c5a39a 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -25,6 +25,7 @@ #include "wine/debug.h" #include +#include #ifdef HAVE_ARPA_INET_H # include #endif @@ -1005,6 +1006,8 @@ static BOOL open_connection( request_t *request ) done: request->read_pos = request->read_size = 0; request->read_chunked = FALSE; + request->read_chunked_size = ~0u; + request->read_chunked_eof = FALSE; heap_free( addressW ); return TRUE; } @@ -1131,7 +1134,7 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len request->optional_len = optional_len; len += optional_len; } - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(DWORD) ); + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_SENT, &len, sizeof(len) ); end: if (async) @@ -1808,15 +1811,20 @@ static DWORD set_content_length( request_t *request ) { request->content_length = ~0u; request->read_chunked = TRUE; + request->read_chunked_size = ~0u; + request->read_chunked_eof = FALSE; } request->content_read = 0; return request->content_length; } /* read some more data into the read buffer */ -static BOOL read_more_data( request_t *request, int maxlen ) +static BOOL read_more_data( request_t *request, int maxlen, BOOL notify ) { int len; + BOOL ret; + + if (request->read_chunked_eof) return FALSE; if (request->read_size && request->read_pos) { @@ -1825,10 +1833,16 @@ static BOOL read_more_data( request_t *request, int maxlen ) request->read_pos = 0; } if (maxlen == -1) maxlen = sizeof(request->read_buf); - if (!netconn_recv( &request->netconn, request->read_buf + request->read_size, - maxlen - request->read_size, 0, &len )) return FALSE; + + if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 ); + + ret = netconn_recv( &request->netconn, request->read_buf + request->read_size, + maxlen - request->read_size, 0, &len ); + + if (notify) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &len, sizeof(len) ); + request->read_size += len; - return TRUE; + return ret; } /* remove some amount of data from the read buffer */ @@ -1858,7 +1872,7 @@ static BOOL read_line( request_t *request, char *buffer, DWORD *len ) remove_data( request, bytes_read ); if (eol) break; - if (!read_more_data( request, -1 )) return FALSE; + if (!read_more_data( request, -1, TRUE )) return FALSE; if (!request->read_size) { *len = 0; @@ -1877,7 +1891,7 @@ static BOOL read_line( request_t *request, char *buffer, DWORD *len ) } /* discard data contents until we reach end of line */ -static BOOL discard_eol( request_t *request ) +static BOOL discard_eol( request_t *request, BOOL notify ) { do { @@ -1888,24 +1902,23 @@ static BOOL discard_eol( request_t *request ) break; } request->read_pos = request->read_size = 0; /* discard everything */ - if (!read_more_data( request, -1 )) return FALSE; + if (!read_more_data( request, -1, notify )) return FALSE; } while (request->read_size); return TRUE; } /* read the size of the next chunk */ -static BOOL start_next_chunk( request_t *request ) +static BOOL start_next_chunk( request_t *request, BOOL notify ) { DWORD chunk_size = 0; - if (!request->content_length) return TRUE; - if (request->content_length == request->content_read) - { - /* read terminator for the previous chunk */ - if (!discard_eol( request )) return FALSE; - request->content_length = ~0u; - request->content_read = 0; - } + assert(!request->read_chunked_size || request->read_chunked_size == ~0u); + + if (request->read_chunked_eof) return FALSE; + + /* read terminator for the previous chunk */ + if (!request->read_chunked_size && !discard_eol( request, notify )) return FALSE; + for (;;) { while (request->read_size) @@ -1917,17 +1930,22 @@ static BOOL start_next_chunk( request_t *request ) else if (ch == ';' || ch == '\r' || ch == '\n') { TRACE("reading %u byte chunk\n", chunk_size); - request->content_length = chunk_size; - request->content_read = 0; - if (!discard_eol( request )) return FALSE; - return TRUE; + + if (request->content_length == ~0u) request->content_length = chunk_size; + else request->content_length += chunk_size; + + request->read_chunked_size = chunk_size; + if (!chunk_size) request->read_chunked_eof = TRUE; + + return discard_eol( request, notify ); } remove_data( request, 1 ); } - if (!read_more_data( request, -1 )) return FALSE; + if (!read_more_data( request, -1, notify )) return FALSE; if (!request->read_size) { request->content_length = request->content_read = 0; + request->read_chunked_size = 0; return TRUE; } } @@ -1936,32 +1954,34 @@ static BOOL start_next_chunk( request_t *request ) /* return the size of data available to be read immediately */ static DWORD get_available_data( request_t *request ) { - if (request->read_chunked && - (request->content_length == ~0u || request->content_length == request->content_read)) - return 0; - return min( request->read_size, request->content_length - request->content_read ); + if (request->read_chunked) return min( request->read_chunked_size, request->read_size ); + return request->read_size; } /* check if we have reached the end of the data to read */ static BOOL end_of_read_data( request_t *request ) { - if (request->read_chunked) return (request->content_length == 0); + if (request->read_chunked) return request->read_chunked_eof; if (request->content_length == ~0u) return FALSE; return (request->content_length == request->content_read); } -static BOOL refill_buffer( request_t *request ) +static BOOL refill_buffer( request_t *request, BOOL notify ) { int len = sizeof(request->read_buf); - if (request->read_chunked && - (request->content_length == ~0u || request->content_length == request->content_read)) + if (request->read_chunked) { - if (!start_next_chunk( request )) return FALSE; + if (request->read_chunked_eof) return FALSE; + if (request->read_chunked_size == ~0u || !request->read_chunked_size) + { + if (!start_next_chunk( request, notify )) return FALSE; + } } - if (request->content_length != ~0u) len = min( len, request->content_length - request->content_read ); + if (!request->read_chunked && request->content_length != ~0u) + len = min( len, request->content_length - request->content_read ); if (len <= request->read_size) return TRUE; - if (!read_more_data( request, len )) return FALSE; + if (!read_more_data( request, len, notify )) return FALSE; if (!request->read_size) request->content_length = request->content_read = 0; return TRUE; } @@ -1981,8 +2001,6 @@ static BOOL read_reply( request_t *request ) if (!netconn_connected( &request->netconn )) return FALSE; - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 ); - received_len = 0; do { @@ -2038,7 +2056,7 @@ static BOOL read_reply( request_t *request ) header_t *header; buflen = MAX_REPLY_LEN; - if (!read_line( request, buffer, &buflen )) goto end; + if (!read_line( request, buffer, &buflen )) return TRUE; received_len += buflen; if (!*buffer) break; @@ -2063,9 +2081,6 @@ static BOOL read_reply( request_t *request ) } TRACE("raw headers: %s\n", debugstr_w(raw_headers)); - -end: - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, &received_len, sizeof(DWORD) ); return TRUE; } @@ -2089,46 +2104,35 @@ static void finished_reading( request_t *request ) static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async ) { - BOOL ret = TRUE; - int len, bytes_read = 0; + int count, bytes_read = 0; - if (request->read_chunked && - (request->content_length == ~0u || request->content_length == request->content_read)) - { - if (!start_next_chunk( request )) goto done; - } - if (request->content_length != ~0u) size = min( size, request->content_length - request->content_read ); + if (end_of_read_data( request )) goto done; - if (request->read_size) + while (size) { - bytes_read = min( request->read_size, size ); - memcpy( buffer, request->read_buf + request->read_pos, bytes_read ); - remove_data( request, bytes_read ); - } - if (size > bytes_read && (!bytes_read || !async)) - { - if ((ret = netconn_recv( &request->netconn, (char *)buffer + bytes_read, size - bytes_read, - async ? 0 : MSG_WAITALL, &len ))) - bytes_read += len; + if (!(count = get_available_data( request ))) + { + if (!refill_buffer( request, async )) goto done; + if (!(count = get_available_data( request ))) goto done; + } + count = min( count, size ); + memcpy( (char *)buffer + bytes_read, request->read_buf + request->read_pos, count ); + remove_data( request, count ); + if (request->read_chunked) request->read_chunked_size -= count; + size -= count; + bytes_read += count; + request->content_read += count; + if (end_of_read_data( request )) goto done; } + if (request->read_chunked && !request->read_chunked_size) refill_buffer( request, async ); done: - request->content_read += bytes_read; TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, request->content_read, request->content_length ); - if (async) - { - if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read ); - else - { - WINHTTP_ASYNC_RESULT result; - result.dwResult = API_READ_DATA; - result.dwError = get_last_error(); - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) ); - } - } + + if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read ); if (read) *read = bytes_read; - if (!bytes_read && request->content_read == request->content_length) finished_reading( request ); - return ret; + if (end_of_read_data( request )) finished_reading( request ); + return TRUE; } /* read any content returned by the server so that the connection can be reused */ @@ -2137,11 +2141,6 @@ static void drain_content( request_t *request ) DWORD bytes_read; char buffer[2048]; - if (request->content_length == ~0u) - { - finished_reading( request ); - return; - } for (;;) { if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return; @@ -2243,6 +2242,7 @@ static BOOL handle_redirect( request_t *request, DWORD status ) if (!(ret = netconn_init( &request->netconn ))) goto end; request->read_pos = request->read_size = 0; request->read_chunked = FALSE; + request->read_chunked_eof = FALSE; } if (!(ret = add_host_header( request, WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end; if (!(ret = open_connection( request ))) goto end; @@ -2325,6 +2325,8 @@ static BOOL receive_response( request_t *request, BOOL async ) break; } + if (ret) refill_buffer( request, FALSE ); + if (async) { if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NULL, 0 ); @@ -2387,45 +2389,12 @@ BOOL WINAPI WinHttpReceiveResponse( HINTERNET hrequest, LPVOID reserved ) static BOOL query_data_available( request_t *request, DWORD *available, BOOL async ) { - BOOL ret = TRUE; - DWORD count; - - if (!(count = get_available_data( request ))) - { - if (end_of_read_data( request )) - { - if (available) *available = 0; - return TRUE; - } - } - refill_buffer( request ); - count = get_available_data( request ); + DWORD count = get_available_data( request ); - if (count == sizeof(request->read_buf)) /* check if we have even more pending in the socket */ - { - DWORD extra; - if ((ret = netconn_query_data_available( &request->netconn, &extra ))) - { - count = min( count + extra, request->content_length - request->content_read ); - } - } - if (async) - { - if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &count, sizeof(count) ); - else - { - WINHTTP_ASYNC_RESULT result; - result.dwResult = API_QUERY_DATA_AVAILABLE; - result.dwError = get_last_error(); - send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, &result, sizeof(result) ); - } - } - if (ret) - { - TRACE("%u bytes available\n", count); - if (available) *available = count; - } - return ret; + if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &count, sizeof(count) ); + TRACE("%u bytes available\n", count); + if (available) *available = count; + return TRUE; } static void task_query_data_available( task_header_t *task ) @@ -2533,7 +2502,7 @@ static BOOL write_data( request_t *request, LPCVOID buffer, DWORD to_write, LPDW if (async) { - if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &num_bytes, sizeof(DWORD) ); + if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE, &num_bytes, sizeof(num_bytes) ); else { WINHTTP_ASYNC_RESULT result; diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index bd50108..12255ed 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -187,9 +187,11 @@ typedef struct int send_timeout; int recv_timeout; LPWSTR status_text; - DWORD content_length; /* total number of bytes to be read (per chunk) */ + DWORD content_length; /* total number of bytes to be read */ DWORD content_read; /* bytes read so far */ BOOL read_chunked; /* are we reading in chunked mode? */ + BOOL read_chunked_eof; /* end of stream in chunked mode */ + BOOL read_chunked_size; /* chunk size remaining */ DWORD read_pos; /* current read position in read_buf */ DWORD read_size; /* valid data size in read_buf */ char read_buf[4096]; /* buffer for already read but not returned data */ -- 1.8.1.5