WININET: various http fixes

Mike McCormack mike at codeweavers.com
Wed Jul 14 09:58:21 CDT 2004


ChangeLog:
<rob at codeweavers.com>
<mike at codeweavers.com>
* Change the way that the response headers are reconstructed to reduce
   the chance of memory being incorrectly allocated
* return the correct error when a header is not found
* remove all the response headers before receiving new ones so that
   we don't merge headers from two responses
* Add extra standard fields
* split the status line into http version, status code and status text
   and store those in the response data
* Replace fields in (6) without using use a fake field
-------------- next part --------------
Index: dlls/wininet/http.c
===================================================================
RCS file: /home/wine/wine/dlls/wininet/http.c,v
retrieving revision 1.66
diff -u -r1.66 http.c
--- dlls/wininet/http.c	13 Jul 2004 23:34:28 -0000	1.66
+++ dlls/wininet/http.c	14 Jul 2004 14:53:27 -0000
@@ -88,6 +88,7 @@
 	void *Buffer, int BytesToRead);
 BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr);
 BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
+BOOL HTTP_ReplaceHeaderValue( LPHTTPHEADERW lphttpHdr, LPCWSTR lpsztmp );
 void HTTP_CloseConnection(LPWININETHTTPREQW lpwhr);
 BOOL HTTP_InterpretHttpHeader(LPWSTR buffer, LPWSTR field, INT fieldlen, LPWSTR value, INT valuelen);
 INT HTTP_GetStdHeaderIndex(LPCWSTR lpszField);
@@ -787,6 +788,140 @@
     return handle;
 }
 
