[2/2] winhttp: Always drain content before sending the next request.

Hans Leidekker hans at codeweavers.com
Wed Apr 12 03:46:35 CDT 2017


Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 dlls/winhttp/request.c       | 402 +++++++++++++++++++++----------------------
 dlls/winhttp/tests/winhttp.c |   6 +-
 2 files changed, 203 insertions(+), 205 deletions(-)

diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c
index fc047940f8..db6d12d027 100644
--- a/dlls/winhttp/request.c
+++ b/dlls/winhttp/request.c
@@ -1113,6 +1113,205 @@ static void clear_response_headers( request_t *request )
     }
 }
 
+/* remove some amount of data from the read buffer */
+static void remove_data( request_t *request, int count )
+{
+    if (!(request->read_size -= count)) request->read_pos = 0;
+    else request->read_pos += count;
+}
+
+/* read some more data into the read buffer */
+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)
+    {
+        /* move existing data to the start of the buffer */
+        memmove( request->read_buf, request->read_buf + request->read_pos, request->read_size );
+        request->read_pos = 0;
+    }
+    if (maxlen == -1) maxlen = sizeof(request->read_buf);
+
+    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 ret;
+}
+
+/* discard data contents until we reach end of line */
+static BOOL discard_eol( request_t *request, BOOL notify )
+{
+    do
+    {
+        char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
+        if (eol)
+        {
+            remove_data( request, (eol + 1) - (request->read_buf + request->read_pos) );
+            break;
+        }
+        request->read_pos = request->read_size = 0;  /* discard everything */
+        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, BOOL notify )
+{
+    DWORD chunk_size = 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)
+        {
+            char ch = request->read_buf[request->read_pos];
+            if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
+            else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
+            else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
+            else if (ch == ';' || ch == '\r' || ch == '\n')
+            {
+                TRACE("reading %u byte chunk\n", chunk_size);
+
+                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, notify )) return FALSE;
+        if (!request->read_size)
+        {
+            request->content_length = request->content_read = 0;
+            request->read_chunked_size = 0;
+            return TRUE;
+        }
+    }
+}
+
+static BOOL refill_buffer( request_t *request, BOOL notify )
+{
+    int len = sizeof(request->read_buf);
+
+    if (request->read_chunked)
+    {
+        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;
+        }
+        len = min( len, request->read_chunked_size );
+    }
+    else if (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, notify )) return FALSE;
+    if (!request->read_size) request->content_length = request->content_read = 0;
+    return TRUE;
+}
+
+static void finished_reading( request_t *request )
+{
+    static const WCHAR closeW[] = {'c','l','o','s','e',0};
+
+    BOOL close = FALSE;
+    WCHAR connection[20];
+    DWORD size = sizeof(connection);
+
+    if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE;
+    else if (query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) ||
+             query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION, NULL, connection, &size, NULL ))
+    {
+        if (!strcmpiW( connection, closeW )) close = TRUE;
+    }
+    else if (!strcmpW( request->version, http1_0 )) close = TRUE;
+    if (close) close_connection( request );
+}
+
+/* return the size of data available to be read immediately */
+static DWORD get_available_data( request_t *request )
+{
+    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->content_length) return TRUE;
+    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 read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
+{
+    int count, bytes_read = 0;
+
+    if (end_of_read_data( request )) goto done;
+
+    while (size)
+    {
+        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:
+    TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, request->content_read, request->content_length );
+
+    if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read );
+    if (read) *read = bytes_read;
+    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 */
+static void drain_content( request_t *request )
+{
+    DWORD bytes_read;
+    char buffer[2048];
+
+    refill_buffer( request, FALSE );
+    for (;;)
+    {
+        if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return;
+    }
+}
+
 static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len, LPVOID optional,
                           DWORD optional_len, DWORD total_len, DWORD_PTR context, BOOL async )
 {
@@ -1129,6 +1328,7 @@ static BOOL send_request( request_t *request, LPCWSTR headers, DWORD headers_len
     DWORD len, i, flags;
 
     clear_response_headers( request );
+    drain_content( request );
 
     flags = WINHTTP_ADDREQ_FLAG_ADD|WINHTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA;
     for (i = 0; i < request->num_accept_types; i++)
@@ -1909,40 +2109,6 @@ static DWORD set_content_length( request_t *request, DWORD status )
     return request->content_length;
 }
 
-/* read some more data into the read buffer */
-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)
-    {
-        /* move existing data to the start of the buffer */
-        memmove( request->read_buf, request->read_buf + request->read_pos, request->read_size );
-        request->read_pos = 0;
-    }
-    if (maxlen == -1) maxlen = sizeof(request->read_buf);
-
-    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 ret;
-}
-
-/* remove some amount of data from the read buffer */
-static void remove_data( request_t *request, int count )
-{
-    if (!(request->read_size -= count)) request->read_pos = 0;
-    else request->read_pos += count;
-}
-
 static BOOL read_line( request_t *request, char *buffer, DWORD *len )
 {
     int count, bytes_read, pos = 0;
@@ -1981,107 +2147,6 @@ static BOOL read_line( request_t *request, char *buffer, DWORD *len )
     return TRUE;
 }
 
