winhttp: Don't assume that end of chunk means end of stream.
This commit is contained in:
parent
bee390e1ef
commit
59b0374710
|
@ -25,6 +25,7 @@
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <assert.h>
|
||||||
#ifdef HAVE_ARPA_INET_H
|
#ifdef HAVE_ARPA_INET_H
|
||||||
# include <arpa/inet.h>
|
# include <arpa/inet.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -1005,6 +1006,8 @@ static BOOL open_connection( request_t *request )
|
||||||
done:
|
done:
|
||||||
request->read_pos = request->read_size = 0;
|
request->read_pos = request->read_size = 0;
|
||||||
request->read_chunked = FALSE;
|
request->read_chunked = FALSE;
|
||||||
|
request->read_chunked_size = ~0u;
|
||||||
|
request->read_chunked_eof = FALSE;
|
||||||
heap_free( addressW );
|
heap_free( addressW );
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -1131,7 +1134,7 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len
|
||||||
request->optional_len = optional_len;
|
request->optional_len = optional_len;
|
||||||
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:
|
end:
|
||||||
if (async)
|
if (async)
|
||||||
|
@ -1808,15 +1811,20 @@ static DWORD set_content_length( request_t *request )
|
||||||
{
|
{
|
||||||
request->content_length = ~0u;
|
request->content_length = ~0u;
|
||||||
request->read_chunked = TRUE;
|
request->read_chunked = TRUE;
|
||||||
|
request->read_chunked_size = ~0u;
|
||||||
|
request->read_chunked_eof = FALSE;
|
||||||
}
|
}
|
||||||
request->content_read = 0;
|
request->content_read = 0;
|
||||||
return request->content_length;
|
return request->content_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read some more data into the read buffer */
|
/* 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;
|
int len;
|
||||||
|
BOOL ret;
|
||||||
|
|
||||||
|
if (request->read_chunked_eof) return FALSE;
|
||||||
|
|
||||||
if (request->read_size && request->read_pos)
|
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;
|
request->read_pos = 0;
|
||||||
}
|
}
|
||||||
if (maxlen == -1) maxlen = sizeof(request->read_buf);
|
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;
|
request->read_size += len;
|
||||||
return TRUE;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remove some amount of data from the read buffer */
|
/* 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 );
|
remove_data( request, bytes_read );
|
||||||
if (eol) break;
|
if (eol) break;
|
||||||
|
|
||||||
if (!read_more_data( request, -1 )) return FALSE;
|
if (!read_more_data( request, -1, TRUE )) return FALSE;
|
||||||
if (!request->read_size)
|
if (!request->read_size)
|
||||||
{
|
{
|
||||||
*len = 0;
|
*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 */
|
/* 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
|
do
|
||||||
{
|
{
|
||||||
|
@ -1888,24 +1902,23 @@ static BOOL discard_eol( request_t *request )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
request->read_pos = request->read_size = 0; /* discard everything */
|
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);
|
} while (request->read_size);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read the size of the next chunk */
|
/* 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;
|
DWORD chunk_size = 0;
|
||||||
|
|
||||||
if (!request->content_length) return TRUE;
|
assert(!request->read_chunked_size || request->read_chunked_size == ~0u);
|
||||||
if (request->content_length == request->content_read)
|
|
||||||
{
|
if (request->read_chunked_eof) return FALSE;
|
||||||
/* read terminator for the previous chunk */
|
|
||||||
if (!discard_eol( request )) return FALSE;
|
/* read terminator for the previous chunk */
|
||||||
request->content_length = ~0u;
|
if (!request->read_chunked_size && !discard_eol( request, notify )) return FALSE;
|
||||||
request->content_read = 0;
|
|
||||||
}
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
while (request->read_size)
|
while (request->read_size)
|
||||||
|
@ -1917,17 +1930,22 @@ static BOOL start_next_chunk( request_t *request )
|
||||||
else if (ch == ';' || ch == '\r' || ch == '\n')
|
else if (ch == ';' || ch == '\r' || ch == '\n')
|
||||||
{
|
{
|
||||||
TRACE("reading %u byte chunk\n", chunk_size);
|
TRACE("reading %u byte chunk\n", chunk_size);
|
||||||
request->content_length = chunk_size;
|
|
||||||
request->content_read = 0;
|
if (request->content_length == ~0u) request->content_length = chunk_size;
|
||||||
if (!discard_eol( request )) return FALSE;
|
else request->content_length += chunk_size;
|
||||||
return TRUE;
|
|
||||||
|
request->read_chunked_size = chunk_size;
|
||||||
|
if (!chunk_size) request->read_chunked_eof = TRUE;
|
||||||
|
|
||||||
|
return discard_eol( request, notify );
|
||||||
}
|
}
|
||||||
remove_data( request, 1 );
|
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)
|
if (!request->read_size)
|
||||||
{
|
{
|
||||||
request->content_length = request->content_read = 0;
|
request->content_length = request->content_read = 0;
|
||||||
|
request->read_chunked_size = 0;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1936,32 +1954,34 @@ static BOOL start_next_chunk( request_t *request )
|
||||||
/* return the size of data available to be read immediately */
|
/* return the size of data available to be read immediately */
|
||||||
static DWORD get_available_data( request_t *request )
|
static DWORD get_available_data( request_t *request )
|
||||||
{
|
{
|
||||||
if (request->read_chunked &&
|
if (request->read_chunked) return min( request->read_chunked_size, request->read_size );
|
||||||
(request->content_length == ~0u || request->content_length == request->content_read))
|
return request->read_size;
|
||||||
return 0;
|
|
||||||
return min( request->read_size, request->content_length - request->content_read );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if we have reached the end of the data to read */
|
/* check if we have reached the end of the data to read */
|
||||||
static BOOL end_of_read_data( request_t *request )
|
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;
|
if (request->content_length == ~0u) return FALSE;
|
||||||
return (request->content_length == request->content_read);
|
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);
|
int len = sizeof(request->read_buf);
|
||||||
|
|
||||||
if (request->read_chunked &&
|
if (request->read_chunked)
|
||||||
(request->content_length == ~0u || request->content_length == request->content_read))
|
|
||||||
{
|
{
|
||||||
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 (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;
|
if (!request->read_size) request->content_length = request->content_read = 0;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -1981,8 +2001,6 @@ static BOOL read_reply( request_t *request )
|
||||||
|
|
||||||
if (!netconn_connected( &request->netconn )) return FALSE;
|
if (!netconn_connected( &request->netconn )) return FALSE;
|
||||||
|
|
||||||
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NULL, 0 );
|
|
||||||
|
|
||||||
received_len = 0;
|
received_len = 0;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
@ -2038,7 +2056,7 @@ static BOOL read_reply( request_t *request )
|
||||||
header_t *header;
|
header_t *header;
|
||||||
|
|
||||||
buflen = MAX_REPLY_LEN;
|
buflen = MAX_REPLY_LEN;
|
||||||
if (!read_line( request, buffer, &buflen )) goto end;
|
if (!read_line( request, buffer, &buflen )) return TRUE;
|
||||||
received_len += buflen;
|
received_len += buflen;
|
||||||
if (!*buffer) break;
|
if (!*buffer) break;
|
||||||
|
|
||||||
|
@ -2063,9 +2081,6 @@ static BOOL read_reply( request_t *request )
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("raw headers: %s\n", debugstr_w(raw_headers));
|
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;
|
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 )
|
static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
|
||||||
{
|
{
|
||||||
BOOL ret = TRUE;
|
int count, bytes_read = 0;
|
||||||
int len, bytes_read = 0;
|
|
||||||
|
|
||||||
if (request->read_chunked &&
|
if (end_of_read_data( request )) goto done;
|
||||||
(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 (request->read_size)
|
while (size)
|
||||||
{
|
{
|
||||||
bytes_read = min( request->read_size, size );
|
if (!(count = get_available_data( request )))
|
||||||
memcpy( buffer, request->read_buf + request->read_pos, bytes_read );
|
{
|
||||||
remove_data( request, bytes_read );
|
if (!refill_buffer( request, async )) goto done;
|
||||||
}
|
if (!(count = get_available_data( request ))) goto done;
|
||||||
if (size > bytes_read && (!bytes_read || !async))
|
}
|
||||||
{
|
count = min( count, size );
|
||||||
if ((ret = netconn_recv( &request->netconn, (char *)buffer + bytes_read, size - bytes_read,
|
memcpy( (char *)buffer + bytes_read, request->read_buf + request->read_pos, count );
|
||||||
async ? 0 : MSG_WAITALL, &len )))
|
remove_data( request, count );
|
||||||
bytes_read += len;
|
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:
|
done:
|
||||||
request->content_read += bytes_read;
|
|
||||||
TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, request->content_read, request->content_length );
|
TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, request->content_read, request->content_length );
|
||||||
if (async)
|
|
||||||
{
|
if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read );
|
||||||
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 (read) *read = bytes_read;
|
if (read) *read = bytes_read;
|
||||||
if (!bytes_read && request->content_read == request->content_length) finished_reading( request );
|
if (end_of_read_data( request )) finished_reading( request );
|
||||||
return ret;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* read any content returned by the server so that the connection can be reused */
|
/* 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;
|
DWORD bytes_read;
|
||||||
char buffer[2048];
|
char buffer[2048];
|
||||||
|
|
||||||
if (request->content_length == ~0u)
|
|
||||||
{
|
|
||||||
finished_reading( request );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return;
|
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;
|
if (!(ret = netconn_init( &request->netconn ))) goto end;
|
||||||
request->read_pos = request->read_size = 0;
|
request->read_pos = request->read_size = 0;
|
||||||
request->read_chunked = FALSE;
|
request->read_chunked = FALSE;
|
||||||
|
request->read_chunked_eof = FALSE;
|
||||||
}
|
}
|
||||||
if (!(ret = add_host_header( request, WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end;
|
if (!(ret = add_host_header( request, WINHTTP_ADDREQ_FLAG_REPLACE ))) goto end;
|
||||||
if (!(ret = open_connection( request ))) goto end;
|
if (!(ret = open_connection( request ))) goto end;
|
||||||
|
@ -2325,6 +2325,8 @@ static BOOL receive_response( request_t *request, BOOL async )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ret) refill_buffer( request, FALSE );
|
||||||
|
|
||||||
if (async)
|
if (async)
|
||||||
{
|
{
|
||||||
if (ret) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE, NULL, 0 );
|
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 )
|
static BOOL query_data_available( request_t *request, DWORD *available, BOOL async )
|
||||||
{
|
{
|
||||||
BOOL ret = TRUE;
|
DWORD count = get_available_data( request );
|
||||||
DWORD count;
|
|
||||||
|
|
||||||
if (!(count = get_available_data( request )))
|
if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE, &count, sizeof(count) );
|
||||||
{
|
TRACE("%u bytes available\n", count);
|
||||||
if (end_of_read_data( request ))
|
if (available) *available = count;
|
||||||
{
|
return TRUE;
|
||||||
if (available) *available = 0;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
refill_buffer( request );
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void task_query_data_available( task_header_t *task )
|
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 (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
|
else
|
||||||
{
|
{
|
||||||
WINHTTP_ASYNC_RESULT result;
|
WINHTTP_ASYNC_RESULT result;
|
||||||
|
|
|
@ -187,9 +187,11 @@ typedef struct
|
||||||
int send_timeout;
|
int send_timeout;
|
||||||
int recv_timeout;
|
int recv_timeout;
|
||||||
LPWSTR status_text;
|
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 */
|
DWORD content_read; /* bytes read so far */
|
||||||
BOOL read_chunked; /* are we reading in chunked mode? */
|
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_pos; /* current read position in read_buf */
|
||||||
DWORD read_size; /* valid data size in read_buf */
|
DWORD read_size; /* valid data size in read_buf */
|
||||||
char read_buf[4096]; /* buffer for already read but not returned data */
|
char read_buf[4096]; /* buffer for already read but not returned data */
|
||||||
|
|
Loading…
Reference in New Issue