+/***********************************************************************
+ *           HTTP_build_resp_header (internal)
+ *
+ *   This function reconstructs the response header.  It takes an array
+ *  of strings in the response buffer, and the count of those strings.
+ *  A null pointer in the array represents a seperator.
+ *
+ *  RETURNS:
+ *    a buffer allocated and initialized with the reconstructed response
+ *    *pSize is set to the number of wide characters in the returned buffer
+ */
+static LPWSTR HTTP_build_resp_header( 
+          LPCWSTR *str, DWORD count, BOOL bUseCrlf, DWORD *pSize )
+{
+    static const WCHAR szcrlf[] = { '\r','\n',0 };
+    DWORD len, i;
+    LPWSTR ret;
+
+    /* calculate the length of the response buffer */
+    len = 0;
+    for( i=0; i<count; i++ )
+    {
+        if( str[i] )
+            len += strlenW( str[i] );
+        else if( bUseCrlf )
+            len += 2;
+        else
+            len ++;
+    }
+    len++;
+
+    /* fill the buffer in */
+    ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+    len = 0;
+    for( i=0; i<count; i++ )
+    {
+        if( str[i] )
+        {
+            strcpyW( &ret[len], str[i] );
+            len += strlenW( str[i] );
+        }
+        else if( bUseCrlf )
+        {
+            strcpyW( &ret[len], szcrlf );
+            len += 2;
+        }
+        else
+            ret[len++] = 0;
+    }
+    ret[len++] = 0;
+    *pSize = len;
+    return ret;
+}
+
+/***********************************************************************
+ *           HTTP_query_raw_headers (internal)
+ *
+ *  Reconstruct the raw HTTP header and copy it into the buffer provided
+ */
+static BOOL HTTP_query_raw_headers( LPWININETHTTPREQW lpwhr, BOOL bUseCrlf,
+	                     LPVOID lpBuffer, LPDWORD lpdwBufferLength )
+{
+    static const WCHAR szColonSpace[] = { ':',' ',0 };
+    static const WCHAR szSpace[] = { ' ',0 };
+    BOOL bSuccess = FALSE;
+    LPCWSTR *str;
+    DWORD i, n, size = 0;
+    LPWSTR hdr;
+
+    n = 7 + ( HTTP_QUERY_MAX + 1 + lpwhr->nCustHeaders )*4 ;
+    str = HeapAlloc( GetProcessHeap(), 0, sizeof(LPCWSTR)*n );
+    n = 0;
+
+    /* reconstruct the status line */
+    str[n++] = lpwhr->StdHeaders[HTTP_QUERY_VERSION].lpszValue;
+    str[n++] = szSpace;
+    str[n++] = lpwhr->StdHeaders[HTTP_QUERY_STATUS_CODE].lpszValue;
+    str[n++] = szSpace;
+    str[n++] = lpwhr->StdHeaders[HTTP_QUERY_STATUS_TEXT].lpszValue;
+    str[n++] = NULL;
+
+    /* Append standard request heades */
+    for (i = 0; i <= HTTP_QUERY_MAX; i++)
+    {
+        if( lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST )
+            continue;
+        if( !lpwhr->StdHeaders[i].lpszField )
+            continue;
+        if( !lpwhr->StdHeaders[i].lpszValue )
+            continue;
+        /* ignore the stuff that's in the status line */
+        if( ( i == HTTP_QUERY_VERSION ) ||
+            ( i == HTTP_QUERY_STATUS_CODE ) ||
+            ( i == HTTP_QUERY_STATUS_TEXT ) )
+            continue;
+        str[n++] = lpwhr->StdHeaders[i].lpszField;
+        str[n++] = szColonSpace;
+        str[n++] = lpwhr->StdHeaders[i].lpszValue,
+        str[n++] = NULL;
+    }
+
+    /* Append custom request heades */
+    for (i = 0; i < lpwhr->nCustHeaders; i++)
+    {
+        if( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
+            continue;
+        if( !lpwhr->pCustHeaders[i].lpszField )
+            continue;
+        if( !lpwhr->pCustHeaders[i].lpszValue )
+            continue;
+        str[n++] = lpwhr->pCustHeaders[i].lpszField;
+        str[n++] = szColonSpace;
+        str[n++] = lpwhr->pCustHeaders[i].lpszValue;
+        str[n++] = NULL;
+    }
+    str[n++] = NULL;
+
+    /* concatenate all the strings together */
+    hdr = HTTP_build_resp_header( str, n, bUseCrlf, &size );
+    HeapFree( GetProcessHeap(), 0, str );
+
+    /* check that the target buffer is big enough */
+    if ( size > (*lpdwBufferLength/sizeof(WCHAR)) )
+        INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
+    else
+    {
+        memcpy( lpBuffer, hdr, size*sizeof(WCHAR) );
+        bSuccess = TRUE;
+    }
+    HeapFree( GetProcessHeap(), 0, hdr );
+    *lpdwBufferLength = size*sizeof(WCHAR);
+
+    return bSuccess;
+}
 
 /***********************************************************************
  *           HTTP_HttpQueryInfoW (internal)
@@ -796,9 +931,6 @@
 {
     LPHTTPHEADERW lphttpHdr = NULL;
     BOOL bSuccess = FALSE;
-    static const WCHAR szFmt[] = { '%','s',':',' ','%','s','%','s',0 };
-    static const WCHAR szcrlf[] = { '\r','\n',0 };
-    static const WCHAR sznul[] = { 0 };
 
     /* Find requested header structure */
     if ((dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK) == HTTP_QUERY_CUSTOM)
@@ -814,86 +946,28 @@
     {
         INT index = dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK;
 
-        if (index == HTTP_QUERY_RAW_HEADERS_CRLF || index == HTTP_QUERY_RAW_HEADERS)
-        {
-            INT i, delim, size = 0, cnt = 0;
-
-            delim = index == HTTP_QUERY_RAW_HEADERS_CRLF ? 2 : 1;
-
-           /* Calculate length of custom reuqest headers */
-           for (i = 0; i < lpwhr->nCustHeaders; i++)
-           {
-               if ((~lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST) && lpwhr->pCustHeaders[i].lpszField &&
-		   lpwhr->pCustHeaders[i].lpszValue)
-	       {
-                  size += strlenW(lpwhr->pCustHeaders[i].lpszField) +
-                       strlenW(lpwhr->pCustHeaders[i].lpszValue) + delim + 2;
-	       }
-           }
-
-           /* Calculate the length of stadard request headers */
-           for (i = 0; i <= HTTP_QUERY_MAX; i++)
-           {
-              if ((~lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST) && lpwhr->StdHeaders[i].lpszField &&
-                   lpwhr->StdHeaders[i].lpszValue)
-              {
-                 size += strlenW(lpwhr->StdHeaders[i].lpszField) +
-                    strlenW(lpwhr->StdHeaders[i].lpszValue) + delim + 2;
-              }
-           }
-           size += delim;
-
-           if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
-           {
-              *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
-              INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
-              return bSuccess;
-           }
-
-           /* Append standard request heades */
-           for (i = 0; i <= HTTP_QUERY_MAX; i++)
-           {
-               if ((~lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST) &&
-					   lpwhr->StdHeaders[i].lpszField &&
-					   lpwhr->StdHeaders[i].lpszValue)
-               {
-                   cnt += sprintfW((WCHAR*)lpBuffer + cnt, szFmt, 
-                            lpwhr->StdHeaders[i].lpszField, lpwhr->StdHeaders[i].lpszValue,
-                          index == HTTP_QUERY_RAW_HEADERS_CRLF ? szcrlf : sznul );
-               }
-            }
-
-            /* Append custom request heades */
-            for (i = 0; i < lpwhr->nCustHeaders; i++)
-            {
-                if ((~lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST) &&
-						lpwhr->pCustHeaders[i].lpszField &&
-						lpwhr->pCustHeaders[i].lpszValue)
-                {
-                   cnt += sprintfW((WCHAR*)lpBuffer + cnt, szFmt,
-                    lpwhr->pCustHeaders[i].lpszField, lpwhr->pCustHeaders[i].lpszValue,
-					index == HTTP_QUERY_RAW_HEADERS_CRLF ? szcrlf : sznul);
-                }
-            }
-
-            strcpyW((WCHAR*)lpBuffer + cnt, index == HTTP_QUERY_RAW_HEADERS_CRLF ? szcrlf : sznul);
-
-            *lpdwBufferLength = (cnt + delim) * sizeof(WCHAR);
-            bSuccess = TRUE;
-            return bSuccess;
-        }
+        if (index == HTTP_QUERY_RAW_HEADERS_CRLF)
+            return HTTP_query_raw_headers(lpwhr, TRUE, lpBuffer, lpdwBufferLength );
+        if (index == HTTP_QUERY_RAW_HEADERS)
+            return HTTP_query_raw_headers(lpwhr, FALSE, lpBuffer, lpdwBufferLength );
 	else if (index >= 0 && index <= HTTP_QUERY_MAX && lpwhr->StdHeaders[index].lpszValue)
 	{
 	    lphttpHdr = &lpwhr->StdHeaders[index];
 	}
 	else
+        {
+            SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
             return bSuccess;
+        }
     }
 
     /* Ensure header satisifies requested attributes */
     if ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
 	    (~lphttpHdr->wFlags & HDR_ISREQUEST))