-/* discard data contents until we reach end of line */
-static BOOL discard_eol( request_t *request, BOOL notify )
-{
-    do
-    {
-        char *eol = memchr( request->read_buf + request->read_pos, '\n', request->read_size );
-        if (eol)
-        {
-            remove_data( request, (eol + 1) - (request->read_buf + request->read_pos) );
-            break;
-        }
-        request->read_pos = request->read_size = 0;  /* discard everything */
-        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, BOOL notify )
-{
-    DWORD chunk_size = 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)
-        {
-            char ch = request->read_buf[request->read_pos];
-            if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
-            else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
-            else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
-            else if (ch == ';' || ch == '\r' || ch == '\n')
-            {
-                TRACE("reading %u byte chunk\n", chunk_size);
-
-                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, notify )) return FALSE;
-        if (!request->read_size)
-        {
-            request->content_length = request->content_read = 0;
-            request->read_chunked_size = 0;
-            return TRUE;
-        }
-    }
-}
-
-/* return the size of data available to be read immediately */
-static DWORD get_available_data( request_t *request )
-{
-    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->content_length) return TRUE;
-    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, BOOL notify )
-{
-    int len = sizeof(request->read_buf);
-
-    if (request->read_chunked)
-    {
-        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;
-        }
-        len = min( len, request->read_chunked_size );
-    }
-    else if (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, notify )) return FALSE;
-    if (!request->read_size) request->content_length = request->content_read = 0;
-    return TRUE;
-}
-
 #define MAX_REPLY_LEN   1460
 #define INITIAL_HEADER_BUFFER_LEN  512
 
@@ -2184,70 +2249,6 @@ static BOOL read_reply( request_t *request )
     return TRUE;
 }
 
-static void finished_reading( request_t *request )
-{
-    static const WCHAR closeW[] = {'c','l','o','s','e',0};
-
-    BOOL close = FALSE;
-    WCHAR connection[20];
-    DWORD size = sizeof(connection);
-
-    if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE;
-    else if (query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) ||
-             query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION, NULL, connection, &size, NULL ))
-    {
-        if (!strcmpiW( connection, closeW )) close = TRUE;
-    }
-    else if (!strcmpW( request->version, http1_0 )) close = TRUE;
-    if (close) close_connection( request );
-}
-
-static BOOL read_data( request_t *request, void *buffer, DWORD size, DWORD *read, BOOL async )
-{
-    int count, bytes_read = 0;
-
-    if (end_of_read_data( request )) goto done;
-
-    while (size)
-    {
-        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:
-    TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, request->content_read, request->content_length );
-
-    if (async) send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, buffer, bytes_read );
-    if (read) *read = bytes_read;
-    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 */
-static void drain_content( request_t *request )
-{
-    DWORD bytes_read;
-    char buffer[2048];
-
-    refill_buffer( request, FALSE );
-    for (;;)
-    {
-        if (!read_data( request, buffer, sizeof(buffer), &bytes_read, FALSE ) || !bytes_read) return;
-    }
-}
-
 static void record_cookies( request_t *request )
 {
     unsigned int i;
@@ -2314,7 +2315,6 @@ static BOOL handle_redirect( request_t *request, DWORD status )
         heap_free( request->path );
         request->path = path;
 
-        drain_content( request );
         send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_url + 1 );
     }
     else
@@ -2331,7 +2331,6 @@ static BOOL handle_redirect( request_t *request, DWORD status )
             request->hdr.flags |= WINHTTP_FLAG_SECURE;
         }
 
-        drain_content( request );
         send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_url + 1 );
 
         len = uc.dwHostNameLength;
@@ -2422,7 +2421,6 @@ static BOOL receive_response( request_t *request, BOOL async )
             if (request->hdr.disable_flags & WINHTTP_DISABLE_AUTHENTICATION) break;
 
             if (!handle_authorization( request, status )) break;
-            drain_content( request );
 
             /* recurse synchronously */
             if ((ret = send_request( request, NULL, 0, request->optional, request->optional_len, 0, 0, FALSE ))) continue;
diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c
index 19be82c607..c9f5ecd7d4 100644
--- a/dlls/winhttp/tests/winhttp.c
+++ b/dlls/winhttp/tests/winhttp.c
@@ -2612,13 +2612,13 @@ static void test_large_data_authentication(int port)
     ok(ret, "expected success %d\n", GetLastError());
 
     ret = WinHttpReceiveResponse(req, NULL);
-    todo_wine ok(ret, "expected success\n");
+    ok(ret, "expected success\n");
 
     size = sizeof(status);
     ret = WinHttpQueryHeaders(req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL,
                               &status, &size, NULL);
-    todo_wine ok(ret, "expected success\n");
-    todo_wine ok(status == HTTP_STATUS_OK, "got %d\n", status);
+    ok(ret, "expected success\n");
+    ok(status == HTTP_STATUS_OK, "got %d\n", status);
 
     WinHttpCloseHandle(req);
     WinHttpCloseHandle(con);
-- 
2.11.0




More information about the wine-patches mailing list