[2/2] winhttp: Don't assume that end of chunk means end of stream.

Hans Leidekker hans at codeweavers.com
Mon Sep 23 03:03:40 CDT 2013


---
 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 <stdarg.h>
+#include <assert.h>
 #ifdef HAVE_ARPA_INET_H
 # include <arpa/inet.h>
 #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






More information about the wine-patches mailing list