+    {
+        SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
 	return bSuccess;
+    }
 
     /* coalesce value to reuqested type */
     if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER)
@@ -1501,7 +1575,7 @@
         }
 
         /* allocate space for an array of all the string pointers to be added */
-        len = (1 + HTTP_QUERY_MAX + lpwhr->nCustHeaders)*4 + 3;
+        len = (2 + HTTP_QUERY_MAX + lpwhr->nCustHeaders)*4 + 3;
         req = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, len*sizeof(LPCWSTR) );
 
         /* add the verb, path and HTTP/1.0 */
@@ -1888,6 +1962,37 @@
 
 
 /***********************************************************************
+ *           HTTP_clear_response_headers (internal)
+ *
+ * clear out any old response headers
+ */
+static void HTTP_clear_response_headers( LPWININETHTTPREQW lpwhr )
+{
+    DWORD i;
+
+    for( i=0; i<=HTTP_QUERY_MAX; i++ )
+    {
+        if( !lpwhr->StdHeaders[i].lpszField )
+            continue;
+        if( !lpwhr->StdHeaders[i].lpszValue )
+            continue;
+        if ( lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST )
+            continue;
+        HTTP_ReplaceHeaderValue( &lpwhr->StdHeaders[i], NULL );
+    }
+    for( i=0; i<lpwhr->nCustHeaders; i++)
+    {
+        if( !lpwhr->pCustHeaders[i].lpszField )
+            continue;
+        if( !lpwhr->pCustHeaders[i].lpszValue )
+            continue;
+        if ( lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST )
+            continue;
+        HTTP_ReplaceHeaderValue( &lpwhr->pCustHeaders[i], NULL );
+    }
+}
+
+/***********************************************************************
  *           HTTP_GetResponseHeaders (internal)
  *
  * Read server response
@@ -1905,12 +2010,15 @@
     BOOL bSuccess = FALSE;
     INT  rc = 0;
     WCHAR value[MAX_FIELD_VALUE_LEN], field[MAX_FIELD_LEN];
-    static const WCHAR szStatus[] = {'S','t','a','t','u','s',0};
     static const WCHAR szHttp[] = { 'H','T','T','P',0 };
     char bufferA[MAX_REPLY_LEN];
+    LPWSTR status_code, status_text;
 
     TRACE("-->\n");
 
+    /* clear old response headers (eg. from a redirect response) */
+    HTTP_clear_response_headers( lpwhr );
+
     if (!NETCON_connected(&lpwhr->netConnection))
         goto lend;
 
@@ -1920,7 +2028,7 @@
     NETCON_recv(&lpwhr->netConnection, buffer, buflen, MSG_PEEK, &rc);
 
     /*
-     * We should first receive 'HTTP/1.x nnn' where nnn is the status code.
+     * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
      */
     buflen = MAX_REPLY_LEN;
     memset(buffer, 0, MAX_REPLY_LEN);
@@ -1931,8 +2039,23 @@
     if (strncmpW(buffer, szHttp, 4) != 0)
         goto lend;
 
