wininet: Don't assume that end of chunk means end of stream.
Hans Leidekker
hans at codeweavers.com
Wed Sep 11 06:50:18 CDT 2013
This makes Outlook happy. I also tested this with native IE against
test.winehq.org/tests/chunked as well as a local CGI script to trigger some
corner cases.
---
dlls/wininet/http.c | 69 ++++++++++++++++++++++-------------------------------
1 file changed, 28 insertions(+), 41 deletions(-)
diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c
index 1832b4f..cf2885f 100644
--- a/dlls/wininet/http.c
+++ b/dlls/wininet/http.c
@@ -400,6 +400,7 @@ typedef struct {
DWORD buf_size;
DWORD buf_pos;
DWORD chunk_size;
+ BOOL end_of_data;
} chunked_stream_t;
static inline void destroy_data_stream(data_stream_t *stream)
@@ -2697,6 +2698,8 @@ static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *re
DWORD res;
int len;
+ assert(!stream->end_of_data);
+
if (stream->buf_pos)
{
/* move existing data to the start of the buffer */
@@ -2744,9 +2747,10 @@ static DWORD discard_chunked_eol(chunked_stream_t *stream, http_request_t *req)
/* read the size of the next chunk (the read section must be held) */
static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
{
- /* TODOO */
DWORD chunk_size = 0, res;
+ if (stream->end_of_data) return ERROR_NO_MORE_FILES;
+
if(stream->chunk_size != ~0u && (res = discard_chunked_eol(stream, req)) != ERROR_SUCCESS)
return res;
@@ -2764,6 +2768,8 @@ static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
stream->chunk_size = chunk_size;
if (req->contentLength == ~0u) req->contentLength = chunk_size;
else req->contentLength += chunk_size;
+
+ if (!chunk_size) stream->end_of_data = TRUE;
return discard_chunked_eol(stream, req);
}
remove_chunked_data(stream, 1);
@@ -2779,75 +2785,55 @@ static DWORD start_next_chunk(chunked_stream_t *stream, http_request_t *req)
static DWORD chunked_get_avail_data(data_stream_t *stream, http_request_t *req)
{
- /* Allow reading only from read buffer */
+ chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
+ DWORD res;
+
+ if(!chunked_stream->chunk_size || chunked_stream->chunk_size == ~0u) {
+ res = start_next_chunk(chunked_stream, req);
+ if(res != ERROR_SUCCESS)
+ return 0;
+ }
+ /* only report data in read buffer */
return 0;
}
static BOOL chunked_end_of_data(data_stream_t *stream, http_request_t *req)
{
chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
- return !chunked_stream->chunk_size;
+ return chunked_stream->end_of_data;
}
static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
DWORD *read, read_mode_t read_mode)
{
chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
- DWORD read_bytes = 0, ret_read = 0, res = ERROR_SUCCESS;
+ DWORD read_bytes = 0, res = ERROR_SUCCESS;
- if(chunked_stream->chunk_size == ~0u) {
+ if(!chunked_stream->chunk_size || chunked_stream->chunk_size == ~0u) {
res = start_next_chunk(chunked_stream, req);
if(res != ERROR_SUCCESS)
return res;
}
- while(size && chunked_stream->chunk_size) {
+ if(size && chunked_stream->chunk_size) {
if(chunked_stream->buf_size) {
read_bytes = min(size, min(chunked_stream->buf_size, chunked_stream->chunk_size));
- /* this could block */
- if(read_mode == READMODE_NOBLOCK && read_bytes == chunked_stream->chunk_size)
- break;
-
- memcpy(buf+ret_read, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
+ memcpy(buf, chunked_stream->buf+chunked_stream->buf_pos, read_bytes);
remove_chunked_data(chunked_stream, read_bytes);
}else {
read_bytes = min(size, chunked_stream->chunk_size);
- if(read_mode == READMODE_NOBLOCK) {
- DWORD avail;
-
- if(!req->netconn || !NETCON_query_data_available(req->netconn, &avail) || !avail)
- break;
- if(read_bytes > avail)
- read_bytes = avail;
-
- /* this could block */
- if(read_bytes == chunked_stream->chunk_size)
- break;
- }
-
- res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
+ res = NETCON_recv(req->netconn, (char *)buf, read_bytes, 0, (int*)&read_bytes);
if(res != ERROR_SUCCESS)
- break;
+ return res;
}
chunked_stream->chunk_size -= read_bytes;
- size -= read_bytes;
- ret_read += read_bytes;
- if(!chunked_stream->chunk_size) {
- assert(read_mode != READMODE_NOBLOCK);
- res = start_next_chunk(chunked_stream, req);
- if(res != ERROR_SUCCESS)
- break;
- }
-
- if(read_mode == READMODE_ASYNC)
- read_mode = READMODE_NOBLOCK;
}
- TRACE("read %u bytes\n", ret_read);
- *read = ret_read;
+ TRACE("read %u bytes\n", read_bytes);
+ *read = read_bytes;
return res;
}
@@ -2855,8 +2841,8 @@ static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
{
chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
- /* FIXME: we can do better */
- return !chunked_stream->chunk_size;
+ remove_chunked_data(chunked_stream, chunked_stream->buf_size);
+ return chunked_stream->end_of_data;
}
static void chunked_destroy(data_stream_t *stream)
@@ -2905,6 +2891,7 @@ static DWORD set_content_length(http_request_t *request)
chunked_stream->data_stream.vtbl = &chunked_stream_vtbl;
chunked_stream->buf_size = chunked_stream->buf_pos = 0;
chunked_stream->chunk_size = ~0u;
+ chunked_stream->end_of_data = FALSE;
if(request->read_size) {
memcpy(chunked_stream->buf, request->read_buf+request->read_pos, request->read_size);
--
1.8.1.5
More information about the wine-patches
mailing list