-    buffer[12]='\0';
-    HTTP_ProcessHeader(lpwhr, szStatus, buffer+9, (HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE));
+    /* split the version from the status code */
+    status_code = strchrW( buffer, ' ' );
+    if( !status_code )
+        goto lend;
+    *status_code++=0;
+
+    /* split the status code from the status text */
+    status_text = strchrW( status_code, ' ' );
+    if( !status_text )
+        goto lend;
+    *status_text++=0;
+
+    TRACE("version [%s] status code [%s] status text [%s]\n",
+         debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
+    HTTP_ReplaceHeaderValue( &lpwhr->StdHeaders[HTTP_QUERY_VERSION], buffer );
+    HTTP_ReplaceHeaderValue( &lpwhr->StdHeaders[HTTP_QUERY_STATUS_CODE], status_code );
+    HTTP_ReplaceHeaderValue( &lpwhr->StdHeaders[HTTP_QUERY_STATUS_TEXT], status_text );
 
     /* Parse each response line */
     do
@@ -2042,7 +2165,6 @@
     INT index = -1;
     static const WCHAR szContentLength[] = {
        'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0};
-    static const WCHAR szStatus[] = {'S','t','a','t','u','s',0};
     static const WCHAR szContentType[] = {
        'C','o','n','t','e','n','t','-','T','y','p','e',0};
     static const WCHAR szLastModified[] = {
@@ -2068,11 +2190,14 @@
     static const WCHAR szProxyAuth[] = {
        'P','r','o','x','y','-',
        'A','u','t','h','e','n','t','i','c','a','t','e', 0};
+    static const WCHAR szContentEncoding[] = {
+       'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0};
+    static const WCHAR szCookie[] = {'C','o','o','k','i','e',0};
+    static const WCHAR szVary[] = {'V','a','r','y',0};
+    static const WCHAR szVia[] = {'V','i','a',0};
 
     if (!strcmpiW(lpszField, szContentLength))
         index = HTTP_QUERY_CONTENT_LENGTH;
-    else if (!strcmpiW(lpszField,szStatus))
-        index = HTTP_QUERY_STATUS_CODE;
     else if (!strcmpiW(lpszField,szContentType))
         index = HTTP_QUERY_CONTENT_TYPE;
     else if (!strcmpiW(lpszField,szLastModified))
@@ -2107,6 +2232,14 @@
         index = HTTP_QUERY_USER_AGENT;
     else if (!strcmpiW(lpszField,szProxyAuth))
         index = HTTP_QUERY_PROXY_AUTHENTICATE;
+    else if (!strcmpiW(lpszField,szContentEncoding))
+        index = HTTP_QUERY_CONTENT_ENCODING;
+    else if (!strcmpiW(lpszField,szCookie))
+        index = HTTP_QUERY_COOKIE;
+    else if (!strcmpiW(lpszField,szVary))
+        index = HTTP_QUERY_VARY;
+    else if (!strcmpiW(lpszField,szVia))
+        index = HTTP_QUERY_VIA;
     else
     {
         TRACE("Couldn't find %s in standard header table\n", debugstr_w(lpszField));
@@ -2115,6 +2248,27 @@
     return index;
 }
 
+/***********************************************************************
+ *           HTTP_ReplaceHeaderValue (internal)
+ */
+BOOL HTTP_ReplaceHeaderValue( LPHTTPHEADERW lphttpHdr, LPCWSTR value )
+{
+    INT len = 0;
+
+    if( lphttpHdr->lpszValue )
+        HeapFree( GetProcessHeap(), 0, lphttpHdr->lpszValue );
+    lphttpHdr->lpszValue = NULL;
+
+    if( value )
+        len = strlenW(value);
+    if (len)
+    {
+        lphttpHdr->lpszValue = HeapAlloc(GetProcessHeap(), 0,
+                                        (len+1)*sizeof(WCHAR));
+        strcpyW(lphttpHdr->lpszValue, value);
+    }
+    return TRUE;
+}
 
 /***********************************************************************
  *           HTTP_ProcessHeader (internal)
@@ -2201,35 +2355,7 @@
     else if (lphttpHdr->lpszValue)
     {
         if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
-        {
-            LPWSTR lpsztmp;
-            INT len;
-
-            len = strlenW(value);
-
-            if (len <= 0)
-            {
-	        /* if custom header delete from array */
-                HeapFree(GetProcessHeap(), 0, lphttpHdr->lpszValue);
-                lphttpHdr->lpszValue = NULL;
-                bSuccess = TRUE;
-            }
-            else
-            {
-                lpsztmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,  lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
-                if (lpsztmp)
-                {
-                    lphttpHdr->lpszValue = lpsztmp;
-                    strcpyW(lpsztmp, value);
-                    bSuccess = TRUE;
-                }
-                else
-                {
-                    WARN("HeapReAlloc (%d bytes) failed\n",len+1);
-                    INTERNET_SetLastError(ERROR_OUTOFMEMORY);
-                }
-            }
-        }
+            bSuccess = HTTP_ReplaceHeaderValue( lphttpHdr, value );
         else if (dwModifier & COALESCEFLASG)
         {
             LPWSTR lpsztmp;


More information about the wine-patches mailing list