[WININET]
[reactos.git] / reactos / dll / win32 / wininet / http.c
index 47a0ce9..57b7372 100644 (file)
@@ -6,6 +6,7 @@
  * Copyright 2002 TransGaming Technologies Inc.
  * Copyright 2004 Mike McCormack for CodeWeavers
  * Copyright 2005 Aric Stewart for CodeWeavers
+ * Copyright 2006 Robert Shearman for CodeWeavers
  *
  * Ulrich Czekalla
  * David Hammerton
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
 #include "config.h"
 #include "wine/port.h"
 
+#if defined(__MINGW32__) || defined (_MSC_VER)
+#include <ws2tcpip.h>
+#endif
+
 #include <sys/types.h>
 #ifdef HAVE_SYS_SOCKET_H
 # include <sys/socket.h>
 #endif
 #include <time.h>
 #include <assert.h>
+#ifdef HAVE_ZLIB
+#  include <zlib.h>
+#endif
 
 #include "windef.h"
 #include "winbase.h"
 #include "wininet.h"
-#include "winreg.h"
 #include "winerror.h"
 #define NO_SHLWAPI_STREAM
 #define NO_SHLWAPI_REG
 #define NO_SHLWAPI_STRFCNS
 #define NO_SHLWAPI_GDI
 #include "shlwapi.h"
+#include "sspi.h"
+#include "wincrypt.h"
 
 #include "internet.h"
 #include "wine/debug.h"
+#include "wine/exception.h"
 #include "wine/unicode.h"
 
 #include "inet_ntop.c"
 
 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
 
-static const WCHAR g_szHttp1_0[] = {' ','H','T','T','P','/','1','.','0',0 };
-static const WCHAR g_szHttp1_1[] = {' ','H','T','T','P','/','1','.','1',0 };
-static const WCHAR g_szReferer[] = {'R','e','f','e','r','e','r',0};
-static const WCHAR g_szAccept[] = {'A','c','c','e','p','t',0};
-static const WCHAR g_szUserAgent[] = {'U','s','e','r','-','A','g','e','n','t',0};
-static const WCHAR szHost[] = { 'H','o','s','t',0 };
+static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
+static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
+static const WCHAR szOK[] = {'O','K',0};
+static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
+static const WCHAR hostW[] = { 'H','o','s','t',0 };
+static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
+static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
+static const WCHAR szGET[] = { 'G','E','T', 0 };
+static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
+static const WCHAR szCrLf[] = {'\r','\n', 0};
+
+static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
+static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
+static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
+static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
+static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
+static const WCHAR szAge[] = { 'A','g','e',0 };
+static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
+static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
+static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
+static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
+static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
+static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
+static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
+static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
+static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
+static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
+static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
+static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
+static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
+static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
+static const WCHAR szDate[] = { 'D','a','t','e',0 };
+static const WCHAR szFrom[] = { 'F','r','o','m',0 };
+static const WCHAR szETag[] = { 'E','T','a','g',0 };
+static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
+static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
+static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
+static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
+static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
+static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
+static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
+static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
+static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
+static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
+static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
+static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
+static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
+static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
+static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
+static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
+static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
+static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
+static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
+static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
+static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
+static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
+static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
+static const WCHAR szURI[] = { 'U','R','I',0 };
+static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
+static const WCHAR szVary[] = { 'V','a','r','y',0 };
+static const WCHAR szVia[] = { 'V','i','a',0 };
+static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
+static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
 
 #define MAXHOSTNAME 100
 #define MAX_FIELD_VALUE_LEN 256
 #define MAX_FIELD_LEN 256
 
-#define HTTP_REFERER    g_szReferer
-#define HTTP_ACCEPT     g_szAccept
-#define HTTP_USERAGENT  g_szUserAgent
+#define HTTP_REFERER    szReferer
+#define HTTP_ACCEPT     szAccept
+#define HTTP_USERAGENT  szUser_Agent
 
 #define HTTP_ADDHDR_FLAG_ADD                           0x20000000
 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW                    0x10000000
@@ -88,28 +155,69 @@ static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
 #define HTTP_ADDHDR_FLAG_REPLACE                       0x80000000
 #define HTTP_ADDHDR_FLAG_REQ                           0x02000000
 
+#define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
 
-static void HTTP_CloseHTTPRequestHandle(LPWININETHANDLEHEADER hdr);
-static void HTTP_CloseHTTPSessionHandle(LPWININETHANDLEHEADER hdr);
-static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr);
-static BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr);
-static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
-static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
-static BOOL HTTP_InsertCustomHeader(LPWININETHTTPREQW lpwhr, LPHTTPHEADERW lpHdr);
-static INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQW lpwhr, LPCWSTR lpszField, INT index, BOOL Request);
-static BOOL HTTP_DeleteCustomHeader(LPWININETHTTPREQW lpwhr, DWORD index);
-static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
-static BOOL HTTP_InsertProxyAuthorization( LPWININETHTTPREQW lpwhr,
-                       LPCWSTR username, LPCWSTR password );
-static BOOL WINAPI HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD
-        dwInfoLevel, LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD
-        lpdwIndex);
-static BOOL HTTP_HandleRedirect(LPWININETHTTPREQW lpwhr, LPCWSTR lpszUrl,
-        LPCWSTR lpszHeaders, DWORD dwHeaderLength, LPVOID lpOptional, DWORD
-        dwOptionalLength, DWORD dwContentLength);
+struct HttpAuthInfo
+{
+    LPWSTR scheme;
+    CredHandle cred;
+    CtxtHandle ctx;
+    TimeStamp exp;
+    ULONG attr;
+    ULONG max_token;
+    void *auth_data;
+    unsigned int auth_data_len;
+    BOOL finished; /* finished authenticating */
+};
+
+
+struct gzip_stream_t {
+#ifdef HAVE_ZLIB
+    z_stream zstream;
+#endif
+    BYTE buf[8192];
+    DWORD buf_size;
+    DWORD buf_pos;
+    BOOL end_of_data;
+};
+
+typedef struct _authorizationData
+{
+    struct list entry;
+
+    LPWSTR lpszwHost;
+    LPWSTR lpszwRealm;
+    LPSTR  lpszAuthorization;
+    UINT   AuthorizationLen;
+} authorizationData;
 
+static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
 
-LPHTTPHEADERW HTTP_GetHeader(LPWININETHTTPREQW req, LPCWSTR head)
+static CRITICAL_SECTION authcache_cs;
+static CRITICAL_SECTION_DEBUG critsect_debug =
+{
+    0, 0, &authcache_cs,
+    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
+};
+static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
+
+static DWORD HTTP_OpenConnection(http_request_t *req);
+static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
+static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
+static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
+static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
+static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
+static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
+static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
+static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
+static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
+static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
+static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
+static void HTTP_DrainContent(http_request_t *req);
+static BOOL HTTP_FinishedReading(http_request_t *req);
+
+static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
 {
     int HeaderIndex = 0;
     HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
@@ -119,6 +227,91 @@ LPHTTPHEADERW HTTP_GetHeader(LPWININETHTTPREQW req, LPCWSTR head)
         return &req->pCustHeaders[HeaderIndex];
 }
 
+#ifdef HAVE_ZLIB
+
+static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
+{
+    return HeapAlloc(GetProcessHeap(), 0, items*size);
+}
+
+static void wininet_zfree(voidpf opaque, voidpf address)
+{
+    HeapFree(GetProcessHeap(), 0, address);
+}
+
+static void init_gzip_stream(http_request_t *req)
+{
+    gzip_stream_t *gzip_stream;
+    int index, zres;
+
+    gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
+    gzip_stream->zstream.zalloc = wininet_zalloc;
+    gzip_stream->zstream.zfree = wininet_zfree;
+    gzip_stream->zstream.opaque = NULL;
+    gzip_stream->zstream.next_in = NULL;
+    gzip_stream->zstream.avail_in = 0;
+    gzip_stream->zstream.next_out = NULL;
+    gzip_stream->zstream.avail_out = 0;
+    gzip_stream->buf_pos = 0;
+    gzip_stream->buf_size = 0;
+    gzip_stream->end_of_data = FALSE;
+
+    zres = inflateInit2(&gzip_stream->zstream, 0x1f);
+    if(zres != Z_OK) {
+        ERR("inflateInit failed: %d\n", zres);
+        HeapFree(GetProcessHeap(), 0, gzip_stream);
+        return;
+    }
+
+    req->gzip_stream = gzip_stream;
+
+    index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
+    if(index != -1)
+        HTTP_DeleteCustomHeader(req, index);
+}
+
+#else
+
+static void init_gzip_stream(http_request_t *req)
+{
+    ERR("gzip stream not supported, missing zlib.\n");
+}
+
+#endif
+
+/* set the request content length based on the headers */
+static DWORD set_content_length( http_request_t *lpwhr )
+{
+    static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
+    WCHAR encoding[20];
+    DWORD size;
+
+    size = sizeof(lpwhr->dwContentLength);
+    if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
+                            &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
+        lpwhr->dwContentLength = ~0u;
+
+    size = sizeof(encoding);
+    if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
+        !strcmpiW(encoding, szChunked))
+    {
+        lpwhr->dwContentLength = ~0u;
+        lpwhr->read_chunked = TRUE;
+    }
+
+    if(lpwhr->decoding) {
+        int encoding_idx;
+
+        static const WCHAR gzipW[] = {'g','z','i','p',0};
+
+        encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
+        if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
+            init_gzip_stream(lpwhr);
+    }
+
+    return lpwhr->dwContentLength;
+}
+
 /***********************************************************************
  *           HTTP_Tokenize (internal)
  *
@@ -131,21 +324,26 @@ static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
     int i;
     LPCWSTR next_token;
 
-    /* empty string has no tokens */
-    if (*string)
-        tokens++;
-    /* count tokens */
-    for (i = 0; string[i]; i++)
-        if (!strncmpW(string+i, token_string, strlenW(token_string)))
-        {
-            DWORD j;
+    if (string)
+    {
+        /* empty string has no tokens */
+        if (*string)
             tokens++;
-            /* we want to skip over separators, but not the null terminator */
-            for (j = 0; j < strlenW(token_string) - 1; j++)
-                if (!string[i+j])
-                    break;
-            i += j;
+        /* count tokens */
+        for (i = 0; string[i]; i++)
+        {
+            if (!strncmpW(string+i, token_string, strlenW(token_string)))
+            {
+                DWORD j;
+                tokens++;
+                /* we want to skip over separators, but not the null terminator */
+                for (j = 0; j < strlenW(token_string) - 1; j++)
+                    if (!string[i+j])
+                        break;
+                i += j;
+            }
         }
+    }
 
     /* add 1 for terminating NULL */
     token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
@@ -179,29 +377,14 @@ static void HTTP_FreeTokens(LPWSTR * token_array)
     HeapFree(GetProcessHeap(), 0, token_array);
 }
 
-/* **********************************************************************
- *
- * Helper functions for the HttpSendRequest(Ex) functions
- *
- */
-static void HTTP_FixVerb( LPWININETHTTPREQW lpwhr )
-{
-    /* if the verb is NULL default to GET */
-    if (NULL == lpwhr->lpszVerb)
-    {
-           static const WCHAR szGET[] = { 'G','E','T', 0 };
-           lpwhr->lpszVerb = WININET_strdupW(szGET);
-    }
-}
-
-static void HTTP_FixURL( LPWININETHTTPREQW lpwhr)
+static void HTTP_FixURL(http_request_t *lpwhr)
 {
     static const WCHAR szSlash[] = { '/',0 };
     static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
 
     /* If we don't have a path we set it to root */
     if (NULL == lpwhr->lpszPath)
-        lpwhr->lpszPath = WININET_strdupW(szSlash);
+        lpwhr->lpszPath = heap_strdupW(szSlash);
     else /* remove \r and \n*/
     {
         int nLen = strlenW(lpwhr->lpszPath);
@@ -218,10 +401,10 @@ static void HTTP_FixURL( LPWININETHTTPREQW lpwhr)
     }
 
     if(CSTR_EQUAL != CompareStringW( LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
-                       lpwhr->lpszPath, strlenW(szHttp), szHttp, strlenW(szHttp) )
+                       lpwhr->lpszPath, strlenW(lpwhr->lpszPath), szHttp, strlenW(szHttp) )
        && lpwhr->lpszPath[0] != '/') /* not an absolute path ?? --> fix it !! */
     {
-        WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0,
+        WCHAR *fixurl = HeapAlloc(GetProcessHeap(), 0, 
                              (strlenW(lpwhr->lpszPath) + 2)*sizeof(WCHAR));
         *fixurl = '/';
         strcpyW(fixurl + 1, lpwhr->lpszPath);
@@ -230,21 +413,20 @@ static void HTTP_FixURL( LPWININETHTTPREQW lpwhr)
     }
 }
 
-static LPWSTR HTTP_BuildHeaderRequestString( LPWININETHTTPREQW lpwhr, LPCWSTR verb, LPCWSTR path, BOOL http1_1 )
+static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
 {
     LPWSTR requestString;
     DWORD len, n;
     LPCWSTR *req;
-    INT i;
+    UINT i;
     LPWSTR p;
 
     static const WCHAR szSpace[] = { ' ',0 };
-    static const WCHAR szcrlf[] = {'\r','\n', 0};
     static const WCHAR szColon[] = { ':',' ',0 };
     static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
 
     /* allocate space for an array of all the string pointers to be added */
-    len = (lpwhr->nCustHeaders)*4 + 9;
+    len = (lpwhr->nCustHeaders)*4 + 10;
     req = HeapAlloc( GetProcessHeap(), 0, len*sizeof(LPCWSTR) );
 
     /* add the verb, path and HTTP version string */
@@ -252,14 +434,15 @@ static LPWSTR HTTP_BuildHeaderRequestString( LPWININETHTTPREQW lpwhr, LPCWSTR ve
     req[n++] = verb;
     req[n++] = szSpace;
     req[n++] = path;
-    req[n++] = http1_1 ? g_szHttp1_1 : g_szHttp1_0;
+    req[n++] = szSpace;
+    req[n++] = version;
 
-    /* Append custom request heades */
+    /* Append custom request headers */
     for (i = 0; i < lpwhr->nCustHeaders; i++)
     {
         if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
         {
-            req[n++] = szcrlf;
+            req[n++] = szCrLf;
             req[n++] = lpwhr->pCustHeaders[i].lpszField;
             req[n++] = szColon;
             req[n++] = lpwhr->pCustHeaders[i].lpszValue;
@@ -285,421 +468,541 @@ static LPWSTR HTTP_BuildHeaderRequestString( LPWININETHTTPREQW lpwhr, LPCWSTR ve
     while ( (*p == '\n') || (*p == '\r') )
        p--;
     strcpyW( p+1, sztwocrlf );
-
+    
     return requestString;
 }
 
-static void HTTP_ProcessHeaders( LPWININETHTTPREQW lpwhr )
+static void HTTP_ProcessCookies( http_request_t *lpwhr )
 {
-    static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
     int HeaderIndex;
+    int numCookies = 0;
     LPHTTPHEADERW setCookieHeader;
 
-    HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, 0, FALSE);
-    if (HeaderIndex == -1)
-            return;
-    setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
-
-    if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
+    while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
     {
-        int nPosStart = 0, nPosEnd = 0, len;
-        static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','/',0};
+        setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
 
-        while (setCookieHeader->lpszValue[nPosEnd] != '\0')
+        if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
         {
-            LPWSTR buf_cookie, cookie_name, cookie_data;
+            int len;
+            static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
             LPWSTR buf_url;
-            LPWSTR domain = NULL;
             LPHTTPHEADERW Host;
 
-            int nEqualPos = 0;
-            while (setCookieHeader->lpszValue[nPosEnd] != ';' && setCookieHeader->lpszValue[nPosEnd] != ',' &&
-                   setCookieHeader->lpszValue[nPosEnd] != '\0')
-            {
-                nPosEnd++;
-            }
-            if (setCookieHeader->lpszValue[nPosEnd] == ';')
-            {
-                /* fixme: not case sensitive, strcasestr is gnu only */
-                int nDomainPosEnd = 0;
-                int nDomainPosStart = 0, nDomainLength = 0;
-                static const WCHAR szDomain[] = {'d','o','m','a','i','n','=',0};
-                LPWSTR lpszDomain = strstrW(&setCookieHeader->lpszValue[nPosEnd], szDomain);
-                if (lpszDomain)
-                { /* they have specified their own domain, lets use it */
-                    while (lpszDomain[nDomainPosEnd] != ';' && lpszDomain[nDomainPosEnd] != ',' &&
-                           lpszDomain[nDomainPosEnd] != '\0')
-                    {
-                        nDomainPosEnd++;
-                    }
-                    nDomainPosStart = strlenW(szDomain);
-                    nDomainLength = (nDomainPosEnd - nDomainPosStart) + 1;
-                    domain = HeapAlloc(GetProcessHeap(), 0, (nDomainLength + 1)*sizeof(WCHAR));
-                    lstrcpynW(domain, &lpszDomain[nDomainPosStart], nDomainLength + 1);
-                }
-            }
-            if (setCookieHeader->lpszValue[nPosEnd] == '\0') break;
-            buf_cookie = HeapAlloc(GetProcessHeap(), 0, ((nPosEnd - nPosStart) + 1)*sizeof(WCHAR));
-            lstrcpynW(buf_cookie, &setCookieHeader->lpszValue[nPosStart], (nPosEnd - nPosStart) + 1);
-            TRACE("%s\n", debugstr_w(buf_cookie));
-            while (buf_cookie[nEqualPos] != '=' && buf_cookie[nEqualPos] != '\0')
-            {
-                nEqualPos++;
-            }
-            if (buf_cookie[nEqualPos] == '\0' || buf_cookie[nEqualPos + 1] == '\0')
-            {
-                HeapFree(GetProcessHeap(), 0, buf_cookie);
-                break;
-            }
-
-            cookie_name = HeapAlloc(GetProcessHeap(), 0, (nEqualPos + 1)*sizeof(WCHAR));
-            lstrcpynW(cookie_name, buf_cookie, nEqualPos + 1);
-            cookie_data = &buf_cookie[nEqualPos + 1];
-
-            Host = HTTP_GetHeader(lpwhr,szHost);
-            len = lstrlenW((domain ? domain : (Host?Host->lpszValue:NULL))) +
-                strlenW(lpwhr->lpszPath) + 9;
+            Host = HTTP_GetHeader(lpwhr, hostW);
+            len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
             buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
-            sprintfW(buf_url, szFmt, (domain ? domain : (Host?Host->lpszValue:NULL))); /* FIXME PATH!!! */
-            InternetSetCookieW(buf_url, cookie_name, cookie_data);
+            sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
+            InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
 
             HeapFree(GetProcessHeap(), 0, buf_url);
-            HeapFree(GetProcessHeap(), 0, buf_cookie);
-            HeapFree(GetProcessHeap(), 0, cookie_name);
-            HeapFree(GetProcessHeap(), 0, domain);
-            nPosStart = nPosEnd;
         }
+        numCookies++;
     }
 }
 
-static void HTTP_AddProxyInfo( LPWININETHTTPREQW lpwhr )
+static void strip_spaces(LPWSTR start)
 {
-    LPWININETHTTPSESSIONW lpwhs = (LPWININETHTTPSESSIONW)lpwhr->hdr.lpwhparent;
-    LPWININETAPPINFOW hIC = (LPWININETAPPINFOW)lpwhs->hdr.lpwhparent;
+    LPWSTR str = start;
+    LPWSTR end;
 
-    assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
-    assert(hIC->hdr.htype == WH_HINIT);
+    while (*str == ' ' && *str != '\0')
+        str++;
+
+    if (str != start)
+        memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
 
-    if (hIC && (hIC->lpszProxyUsername || hIC->lpszProxyPassword ))
-        HTTP_InsertProxyAuthorization(lpwhr, hIC->lpszProxyUsername,
-                hIC->lpszProxyPassword);
+    end = start + strlenW(start) - 1;
+    while (end >= start && *end == ' ')
+    {
+        *end = '\0';
+        end--;
+    }
 }
 
-/***********************************************************************
- *           HTTP_HttpAddRequestHeadersW (internal)
- */
-static BOOL WINAPI HTTP_HttpAddRequestHeadersW(LPWININETHTTPREQW lpwhr,
-       LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
+static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
 {
-    LPWSTR lpszStart;
-    LPWSTR lpszEnd;
-    LPWSTR buffer;
-    BOOL bSuccess = FALSE;
-    DWORD len;
-
-    TRACE("copying header: %s\n", debugstr_w(lpszHeader));
+    static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
+    static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
+    BOOL is_basic;
+    is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
+        ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
+    if (is_basic && pszRealm)
+    {
+        LPCWSTR token;
+        LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
+        LPCWSTR realm;
+        ptr++;
+        *pszRealm=NULL;
+        token = strchrW(ptr,'=');
+        if (!token)
+            return TRUE;
+        realm = ptr;
+        while (*realm == ' ' && *realm != '\0')
+            realm++;
+        if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
+            (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
+        {
+            token++;
+            while (*token == ' ' && *token != '\0')
+                token++;
+            if (*token == '\0')
+                return TRUE;
+            *pszRealm = heap_strdupW(token);
+            strip_spaces(*pszRealm);
+        }
+    }
 
-    if( dwHeaderLength == ~0U )
-        len = strlenW(lpszHeader);
-    else
-        len = dwHeaderLength;
-    buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
-    lstrcpynW( buffer, lpszHeader, len + 1);
+    return is_basic;
+}
 
-    lpszStart = buffer;
+static void destroy_authinfo( struct HttpAuthInfo *authinfo )
+{
+    if (!authinfo) return;
 
-    do
-    {
-        LPWSTR * pFieldAndValue;
+    if (SecIsValidHandle(&authinfo->ctx))
+        DeleteSecurityContext(&authinfo->ctx);
+    if (SecIsValidHandle(&authinfo->cred))
+        FreeCredentialsHandle(&authinfo->cred);
 
-        lpszEnd = lpszStart;
+    HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
+    HeapFree(GetProcessHeap(), 0, authinfo->scheme);
+    HeapFree(GetProcessHeap(), 0, authinfo);
+}
 
-        while (*lpszEnd != '\0')
-        {
-            if (*lpszEnd == '\r' && *(lpszEnd + 1) == '\n')
-                 break;
-            lpszEnd++;
-        }
+static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
+{
+    authorizationData *ad;
+    UINT rc = 0;
 
-        if (*lpszStart == '\0')
-           break;
+    TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
 
-        if (*lpszEnd == '\r')
-        {
-            *lpszEnd = '\0';
-            lpszEnd += 2; /* Jump over \r\n */
-        }
-        TRACE("interpreting header %s\n", debugstr_w(lpszStart));
-        pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
-        if (pFieldAndValue)
+    EnterCriticalSection(&authcache_cs);
+    LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
+    {
+        if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
         {
-            bSuccess = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
-                pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
-            HTTP_FreeTokens(pFieldAndValue);
+            TRACE("Authorization found in cache\n");
+            *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
+            memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
+            rc = ad->AuthorizationLen;
+            break;
         }
-
-        lpszStart = lpszEnd;
-    } while (bSuccess);
-
-    HeapFree(GetProcessHeap(), 0, buffer);
-
-    return bSuccess;
+    }
+    LeaveCriticalSection(&authcache_cs);
+    return rc;
 }
 
-/***********************************************************************
- *           HttpAddRequestHeadersW (WININET.@)
- *
- * Adds one or more HTTP header to the request handler
- *
- * RETURNS
- *    TRUE  on success
- *    FALSE on failure
- *
- */
-BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
-       LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
+static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
 {
-    BOOL bSuccess = FALSE;
-    LPWININETHTTPREQW lpwhr;
+    struct list *cursor;
+    authorizationData* ad = NULL;
 
-    TRACE("%p, %s, %li, %li\n", hHttpRequest, debugstr_w(lpszHeader), dwHeaderLength,
-          dwModifier);
+    TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
 
-    if (!lpszHeader)
-      return TRUE;
-
-    lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hHttpRequest );
-    if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
+    EnterCriticalSection(&authcache_cs);
+    LIST_FOR_EACH(cursor, &basicAuthorizationCache)
     {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-        goto lend;
+        authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
+        if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
+        {
+            ad = check;
+            break;
+        }
     }
-    bSuccess = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
-lend:
-    if( lpwhr )
-        WININET_Release( &lpwhr->hdr );
 
-    return bSuccess;
+    if (ad)
+    {
+        TRACE("Found match in cache, replacing\n");
+        HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
+        ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
+        memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
+        ad->AuthorizationLen = auth_data_len;
+    }
+    else
+    {
+        ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
+        ad->lpszwHost = heap_strdupW(host);
+        ad->lpszwRealm = heap_strdupW(realm);
+        ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
+        memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
+        ad->AuthorizationLen = auth_data_len;
+        list_add_head(&basicAuthorizationCache,&ad->entry);
+        TRACE("authorization cached\n");
+    }
+    LeaveCriticalSection(&authcache_cs);
 }
 
-/***********************************************************************
- *           HttpAddRequestHeadersA (WININET.@)
- *
- * Adds one or more HTTP header to the request handler
- *
- * RETURNS
- *    TRUE  on success
- *    FALSE on failure
- *
- */
-BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
-       LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
+static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
+                                  struct HttpAuthInfo **ppAuthInfo,
+                                  LPWSTR domain_and_username, LPWSTR password,
+                                  LPWSTR host )
 {
-    DWORD len;
-    LPWSTR hdr;
-    BOOL r;
+    SECURITY_STATUS sec_status;
+    struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
+    BOOL first = FALSE;
+    LPWSTR szRealm = NULL;
 
-    TRACE("%p, %s, %li, %li\n", hHttpRequest, debugstr_a(lpszHeader), dwHeaderLength,
-          dwModifier);
+    TRACE("%s\n", debugstr_w(pszAuthValue));
 
-    len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
-    hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
-    MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
-    if( dwHeaderLength != ~0U )
-        dwHeaderLength = len;
+    if (!pAuthInfo)
+    {
+        TimeStamp exp;
 
-    r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
+        first = TRUE;
+        pAuthInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*pAuthInfo));
+        if (!pAuthInfo)
+            return FALSE;
 
-    HeapFree( GetProcessHeap(), 0, hdr );
+        SecInvalidateHandle(&pAuthInfo->cred);
+        SecInvalidateHandle(&pAuthInfo->ctx);
+        memset(&pAuthInfo->exp, 0, sizeof(pAuthInfo->exp));
+        pAuthInfo->attr = 0;
+        pAuthInfo->auth_data = NULL;
+        pAuthInfo->auth_data_len = 0;
+        pAuthInfo->finished = FALSE;
 
-    return r;
-}
+        if (is_basic_auth_value(pszAuthValue,NULL))
+        {
+            static const WCHAR szBasic[] = {'B','a','s','i','c',0};
+            pAuthInfo->scheme = heap_strdupW(szBasic);
+            if (!pAuthInfo->scheme)
+            {
+                HeapFree(GetProcessHeap(), 0, pAuthInfo);
+                return FALSE;
+            }
+        }
+        else
+        {
+            PVOID pAuthData;
+            SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
 
-/***********************************************************************
- *           HttpEndRequestA (WININET.@)
- *
- * Ends an HTTP request that was started by HttpSendRequestEx
- *
- * RETURNS
- *    TRUE     if successful
- *    FALSE    on failure
- *
- */
-BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
-        LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD dwContext)
-{
-    LPINTERNET_BUFFERSA ptr;
-    LPINTERNET_BUFFERSW lpBuffersOutW,ptrW;
-    BOOL rc = FALSE;
+            pAuthInfo->scheme = heap_strdupW(pszAuthValue);
+            if (!pAuthInfo->scheme)
+            {
+                HeapFree(GetProcessHeap(), 0, pAuthInfo);
+                return FALSE;
+            }
 
-    TRACE("(%p, %p, %08lx, %08lx): stub\n", hRequest, lpBuffersOut, dwFlags,
-            dwContext);
+            if (domain_and_username)
+            {
+                WCHAR *user = strchrW(domain_and_username, '\\');
+                WCHAR *domain = domain_and_username;
 
-    ptr = lpBuffersOut;
-    if (ptr)
-        lpBuffersOutW = (LPINTERNET_BUFFERSW)HeapAlloc(GetProcessHeap(),
-                HEAP_ZERO_MEMORY, sizeof(INTERNET_BUFFERSW));
-    else
-        lpBuffersOutW = NULL;
+                /* FIXME: make sure scheme accepts SEC_WINNT_AUTH_IDENTITY before calling AcquireCredentialsHandle */
 
-    ptrW = lpBuffersOutW;
-    while (ptr)
-    {
-        if (ptr->lpvBuffer && ptr->dwBufferLength)
-            ptrW->lpvBuffer = HeapAlloc(GetProcessHeap(),0,ptr->dwBufferLength);
-        ptrW->dwBufferLength = ptr->dwBufferLength;
-        ptrW->dwBufferTotal= ptr->dwBufferTotal;
+                pAuthData = &nt_auth_identity;
+
+                if (user) user++;
+                else
+                {
+                    user = domain_and_username;
+                    domain = NULL;
+                }
 
-        if (ptr->Next)
-            ptrW->Next = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
-                    sizeof(INTERNET_BUFFERSW));
+                nt_auth_identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+                nt_auth_identity.User = user;
+                nt_auth_identity.UserLength = strlenW(nt_auth_identity.User);
+                nt_auth_identity.Domain = domain;
+                nt_auth_identity.DomainLength = domain ? user - domain - 1 : 0;
+                nt_auth_identity.Password = password;
+                nt_auth_identity.PasswordLength = strlenW(nt_auth_identity.Password);
+            }
+            else
+                /* use default credentials */
+                pAuthData = NULL;
+
+            sec_status = AcquireCredentialsHandleW(NULL, pAuthInfo->scheme,
+                                                   SECPKG_CRED_OUTBOUND, NULL,
+                                                   pAuthData, NULL,
+                                                   NULL, &pAuthInfo->cred,
+                                                   &exp);
+            if (sec_status == SEC_E_OK)
+            {
+                PSecPkgInfoW sec_pkg_info;
+                sec_status = QuerySecurityPackageInfoW(pAuthInfo->scheme, &sec_pkg_info);
+                if (sec_status == SEC_E_OK)
+                {
+                    pAuthInfo->max_token = sec_pkg_info->cbMaxToken;
+                    FreeContextBuffer(sec_pkg_info);
+                }
+            }
+            if (sec_status != SEC_E_OK)
+            {
+                WARN("AcquireCredentialsHandleW for scheme %s failed with error 0x%08x\n",
+                     debugstr_w(pAuthInfo->scheme), sec_status);
+                HeapFree(GetProcessHeap(), 0, pAuthInfo->scheme);
+                HeapFree(GetProcessHeap(), 0, pAuthInfo);
+                return FALSE;
+            }
+        }
+        *ppAuthInfo = pAuthInfo;
+    }
+    else if (pAuthInfo->finished)
+        return FALSE;
 
-        ptr = ptr->Next;
-        ptrW = ptrW->Next;
+    if ((strlenW(pszAuthValue) < strlenW(pAuthInfo->scheme)) ||
+        strncmpiW(pszAuthValue, pAuthInfo->scheme, strlenW(pAuthInfo->scheme)))
+    {
+        ERR("authentication scheme changed from %s to %s\n",
+            debugstr_w(pAuthInfo->scheme), debugstr_w(pszAuthValue));
+        return FALSE;
     }
 
-    rc = HttpEndRequestW(hRequest, lpBuffersOutW, dwFlags, dwContext);
+    if (is_basic_auth_value(pszAuthValue,&szRealm))
+    {
+        int userlen;
+        int passlen;
+        char *auth_data = NULL;
+        UINT auth_data_len = 0;
+
+        TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
+
+        if (!domain_and_username)
+        {
+            if (host && szRealm)
+                auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
+            if (auth_data_len == 0)
+            {
+                HeapFree(GetProcessHeap(),0,szRealm);
+                return FALSE;
+            }
+        }
+        else
+        {
+            userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
+            passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
+
+            /* length includes a nul terminator, which will be re-used for the ':' */
+            auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
+            if (!auth_data)
+            {
+                HeapFree(GetProcessHeap(),0,szRealm);
+                return FALSE;
+            }
+
+            WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
+            auth_data[userlen] = ':';
+            WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
+            auth_data_len = userlen + 1 + passlen;
+            if (host && szRealm)
+                cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
+        }
+
+        pAuthInfo->auth_data = auth_data;
+        pAuthInfo->auth_data_len = auth_data_len;
+        pAuthInfo->finished = TRUE;
+        HeapFree(GetProcessHeap(),0,szRealm);
 
-    if (lpBuffersOutW)
+        return TRUE;
+    }
+    else
     {
-        ptrW = lpBuffersOutW;
-        while (ptrW)
+        LPCWSTR pszAuthData;
+        SecBufferDesc out_desc, in_desc;
+        SecBuffer out, in;
+        unsigned char *buffer;
+        ULONG context_req = ISC_REQ_CONNECTION | ISC_REQ_USE_DCE_STYLE |
+            ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
+
+        in.BufferType = SECBUFFER_TOKEN;
+        in.cbBuffer = 0;
+        in.pvBuffer = NULL;
+
+        in_desc.ulVersion = 0;
+        in_desc.cBuffers = 1;
+        in_desc.pBuffers = &in;
+
+        pszAuthData = pszAuthValue + strlenW(pAuthInfo->scheme);
+        if (*pszAuthData == ' ')
         {
-            LPINTERNET_BUFFERSW ptrW2;
+            pszAuthData++;
+            in.cbBuffer = HTTP_DecodeBase64(pszAuthData, NULL);
+            in.pvBuffer = HeapAlloc(GetProcessHeap(), 0, in.cbBuffer);
+            HTTP_DecodeBase64(pszAuthData, in.pvBuffer);
+        }
+
+        buffer = HeapAlloc(GetProcessHeap(), 0, pAuthInfo->max_token);
 
-            FIXME("Do we need to translate info out of these buffer?\n");
+        out.BufferType = SECBUFFER_TOKEN;
+        out.cbBuffer = pAuthInfo->max_token;
+        out.pvBuffer = buffer;
 
-            HeapFree(GetProcessHeap(),0,(LPVOID)ptrW->lpvBuffer);
-            ptrW2 = ptrW->Next;
-            HeapFree(GetProcessHeap(),0,ptrW);
-            ptrW = ptrW2;
+        out_desc.ulVersion = 0;
+        out_desc.cBuffers = 1;
+        out_desc.pBuffers = &out;
+
+        sec_status = InitializeSecurityContextW(first ? &pAuthInfo->cred : NULL,
+                                                first ? NULL : &pAuthInfo->ctx,
+                                                first ? lpwhr->lpHttpSession->lpszServerName : NULL,
+                                                context_req, 0, SECURITY_NETWORK_DREP,
+                                                in.pvBuffer ? &in_desc : NULL,
+                                                0, &pAuthInfo->ctx, &out_desc,
+                                                &pAuthInfo->attr, &pAuthInfo->exp);
+        if (sec_status == SEC_E_OK)
+        {
+            pAuthInfo->finished = TRUE;
+            pAuthInfo->auth_data = out.pvBuffer;
+            pAuthInfo->auth_data_len = out.cbBuffer;
+            TRACE("sending last auth packet\n");
+        }
+        else if (sec_status == SEC_I_CONTINUE_NEEDED)
+        {
+            pAuthInfo->auth_data = out.pvBuffer;
+            pAuthInfo->auth_data_len = out.cbBuffer;
+            TRACE("sending next auth packet\n");
+        }
+        else
+        {
+            ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
+            HeapFree(GetProcessHeap(), 0, out.pvBuffer);
+            destroy_authinfo(pAuthInfo);
+            *ppAuthInfo = NULL;
+            return FALSE;
         }
     }
 
-    return rc;
+    return TRUE;
 }
 
 /***********************************************************************
- *           HttpEndRequestW (WININET.@)
- *
- * Ends an HTTP request that was started by HttpSendRequestEx
- *
- * RETURNS
- *    TRUE     if successful
- *    FALSE    on failure
- *
+ *           HTTP_HttpAddRequestHeadersW (internal)
  */
-BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
-        LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD dwContext)
+static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
+       LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
 {
-    BOOL rc = FALSE;
-    LPWININETHTTPREQW lpwhr;
-    INT responseLen;
-
-    TRACE("-->\n");
-    lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hRequest );
+    LPWSTR lpszStart;
+    LPWSTR lpszEnd;
+    LPWSTR buffer;
+    DWORD len, res = ERROR_HTTP_INVALID_HEADER;
 
-    if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
-    {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-       return FALSE;
-    }
+    TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
 
-    lpwhr->hdr.dwFlags |= dwFlags;
-    lpwhr->hdr.dwContext = dwContext;
+    if( dwHeaderLength == ~0U )
+        len = strlenW(lpszHeader);
+    else
+        len = dwHeaderLength;
+    buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR)*(len+1) );
+    lstrcpynW( buffer, lpszHeader, len + 1);
 
-    SendAsyncCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-            INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
+    lpszStart = buffer;
 
-    responseLen = HTTP_GetResponseHeaders(lpwhr);
-    if (responseLen)
-           rc = TRUE;
+    do
+    {
+        LPWSTR * pFieldAndValue;
 
-    SendAsyncCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-            INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
+        lpszEnd = lpszStart;
 
-    /* process headers here. Is this right? */
-    HTTP_ProcessHeaders(lpwhr);
+        while (*lpszEnd != '\0')
+        {
+            if (*lpszEnd == '\r' || *lpszEnd == '\n')
+                 break;
+            lpszEnd++;
+        }
 
-    /* We appear to do nothing with the buffer.. is that correct? */
+        if (*lpszStart == '\0')
+           break;
 
-    if(!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
-    {
-        DWORD dwCode,dwCodeLength=sizeof(DWORD),dwIndex=0;
-        if(HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,&dwCode,&dwCodeLength,&dwIndex) &&
-            (dwCode==302 || dwCode==301))
+        if (*lpszEnd == '\r' || *lpszEnd == '\n')
         {
-            WCHAR szNewLocation[2048];
-            DWORD dwBufferSize=2048;
-            dwIndex=0;
-            if(HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,&dwIndex))
-            {
-                   static const WCHAR szGET[] = { 'G','E','T', 0 };
-                /* redirects are always GETs */
-                HeapFree(GetProcessHeap(),0,lpwhr->lpszVerb);
-                   lpwhr->lpszVerb = WININET_strdupW(szGET);
-                return HTTP_HandleRedirect(lpwhr, szNewLocation, NULL, 0, NULL, 0, 0);
-            }
+            *lpszEnd = '\0';
+            lpszEnd++; /* Jump over newline */
+        }
+        TRACE("interpreting header %s\n", debugstr_w(lpszStart));
+        if (*lpszStart == '\0')
+        {
+            /* Skip 0-length headers */
+            lpszStart = lpszEnd;
+            res = ERROR_SUCCESS;
+            continue;
+        }
+        pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
+        if (pFieldAndValue)
+        {
+            res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
+            if (res == ERROR_SUCCESS)
+                res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
+                    pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
+            HTTP_FreeTokens(pFieldAndValue);
         }
-    }
 
-    TRACE("%i <--\n",rc);
-    return rc;
+        lpszStart = lpszEnd;
+    } while (res == ERROR_SUCCESS);
+
+    HeapFree(GetProcessHeap(), 0, buffer);
+
+    return res;
 }
 
 /***********************************************************************
- *           HttpOpenRequestW (WININET.@)
+ *           HttpAddRequestHeadersW (WININET.@)
  *
- * Open a HTTP request handle
+ * Adds one or more HTTP header to the request handler
+ *
+ * NOTE
+ * On Windows if dwHeaderLength includes the trailing '\0', then
+ * HttpAddRequestHeadersW() adds it too. However this results in an
+ * invalid Http header which is rejected by some servers so we probably
+ * don't need to match Windows on that point.
  *
  * RETURNS
- *    HINTERNET  a HTTP request handle on success
- *    NULL      on failure
+ *    TRUE  on success
+ *    FALSE on failure
  *
  */
-HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
-       LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
-       LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
-       DWORD dwFlags, DWORD dwContext)
+BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
+       LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
 {
-    LPWININETHTTPSESSIONW lpwhs;
-    HINTERNET handle = NULL;
+    http_request_t *lpwhr;
+    DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
 
-    TRACE("(%p, %s, %s, %s, %s, %p, %08lx, %08lx)\n", hHttpSession,
-          debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
-          debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
-          dwFlags, dwContext);
-    if(lpszAcceptTypes!=NULL)
-    {
-        int i;
-        for(i=0;lpszAcceptTypes[i]!=NULL;i++)
-            TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
-    }
+    TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
 
-    lpwhs = (LPWININETHTTPSESSIONW) WININET_GetObject( hHttpSession );
-    if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
-    {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-       goto lend;
-    }
+    if (!lpszHeader) 
+      return TRUE;
 
-    /*
-     * My tests seem to show that the windows version does not
-     * become asynchronous until after this point. And anyhow
-     * if this call was asynchronous then how would you get the
-     * necessary HINTERNET pointer returned by this function.
-     *
-     */
-    handle = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
-                                   lpszVersion, lpszReferrer, lpszAcceptTypes,
-                                   dwFlags, dwContext);
-lend:
-    if( lpwhs )
-        WININET_Release( &lpwhs->hdr );
-    TRACE("returning %p\n", handle);
-    return handle;
+    lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
+    if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
+        res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
+    if( lpwhr )
+        WININET_Release( &lpwhr->hdr );
+
+    if(res != ERROR_SUCCESS)
+        SetLastError(res);
+    return res == ERROR_SUCCESS;
 }
 
+/***********************************************************************
+ *           HttpAddRequestHeadersA (WININET.@)
+ *
+ * Adds one or more HTTP header to the request handler
+ *
+ * RETURNS
+ *    TRUE  on success
+ *    FALSE on failure
+ *
+ */
+BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
+       LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
+{
+    DWORD len;
+    LPWSTR hdr;
+    BOOL r;
+
+    TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_an(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
+
+    len = MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, NULL, 0 );
+    hdr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+    MultiByteToWideChar( CP_ACP, 0, lpszHeader, dwHeaderLength, hdr, len );
+    if( dwHeaderLength != ~0U )
+        dwHeaderLength = len;
+
+    r = HttpAddRequestHeadersW( hHttpRequest, hdr, dwHeaderLength, dwModifier );
+
+    HeapFree( GetProcessHeap(), 0, hdr );
+
+    return r;
+}
 
 /***********************************************************************
  *           HttpOpenRequestA (WININET.@)
@@ -714,76 +1017,90 @@ lend:
 HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
        LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
        LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
-       DWORD dwFlags, DWORD dwContext)
+       DWORD dwFlags, DWORD_PTR dwContext)
 {
     LPWSTR szVerb = NULL, szObjectName = NULL;
     LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
-    INT len;
     INT acceptTypesCount;
     HINTERNET rc = FALSE;
-    TRACE("(%p, %s, %s, %s, %s, %p, %08lx, %08lx)\n", hHttpSession,
+    LPCSTR *types;
+
+    TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
           debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
           debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
           dwFlags, dwContext);
 
     if (lpszVerb)
     {
-        len = MultiByteToWideChar(CP_ACP, 0, lpszVerb, -1, NULL, 0 );
-        szVerb = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR) );
+        szVerb = heap_strdupAtoW(lpszVerb);
         if ( !szVerb )
             goto end;
-        MultiByteToWideChar(CP_ACP, 0, lpszVerb, -1, szVerb, len);
     }
 
     if (lpszObjectName)
     {
-        len = MultiByteToWideChar(CP_ACP, 0, lpszObjectName, -1, NULL, 0 );
-        szObjectName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR) );
+        szObjectName = heap_strdupAtoW(lpszObjectName);
         if ( !szObjectName )
             goto end;
-        MultiByteToWideChar(CP_ACP, 0, lpszObjectName, -1, szObjectName, len );
     }
 
     if (lpszVersion)
     {
-        len = MultiByteToWideChar(CP_ACP, 0, lpszVersion, -1, NULL, 0 );
-        szVersion = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        szVersion = heap_strdupAtoW(lpszVersion);
         if ( !szVersion )
             goto end;
-        MultiByteToWideChar(CP_ACP, 0, lpszVersion, -1, szVersion, len );
     }
 
     if (lpszReferrer)
     {
-        len = MultiByteToWideChar(CP_ACP, 0, lpszReferrer, -1, NULL, 0 );
-        szReferrer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        szReferrer = heap_strdupAtoW(lpszReferrer);
         if ( !szReferrer )
             goto end;
-        MultiByteToWideChar(CP_ACP, 0, lpszReferrer, -1, szReferrer, len );
     }
 
-    acceptTypesCount = 0;
     if (lpszAcceptTypes)
     {
-        /* find out how many there are */
-        while (lpszAcceptTypes[acceptTypesCount])
-            acceptTypesCount++;
+        acceptTypesCount = 0;
+        types = lpszAcceptTypes;
+        while (*types)
+        {
+            __TRY
+            {
+                /* find out how many there are */
+                if (*types && **types)
+                {
+                    TRACE("accept type: %s\n", debugstr_a(*types));
+                    acceptTypesCount++;
+                }
+            }
+            __EXCEPT_PAGE_FAULT
+            {
+                WARN("invalid accept type pointer\n");
+            }
+            __ENDTRY;
+            types++;
+        }
         szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
+        if (!szAcceptTypes) goto end;
+
         acceptTypesCount = 0;
-        while (lpszAcceptTypes[acceptTypesCount])
-        {
-            len = MultiByteToWideChar(CP_ACP, 0, lpszAcceptTypes[acceptTypesCount],
-                                -1, NULL, 0 );
-            szAcceptTypes[acceptTypesCount] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
-            if (!szAcceptTypes[acceptTypesCount] )
-                goto end;
-            MultiByteToWideChar(CP_ACP, 0, lpszAcceptTypes[acceptTypesCount],
-                                -1, szAcceptTypes[acceptTypesCount], len );
-            acceptTypesCount++;
+        types = lpszAcceptTypes;
+        while (*types)
+        {
+            __TRY
+            {
+                if (*types && **types)
+                    szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
+            }
+            __EXCEPT_PAGE_FAULT
+            {
+                /* ignore invalid pointer */
+            }
+            __ENDTRY;
+            types++;
         }
         szAcceptTypes[acceptTypesCount] = NULL;
     }
-    else szAcceptTypes = 0;
 
     rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
                           szVersion, szReferrer,
@@ -809,22 +1126,22 @@ end:
 }
 
 /***********************************************************************
- *  HTTP_Base64
+ *  HTTP_EncodeBase64
  */
-static UINT HTTP_Base64( LPCWSTR bin, LPWSTR base64 )
+static UINT HTTP_EncodeBase64( LPCSTR bin, unsigned int len, LPWSTR base64 )
 {
     UINT n = 0, x;
-    static LPCSTR HTTP_Base64Enc =
+    static const CHAR HTTP_Base64Enc[] =
         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-    while( bin[0] )
+    while( len > 0 )
     {
         /* first 6 bits, all from bin[0] */
         base64[n++] = HTTP_Base64Enc[(bin[0] & 0xfc) >> 2];
         x = (bin[0] & 3) << 4;
 
         /* next 6 bits, 2 from bin[0] and 4 from bin[1] */
-        if( !bin[1] )
+        if( len == 1 )
         {
             base64[n++] = HTTP_Base64Enc[x];
             base64[n++] = '=';
@@ -835,7 +1152,7 @@ static UINT HTTP_Base64( LPCWSTR bin, LPWSTR base64 )
         x = ( bin[1] & 0x0f ) << 2;
 
         /* next 6 bits 4 from bin[1] and 2 from bin[2] */
-        if( !bin[2] )
+        if( len == 2 )
         {
             base64[n++] = HTTP_Base64Enc[x];
             base64[n++] = '=';
@@ -846,1395 +1163,2859 @@ static UINT HTTP_Base64( LPCWSTR bin, LPWSTR base64 )
         /* last 6 bits, all from bin [2] */
         base64[n++] = HTTP_Base64Enc[ bin[2] & 0x3f ];
         bin += 3;
+        len -= 3;
     }
     base64[n] = 0;
     return n;
 }
 
+#define CH(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' : \
+               ((x) >= 'a' && (x) <= 'z') ? (x) - 'a' + 26 : \
+               ((x) >= '0' && (x) <= '9') ? (x) - '0' + 52 : \
+               ((x) == '+') ? 62 : ((x) == '/') ? 63 : -1)
+static const signed char HTTP_Base64Dec[256] =
+{
+    CH( 0),CH( 1),CH( 2),CH( 3),CH( 4),CH( 5),CH( 6),CH( 7),CH( 8),CH( 9),
+    CH(10),CH(11),CH(12),CH(13),CH(14),CH(15),CH(16),CH(17),CH(18),CH(19),
+    CH(20),CH(21),CH(22),CH(23),CH(24),CH(25),CH(26),CH(27),CH(28),CH(29),
+    CH(30),CH(31),CH(32),CH(33),CH(34),CH(35),CH(36),CH(37),CH(38),CH(39),
+    CH(40),CH(41),CH(42),CH(43),CH(44),CH(45),CH(46),CH(47),CH(48),CH(49),
+    CH(50),CH(51),CH(52),CH(53),CH(54),CH(55),CH(56),CH(57),CH(58),CH(59),
+    CH(60),CH(61),CH(62),CH(63),CH(64),CH(65),CH(66),CH(67),CH(68),CH(69),
+    CH(70),CH(71),CH(72),CH(73),CH(74),CH(75),CH(76),CH(77),CH(78),CH(79),
+    CH(80),CH(81),CH(82),CH(83),CH(84),CH(85),CH(86),CH(87),CH(88),CH(89),
+    CH(90),CH(91),CH(92),CH(93),CH(94),CH(95),CH(96),CH(97),CH(98),CH(99),
+    CH(100),CH(101),CH(102),CH(103),CH(104),CH(105),CH(106),CH(107),CH(108),CH(109),
+    CH(110),CH(111),CH(112),CH(113),CH(114),CH(115),CH(116),CH(117),CH(118),CH(119),
+    CH(120),CH(121),CH(122),CH(123),CH(124),CH(125),CH(126),CH(127),CH(128),CH(129),
+    CH(130),CH(131),CH(132),CH(133),CH(134),CH(135),CH(136),CH(137),CH(138),CH(139),
+    CH(140),CH(141),CH(142),CH(143),CH(144),CH(145),CH(146),CH(147),CH(148),CH(149),
+    CH(150),CH(151),CH(152),CH(153),CH(154),CH(155),CH(156),CH(157),CH(158),CH(159),
+    CH(160),CH(161),CH(162),CH(163),CH(164),CH(165),CH(166),CH(167),CH(168),CH(169),
+    CH(170),CH(171),CH(172),CH(173),CH(174),CH(175),CH(176),CH(177),CH(178),CH(179),
+    CH(180),CH(181),CH(182),CH(183),CH(184),CH(185),CH(186),CH(187),CH(188),CH(189),
+    CH(190),CH(191),CH(192),CH(193),CH(194),CH(195),CH(196),CH(197),CH(198),CH(199),
+    CH(200),CH(201),CH(202),CH(203),CH(204),CH(205),CH(206),CH(207),CH(208),CH(209),
+    CH(210),CH(211),CH(212),CH(213),CH(214),CH(215),CH(216),CH(217),CH(218),CH(219),
+    CH(220),CH(221),CH(222),CH(223),CH(224),CH(225),CH(226),CH(227),CH(228),CH(229),
+    CH(230),CH(231),CH(232),CH(233),CH(234),CH(235),CH(236),CH(237),CH(238),CH(239),
+    CH(240),CH(241),CH(242),CH(243),CH(244),CH(245),CH(246),CH(247),CH(248), CH(249),
+    CH(250),CH(251),CH(252),CH(253),CH(254),CH(255),
+};
+#undef CH
+
 /***********************************************************************
- *  HTTP_EncodeBasicAuth
- *
- *  Encode the basic authentication string for HTTP 1.1
+ *  HTTP_DecodeBase64
  */
-static LPWSTR HTTP_EncodeBasicAuth( LPCWSTR username, LPCWSTR password)
+static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
 {
-    UINT len;
-    LPWSTR in, out;
-    static const WCHAR szBasic[] = {'B','a','s','i','c',' ',0};
-    static const WCHAR szColon[] = {':',0};
-
-    len = lstrlenW( username ) + 1 + lstrlenW ( password ) + 1;
-    in = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
-    if( !in )
-        return NULL;
+    unsigned int n = 0;
 
-    len = lstrlenW(szBasic) +
-          (lstrlenW( username ) + 1 + lstrlenW ( password ))*2 + 1 + 1;
-    out = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
-    if( out )
+    while(*base64)
     {
-        lstrcpyW( in, username );
-        lstrcatW( in, szColon );
-        lstrcatW( in, password );
-        lstrcpyW( out, szBasic );
-        HTTP_Base64( in, &out[strlenW(out)] );
+        signed char in[4];
+
+        if (base64[0] >= ARRAYSIZE(HTTP_Base64Dec) ||
+            ((in[0] = HTTP_Base64Dec[base64[0]]) == -1) ||
+            base64[1] >= ARRAYSIZE(HTTP_Base64Dec) ||
+            ((in[1] = HTTP_Base64Dec[base64[1]]) == -1))
+        {
+            WARN("invalid base64: %s\n", debugstr_w(base64));
+            return 0;
+        }
+        if (bin)
+            bin[n] = (unsigned char) (in[0] << 2 | in[1] >> 4);
+        n++;
+
+        if ((base64[2] == '=') && (base64[3] == '='))
+            break;
+        if (base64[2] > ARRAYSIZE(HTTP_Base64Dec) ||
+            ((in[2] = HTTP_Base64Dec[base64[2]]) == -1))
+        {
+            WARN("invalid base64: %s\n", debugstr_w(&base64[2]));
+            return 0;
+        }
+        if (bin)
+            bin[n] = (unsigned char) (in[1] << 4 | in[2] >> 2);
+        n++;
+
+        if (base64[3] == '=')
+            break;
+        if (base64[3] > ARRAYSIZE(HTTP_Base64Dec) ||
+            ((in[3] = HTTP_Base64Dec[base64[3]]) == -1))
+        {
+            WARN("invalid base64: %s\n", debugstr_w(&base64[3]));
+            return 0;
+        }
+        if (bin)
+            bin[n] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]);
+        n++;
+
+        base64 += 4;
     }
-    HeapFree( GetProcessHeap(), 0, in );
 
-    return out;
+    return n;
 }
 
 /***********************************************************************
- *  HTTP_InsertProxyAuthorization
+ *  HTTP_InsertAuthorization
  *
- *   Insert the basic authorization field in the request header
+ *   Insert or delete the authorization field in the request header.
  */
-static BOOL HTTP_InsertProxyAuthorization( LPWININETHTTPREQW lpwhr,
-                       LPCWSTR username, LPCWSTR password )
+static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
 {
-    WCHAR *authorization = HTTP_EncodeBasicAuth( username, password );
-    BOOL ret = TRUE;
+    if (pAuthInfo)
+    {
+        static const WCHAR wszSpace[] = {' ',0};
+        static const WCHAR wszBasic[] = {'B','a','s','i','c',0};
+        unsigned int len;
+        WCHAR *authorization = NULL;
 
-    if (!authorization)
-        return FALSE;
+        if (pAuthInfo->auth_data_len)
+        {
+            /* scheme + space + base64 encoded data (3/2/1 bytes data -> 4 bytes of characters) */
+            len = strlenW(pAuthInfo->scheme)+1+((pAuthInfo->auth_data_len+2)*4)/3;
+            authorization = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
+            if (!authorization)
+                return FALSE;
 
-    TRACE( "Inserting authorization: %s\n", debugstr_w( authorization ) );
+            strcpyW(authorization, pAuthInfo->scheme);
+            strcatW(authorization, wszSpace);
+            HTTP_EncodeBase64(pAuthInfo->auth_data,
+                              pAuthInfo->auth_data_len,
+                              authorization+strlenW(authorization));
 
-    HTTP_ProcessHeader(lpwhr, szProxy_Authorization, authorization,
-            HTTP_ADDHDR_FLAG_REPLACE);
+            /* clear the data as it isn't valid now that it has been sent to the
+             * server, unless it's Basic authentication which doesn't do
+             * connection tracking */
+            if (strcmpiW(pAuthInfo->scheme, wszBasic))
+            {
+                HeapFree(GetProcessHeap(), 0, pAuthInfo->auth_data);
+                pAuthInfo->auth_data = NULL;
+                pAuthInfo->auth_data_len = 0;
+            }
+        }
 
-    HeapFree( GetProcessHeap(), 0, authorization );
+        TRACE("Inserting authorization: %s\n", debugstr_w(authorization));
 
-    return ret;
+        HTTP_ProcessHeader(lpwhr, header, authorization, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
+
+        HeapFree(GetProcessHeap(), 0, authorization);
+    }
+    return TRUE;
+}
+
+static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
+{
+    WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
+    DWORD size;
+
+    size = sizeof(new_location);
+    if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
+    {
+        if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
+        strcpyW( url, new_location );
+    }
+    else
+    {
+        static const WCHAR slash[] = { '/',0 };
+        static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
+        static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
+        http_session_t *session = req->lpHttpSession;
+
+        size = 16; /* "https://" + sizeof(port#) + ":/\0" */
+        size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
+
+        if (!(url = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return NULL;
+
+        if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
+            sprintfW( url, formatSSL, session->lpszHostName, session->nHostPort );
+        else
+            sprintfW( url, format, session->lpszHostName, session->nHostPort );
+        if (req->lpszPath[0] != '/') strcatW( url, slash );
+        strcatW( url, req->lpszPath );
+    }
+    TRACE("url=%s\n", debugstr_w(url));
+    return url;
 }
 
 /***********************************************************************
  *           HTTP_DealWithProxy
  */
-static BOOL HTTP_DealWithProxy( LPWININETAPPINFOW hIC,
-    LPWININETHTTPSESSIONW lpwhs, LPWININETHTTPREQW lpwhr)
+static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
 {
     WCHAR buf[MAXHOSTNAME];
+    WCHAR protoProxy[MAXHOSTNAME + 15];
+    DWORD protoProxyLen = sizeof(protoProxy) / sizeof(protoProxy[0]);
     WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
-    WCHAR* url;
-    static const WCHAR szNul[] = { 0 };
+    static WCHAR szNul[] = { 0 };
     URL_COMPONENTSW UrlComponents;
-    static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 }, szSlash[] = { '/',0 } ;
-    static const WCHAR szFormat1[] = { 'h','t','t','p',':','/','/','%','s',0 };
-    static const WCHAR szFormat2[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
-    int len;
+    static const WCHAR protoHttp[] = { 'h','t','t','p',0 };
+    static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/',0 };
+    static const WCHAR szFormat[] = { 'h','t','t','p',':','/','/','%','s',0 };
 
     memset( &UrlComponents, 0, sizeof UrlComponents );
     UrlComponents.dwStructSize = sizeof UrlComponents;
     UrlComponents.lpszHostName = buf;
     UrlComponents.dwHostNameLength = MAXHOSTNAME;
 
+    if (!INTERNET_FindProxyForProtocol(hIC->lpszProxy, protoHttp, protoProxy, &protoProxyLen))
+        return FALSE;
     if( CSTR_EQUAL != CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE,
-                                 hIC->lpszProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
-        sprintfW(proxy, szFormat1, hIC->lpszProxy);
+                                 protoProxy,strlenW(szHttp),szHttp,strlenW(szHttp)) )
+        sprintfW(proxy, szFormat, protoProxy);
     else
-       strcpyW(proxy, hIC->lpszProxy);
+       strcpyW(proxy, protoProxy);
     if( !InternetCrackUrlW(proxy, 0, 0, &UrlComponents) )
         return FALSE;
     if( UrlComponents.dwHostNameLength == 0 )
         return FALSE;
 
     if( !lpwhr->lpszPath )
-        lpwhr->lpszPath = (LPWSTR)szNul;
-    TRACE("server='%s' path='%s'\n",
-          debugstr_w(lpwhs->lpszHostName), debugstr_w(lpwhr->lpszPath));
-    /* for constant 15 see above */
-    len = strlenW(lpwhs->lpszHostName) + strlenW(lpwhr->lpszPath) + 15;
-    url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
+        lpwhr->lpszPath = szNul;
 
     if(UrlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
         UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
 
-    sprintfW(url, szFormat2, lpwhs->lpszHostName, lpwhs->nHostPort);
-
-    if( lpwhr->lpszPath[0] != '/' )
-        strcatW( url, szSlash );
-    strcatW(url, lpwhr->lpszPath);
-    if(lpwhr->lpszPath != szNul)
-        HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
-    lpwhr->lpszPath = url;
-
     HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
-    lpwhs->lpszServerName = WININET_strdupW(UrlComponents.lpszHostName);
+    lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
     lpwhs->nServerPort = UrlComponents.nPort;
 
+    TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
     return TRUE;
 }
 
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
+static DWORD HTTP_ResolveName(http_request_t *lpwhr)
+{
+    char szaddr[INET6_ADDRSTRLEN];
+    http_session_t *lpwhs = lpwhr->lpHttpSession;
+    const void *addr;
+
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                          INTERNET_STATUS_RESOLVING_NAME,
+                          lpwhs->lpszServerName,
+                          (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
+
+    lpwhs->sa_len = sizeof(lpwhs->socketAddress);
+    if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
+                    (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
+        return ERROR_INTERNET_NAME_NOT_RESOLVED;
+
+    switch (lpwhs->socketAddress.ss_family)
+    {
+    case AF_INET:
+        addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
+        break;
+    case AF_INET6:
+        addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
+        break;
+    default:
+        WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
+        return ERROR_INTERNET_NAME_NOT_RESOLVED;
+    }
+    inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                          INTERNET_STATUS_NAME_RESOLVED,
+                          szaddr, strlen(szaddr)+1);
+
+    TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
+    return ERROR_SUCCESS;
+}
+
+
 /***********************************************************************
- *           HTTP_HttpOpenRequestW (internal)
- *
- * Open a HTTP request handle
+ *           HTTPREQ_Destroy (internal)
  *
- * RETURNS
- *    HINTERNET  a HTTP request handle on success
- *    NULL      on failure
+ * Deallocate request handle
  *
  */
-HINTERNET WINAPI HTTP_HttpOpenRequestW(LPWININETHTTPSESSIONW lpwhs,
-       LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
-       LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
-       DWORD dwFlags, DWORD dwContext)
+static void HTTPREQ_Destroy(object_header_t *hdr)
 {
-    LPWININETAPPINFOW hIC = NULL;
-    LPWININETHTTPREQW lpwhr;
-    LPWSTR lpszCookies;
-    LPWSTR lpszUrl = NULL;
-    DWORD nCookieSize;
-    HINTERNET handle = NULL;
-    static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s',0};
-    DWORD len;
-    LPHTTPHEADERW Host;
-    char szaddr[32];
+    http_request_t *lpwhr = (http_request_t*) hdr;
+    DWORD i;
 
-    TRACE("-->\n");
+    TRACE("\n");
 
-    assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
-    hIC = (LPWININETAPPINFOW) lpwhs->hdr.lpwhparent;
+    if(lpwhr->hCacheFile)
+        CloseHandle(lpwhr->hCacheFile);
 
-    lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETHTTPREQW));
-    if (NULL == lpwhr)
-    {
-        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
-        goto lend;
-    }
-    lpwhr->hdr.htype = WH_HHTTPREQ;
-    lpwhr->hdr.lpwhparent = WININET_AddRef( &lpwhs->hdr );
-    lpwhr->hdr.dwFlags = dwFlags;
-    lpwhr->hdr.dwContext = dwContext;
-    lpwhr->hdr.dwRefCount = 1;
-    lpwhr->hdr.destroy = HTTP_CloseHTTPRequestHandle;
-    lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
+    HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
 
-    handle = WININET_AllocHandle( &lpwhr->hdr );
-    if (NULL == handle)
+    DeleteCriticalSection( &lpwhr->read_section );
+    WININET_Release(&lpwhr->lpHttpSession->hdr);
+
+    destroy_authinfo(lpwhr->pAuthInfo);
+    destroy_authinfo(lpwhr->pProxyAuthInfo);
+
+    HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
+    HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
+    HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
+    HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
+    HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
+
+    for (i = 0; i < lpwhr->nCustHeaders; i++)
     {
-        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
-        goto lend;
+        HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
+        HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
     }
 
-    if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
-    {
-        InternetCloseHandle( handle );
-        handle = NULL;
-        goto lend;
+#ifdef HAVE_ZLIB
+    if(lpwhr->gzip_stream) {
+        if(!lpwhr->gzip_stream->end_of_data)
+            inflateEnd(&lpwhr->gzip_stream->zstream);
+        HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
     }
+#endif
 
-    if (NULL != lpszObjectName && strlenW(lpszObjectName)) {
-        HRESULT rc;
+    HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
+    HeapFree(GetProcessHeap(), 0, lpwhr);
+}
 
-        len = 0;
-        rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
-        if (rc != E_POINTER)
-            len = strlenW(lpszObjectName)+1;
-        lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
-        rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
-                   URL_ESCAPE_SPACES_ONLY);
-        if (rc)
-        {
-            ERR("Unable to escape string!(%s) (%ld)\n",debugstr_w(lpszObjectName),rc);
-            strcpyW(lpwhr->lpszPath,lpszObjectName);
-        }
-    }
+static void HTTPREQ_CloseConnection(object_header_t *hdr)
+{
+    http_request_t *lpwhr = (http_request_t*) hdr;
 
-    if (NULL != lpszReferrer && strlenW(lpszReferrer))
-        HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDHDR_FLAG_COALESCE);
+    TRACE("%p\n",lpwhr);
 
-    if(lpszAcceptTypes!=NULL)
-    {
-        int i;
-        for(i=0;lpszAcceptTypes[i]!=NULL;i++)
-            HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i], HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_REQ|HTTP_ADDHDR_FLAG_ADD_IF_NEW);
-    }
+    if (!NETCON_connected(&lpwhr->netConnection))
+        return;
 
-    if (NULL == lpszVerb)
-    {
-        static const WCHAR szGet[] = {'G','E','T',0};
-        lpwhr->lpszVerb = WININET_strdupW(szGet);
-    }
-    else if (strlenW(lpszVerb))
-        lpwhr->lpszVerb = WININET_strdupW(lpszVerb);
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                          INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
 
-    if (NULL != lpszReferrer && strlenW(lpszReferrer))
-    {
-        WCHAR buf[MAXHOSTNAME];
-        URL_COMPONENTSW UrlComponents;
+    NETCON_close(&lpwhr->netConnection);
 
-        memset( &UrlComponents, 0, sizeof UrlComponents );
-        UrlComponents.dwStructSize = sizeof UrlComponents;
-        UrlComponents.lpszHostName = buf;
-        UrlComponents.dwHostNameLength = MAXHOSTNAME;
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                          INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
+}
 
-        InternetCrackUrlW(lpszReferrer, 0, 0, &UrlComponents);
-        if (strlenW(UrlComponents.lpszHostName))
-            HTTP_ProcessHeader(lpwhr, szHost, UrlComponents.lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
-    }
-    else
-        HTTP_ProcessHeader(lpwhr, szHost, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
+static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
+{
+    LPHTTPHEADERW host_header;
 
-    if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
-        lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
-                        INTERNET_DEFAULT_HTTPS_PORT :
-                        INTERNET_DEFAULT_HTTP_PORT);
-    lpwhs->nHostPort = lpwhs->nServerPort;
+    static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
 
-    if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
-        HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
+    host_header = HTTP_GetHeader(req, hostW);
+    if(!host_header)
+        return FALSE;
 
-    if (hIC->lpszAgent)
-    {
-        WCHAR *agent_header;
-        static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0 };
+    sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
+    return TRUE;
+}
 
-        len = strlenW(hIC->lpszAgent) + strlenW(user_agent);
-        agent_header = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
-        sprintfW(agent_header, user_agent, hIC->lpszAgent );
+static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
+{
+    WCHAR szVersion[10];
+    WCHAR szConnectionResponse[20];
+    DWORD dwBufferSize = sizeof(szVersion);
+    BOOL keepalive = FALSE;
+
+    /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
+     * the connection is keep-alive by default */
+    if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
+        && !strcmpiW(szVersion, g_szHttp1_1))
+    {
+        keepalive = TRUE;
+    }
 
-        HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header),
-                               HTTP_ADDREQ_FLAG_ADD);
-        HeapFree(GetProcessHeap(), 0, agent_header);
+    dwBufferSize = sizeof(szConnectionResponse);
+    if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
+        || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
+    {
+        keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
     }
 
-    Host = HTTP_GetHeader(lpwhr,szHost);
+    return keepalive;
+}
 
-    len = lstrlenW(Host->lpszValue) + strlenW(szUrlForm);
-    lpszUrl = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
-    sprintfW( lpszUrl, szUrlForm, Host->lpszValue );
+static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
+{
+    http_request_t *req = (http_request_t*)hdr;
 
-    if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) &&
-        InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
+    switch(option) {
+    case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
     {
-        int cnt = 0;
-        static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
-        static const WCHAR szcrlf[] = {'\r','\n',0};
+        http_session_t *lpwhs = req->lpHttpSession;
+        INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
 
-        lpszCookies = HeapAlloc(GetProcessHeap(), 0, (nCookieSize + 1 + 8)*sizeof(WCHAR));
+        FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
 
-        cnt += sprintfW(lpszCookies, szCookie);
-        InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
-        strcatW(lpszCookies, szcrlf);
-
-        HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies),
-                               HTTP_ADDREQ_FLAG_ADD);
-        HeapFree(GetProcessHeap(), 0, lpszCookies);
+        if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
+            return ERROR_INSUFFICIENT_BUFFER;
+        *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
+        /* FIXME: can't get a SOCKET from our connection since we don't use
+         * winsock
+         */
+        info->Socket = 0;
+        /* FIXME: get source port from req->netConnection */
+        info->SourcePort = 0;
+        info->DestPort = lpwhs->nHostPort;
+        info->Flags = 0;
+        if (HTTP_KeepAlive(req))
+            info->Flags |= IDSI_FLAG_KEEP_ALIVE;
+        if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
+            info->Flags |= IDSI_FLAG_PROXY;
+        if (req->netConnection.useSSL)
+            info->Flags |= IDSI_FLAG_SECURE;
+
+        return ERROR_SUCCESS;
     }
-    HeapFree(GetProcessHeap(), 0, lpszUrl);
-
 
-    INTERNET_SendCallback(&lpwhs->hdr, dwContext,
-                          INTERNET_STATUS_HANDLE_CREATED, &handle,
-                          sizeof(handle));
-
-    /*
-     * A STATUS_REQUEST_COMPLETE is NOT sent here as per my tests on windows
-     */
+    case INTERNET_OPTION_SECURITY_FLAGS:
+    {
+        http_session_t *lpwhs;
+        lpwhs = req->lpHttpSession;
 
-    /*
-     * According to my tests. The name is not resolved until a request is Opened
-     */
-    INTERNET_SendCallback(&lpwhr->hdr, dwContext,
-                          INTERNET_STATUS_RESOLVING_NAME,
-                          lpwhs->lpszServerName,
-                          strlenW(lpwhs->lpszServerName)+1);
+        if (*size < sizeof(ULONG))
+            return ERROR_INSUFFICIENT_BUFFER;
 
-    if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
-                    &lpwhs->socketAddress))
-    {
-        INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
-        InternetCloseHandle( handle );
-        handle = NULL;
-        goto lend;
+        *size = sizeof(DWORD);
+        if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
+            *(DWORD*)buffer = SECURITY_FLAG_SECURE;
+        else
+            *(DWORD*)buffer = 0;
+        FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
+        return ERROR_SUCCESS;
     }
 
-    inet_ntop(lpwhs->socketAddress.sin_family, &lpwhs->socketAddress.sin_addr,
-              szaddr, sizeof(szaddr));
-    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                          INTERNET_STATUS_NAME_RESOLVED,
-                          szaddr, strlen(szaddr)+1);
+    case INTERNET_OPTION_HANDLE_TYPE:
+        TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
 
-lend:
-    if( lpwhr )
-        WININET_Release( &lpwhr->hdr );
+        if (*size < sizeof(ULONG))
+            return ERROR_INSUFFICIENT_BUFFER;
 
-    TRACE("<-- %p (%p)\n", handle, lpwhr);
-    return handle;
-}
+        *size = sizeof(DWORD);
+        *(DWORD*)buffer = INTERNET_HANDLE_TYPE_HTTP_REQUEST;
+        return ERROR_SUCCESS;
 
-typedef struct std_hdr_data
-{
-       const WCHAR* hdrStr;
-       INT hdrIndex;
-} std_hdr_data;
+    case INTERNET_OPTION_URL: {
+        WCHAR url[INTERNET_MAX_URL_LENGTH];
+        HTTPHEADERW *host;
+        DWORD len;
+        WCHAR *pch;
 
-static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
-static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
-static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
-static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
-static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
-static const WCHAR szAge[] = { 'A','g','e',0 };
-static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
-static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
-static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
-static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
-static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
-static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
-static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
-static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
-static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
-static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
-static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
-static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
-static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
-static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
-static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
-static const WCHAR szDate[] = { 'D','a','t','e',0 };
-static const WCHAR szFrom[] = { 'F','r','o','m',0 };
-static const WCHAR szETag[] = { 'E','T','a','g',0 };
-static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
-static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
-static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
-static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
-static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
-static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
-static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
-static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
-static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
-static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
-static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
-static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
-static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
-static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
-static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
-static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
-static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
-static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
-static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
-static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
-static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
-static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
-static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
-static const WCHAR szURI[] = { 'U','R','I',0 };
-static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
-static const WCHAR szVary[] = { 'V','a','r','y',0 };
-static const WCHAR szVia[] = { 'V','i','a',0 };
-static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
-static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
+        static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
 
-static const std_hdr_data SORTED_STANDARD_HEADERS[] = {
-    {szAccept,                 HTTP_QUERY_ACCEPT,},
-    {szAccept_Charset,         HTTP_QUERY_ACCEPT_CHARSET,},
-    {szAccept_Encoding,                HTTP_QUERY_ACCEPT_ENCODING,},
-    {szAccept_Language,                HTTP_QUERY_ACCEPT_LANGUAGE,},
-    {szAccept_Ranges,          HTTP_QUERY_ACCEPT_RANGES,},
-    {szAge,                    HTTP_QUERY_AGE,},
-    {szAllow,                  HTTP_QUERY_ALLOW,},
-    {szAuthorization,          HTTP_QUERY_AUTHORIZATION,},
-    {szCache_Control,          HTTP_QUERY_CACHE_CONTROL,},
-    {szConnection,             HTTP_QUERY_CONNECTION,},
-    {szContent_Base,           HTTP_QUERY_CONTENT_BASE,},
-    {szContent_Encoding,       HTTP_QUERY_CONTENT_ENCODING,},
-    {szContent_ID,             HTTP_QUERY_CONTENT_ID,},
-    {szContent_Language,       HTTP_QUERY_CONTENT_LANGUAGE,},
-    {szContent_Length,         HTTP_QUERY_CONTENT_LENGTH,},
-    {szContent_Location,       HTTP_QUERY_CONTENT_LOCATION,},
-    {szContent_MD5,            HTTP_QUERY_CONTENT_MD5,},
-    {szContent_Range,          HTTP_QUERY_CONTENT_RANGE,},
-    {szContent_Transfer_Encoding,HTTP_QUERY_CONTENT_TRANSFER_ENCODING,},
-    {szContent_Type,           HTTP_QUERY_CONTENT_TYPE,},
-    {szCookie,                 HTTP_QUERY_COOKIE,},
-    {szDate,                   HTTP_QUERY_DATE,},
-    {szETag,                   HTTP_QUERY_ETAG,},
-    {szExpect,                 HTTP_QUERY_EXPECT,},
-    {szExpires,                        HTTP_QUERY_EXPIRES,},
-    {szFrom,                   HTTP_QUERY_DERIVED_FROM,},
-    {szHost,                   HTTP_QUERY_HOST,},
-    {szIf_Match,               HTTP_QUERY_IF_MATCH,},
-    {szIf_Modified_Since,      HTTP_QUERY_IF_MODIFIED_SINCE,},
-    {szIf_None_Match,          HTTP_QUERY_IF_NONE_MATCH,},
-    {szIf_Range,               HTTP_QUERY_IF_RANGE,},
-    {szIf_Unmodified_Since,    HTTP_QUERY_IF_UNMODIFIED_SINCE,},
-    {szLast_Modified,          HTTP_QUERY_LAST_MODIFIED,},
-    {szLocation,               HTTP_QUERY_LOCATION,},
-    {szMax_Forwards,           HTTP_QUERY_MAX_FORWARDS,},
-    {szMime_Version,           HTTP_QUERY_MIME_VERSION,},
-    {szPragma,                 HTTP_QUERY_PRAGMA,},
-    {szProxy_Authenticate,     HTTP_QUERY_PROXY_AUTHENTICATE,},
-    {szProxy_Authorization,    HTTP_QUERY_PROXY_AUTHORIZATION,},
-    {szProxy_Connection,       HTTP_QUERY_PROXY_CONNECTION,},
-    {szPublic,                 HTTP_QUERY_PUBLIC,},
-    {szRange,                  HTTP_QUERY_RANGE,},
-    {szReferer,                        HTTP_QUERY_REFERER,},
-    {szRetry_After,            HTTP_QUERY_RETRY_AFTER,},
-    {szServer,                 HTTP_QUERY_SERVER,},
-    {szSet_Cookie,             HTTP_QUERY_SET_COOKIE,},
-    {szStatus,                 HTTP_QUERY_STATUS_CODE,},
-    {szTransfer_Encoding,      HTTP_QUERY_TRANSFER_ENCODING,},
-    {szUnless_Modified_Since,  HTTP_QUERY_UNLESS_MODIFIED_SINCE,},
-    {szUpgrade,                        HTTP_QUERY_UPGRADE,},
-    {szURI,                    HTTP_QUERY_URI,},
-    {szUser_Agent,             HTTP_QUERY_USER_AGENT,},
-    {szVary,                   HTTP_QUERY_VARY,},
-    {szVia,                    HTTP_QUERY_VIA,},
-    {szWarning,                        HTTP_QUERY_WARNING,},
-    {szWWW_Authenticate,       HTTP_QUERY_WWW_AUTHENTICATE,},
-};
+        TRACE("INTERNET_OPTION_URL\n");
 
-/***********************************************************************
- *           HTTP_HttpQueryInfoW (internal)
- */
-static BOOL WINAPI HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLevel,
-       LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
-{
-    LPHTTPHEADERW lphttpHdr = NULL;
-    BOOL bSuccess = FALSE;
-    BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
+        host = HTTP_GetHeader(req, hostW);
+        strcpyW(url, httpW);
+        strcatW(url, host->lpszValue);
+        if (NULL != (pch = strchrW(url + strlenW(httpW), ':')))
+            *pch = 0;
+        strcatW(url, req->lpszPath);
 
+        TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
 
-    /* Find requested header structure */
-    if ((dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK) == HTTP_QUERY_CUSTOM)
-    {
-        INT requested_index = (lpdwIndex)?(*lpdwIndex):0;
-        INT index = HTTP_GetCustomHeaderIndex(lpwhr, (LPWSTR)lpBuffer,
-                requested_index,request_only);
+        if(unicode) {
+            len = (strlenW(url)+1) * sizeof(WCHAR);
+            if(*size < len)
+                return ERROR_INSUFFICIENT_BUFFER;
 
-        if (index < 0)
-            return bSuccess;
-        else
-            lphttpHdr = &lpwhr->pCustHeaders[index];
+            *size = len;
+            strcpyW(buffer, url);
+            return ERROR_SUCCESS;
+        }else {
+            len = WideCharToMultiByte(CP_ACP, 0, url, -1, buffer, *size, NULL, NULL);
+            if(len > *size)
+                return ERROR_INSUFFICIENT_BUFFER;
 
-        if (lpdwIndex)
-            (*lpdwIndex)++;
+            *size = len;
+            return ERROR_SUCCESS;
+        }
     }
-    else
-    {
-        INT index = dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK;
 
-        if (index == HTTP_QUERY_RAW_HEADERS_CRLF)
-        {
-            DWORD len = strlenW(lpwhr->lpszRawHeaders);
-            if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
-            {
-                *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
-                INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
-                return FALSE;
-            }
-            memcpy(lpBuffer, lpwhr->lpszRawHeaders, (len+1)*sizeof(WCHAR));
-            *lpdwBufferLength = len * sizeof(WCHAR);
+    case INTERNET_OPTION_CACHE_TIMESTAMPS: {
+        INTERNET_CACHE_ENTRY_INFOW *info;
+        INTERNET_CACHE_TIMESTAMPS *ts = buffer;
+        WCHAR url[INTERNET_MAX_URL_LENGTH];
+        DWORD nbytes, error;
+        BOOL ret;
 
-            TRACE("returning data: %s\n", debugstr_wn((WCHAR*)lpBuffer, len));
+        TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
 
-            return TRUE;
+        if (*size < sizeof(*ts))
+        {
+            *size = sizeof(*ts);
+            return ERROR_INSUFFICIENT_BUFFER;
         }
-        else if (index == HTTP_QUERY_RAW_HEADERS)
+        nbytes = 0;
+        HTTP_GetRequestURL(req, url);
+        ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
+        error = GetLastError();
+        if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
         {
-            static const WCHAR szCrLf[] = {'\r','\n',0};
-            LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
-            DWORD i, size = 0;
-            LPWSTR pszString = (WCHAR*)lpBuffer;
+            if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
+                return ERROR_OUTOFMEMORY;
 
-            for (i = 0; ppszRawHeaderLines[i]; i++)
-                size += strlenW(ppszRawHeaderLines[i]) + 1;
+            GetUrlCacheEntryInfoW(url, info, &nbytes);
 
-            if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
-            {
-                HTTP_FreeTokens(ppszRawHeaderLines);
-                *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
-                INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
-                return FALSE;
-            }
+            ts->ftExpires = info->ExpireTime;
+            ts->ftLastModified = info->LastModifiedTime;
 
-            for (i = 0; ppszRawHeaderLines[i]; i++)
-            {
-                DWORD len = strlenW(ppszRawHeaderLines[i]);
-                memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
-                pszString += len+1;
-            }
-            *pszString = '\0';
+            HeapFree(GetProcessHeap(), 0, info);
+            *size = sizeof(*ts);
+            return ERROR_SUCCESS;
+        }
+        return error;
+    }
 
-            TRACE("returning data: %s\n", debugstr_wn((WCHAR*)lpBuffer, size));
+    case INTERNET_OPTION_DATAFILE_NAME: {
+        DWORD req_size;
 
-            *lpdwBufferLength = size * sizeof(WCHAR);
-            HTTP_FreeTokens(ppszRawHeaderLines);
+        TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
 
-            return TRUE;
+        if(!req->lpszCacheFile) {
+            *size = 0;
+            return ERROR_INTERNET_ITEM_NOT_FOUND;
         }
-        else if (index == HTTP_QUERY_STATUS_TEXT)
-        {
-            DWORD len = strlenW(lpwhr->lpszStatusText);
-            if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
-            {
-                *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
-                INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
-                return FALSE;
-            }
-            memcpy(lpBuffer, lpwhr->lpszStatusText, (len+1)*sizeof(WCHAR));
-            *lpdwBufferLength = len * sizeof(WCHAR);
 
-            TRACE("returning data: %s\n", debugstr_wn((WCHAR*)lpBuffer, len));
-
-            return TRUE;
+        if(unicode) {
+            req_size = (lstrlenW(req->lpszCacheFile)+1) * sizeof(WCHAR);
+            if(*size < req_size)
+                return ERROR_INSUFFICIENT_BUFFER;
+
+            *size = req_size;
+            memcpy(buffer, req->lpszCacheFile, *size);
+            return ERROR_SUCCESS;
+        }else {
+            req_size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile, -1, NULL, 0, NULL, NULL);
+            if (req_size > *size)
+                return ERROR_INSUFFICIENT_BUFFER;
+
+            *size = WideCharToMultiByte(CP_ACP, 0, req->lpszCacheFile,
+                    -1, buffer, *size, NULL, NULL);
+            return ERROR_SUCCESS;
         }
-        else if (index == HTTP_QUERY_VERSION)
-        {
-            DWORD len = strlenW(lpwhr->lpszVersion);
-            if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
-            {
-                *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
-                INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
-                return FALSE;
-            }
-            memcpy(lpBuffer, lpwhr->lpszVersion, (len+1)*sizeof(WCHAR));
-            *lpdwBufferLength = len * sizeof(WCHAR);
+    }
 
-            TRACE("returning data: %s\n", debugstr_wn((WCHAR*)lpBuffer, len));
+    case INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: {
+        PCCERT_CONTEXT context;
 
-            return TRUE;
+        if(*size < sizeof(INTERNET_CERTIFICATE_INFOW)) {
+            *size = sizeof(INTERNET_CERTIFICATE_INFOW);
+            return ERROR_INSUFFICIENT_BUFFER;
         }
-           else if (index >= 0 && index <= HTTP_QUERY_MAX )
-           {
-            int i;
-            for (i = 0; i <  sizeof(SORTED_STANDARD_HEADERS)/sizeof(std_hdr_data) ; i++)
-            {
-                if (SORTED_STANDARD_HEADERS[i].hdrIndex == index)
-                {
-                    INT requested_index = (lpdwIndex)?(*lpdwIndex):0;
-                    INT index = HTTP_GetCustomHeaderIndex(lpwhr,
-                            (LPWSTR)SORTED_STANDARD_HEADERS[i].hdrStr,
-                            requested_index,request_only);
-
-                    if (index < 0)
-                        return bSuccess;
-                    else
-                        lphttpHdr = &lpwhr->pCustHeaders[index];
-
-                    if (lpdwIndex)
-                        (*lpdwIndex)++;
 
-                    break;
-                }
+        context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
+        if(context) {
+            INTERNET_CERTIFICATE_INFOW *info = (INTERNET_CERTIFICATE_INFOW*)buffer;
+            DWORD len;
+
+            memset(info, 0, sizeof(INTERNET_CERTIFICATE_INFOW));
+            info->ftExpiry = context->pCertInfo->NotAfter;
+            info->ftStart = context->pCertInfo->NotBefore;
+            if(unicode) {
+                len = CertNameToStrW(context->dwCertEncodingType,
+                        &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
+                info->lpszSubjectInfo = LocalAlloc(0, len*sizeof(WCHAR));
+                if(info->lpszSubjectInfo)
+                    CertNameToStrW(context->dwCertEncodingType,
+                             &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
+                             info->lpszSubjectInfo, len);
+                len = CertNameToStrW(context->dwCertEncodingType,
+                         &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
+                info->lpszIssuerInfo = LocalAlloc(0, len*sizeof(WCHAR));
+                if (info->lpszIssuerInfo)
+                    CertNameToStrW(context->dwCertEncodingType,
+                             &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
+                             info->lpszIssuerInfo, len);
+            }else {
+                INTERNET_CERTIFICATE_INFOA *infoA = (INTERNET_CERTIFICATE_INFOA*)info;
+
+                len = CertNameToStrA(context->dwCertEncodingType,
+                         &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR, NULL, 0);
+                infoA->lpszSubjectInfo = LocalAlloc(0, len);
+                if(infoA->lpszSubjectInfo)
+                    CertNameToStrA(context->dwCertEncodingType,
+                             &context->pCertInfo->Subject, CERT_SIMPLE_NAME_STR,
+                             infoA->lpszSubjectInfo, len);
+                len = CertNameToStrA(context->dwCertEncodingType,
+                         &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, NULL, 0);
+                infoA->lpszIssuerInfo = LocalAlloc(0, len);
+                if(infoA->lpszIssuerInfo)
+                    CertNameToStrA(context->dwCertEncodingType,
+                             &context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
+                             infoA->lpszIssuerInfo, len);
             }
 
-            if (!lphttpHdr)
-            {
-                SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
-                return bSuccess;
-            }
-           }
-           else
-        {
-            SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
-            return bSuccess;
+            /*
+             * Contrary to MSDN, these do not appear to be set.
+             * lpszProtocolName
+             * lpszSignatureAlgName
+             * lpszEncryptionAlgName
+             * dwKeySize
+             */
+            CertFreeCertificateContext(context);
+            return ERROR_SUCCESS;
         }
     }
-
-    /* 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)
-    {
-       *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
-       bSuccess = TRUE;
+    return INET_QueryOption(option, buffer, size, unicode);
+}
 
-       TRACE(" returning number : %d\n", *(int *)lpBuffer);
+static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
+{
+    http_request_t *req = (http_request_t*)hdr;
+
+    switch(option) {
+    case INTERNET_OPTION_SEND_TIMEOUT:
+    case INTERNET_OPTION_RECEIVE_TIMEOUT:
+        TRACE("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT\n");
+
+        if (size != sizeof(DWORD))
+            return ERROR_INVALID_PARAMETER;
+
+        return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
+                    *(DWORD*)buffer);
+
+    case INTERNET_OPTION_USERNAME:
+        HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
+        if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
+        return ERROR_SUCCESS;
+
+    case INTERNET_OPTION_PASSWORD:
+        HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
+        if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
+        return ERROR_SUCCESS;
+    case INTERNET_OPTION_HTTP_DECODING:
+        if(size != sizeof(BOOL))
+            return ERROR_INVALID_PARAMETER;
+        req->decoding = *(BOOL*)buffer;
+        return ERROR_SUCCESS;
     }
-    else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME)
-    {
-        time_t tmpTime;
-        struct tm tmpTM;
-        SYSTEMTIME *STHook;
 
-        tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
+    return ERROR_INTERNET_INVALID_OPTION;
+}
 
-        tmpTM = *gmtime(&tmpTime);
-        STHook = (SYSTEMTIME *) lpBuffer;
-        if(STHook==NULL)
-            return bSuccess;
-
-       STHook->wDay = tmpTM.tm_mday;
-       STHook->wHour = tmpTM.tm_hour;
-       STHook->wMilliseconds = 0;
-       STHook->wMinute = tmpTM.tm_min;
-       STHook->wDayOfWeek = tmpTM.tm_wday;
-       STHook->wMonth = tmpTM.tm_mon + 1;
-       STHook->wSecond = tmpTM.tm_sec;
-       STHook->wYear = tmpTM.tm_year;
-
-       bSuccess = TRUE;
-
-       TRACE(" returning time : %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
-             STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
-             STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
-    }
-    else if (dwInfoLevel & HTTP_QUERY_FLAG_COALESCE)
-    {
-           if (*lpdwIndex >= lphttpHdr->wCount)
-               {
-               INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
-               }
-           else
-           {
-           /* Copy strncpyW(lpBuffer, lphttpHdr[*lpdwIndex], len); */
-            (*lpdwIndex)++;
-           }
-    }
-    else if (lphttpHdr->lpszValue)
+/* read some more data into the read buffer (the read section must be held) */
+static DWORD read_more_data( http_request_t *req, int maxlen )
+{
+    DWORD res;
+    int len;
+
+    if (req->read_pos)
     {
-        DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
+        /* move existing data to the start of the buffer */
+        if(req->read_size)
+            memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
+        req->read_pos = 0;
+    }
 
-        if (len > *lpdwBufferLength)
-        {
-            *lpdwBufferLength = len;
-            INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
-            return bSuccess;
-        }
+    if (maxlen == -1) maxlen = sizeof(req->read_buf);
 
-        memcpy(lpBuffer, lphttpHdr->lpszValue, len);
-        *lpdwBufferLength = len - sizeof(WCHAR);
-        bSuccess = TRUE;
+    res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
+                       maxlen - req->read_size, 0, &len );
+    if(res == ERROR_SUCCESS)
+        req->read_size += len;
 
-       TRACE(" returning string : '%s'\n", debugstr_w(lpBuffer));
-    }
-    return bSuccess;
+    return res;
 }
 
-/***********************************************************************
- *           HttpQueryInfoW (WININET.@)
- *
- * Queries for information about an HTTP request
- *
- * RETURNS
- *    TRUE  on success
- *    FALSE on failure
- *
- */
-BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
-       LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
+/* remove some amount of data from the read buffer (the read section must be held) */
+static void remove_data( http_request_t *req, int count )
 {
-    BOOL bSuccess = FALSE;
-    LPWININETHTTPREQW lpwhr;
+    if (!(req->read_size -= count)) req->read_pos = 0;
+    else req->read_pos += count;
+}
 
-    if (TRACE_ON(wininet)) {
-#define FE(x) { x, #x }
-       static const wininet_flag_info query_flags[] = {
-           FE(HTTP_QUERY_MIME_VERSION),
-           FE(HTTP_QUERY_CONTENT_TYPE),
-           FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
-           FE(HTTP_QUERY_CONTENT_ID),
-           FE(HTTP_QUERY_CONTENT_DESCRIPTION),
-           FE(HTTP_QUERY_CONTENT_LENGTH),
-           FE(HTTP_QUERY_CONTENT_LANGUAGE),
-           FE(HTTP_QUERY_ALLOW),
-           FE(HTTP_QUERY_PUBLIC),
-           FE(HTTP_QUERY_DATE),
-           FE(HTTP_QUERY_EXPIRES),
-           FE(HTTP_QUERY_LAST_MODIFIED),
-           FE(HTTP_QUERY_MESSAGE_ID),
-           FE(HTTP_QUERY_URI),
-           FE(HTTP_QUERY_DERIVED_FROM),
-           FE(HTTP_QUERY_COST),
-           FE(HTTP_QUERY_LINK),
-           FE(HTTP_QUERY_PRAGMA),
-           FE(HTTP_QUERY_VERSION),
-           FE(HTTP_QUERY_STATUS_CODE),
-           FE(HTTP_QUERY_STATUS_TEXT),
-           FE(HTTP_QUERY_RAW_HEADERS),
-           FE(HTTP_QUERY_RAW_HEADERS_CRLF),
-           FE(HTTP_QUERY_CONNECTION),
-           FE(HTTP_QUERY_ACCEPT),
-           FE(HTTP_QUERY_ACCEPT_CHARSET),
-           FE(HTTP_QUERY_ACCEPT_ENCODING),
-           FE(HTTP_QUERY_ACCEPT_LANGUAGE),
-           FE(HTTP_QUERY_AUTHORIZATION),
-           FE(HTTP_QUERY_CONTENT_ENCODING),
-           FE(HTTP_QUERY_FORWARDED),
-           FE(HTTP_QUERY_FROM),
-           FE(HTTP_QUERY_IF_MODIFIED_SINCE),
-           FE(HTTP_QUERY_LOCATION),
-           FE(HTTP_QUERY_ORIG_URI),
-           FE(HTTP_QUERY_REFERER),
-           FE(HTTP_QUERY_RETRY_AFTER),
-           FE(HTTP_QUERY_SERVER),
-           FE(HTTP_QUERY_TITLE),
-           FE(HTTP_QUERY_USER_AGENT),
-           FE(HTTP_QUERY_WWW_AUTHENTICATE),
-           FE(HTTP_QUERY_PROXY_AUTHENTICATE),
-           FE(HTTP_QUERY_ACCEPT_RANGES),
-        FE(HTTP_QUERY_SET_COOKIE),
-        FE(HTTP_QUERY_COOKIE),
-           FE(HTTP_QUERY_REQUEST_METHOD),
-           FE(HTTP_QUERY_REFRESH),
-           FE(HTTP_QUERY_CONTENT_DISPOSITION),
-           FE(HTTP_QUERY_AGE),
-           FE(HTTP_QUERY_CACHE_CONTROL),
-           FE(HTTP_QUERY_CONTENT_BASE),
-           FE(HTTP_QUERY_CONTENT_LOCATION),
-           FE(HTTP_QUERY_CONTENT_MD5),
-           FE(HTTP_QUERY_CONTENT_RANGE),
-           FE(HTTP_QUERY_ETAG),
-           FE(HTTP_QUERY_HOST),
-           FE(HTTP_QUERY_IF_MATCH),
-           FE(HTTP_QUERY_IF_NONE_MATCH),
-           FE(HTTP_QUERY_IF_RANGE),
-           FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
-           FE(HTTP_QUERY_MAX_FORWARDS),
-           FE(HTTP_QUERY_PROXY_AUTHORIZATION),
-           FE(HTTP_QUERY_RANGE),
-           FE(HTTP_QUERY_TRANSFER_ENCODING),
-           FE(HTTP_QUERY_UPGRADE),
-           FE(HTTP_QUERY_VARY),
-           FE(HTTP_QUERY_VIA),
-           FE(HTTP_QUERY_WARNING),
-           FE(HTTP_QUERY_CUSTOM)
-       };
-       static const wininet_flag_info modifier_flags[] = {
-           FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
-           FE(HTTP_QUERY_FLAG_SYSTEMTIME),
-           FE(HTTP_QUERY_FLAG_NUMBER),
-           FE(HTTP_QUERY_FLAG_COALESCE)
-       };
-#undef FE
-       DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
-       DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
-       DWORD i;
+static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
+{
+    int count, bytes_read, pos = 0;
+    DWORD res;
 
-       TRACE("(%p, 0x%08lx)--> %ld\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
-       TRACE("  Attribute:");
-       for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
-           if (query_flags[i].val == info) {
-               TRACE(" %s", query_flags[i].name);
-               break;
-           }
-       }
-       if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
-           TRACE(" Unknown (%08lx)", info);
-       }
+    EnterCriticalSection( &req->read_section );
+    for (;;)
+    {
+        BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
 
-       TRACE(" Modifier:");
-       for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
-           if (modifier_flags[i].val & info_mod) {
-               TRACE(" %s", modifier_flags[i].name);
-               info_mod &= ~ modifier_flags[i].val;
-           }
-       }
+        if (eol)
+        {
+            count = eol - (req->read_buf + req->read_pos);
+            bytes_read = count + 1;
+        }
+        else count = bytes_read = req->read_size;
 
-       if (info_mod) {
-           TRACE(" Unknown (%08lx)", info_mod);
-       }
-       TRACE("\n");
+        count = min( count, *len - pos );
+        memcpy( buffer + pos, req->read_buf + req->read_pos, count );
+        pos += count;
+        remove_data( req, bytes_read );
+        if (eol) break;
+
+        if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
+        {
+            *len = 0;
+            TRACE( "returning empty string\n" );
+            LeaveCriticalSection( &req->read_section );
+            INTERNET_SetLastError(res);
+            return FALSE;
+        }
     }
+    LeaveCriticalSection( &req->read_section );
 
-    lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hHttpRequest );
-    if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
+    if (pos < *len)
     {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-       goto lend;
+        if (pos && buffer[pos - 1] == '\r') pos--;
+        *len = pos + 1;
     }
+    buffer[*len - 1] = 0;
+    TRACE( "returning %s\n", debugstr_a(buffer));
+    return TRUE;
+}
 
-    bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
-                                   lpBuffer, lpdwBufferLength, lpdwIndex);
-
-lend:
-    if( lpwhr )
-         WININET_Release( &lpwhr->hdr );
+/* discard data contents until we reach end of line (the read section must be held) */
+static DWORD discard_eol( http_request_t *req )
+{
+    DWORD res;
 
-    TRACE("%d <--\n", bSuccess);
-    return bSuccess;
+    do
+    {
+        BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
+        if (eol)
+        {
+            remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
+            break;
+        }
+        req->read_pos = req->read_size = 0;  /* discard everything */
+        if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
+    } while (req->read_size);
+    return ERROR_SUCCESS;
 }
 
-/***********************************************************************
- *           HttpQueryInfoA (WININET.@)
- *
- * Queries for information about an HTTP request
- *
- * RETURNS
- *    TRUE  on success
- *    FALSE on failure
- *
- */
-BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
-       LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
+/* read the size of the next chunk (the read section must be held) */
+static DWORD start_next_chunk( http_request_t *req )
 {
-    BOOL result;
-    DWORD len;
-    WCHAR* bufferW;
+    DWORD chunk_size = 0, res;
 
-    if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
-       (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
+    if (!req->dwContentLength) return ERROR_SUCCESS;
+    if (req->dwContentLength == req->dwContentRead)
     {
-        return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
-                               lpdwBufferLength, lpdwIndex );
+        /* read terminator for the previous chunk */
+        if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
+        req->dwContentLength = ~0u;
+        req->dwContentRead = 0;
     }
-
-    len = (*lpdwBufferLength)*sizeof(WCHAR);
-    bufferW = HeapAlloc( GetProcessHeap(), 0, len );
-    /* buffer is in/out because of HTTP_QUERY_CUSTOM */
-    if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
-        MultiByteToWideChar(CP_ACP,0,lpBuffer,-1,bufferW,len);
-    result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
-                           &len, lpdwIndex );
-    if( result )
+    for (;;)
     {
-        len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
-                                     lpBuffer, *lpdwBufferLength, NULL, NULL );
-        *lpdwBufferLength = len - 1;
+        while (req->read_size)
+        {
+            char ch = req->read_buf[req->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 );
+                req->dwContentLength = chunk_size;
+                req->dwContentRead = 0;
+                return discard_eol( req );
+            }
+            remove_data( req, 1 );
+        }
+        if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
+        if (!req->read_size)
+        {
+            req->dwContentLength = req->dwContentRead = 0;
+            return ERROR_SUCCESS;
+        }
+    }
+}
 
-        TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
+/* check if we have reached the end of the data to read (the read section must be held) */
+static BOOL end_of_read_data( http_request_t *req )
+{
+    if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
+    if (req->read_chunked) return (req->dwContentLength == 0);
+    if (req->dwContentLength == ~0u) return FALSE;
+    return (req->dwContentLength == req->dwContentRead);
+}
+
+/* fetch some more data into the read buffer (the read section must be held) */
+static DWORD refill_buffer( http_request_t *req )
+{
+    int len = sizeof(req->read_buf);
+    DWORD res;
+
+    if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
+    {
+        if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
     }
-    else
-        /* since the strings being returned from HttpQueryInfoW should be
-         * only ASCII characters, it is reasonable to assume that all of
-         * the Unicode characters can be reduced to a single byte */
-        *lpdwBufferLength = len / sizeof(WCHAR);
 
-    HeapFree(GetProcessHeap(), 0, bufferW );
+    if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
+    if (len <= req->read_size) return ERROR_SUCCESS;
 
-    return result;
+    if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
+    if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
+    return ERROR_SUCCESS;
 }
 
-/***********************************************************************
- *           HttpSendRequestExA (WININET.@)
- *
- * Sends the specified request to the HTTP server and allows chunked
- * transfers.
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE, call GetLastError() for more information.
- */
-BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
-                              LPINTERNET_BUFFERSA lpBuffersIn,
-                              LPINTERNET_BUFFERSA lpBuffersOut,
-                              DWORD dwFlags, DWORD dwContext)
+static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
 {
-    INTERNET_BUFFERSW BuffersInW;
-    BOOL rc = FALSE;
-    DWORD headerlen;
+    DWORD ret = ERROR_SUCCESS;
+    int read = 0;
 
-    TRACE("(%p, %p, %p, %08lx, %08lx): stub\n", hRequest, lpBuffersIn,
-           lpBuffersOut, dwFlags, dwContext);
+#ifdef HAVE_ZLIB
+    z_stream *zstream = &req->gzip_stream->zstream;
+    DWORD buf_avail;
+    int zres;
 
-    if (lpBuffersIn)
+    while(read < size && !req->gzip_stream->end_of_data) {
+        if(!req->read_size) {
+            if(!sync || refill_buffer(req) != ERROR_SUCCESS)
+                break;
+        }
+
+        buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
+
+        zstream->next_in = req->read_buf+req->read_pos;
+        zstream->avail_in = buf_avail;
+        zstream->next_out = buf+read;
+        zstream->avail_out = size-read;
+        zres = inflate(zstream, Z_FULL_FLUSH);
+        read = size - zstream->avail_out;
+        req->dwContentRead += buf_avail-zstream->avail_in;
+        remove_data(req, buf_avail-zstream->avail_in);
+        if(zres == Z_STREAM_END) {
+            TRACE("end of data\n");
+            req->gzip_stream->end_of_data = TRUE;
+            inflateEnd(&req->gzip_stream->zstream);
+        }else if(zres != Z_OK) {
+            WARN("inflate failed %d\n", zres);
+            if(!read)
+                ret = ERROR_INTERNET_DECODING_FAILED;
+            break;
+        }
+    }
+#endif
+
+    *read_ret = read;
+    return ret;
+}
+
+static void refill_gzip_buffer(http_request_t *req)
+{
+    DWORD res;
+    int len;
+
+    if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
+        return;
+
+    if(req->gzip_stream->buf_pos) {
+        if(req->gzip_stream->buf_size)
+            memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
+        req->gzip_stream->buf_pos = 0;
+    }
+
+    res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
+            sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
+    if(res == ERROR_SUCCESS)
+        req->gzip_stream->buf_size += len;
+}
+
+/* return the size of data available to be read immediately (the read section must be held) */
+static DWORD get_avail_data( http_request_t *req )
+{
+    if (req->gzip_stream) {
+        refill_gzip_buffer(req);
+        return req->gzip_stream->buf_size;
+    }
+    if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
+        return 0;
+    return min( req->read_size, req->dwContentLength - req->dwContentRead );
+}
+
+static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
+{
+    INTERNET_ASYNC_RESULT iar;
+    DWORD res;
+
+    TRACE("%p\n", req);
+
+    EnterCriticalSection( &req->read_section );
+    if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
+        iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
+        iar.dwError = first_notif ? 0 : get_avail_data(req);
+    }else {
+        iar.dwResult = 0;
+        iar.dwError = res;
+    }
+    LeaveCriticalSection( &req->read_section );
+
+    INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
+                          sizeof(INTERNET_ASYNC_RESULT));
+}
+
+/* read data from the http connection (the read section must be held) */
+static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
+{
+    BOOL finished_reading = FALSE;
+    int len, bytes_read = 0;
+    DWORD ret = ERROR_SUCCESS;
+
+    EnterCriticalSection( &req->read_section );
+
+    if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
     {
-        BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
-        if (lpBuffersIn->lpcszHeader)
+        if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
+    }
+
+    if(req->gzip_stream) {
+        if(req->gzip_stream->buf_size) {
+            bytes_read = min(req->gzip_stream->buf_size, size);
+            memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
+            req->gzip_stream->buf_pos += bytes_read;
+            req->gzip_stream->buf_size -= bytes_read;
+        }else if(!req->read_size && !req->gzip_stream->end_of_data) {
+            refill_buffer(req);
+        }
+
+        if(size > bytes_read) {
+            ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
+            if(ret == ERROR_SUCCESS)
+                bytes_read += len;
+        }
+
+        finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
+    }else {
+        if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
+
+        if (req->read_size) {
+            bytes_read = min( req->read_size, size );
+            memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
+            remove_data( req, bytes_read );
+        }
+
+        if (size > bytes_read && (!bytes_read || sync)) {
+            if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
+                             sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
+                bytes_read += len;
+            /* always return success, even if the network layer returns an error */
+        }
+
+        finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
+        req->dwContentRead += bytes_read;
+    }
+done:
+    *read = bytes_read;
+
+    TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
+    LeaveCriticalSection( &req->read_section );
+
+    if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
+        BOOL res;
+        DWORD dwBytesWritten;
+
+        res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
+        if(!res)
+            WARN("WriteFile failed: %u\n", GetLastError());
+    }
+
+    if(finished_reading)
+        HTTP_FinishedReading(req);
+
+    return ret;
+}
+
+
+static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
+{
+    http_request_t *req = (http_request_t*)hdr;
+    return HTTPREQ_Read(req, buffer, size, read, TRUE);
+}
+
+static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
+{
+    struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
+    http_request_t *req = (http_request_t*)workRequest->hdr;
+    INTERNET_ASYNC_RESULT iar;
+    DWORD res;
+
+    TRACE("INTERNETREADFILEEXA %p\n", workRequest->hdr);
+
+    res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
+            data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
+
+    iar.dwResult = res == ERROR_SUCCESS;
+    iar.dwError = res;
+
+    INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
+                          INTERNET_STATUS_REQUEST_COMPLETE, &iar,
+                          sizeof(INTERNET_ASYNC_RESULT));
+}
+
+static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
+        DWORD flags, DWORD_PTR context)
+{
+    http_request_t *req = (http_request_t*)hdr;
+    DWORD res;
+
+    if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
+        FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
+
+    if (buffers->dwStructSize != sizeof(*buffers))
+        return ERROR_INVALID_PARAMETER;
+
+    INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
+
+    if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
+    {
+        WORKREQUEST workRequest;
+
+        if (TryEnterCriticalSection( &req->read_section ))
         {
-            headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
-                    lpBuffersIn->dwHeadersLength,0,0);
-            BuffersInW.lpcszHeader = HeapAlloc(GetProcessHeap(),0,headerlen*
-                    sizeof(WCHAR));
-            if (!BuffersInW.lpcszHeader)
+            if (get_avail_data(req))
             {
-                SetLastError(ERROR_OUTOFMEMORY);
-                return FALSE;
+                res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
+                                   &buffers->dwBufferLength, FALSE);
+                LeaveCriticalSection( &req->read_section );
+                goto done;
             }
-            BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
-                    lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
-                    (LPWSTR)BuffersInW.lpcszHeader, headerlen);
+            LeaveCriticalSection( &req->read_section );
         }
-        else
-            BuffersInW.lpcszHeader = NULL;
-        BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
-        BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
-        BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
-        BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
-        BuffersInW.Next = NULL;
+
+        workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
+        workRequest.hdr = WININET_AddRef(&req->hdr);
+        workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
+
+        INTERNET_AsyncCall(&workRequest);
+
+        return ERROR_IO_PENDING;
     }
 
-    rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
+    res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
+            !(flags & IRF_NO_WAIT));
 
-    if (lpBuffersIn)
-        HeapFree(GetProcessHeap(),0,(LPVOID)BuffersInW.lpcszHeader);
+done:
+    if (res == ERROR_SUCCESS) {
+        DWORD size = buffers->dwBufferLength;
+        INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
+                &size, sizeof(size));
+    }
 
-    return rc;
+    return res;
 }
 
-/***********************************************************************
- *           HttpSendRequestExW (WININET.@)
- *
- * Sends the specified request to the HTTP server and allows chunked
- * transfers
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE, call GetLastError() for more information.
- */
-BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
-                   LPINTERNET_BUFFERSW lpBuffersIn,
-                   LPINTERNET_BUFFERSW lpBuffersOut,
-                   DWORD dwFlags, DWORD dwContext)
+static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
 {
-    BOOL ret;
-    LPWININETHTTPREQW lpwhr;
-    LPWININETHTTPSESSIONW lpwhs;
-    LPWININETAPPINFOW hIC;
+    struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
+    http_request_t *req = (http_request_t*)workRequest->hdr;
+    INTERNET_ASYNC_RESULT iar;
+    DWORD res;
 
-    TRACE("(%p, %p, %p, %08lx, %08lx)\n", hRequest, lpBuffersIn,
-            lpBuffersOut, dwFlags, dwContext);
+    TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
 
-    lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hRequest );
+    res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
+            data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
 
-    if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
-    {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-       return FALSE;
-    }
+    iar.dwResult = res == ERROR_SUCCESS;
+    iar.dwError = res;
 
-    lpwhs = (LPWININETHTTPSESSIONW) lpwhr->hdr.lpwhparent;
-    assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
-    hIC = (LPWININETAPPINFOW) lpwhs->hdr.lpwhparent;
-    assert(hIC->hdr.htype == WH_HINIT);
+    INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
+                          INTERNET_STATUS_REQUEST_COMPLETE, &iar,
+                          sizeof(INTERNET_ASYNC_RESULT));
+}
 
-    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
+        DWORD flags, DWORD_PTR context)
+{
+
+    http_request_t *req = (http_request_t*)hdr;
+    DWORD res;
+
+    if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
+        FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
+
+    if (buffers->dwStructSize != sizeof(*buffers))
+        return ERROR_INVALID_PARAMETER;
+
+    INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
+
+    if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
     {
         WORKREQUEST workRequest;
-        struct WORKREQ_HTTPSENDREQUESTW *req;
 
-        workRequest.asyncall = HTTPSENDREQUESTW;
-        workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
-        req = &workRequest.u.HttpSendRequestW;
-        if (lpBuffersIn)
+        if (TryEnterCriticalSection( &req->read_section ))
         {
-            if (lpBuffersIn->lpcszHeader)
-                /* FIXME: this should use dwHeadersLength or may not be necessary at all */
-                req->lpszHeader = WININET_strdupW(lpBuffersIn->lpcszHeader);
-            else
-                req->lpszHeader = NULL;
-            req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
-            req->lpOptional = lpBuffersIn->lpvBuffer;
-            req->dwOptionalLength = lpBuffersIn->dwBufferLength;
-            req->dwContentLength = lpBuffersIn->dwBufferTotal;
+            if (get_avail_data(req))
+            {
+                res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
+                                   &buffers->dwBufferLength, FALSE);
+                LeaveCriticalSection( &req->read_section );
+                goto done;
+            }
+            LeaveCriticalSection( &req->read_section );
         }
-        else
+
+        workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
+        workRequest.hdr = WININET_AddRef(&req->hdr);
+        workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
+
+        INTERNET_AsyncCall(&workRequest);
+
+        return ERROR_IO_PENDING;
+    }
+
+    res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
+            !(flags & IRF_NO_WAIT));
+
+done:
+    if (res == ERROR_SUCCESS) {
+        DWORD size = buffers->dwBufferLength;
+        INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
+                &size, sizeof(size));
+    }
+
+    return res;
+}
+
+static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
+{
+    DWORD res;
+    http_request_t *lpwhr = (http_request_t*)hdr;
+
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
+
+    *written = 0;
+    res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
+    if (res == ERROR_SUCCESS)
+        lpwhr->dwBytesWritten += *written;
+
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
+    return res;
+}
+
+static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
+{
+    http_request_t *req = (http_request_t*)workRequest->hdr;
+
+    HTTP_ReceiveRequestData(req, FALSE);
+}
+
+static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
+{
+    http_request_t *req = (http_request_t*)hdr;
+
+    TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
+
+    if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+    {
+        WORKREQUEST workRequest;
+
+        /* never wait, if we can't enter the section we queue an async request right away */
+        if (TryEnterCriticalSection( &req->read_section ))
         {
-            req->lpszHeader = NULL;
-            req->dwHeaderLength = 0;
-            req->lpOptional = NULL;
-            req->dwOptionalLength = 0;
-            req->dwContentLength = 0;
+            if ((*available = get_avail_data( req ))) goto done;
+            if (end_of_read_data( req )) goto done;
+            LeaveCriticalSection( &req->read_section );
         }
 
-        req->bEndRequest = FALSE;
+        workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
+        workRequest.hdr = WININET_AddRef( &req->hdr );
 
         INTERNET_AsyncCall(&workRequest);
-        /*
-         * This is from windows.
-         */
-        SetLastError(ERROR_IO_PENDING);
-        ret = FALSE;
+
+        return ERROR_IO_PENDING;
     }
-    else
+
+    EnterCriticalSection( &req->read_section );
+
+    if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
     {
-        ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
-                                    lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
-                                    lpBuffersIn->dwBufferTotal, FALSE);
+        refill_buffer( req );
+        *available = get_avail_data( req );
     }
 
-    WININET_Release(&lpwhr->hdr);
-    TRACE("<---\n");
-    return ret;
+done:
+    if (*available == sizeof(req->read_buf) && !req->gzip_stream)  /* check if we have even more pending in the socket */
+    {
+        DWORD extra;
+        if (NETCON_query_data_available(&req->netConnection, &extra))
+            *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
+    }
+    LeaveCriticalSection( &req->read_section );
+
+    TRACE( "returning %u\n", *available );
+    return ERROR_SUCCESS;
 }
 
+static const object_vtbl_t HTTPREQVtbl = {
+    HTTPREQ_Destroy,
+    HTTPREQ_CloseConnection,
+    HTTPREQ_QueryOption,
+    HTTPREQ_SetOption,
+    HTTPREQ_ReadFile,
+    HTTPREQ_ReadFileExA,
+    HTTPREQ_ReadFileExW,
+    HTTPREQ_WriteFile,
+    HTTPREQ_QueryDataAvailable,
+    NULL
+};
+
 /***********************************************************************
- *           HttpSendRequestW (WININET.@)
+ *           HTTP_HttpOpenRequestW (internal)
  *
- * Sends the specified request to the HTTP server
+ * Open a HTTP request handle
  *
  * RETURNS
- *    TRUE  on success
- *    FALSE on failure
+ *    HINTERNET  a HTTP request handle on success
+ *    NULL      on failure
  *
  */
-BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
-       DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
+static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
+        LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
+        LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
+        DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
 {
-    LPWININETHTTPREQW lpwhr;
-    LPWININETHTTPSESSIONW lpwhs = NULL;
-    LPWININETAPPINFOW hIC = NULL;
-    BOOL r;
+    appinfo_t *hIC = NULL;
+    http_request_t *lpwhr;
+    LPWSTR lpszHostName = NULL;
+    HINTERNET handle = NULL;
+    static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
+    DWORD len, res;
 
-    TRACE("%p, %p (%s), %li, %p, %li)\n", hHttpRequest,
-            lpszHeaders, debugstr_w(lpszHeaders), dwHeaderLength, lpOptional, dwOptionalLength);
+    TRACE("-->\n");
 
-    lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hHttpRequest );
-    if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
+    assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
+    hIC = lpwhs->lpAppInfo;
+
+    lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
+    if (NULL == lpwhr)
     {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-       r = FALSE;
+        res = ERROR_OUTOFMEMORY;
         goto lend;
     }
+    lpwhr->hdr.htype = WH_HHTTPREQ;
+    lpwhr->hdr.vtbl = &HTTPREQVtbl;
+    lpwhr->hdr.dwFlags = dwFlags;
+    lpwhr->hdr.dwContext = dwContext;
+    lpwhr->hdr.refs = 1;
+    lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
+    lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
+    lpwhr->dwContentLength = ~0u;
+    InitializeCriticalSection( &lpwhr->read_section );
 
-    lpwhs = (LPWININETHTTPSESSIONW) lpwhr->hdr.lpwhparent;
-    if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
+    WININET_AddRef( &lpwhs->hdr );
+    lpwhr->lpHttpSession = lpwhs;
+    list_add_head( &lpwhs->hdr.children, &lpwhr->hdr.entry );
+
+    lpszHostName = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) *
+            (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
+    if (NULL == lpszHostName)
     {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-       r = FALSE;
+        res = ERROR_OUTOFMEMORY;
         goto lend;
     }
 
-    hIC = (LPWININETAPPINFOW) lpwhs->hdr.lpwhparent;
-    if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
+    handle = WININET_AllocHandle( &lpwhr->hdr );
+    if (NULL == handle)
     {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-       r = FALSE;
+        res = ERROR_OUTOFMEMORY;
         goto lend;
     }
 
-    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+    if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
     {
-        WORKREQUEST workRequest;
-        struct WORKREQ_HTTPSENDREQUESTW *req;
+        InternetCloseHandle( handle );
+        handle = NULL;
+        goto lend;
+    }
 
-        workRequest.asyncall = HTTPSENDREQUESTW;
-       workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
-        req = &workRequest.u.HttpSendRequestW;
-        if (lpszHeaders)
-            req->lpszHeader = WININET_strdupW(lpszHeaders);
-        else
-            req->lpszHeader = 0;
-        req->dwHeaderLength = dwHeaderLength;
-        req->lpOptional = lpOptional;
-        req->dwOptionalLength = dwOptionalLength;
-        req->dwContentLength = dwOptionalLength;
-        req->bEndRequest = TRUE;
+    if (lpszObjectName && *lpszObjectName) {
+        HRESULT rc;
 
-        INTERNET_AsyncCall(&workRequest);
-        /*
-         * This is from windows.
-         */
-        SetLastError(ERROR_IO_PENDING);
-        r = FALSE;
-    }
-    else
-    {
-       r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
-               dwHeaderLength, lpOptional, dwOptionalLength,
-               dwOptionalLength, TRUE);
+        len = 0;
+        rc = UrlEscapeW(lpszObjectName, NULL, &len, URL_ESCAPE_SPACES_ONLY);
+        if (rc != E_POINTER)
+            len = strlenW(lpszObjectName)+1;
+        lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
+        rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
+                   URL_ESCAPE_SPACES_ONLY);
+        if (rc != S_OK)
+        {
+            ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
+            strcpyW(lpwhr->lpszPath,lpszObjectName);
+        }
+    }else {
+        static const WCHAR slashW[] = {'/',0};
+
+        lpwhr->lpszPath = heap_strdupW(slashW);
+    }
+
+    if (lpszReferrer && *lpszReferrer)
+        HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
+
+    if (lpszAcceptTypes)
+    {
+        int i;
+        for (i = 0; lpszAcceptTypes[i]; i++)
+        {
+            if (!*lpszAcceptTypes[i]) continue;
+            HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, lpszAcceptTypes[i],
+                               HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA |
+                               HTTP_ADDHDR_FLAG_REQ |
+                               (i == 0 ? HTTP_ADDHDR_FLAG_REPLACE : 0));
+        }
+    }
+
+    lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
+    lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
+
+    if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
+        lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
+        lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
+    {
+        sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
+        HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
+                HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
     }
+    else
+        HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
+                HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
+
+    if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
+        lpwhs->nServerPort = (dwFlags & INTERNET_FLAG_SECURE ?
+                        INTERNET_DEFAULT_HTTPS_PORT :
+                        INTERNET_DEFAULT_HTTP_PORT);
+
+    if (lpwhs->nHostPort == INTERNET_INVALID_PORT_NUMBER)
+        lpwhs->nHostPort = (dwFlags & INTERNET_FLAG_SECURE ?
+                        INTERNET_DEFAULT_HTTPS_PORT :
+                        INTERNET_DEFAULT_HTTP_PORT);
+
+    if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
+        HTTP_DealWithProxy( hIC, lpwhs, lpwhr );
+
+    INTERNET_SendCallback(&lpwhs->hdr, dwContext,
+                          INTERNET_STATUS_HANDLE_CREATED, &handle,
+                          sizeof(handle));
+
 lend:
+    HeapFree(GetProcessHeap(), 0, lpszHostName);
     if( lpwhr )
         WININET_Release( &lpwhr->hdr );
-    return r;
+
+    TRACE("<-- %p (%p)\n", handle, lpwhr);
+    *ret = handle;
+    return res;
 }
 
 /***********************************************************************
- *           HttpSendRequestA (WININET.@)
+ *           HttpOpenRequestW (WININET.@)
  *
- * Sends the specified request to the HTTP server
+ * Open a HTTP request handle
  *
  * RETURNS
- *    TRUE  on success
- *    FALSE on failure
+ *    HINTERNET  a HTTP request handle on success
+ *    NULL      on failure
  *
  */
-BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
-       DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
+HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
+       LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
+       LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
+       DWORD dwFlags, DWORD_PTR dwContext)
 {
-    BOOL result;
-    LPWSTR szHeaders=NULL;
-    DWORD nLen=dwHeaderLength;
-    if(lpszHeaders!=NULL)
+    http_session_t *lpwhs;
+    HINTERNET handle = NULL;
+    DWORD res;
+
+    TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
+          debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
+          debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
+          dwFlags, dwContext);
+    if(lpszAcceptTypes!=NULL)
     {
-        nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
-        szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
-        MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
+        int i;
+        for(i=0;lpszAcceptTypes[i]!=NULL;i++)
+            TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
     }
-    result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
-    HeapFree(GetProcessHeap(),0,szHeaders);
-    return result;
+
+    lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
+    if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
+    {
+        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
+        goto lend;
+    }
+
+    /*
+     * My tests seem to show that the windows version does not
+     * become asynchronous until after this point. And anyhow
+     * if this call was asynchronous then how would you get the
+     * necessary HINTERNET pointer returned by this function.
+     *
+     */
+    res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
+                                lpszVersion, lpszReferrer, lpszAcceptTypes,
+                                dwFlags, dwContext, &handle);
+lend:
+    if( lpwhs )
+        WININET_Release( &lpwhs->hdr );
+    TRACE("returning %p\n", handle);
+    if(res != ERROR_SUCCESS)
+        SetLastError(res);
+    return handle;
 }
 
-/***********************************************************************
- *           HTTP_HandleRedirect (internal)
- */
-static BOOL HTTP_HandleRedirect(LPWININETHTTPREQW lpwhr, LPCWSTR lpszUrl, LPCWSTR lpszHeaders,
-                                DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
-                                DWORD dwContentLength)
+/* read any content returned by the server so that the connection can be
+ * reused */
+static void HTTP_DrainContent(http_request_t *req)
 {
-    LPWININETHTTPSESSIONW lpwhs = (LPWININETHTTPSESSIONW) lpwhr->hdr.lpwhparent;
-    LPWININETAPPINFOW hIC = (LPWININETAPPINFOW) lpwhs->hdr.lpwhparent;
-    WCHAR path[2048];
-    char szaddr[32];
+    DWORD bytes_read;
 
-    if(lpszUrl[0]=='/')
-    {
-        /* if it's an absolute path, keep the same session info */
-        lstrcpynW(path, lpszUrl, 2048);
-    }
-    else if (NULL != hIC->lpszProxy && hIC->lpszProxy[0] != 0)
+    if (!NETCON_connected(&req->netConnection)) return;
+
+    if (req->dwContentLength == -1)
     {
-        TRACE("Redirect through proxy\n");
-        lstrcpynW(path, lpszUrl, 2048);
+        NETCON_close(&req->netConnection);
+        return;
     }
-    else
+    if (!strcmpW(req->lpszVerb, szHEAD)) return;
+
+    do
     {
-        URL_COMPONENTSW urlComponents;
-        WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
-        static const WCHAR szHttp[] = {'h','t','t','p',0};
-        static const WCHAR szHttps[] = {'h','t','t','p','s',0};
-        DWORD url_length = 0;
-        LPWSTR orig_url;
-        LPWSTR combined_url;
+        char buffer[2048];
+        if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
+            return;
+    } while (bytes_read);
+}
 
-        urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
-        urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? (LPWSTR)szHttps : (LPWSTR)szHttp;
-        urlComponents.dwSchemeLength = 0;
-        urlComponents.lpszHostName = lpwhs->lpszHostName;
-        urlComponents.dwHostNameLength = 0;
-        urlComponents.nPort = lpwhs->nHostPort;
-        urlComponents.lpszUserName = lpwhs->lpszUserName;
-        urlComponents.dwUserNameLength = 0;
-        urlComponents.lpszPassword = NULL;
-        urlComponents.dwPasswordLength = 0;
-        urlComponents.lpszUrlPath = lpwhr->lpszPath;
-        urlComponents.dwUrlPathLength = 0;
-        urlComponents.lpszExtraInfo = NULL;
-        urlComponents.dwExtraInfoLength = 0;
+static const LPCWSTR header_lookup[] = {
+    szMime_Version,            /* HTTP_QUERY_MIME_VERSION = 0 */
+    szContent_Type,            /* HTTP_QUERY_CONTENT_TYPE = 1 */
+    szContent_Transfer_Encoding,/* HTTP_QUERY_CONTENT_TRANSFER_ENCODING = 2 */
+    szContent_ID,              /* HTTP_QUERY_CONTENT_ID = 3 */
+    NULL,                      /* HTTP_QUERY_CONTENT_DESCRIPTION = 4 */
+    szContent_Length,          /* HTTP_QUERY_CONTENT_LENGTH =  5 */
+    szContent_Language,                /* HTTP_QUERY_CONTENT_LANGUAGE =  6 */
+    szAllow,                   /* HTTP_QUERY_ALLOW = 7 */
+    szPublic,                  /* HTTP_QUERY_PUBLIC = 8 */
+    szDate,                    /* HTTP_QUERY_DATE = 9 */
+    szExpires,                 /* HTTP_QUERY_EXPIRES = 10 */
+    szLast_Modified,           /* HTTP_QUERY_LAST_MODIFIED = 11 */
+    NULL,                      /* HTTP_QUERY_MESSAGE_ID = 12 */
+    szURI,                     /* HTTP_QUERY_URI = 13 */
+    szFrom,                    /* HTTP_QUERY_DERIVED_FROM = 14 */
+    NULL,                      /* HTTP_QUERY_COST = 15 */
+    NULL,                      /* HTTP_QUERY_LINK = 16 */
+    szPragma,                  /* HTTP_QUERY_PRAGMA = 17 */
+    NULL,                      /* HTTP_QUERY_VERSION = 18 */
+    szStatus,                  /* HTTP_QUERY_STATUS_CODE = 19 */
+    NULL,                      /* HTTP_QUERY_STATUS_TEXT = 20 */
+    NULL,                      /* HTTP_QUERY_RAW_HEADERS = 21 */
+    NULL,                      /* HTTP_QUERY_RAW_HEADERS_CRLF = 22 */
+    szConnection,              /* HTTP_QUERY_CONNECTION = 23 */
+    szAccept,                  /* HTTP_QUERY_ACCEPT = 24 */
+    szAccept_Charset,          /* HTTP_QUERY_ACCEPT_CHARSET = 25 */
+    szAccept_Encoding,         /* HTTP_QUERY_ACCEPT_ENCODING = 26 */
+    szAccept_Language,         /* HTTP_QUERY_ACCEPT_LANGUAGE = 27 */
+    szAuthorization,           /* HTTP_QUERY_AUTHORIZATION = 28 */
+    szContent_Encoding,                /* HTTP_QUERY_CONTENT_ENCODING = 29 */
+    NULL,                      /* HTTP_QUERY_FORWARDED = 30 */
+    NULL,                      /* HTTP_QUERY_FROM = 31 */
+    szIf_Modified_Since,       /* HTTP_QUERY_IF_MODIFIED_SINCE = 32 */
+    szLocation,                        /* HTTP_QUERY_LOCATION = 33 */
+    NULL,                      /* HTTP_QUERY_ORIG_URI = 34 */
+    szReferer,                 /* HTTP_QUERY_REFERER = 35 */
+    szRetry_After,             /* HTTP_QUERY_RETRY_AFTER = 36 */
+    szServer,                  /* HTTP_QUERY_SERVER = 37 */
+    NULL,                      /* HTTP_TITLE = 38 */
+    szUser_Agent,              /* HTTP_QUERY_USER_AGENT = 39 */
+    szWWW_Authenticate,                /* HTTP_QUERY_WWW_AUTHENTICATE = 40 */
+    szProxy_Authenticate,      /* HTTP_QUERY_PROXY_AUTHENTICATE = 41 */
+    szAccept_Ranges,           /* HTTP_QUERY_ACCEPT_RANGES = 42 */
+    szSet_Cookie,              /* HTTP_QUERY_SET_COOKIE = 43 */
+    szCookie,                  /* HTTP_QUERY_COOKIE = 44 */
+    NULL,                      /* HTTP_QUERY_REQUEST_METHOD = 45 */
+    NULL,                      /* HTTP_QUERY_REFRESH = 46 */
+    NULL,                      /* HTTP_QUERY_CONTENT_DISPOSITION = 47 */
+    szAge,                     /* HTTP_QUERY_AGE = 48 */
+    szCache_Control,           /* HTTP_QUERY_CACHE_CONTROL = 49 */
+    szContent_Base,            /* HTTP_QUERY_CONTENT_BASE = 50 */
+    szContent_Location,                /* HTTP_QUERY_CONTENT_LOCATION = 51 */
+    szContent_MD5,             /* HTTP_QUERY_CONTENT_MD5 = 52 */
+    szContent_Range,           /* HTTP_QUERY_CONTENT_RANGE = 53 */
+    szETag,                    /* HTTP_QUERY_ETAG = 54 */
+    hostW,                     /* HTTP_QUERY_HOST = 55 */
+    szIf_Match,                        /* HTTP_QUERY_IF_MATCH = 56 */
+    szIf_None_Match,           /* HTTP_QUERY_IF_NONE_MATCH = 57 */
+    szIf_Range,                        /* HTTP_QUERY_IF_RANGE = 58 */
+    szIf_Unmodified_Since,     /* HTTP_QUERY_IF_UNMODIFIED_SINCE = 59 */
+    szMax_Forwards,            /* HTTP_QUERY_MAX_FORWARDS = 60 */
+    szProxy_Authorization,     /* HTTP_QUERY_PROXY_AUTHORIZATION = 61 */
+    szRange,                   /* HTTP_QUERY_RANGE = 62 */
+    szTransfer_Encoding,       /* HTTP_QUERY_TRANSFER_ENCODING = 63 */
+    szUpgrade,                 /* HTTP_QUERY_UPGRADE = 64 */
+    szVary,                    /* HTTP_QUERY_VARY = 65 */
+    szVia,                     /* HTTP_QUERY_VIA = 66 */
+    szWarning,                 /* HTTP_QUERY_WARNING = 67 */
+    szExpect,                  /* HTTP_QUERY_EXPECT = 68 */
+    szProxy_Connection,                /* HTTP_QUERY_PROXY_CONNECTION = 69 */
+    szUnless_Modified_Since,   /* HTTP_QUERY_UNLESS_MODIFIED_SINCE = 70 */
+};
 
-        if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
-            (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
-            return FALSE;
+#define LAST_TABLE_HEADER (sizeof(header_lookup)/sizeof(header_lookup[0]))
 
-        url_length++; /* for nul terminating character */
-        orig_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
+/***********************************************************************
+ *           HTTP_HttpQueryInfoW (internal)
+ */
+static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
+        LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
+{
+    LPHTTPHEADERW lphttpHdr = NULL;
+    BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
+    INT requested_index = lpdwIndex ? *lpdwIndex : 0;
+    DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
+    INT index = -1;
 
-        if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
+    /* Find requested header structure */
+    switch (level)
+    {
+    case HTTP_QUERY_CUSTOM:
+        if (!lpBuffer) return ERROR_INVALID_PARAMETER;
+        index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
+        break;
+    case HTTP_QUERY_RAW_HEADERS_CRLF:
         {
-            HeapFree(GetProcessHeap(), 0, orig_url);
-            return FALSE;
-        }
+            LPWSTR headers;
+            DWORD len = 0;
+            DWORD res = ERROR_INVALID_PARAMETER;
 
-        url_length = 0;
-        if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
-            (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
-        {
-            HeapFree(GetProcessHeap(), 0, orig_url);
-            return FALSE;
-        }
-        combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
+            if (request_only)
+                headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
+            else
+                headers = lpwhr->lpszRawHeaders;
 
-        if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
-        {
-            HeapFree(GetProcessHeap(), 0, orig_url);
-            HeapFree(GetProcessHeap(), 0, combined_url);
-            return FALSE;
-        }
-        HeapFree(GetProcessHeap(), 0, orig_url);
+            if (headers)
+                len = strlenW(headers) * sizeof(WCHAR);
 
-        userName[0] = 0;
-        hostName[0] = 0;
-        protocol[0] = 0;
+            if (len + sizeof(WCHAR) > *lpdwBufferLength)
+            {
+                len += sizeof(WCHAR);
+                res = ERROR_INSUFFICIENT_BUFFER;
+            }
+            else if (lpBuffer)
+            {
+                if (headers)
+                    memcpy(lpBuffer, headers, len + sizeof(WCHAR));
+                else
+                {
+                    len = strlenW(szCrLf) * sizeof(WCHAR);
+                    memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
+                }
+                TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
+                res = ERROR_SUCCESS;
+            }
+            *lpdwBufferLength = len;
 
-        urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
-        urlComponents.lpszScheme = protocol;
-        urlComponents.dwSchemeLength = 32;
-        urlComponents.lpszHostName = hostName;
-        urlComponents.dwHostNameLength = MAXHOSTNAME;
-        urlComponents.lpszUserName = userName;
-        urlComponents.dwUserNameLength = 1024;
-        urlComponents.lpszPassword = NULL;
-        urlComponents.dwPasswordLength = 0;
-        urlComponents.lpszUrlPath = path;
-        urlComponents.dwUrlPathLength = 2048;
-        urlComponents.lpszExtraInfo = NULL;
-        urlComponents.dwExtraInfoLength = 0;
-        if(!InternetCrackUrlW(combined_url, strlenW(combined_url), 0, &urlComponents))
-        {
-            HeapFree(GetProcessHeap(), 0, combined_url);
-            return FALSE;
+            if (request_only)
+                HeapFree(GetProcessHeap(), 0, headers);
+            return res;
         }
-        HeapFree(GetProcessHeap(), 0, combined_url);
-
-        if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
-            (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
+    case HTTP_QUERY_RAW_HEADERS:
         {
-            TRACE("redirect from secure page to non-secure page\n");
-            /* FIXME: warn about from secure redirect to non-secure page */
-            lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
+            LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
+            DWORD i, size = 0;
+            LPWSTR pszString = lpBuffer;
+
+            for (i = 0; ppszRawHeaderLines[i]; i++)
+                size += strlenW(ppszRawHeaderLines[i]) + 1;
+
+            if (size + 1 > *lpdwBufferLength/sizeof(WCHAR))
+            {
+                HTTP_FreeTokens(ppszRawHeaderLines);
+                *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
+                return ERROR_INSUFFICIENT_BUFFER;
+            }
+            if (pszString)
+            {
+                for (i = 0; ppszRawHeaderLines[i]; i++)
+                {
+                    DWORD len = strlenW(ppszRawHeaderLines[i]);
+                    memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
+                    pszString += len+1;
+                }
+                *pszString = '\0';
+                TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
+            }
+            *lpdwBufferLength = size * sizeof(WCHAR);
+            HTTP_FreeTokens(ppszRawHeaderLines);
+
+            return ERROR_SUCCESS;
         }
-        if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
-            !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
+    case HTTP_QUERY_STATUS_TEXT:
+        if (lpwhr->lpszStatusText)
         {
-            TRACE("redirect from non-secure page to secure page\n");
-            /* FIXME: notify about redirect to secure page */
-            lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
+            DWORD len = strlenW(lpwhr->lpszStatusText);
+            if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
+            {
+                *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
+                return ERROR_INSUFFICIENT_BUFFER;
+            }
+            if (lpBuffer)
+            {
+                memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
+                TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
+            }
+            *lpdwBufferLength = len * sizeof(WCHAR);
+            return ERROR_SUCCESS;
         }
-
-        if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
+        break;
+    case HTTP_QUERY_VERSION:
+        if (lpwhr->lpszVersion)
         {
-            if (lstrlenW(protocol)>4) /*https*/
-                urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
-            else /*http*/
-                urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
+            DWORD len = strlenW(lpwhr->lpszVersion);
+            if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
+            {
+                *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
+                return ERROR_INSUFFICIENT_BUFFER;
+            }
+            if (lpBuffer)
+            {
+                memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
+                TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
+            }
+            *lpdwBufferLength = len * sizeof(WCHAR);
+            return ERROR_SUCCESS;
         }
+        break;
+    case HTTP_QUERY_CONTENT_ENCODING:
+        index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
+                requested_index,request_only);
+        break;
+    default:
+        assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
 
-#if 0
-        /*
-         * This upsets redirects to binary files on sourceforge.net
-         * and gives an html page instead of the target file
-         * Examination of the HTTP request sent by native wininet.dll
-         * reveals that it doesn't send a referrer in that case.
-         * Maybe there's a flag that enables this, or maybe a referrer
-         * shouldn't be added in case of a redirect.
-         */
+        if (level < LAST_TABLE_HEADER && header_lookup[level])
+            index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
+                                              requested_index,request_only);
+    }
 
-        /* consider the current host as the referrer */
-        if (NULL != lpwhs->lpszServerName && strlenW(lpwhs->lpszServerName))
-            HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
-                           HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
-                           HTTP_ADDHDR_FLAG_ADD_IF_NEW);
-#endif
+    if (index >= 0)
+        lphttpHdr = &lpwhr->pCustHeaders[index];
 
-        HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
-        lpwhs->lpszServerName = WININET_strdupW(hostName);
-        HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
-        if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
-                urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
+    /* Ensure header satisfies requested attributes */
+    if (!lphttpHdr ||
+        ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
+         (~lphttpHdr->wFlags & HDR_ISREQUEST)))
+    {
+        return ERROR_HTTP_HEADER_NOT_FOUND;
+    }
+
+    if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
+
+    /* coalesce value to requested type */
+    if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
+    {
+        *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
+        TRACE(" returning number: %d\n", *(int *)lpBuffer);
+     }
+    else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
+    {
+        time_t tmpTime;
+        struct tm tmpTM;
+        SYSTEMTIME *STHook;
+
+        tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
+
+        tmpTM = *gmtime(&tmpTime);
+        STHook = (SYSTEMTIME *)lpBuffer;
+        STHook->wDay = tmpTM.tm_mday;
+        STHook->wHour = tmpTM.tm_hour;
+        STHook->wMilliseconds = 0;
+        STHook->wMinute = tmpTM.tm_min;
+        STHook->wDayOfWeek = tmpTM.tm_wday;
+        STHook->wMonth = tmpTM.tm_mon + 1;
+        STHook->wSecond = tmpTM.tm_sec;
+        STHook->wYear = tmpTM.tm_year;
+
+        TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
+              STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
+              STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
+    }
+    else if (lphttpHdr->lpszValue)
+    {
+        DWORD len = (strlenW(lphttpHdr->lpszValue) + 1) * sizeof(WCHAR);
+
+        if (len > *lpdwBufferLength)
         {
-            int len;
-            static WCHAR fmt[] = {'%','s',':','%','i',0};
-            len = lstrlenW(hostName);
-            len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
-            lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
-            sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
+            *lpdwBufferLength = len;
+            return ERROR_INSUFFICIENT_BUFFER;
         }
+        if (lpBuffer)
+        {
+            memcpy(lpBuffer, lphttpHdr->lpszValue, len);
+            TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
+        }
+        *lpdwBufferLength = len - sizeof(WCHAR);
+    }
+    return ERROR_SUCCESS;
+}
+
+/***********************************************************************
+ *           HttpQueryInfoW (WININET.@)
+ *
+ * Queries for information about an HTTP request
+ *
+ * RETURNS
+ *    TRUE  on success
+ *    FALSE on failure
+ *
+ */
+BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
+        LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
+{
+    http_request_t *lpwhr;
+    DWORD res;
+
+    if (TRACE_ON(wininet)) {
+#define FE(x) { x, #x }
+       static const wininet_flag_info query_flags[] = {
+           FE(HTTP_QUERY_MIME_VERSION),
+           FE(HTTP_QUERY_CONTENT_TYPE),
+           FE(HTTP_QUERY_CONTENT_TRANSFER_ENCODING),
+           FE(HTTP_QUERY_CONTENT_ID),
+           FE(HTTP_QUERY_CONTENT_DESCRIPTION),
+           FE(HTTP_QUERY_CONTENT_LENGTH),
+           FE(HTTP_QUERY_CONTENT_LANGUAGE),
+           FE(HTTP_QUERY_ALLOW),
+           FE(HTTP_QUERY_PUBLIC),
+           FE(HTTP_QUERY_DATE),
+           FE(HTTP_QUERY_EXPIRES),
+           FE(HTTP_QUERY_LAST_MODIFIED),
+           FE(HTTP_QUERY_MESSAGE_ID),
+           FE(HTTP_QUERY_URI),
+           FE(HTTP_QUERY_DERIVED_FROM),
+           FE(HTTP_QUERY_COST),
+           FE(HTTP_QUERY_LINK),
+           FE(HTTP_QUERY_PRAGMA),
+           FE(HTTP_QUERY_VERSION),
+           FE(HTTP_QUERY_STATUS_CODE),
+           FE(HTTP_QUERY_STATUS_TEXT),
+           FE(HTTP_QUERY_RAW_HEADERS),
+           FE(HTTP_QUERY_RAW_HEADERS_CRLF),
+           FE(HTTP_QUERY_CONNECTION),
+           FE(HTTP_QUERY_ACCEPT),
+           FE(HTTP_QUERY_ACCEPT_CHARSET),
+           FE(HTTP_QUERY_ACCEPT_ENCODING),
+           FE(HTTP_QUERY_ACCEPT_LANGUAGE),
+           FE(HTTP_QUERY_AUTHORIZATION),
+           FE(HTTP_QUERY_CONTENT_ENCODING),
+           FE(HTTP_QUERY_FORWARDED),
+           FE(HTTP_QUERY_FROM),
+           FE(HTTP_QUERY_IF_MODIFIED_SINCE),
+           FE(HTTP_QUERY_LOCATION),
+           FE(HTTP_QUERY_ORIG_URI),
+           FE(HTTP_QUERY_REFERER),
+           FE(HTTP_QUERY_RETRY_AFTER),
+           FE(HTTP_QUERY_SERVER),
+           FE(HTTP_QUERY_TITLE),
+           FE(HTTP_QUERY_USER_AGENT),
+           FE(HTTP_QUERY_WWW_AUTHENTICATE),
+           FE(HTTP_QUERY_PROXY_AUTHENTICATE),
+           FE(HTTP_QUERY_ACCEPT_RANGES),
+        FE(HTTP_QUERY_SET_COOKIE),
+        FE(HTTP_QUERY_COOKIE),
+           FE(HTTP_QUERY_REQUEST_METHOD),
+           FE(HTTP_QUERY_REFRESH),
+           FE(HTTP_QUERY_CONTENT_DISPOSITION),
+           FE(HTTP_QUERY_AGE),
+           FE(HTTP_QUERY_CACHE_CONTROL),
+           FE(HTTP_QUERY_CONTENT_BASE),
+           FE(HTTP_QUERY_CONTENT_LOCATION),
+           FE(HTTP_QUERY_CONTENT_MD5),
+           FE(HTTP_QUERY_CONTENT_RANGE),
+           FE(HTTP_QUERY_ETAG),
+           FE(HTTP_QUERY_HOST),
+           FE(HTTP_QUERY_IF_MATCH),
+           FE(HTTP_QUERY_IF_NONE_MATCH),
+           FE(HTTP_QUERY_IF_RANGE),
+           FE(HTTP_QUERY_IF_UNMODIFIED_SINCE),
+           FE(HTTP_QUERY_MAX_FORWARDS),
+           FE(HTTP_QUERY_PROXY_AUTHORIZATION),
+           FE(HTTP_QUERY_RANGE),
+           FE(HTTP_QUERY_TRANSFER_ENCODING),
+           FE(HTTP_QUERY_UPGRADE),
+           FE(HTTP_QUERY_VARY),
+           FE(HTTP_QUERY_VIA),
+           FE(HTTP_QUERY_WARNING),
+           FE(HTTP_QUERY_CUSTOM)
+       };
+       static const wininet_flag_info modifier_flags[] = {
+           FE(HTTP_QUERY_FLAG_REQUEST_HEADERS),
+           FE(HTTP_QUERY_FLAG_SYSTEMTIME),
+           FE(HTTP_QUERY_FLAG_NUMBER),
+           FE(HTTP_QUERY_FLAG_COALESCE)
+       };
+#undef FE
+       DWORD info_mod = dwInfoLevel & HTTP_QUERY_MODIFIER_FLAGS_MASK;
+       DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
+       DWORD i;
+
+       TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
+       TRACE("  Attribute:");
+       for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
+           if (query_flags[i].val == info) {
+               TRACE(" %s", query_flags[i].name);
+               break;
+           }
+       }
+       if (i == (sizeof(query_flags) / sizeof(query_flags[0]))) {
+           TRACE(" Unknown (%08x)", info);
+       }
+
+       TRACE(" Modifier:");
+       for (i = 0; i < (sizeof(modifier_flags) / sizeof(modifier_flags[0])); i++) {
+           if (modifier_flags[i].val & info_mod) {
+               TRACE(" %s", modifier_flags[i].name);
+               info_mod &= ~ modifier_flags[i].val;
+           }
+       }
+       
+       if (info_mod) {
+           TRACE(" Unknown (%08x)", info_mod);
+       }
+       TRACE("\n");
+    }
+    
+    lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
+    if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
+    {
+        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
+        goto lend;
+    }
+
+    if (lpBuffer == NULL)
+        *lpdwBufferLength = 0;
+    res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
+                               lpBuffer, lpdwBufferLength, lpdwIndex);
+
+lend:
+    if( lpwhr )
+         WININET_Release( &lpwhr->hdr );
+
+    TRACE("%u <--\n", res);
+    if(res != ERROR_SUCCESS)
+        SetLastError(res);
+    return res == ERROR_SUCCESS;
+}
+
+/***********************************************************************
+ *           HttpQueryInfoA (WININET.@)
+ *
+ * Queries for information about an HTTP request
+ *
+ * RETURNS
+ *    TRUE  on success
+ *    FALSE on failure
+ *
+ */
+BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
+       LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
+{
+    BOOL result;
+    DWORD len;
+    WCHAR* bufferW;
+
+    if((dwInfoLevel & HTTP_QUERY_FLAG_NUMBER) ||
+       (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME))
+    {
+        return HttpQueryInfoW( hHttpRequest, dwInfoLevel, lpBuffer,
+                               lpdwBufferLength, lpdwIndex );
+    }
+
+    if (lpBuffer)
+    {
+        DWORD alloclen;
+        len = (*lpdwBufferLength)*sizeof(WCHAR);
+        if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
+        {
+            alloclen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 ) * sizeof(WCHAR);
+            if (alloclen < len)
+                alloclen = len;
+        }
+        else
+            alloclen = len;
+        bufferW = HeapAlloc( GetProcessHeap(), 0, alloclen );
+        /* buffer is in/out because of HTTP_QUERY_CUSTOM */
+        if ((dwInfoLevel & HTTP_QUERY_HEADER_MASK) == HTTP_QUERY_CUSTOM)
+            MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, bufferW, alloclen / sizeof(WCHAR) );
+    } else
+    {
+        bufferW = NULL;
+        len = 0;
+    }
+
+    result = HttpQueryInfoW( hHttpRequest, dwInfoLevel, bufferW,
+                           &len, lpdwIndex );
+    if( result )
+    {
+        len = WideCharToMultiByte( CP_ACP,0, bufferW, len / sizeof(WCHAR) + 1,
+                                     lpBuffer, *lpdwBufferLength, NULL, NULL );
+        *lpdwBufferLength = len - 1;
+
+        TRACE("lpBuffer: %s\n", debugstr_a(lpBuffer));
+    }
+    else
+        /* since the strings being returned from HttpQueryInfoW should be
+         * only ASCII characters, it is reasonable to assume that all of
+         * the Unicode characters can be reduced to a single byte */
+        *lpdwBufferLength = len / sizeof(WCHAR);
+
+    HeapFree(GetProcessHeap(), 0, bufferW );
+
+    return result;
+}
+
+/***********************************************************************
+ *           HTTP_GetRedirectURL (internal)
+ */
+static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
+{
+    static WCHAR szHttp[] = {'h','t','t','p',0};
+    static WCHAR szHttps[] = {'h','t','t','p','s',0};
+    http_session_t *lpwhs = lpwhr->lpHttpSession;
+    URL_COMPONENTSW urlComponents;
+    DWORD url_length = 0;
+    LPWSTR orig_url;
+    LPWSTR combined_url;
+
+    urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
+    urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
+    urlComponents.dwSchemeLength = 0;
+    urlComponents.lpszHostName = lpwhs->lpszHostName;
+    urlComponents.dwHostNameLength = 0;
+    urlComponents.nPort = lpwhs->nHostPort;
+    urlComponents.lpszUserName = lpwhs->lpszUserName;
+    urlComponents.dwUserNameLength = 0;
+    urlComponents.lpszPassword = NULL;
+    urlComponents.dwPasswordLength = 0;
+    urlComponents.lpszUrlPath = lpwhr->lpszPath;
+    urlComponents.dwUrlPathLength = 0;
+    urlComponents.lpszExtraInfo = NULL;
+    urlComponents.dwExtraInfoLength = 0;
+
+    if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
+        (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
+        return NULL;
+
+    orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
+
+    /* convert from bytes to characters */
+    url_length = url_length / sizeof(WCHAR) - 1;
+    if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
+    {
+        HeapFree(GetProcessHeap(), 0, orig_url);
+        return NULL;
+    }
+
+    url_length = 0;
+    if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
+        (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
+    {
+        HeapFree(GetProcessHeap(), 0, orig_url);
+        return NULL;
+    }
+    combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
+
+    if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
+    {
+        HeapFree(GetProcessHeap(), 0, orig_url);
+        HeapFree(GetProcessHeap(), 0, combined_url);
+        return NULL;
+    }
+    HeapFree(GetProcessHeap(), 0, orig_url);
+    return combined_url;
+}
+
+
+/***********************************************************************
+ *           HTTP_HandleRedirect (internal)
+ */
+static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
+{
+    http_session_t *lpwhs = lpwhr->lpHttpSession;
+    appinfo_t *hIC = lpwhs->lpAppInfo;
+    BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
+    WCHAR path[INTERNET_MAX_URL_LENGTH];
+    int index;
+
+    if(lpszUrl[0]=='/')
+    {
+        /* if it's an absolute path, keep the same session info */
+        lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
+    }
+    else
+    {
+        URL_COMPONENTSW urlComponents;
+        WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
+        static WCHAR szHttp[] = {'h','t','t','p',0};
+        static WCHAR szHttps[] = {'h','t','t','p','s',0};
+
+        userName[0] = 0;
+        hostName[0] = 0;
+        protocol[0] = 0;
+
+        urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
+        urlComponents.lpszScheme = protocol;
+        urlComponents.dwSchemeLength = 32;
+        urlComponents.lpszHostName = hostName;
+        urlComponents.dwHostNameLength = MAXHOSTNAME;
+        urlComponents.lpszUserName = userName;
+        urlComponents.dwUserNameLength = 1024;
+        urlComponents.lpszPassword = NULL;
+        urlComponents.dwPasswordLength = 0;
+        urlComponents.lpszUrlPath = path;
+        urlComponents.dwUrlPathLength = 2048;
+        urlComponents.lpszExtraInfo = NULL;
+        urlComponents.dwExtraInfoLength = 0;
+        if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
+            return INTERNET_GetLastError();
+
+        if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
+            (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
+        {
+            TRACE("redirect from secure page to non-secure page\n");
+            /* FIXME: warn about from secure redirect to non-secure page */
+            lpwhr->hdr.dwFlags &= ~INTERNET_FLAG_SECURE;
+        }
+        if (!strncmpW(szHttps, urlComponents.lpszScheme, strlenW(szHttps)) &&
+            !(lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
+        {
+            TRACE("redirect from non-secure page to secure page\n");
+            /* FIXME: notify about redirect to secure page */
+            lpwhr->hdr.dwFlags |= INTERNET_FLAG_SECURE;
+        }
+
+        if (urlComponents.nPort == INTERNET_INVALID_PORT_NUMBER)
+        {
+            if (lstrlenW(protocol)>4) /*https*/
+                urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
+            else /*http*/
+                urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
+        }
+
+#if 0
+        /*
+         * This upsets redirects to binary files on sourceforge.net 
+         * and gives an html page instead of the target file
+         * Examination of the HTTP request sent by native wininet.dll
+         * reveals that it doesn't send a referrer in that case.
+         * Maybe there's a flag that enables this, or maybe a referrer
+         * shouldn't be added in case of a redirect.
+         */
+
+        /* consider the current host as the referrer */
+        if (lpwhs->lpszServerName && *lpwhs->lpszServerName)
+            HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpwhs->lpszServerName,
+                           HTTP_ADDHDR_FLAG_REQ|HTTP_ADDREQ_FLAG_REPLACE|
+                           HTTP_ADDHDR_FLAG_ADD_IF_NEW);
+#endif
+        
+        HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
+        if (urlComponents.nPort != INTERNET_DEFAULT_HTTP_PORT &&
+            urlComponents.nPort != INTERNET_DEFAULT_HTTPS_PORT)
+        {
+            int len;
+            static const WCHAR fmt[] = {'%','s',':','%','i',0};
+            len = lstrlenW(hostName);
+            len += 7; /* 5 for strlen("65535") + 1 for ":" + 1 for '\0' */
+            lpwhs->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
+            sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
+        }
+        else
+            lpwhs->lpszHostName = heap_strdupW(hostName);
+
+        HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
+
+        HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
+        lpwhs->lpszUserName = NULL;
+        if (userName[0])
+            lpwhs->lpszUserName = heap_strdupW(userName);
+
+        if (!using_proxy)
+        {
+            if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
+            {
+                DWORD res;
+
+                HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
+                lpwhs->lpszServerName = heap_strdupW(hostName);
+                lpwhs->nServerPort = urlComponents.nPort;
+
+                NETCON_close(&lpwhr->netConnection);
+                if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
+                    return res;
+
+                res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
+                if (res != ERROR_SUCCESS)
+                    return res;
+
+                lpwhr->read_pos = lpwhr->read_size = 0;
+                lpwhr->read_chunked = FALSE;
+            }
+        }
+        else
+            TRACE("Redirect through proxy\n");
+    }
+
+    HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
+    lpwhr->lpszPath=NULL;
+    if (*path)
+    {
+        DWORD needed = 0;
+        HRESULT rc;
+
+        rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
+        if (rc != E_POINTER)
+            needed = strlenW(path)+1;
+        lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
+        rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
+                        URL_ESCAPE_SPACES_ONLY);
+        if (rc != S_OK)
+        {
+            ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
+            strcpyW(lpwhr->lpszPath,path);
+        }
+    }
+
+    /* Remove custom content-type/length headers on redirects.  */
+    index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
+    if (0 <= index)
+        HTTP_DeleteCustomHeader(lpwhr, index);
+    index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
+    if (0 <= index)
+        HTTP_DeleteCustomHeader(lpwhr, index);
+
+    return ERROR_SUCCESS;
+}
+
+/***********************************************************************
+ *           HTTP_build_req (internal)
+ *
+ *  concatenate all the strings in the request together
+ */
+static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
+{
+    LPCWSTR *t;
+    LPWSTR str;
+
+    for( t = list; *t ; t++  )
+        len += strlenW( *t );
+    len++;
+
+    str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+    *str = 0;
+
+    for( t = list; *t ; t++ )
+        strcatW( str, *t );
+
+    return str;
+}
+
+static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
+{
+    LPWSTR lpszPath;
+    LPWSTR requestString;
+    INT len;
+    INT cnt;
+    INT responseLen;
+    char *ascii_req;
+    DWORD res;
+    static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
+    static const WCHAR szFormat[] = {'%','s',':','%','d',0};
+    http_session_t *lpwhs = lpwhr->lpHttpSession;
+
+    TRACE("\n");
+
+    lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
+    sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
+    requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, g_szHttp1_1 );
+    HeapFree( GetProcessHeap(), 0, lpszPath );
+
+    len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
+                                NULL, 0, NULL, NULL );
+    len--; /* the nul terminator isn't needed */
+    ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
+    WideCharToMultiByte( CP_ACP, 0, requestString, -1,
+                            ascii_req, len, NULL, NULL );
+    HeapFree( GetProcessHeap(), 0, requestString );
+
+    TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
+
+    res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
+    HeapFree( GetProcessHeap(), 0, ascii_req );
+    if (res != ERROR_SUCCESS)
+        return res;
+
+    responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
+    if (!responseLen)
+        return ERROR_HTTP_INVALID_HEADER;
+
+    return ERROR_SUCCESS;
+}
+
+static void HTTP_InsertCookies(http_request_t *lpwhr)
+{
+    static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
+    LPWSTR lpszCookies, lpszUrl = NULL;
+    DWORD nCookieSize, size;
+    LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
+
+    size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
+    if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
+    sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
+
+    if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
+    {
+        int cnt = 0;
+        static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
+
+        size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
+        if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
+        {
+            cnt += sprintfW(lpszCookies, szCookie);
+            InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
+            strcatW(lpszCookies, szCrLf);
+
+            HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
+            HeapFree(GetProcessHeap(), 0, lpszCookies);
+        }
+    }
+    HeapFree(GetProcessHeap(), 0, lpszUrl);
+}
+
+/***********************************************************************
+ *           HTTP_HttpSendRequestW (internal)
+ *
+ * Sends the specified request to the HTTP server
+ *
+ * RETURNS
+ *    TRUE  on success
+ *    FALSE on failure
+ *
+ */
+static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
+       DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
+       DWORD dwContentLength, BOOL bEndRequest)
+{
+    INT cnt;
+    BOOL redirected = FALSE;
+    LPWSTR requestString = NULL;
+    INT responseLen;
+    BOOL loop_next;
+    INTERNET_ASYNC_RESULT iar;
+    static const WCHAR szPost[] = { 'P','O','S','T',0 };
+    static const WCHAR szContentLength[] =
+        { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
+    WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
+    DWORD res;
+
+    TRACE("--> %p\n", lpwhr);
+
+    assert(lpwhr->hdr.htype == WH_HHTTPREQ);
+
+    /* if the verb is NULL default to GET */
+    if (!lpwhr->lpszVerb)
+        lpwhr->lpszVerb = heap_strdupW(szGET);
+
+    if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
+    {
+        sprintfW(contentLengthStr, szContentLength, dwContentLength);
+        HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
+        lpwhr->dwBytesToWrite = dwContentLength;
+    }
+    if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
+    {
+        WCHAR *agent_header;
+        static const WCHAR user_agent[] = {'U','s','e','r','-','A','g','e','n','t',':',' ','%','s','\r','\n',0};
+        int len;
+
+        len = strlenW(lpwhr->lpHttpSession->lpAppInfo->lpszAgent) + strlenW(user_agent);
+        agent_header = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        sprintfW(agent_header, user_agent, lpwhr->lpHttpSession->lpAppInfo->lpszAgent);
+
+        HTTP_HttpAddRequestHeadersW(lpwhr, agent_header, strlenW(agent_header), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
+        HeapFree(GetProcessHeap(), 0, agent_header);
+    }
+    if (lpwhr->hdr.dwFlags & INTERNET_FLAG_PRAGMA_NOCACHE)
+    {
+        static const WCHAR pragma_nocache[] = {'P','r','a','g','m','a',':',' ','n','o','-','c','a','c','h','e','\r','\n',0};
+        HTTP_HttpAddRequestHeadersW(lpwhr, pragma_nocache, strlenW(pragma_nocache), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
+    }
+    if ((lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_CACHE_WRITE) && !strcmpW(lpwhr->lpszVerb, szPost))
+    {
+        static const WCHAR cache_control[] = {'C','a','c','h','e','-','C','o','n','t','r','o','l',':',
+                                              ' ','n','o','-','c','a','c','h','e','\r','\n',0};
+        HTTP_HttpAddRequestHeadersW(lpwhr, cache_control, strlenW(cache_control), HTTP_ADDREQ_FLAG_ADD_IF_NEW);
+    }
+
+    do
+    {
+        DWORD len;
+        char *ascii_req;
+
+        loop_next = FALSE;
+
+        /* like native, just in case the caller forgot to call InternetReadFile
+         * for all the data */
+        HTTP_DrainContent(lpwhr);
+        lpwhr->dwContentRead = 0;
+
+        if (TRACE_ON(wininet))
+        {
+            LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
+            TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
+        }
+
+        HTTP_FixURL(lpwhr);
+        if (lpwhr->hdr.dwFlags & INTERNET_FLAG_KEEP_CONNECTION)
+        {
+            HTTP_ProcessHeader(lpwhr, szConnection, szKeepAlive, HTTP_ADDHDR_FLAG_REQ | HTTP_ADDHDR_FLAG_REPLACE);
+        }
+        HTTP_InsertAuthorization(lpwhr, lpwhr->pAuthInfo, szAuthorization);
+        HTTP_InsertAuthorization(lpwhr, lpwhr->pProxyAuthInfo, szProxy_Authorization);
+
+        if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES))
+            HTTP_InsertCookies(lpwhr);
+
+        /* add the headers the caller supplied */
+        if( lpszHeaders && dwHeaderLength )
+        {
+            HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
+                        HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
+        }
+
+        if (lpwhr->lpHttpSession->lpAppInfo->lpszProxy && lpwhr->lpHttpSession->lpAppInfo->lpszProxy[0])
+        {
+            WCHAR *url = HTTP_BuildProxyRequestUrl(lpwhr);
+            requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, url, lpwhr->lpszVersion);
+            HeapFree(GetProcessHeap(), 0, url);
+        }
+        else
+            requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
+
+        TRACE("Request header -> %s\n", debugstr_w(requestString) );
+
+        /* Send the request and store the results */
+        if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
+            goto lend;
+
+        /* send the request as ASCII, tack on the optional data */
+        if (!lpOptional || redirected)
+            dwOptionalLength = 0;
+        len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
+                                   NULL, 0, NULL, NULL );
+        ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
+        WideCharToMultiByte( CP_ACP, 0, requestString, -1,
+                             ascii_req, len, NULL, NULL );
+        if( lpOptional )
+            memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
+        len = (len + dwOptionalLength - 1);
+        ascii_req[len] = 0;
+        TRACE("full request -> %s\n", debugstr_a(ascii_req) );
+
+        INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                              INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
+
+        res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
+        HeapFree( GetProcessHeap(), 0, ascii_req );
+
+        lpwhr->dwBytesWritten = dwOptionalLength;
+
+        INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                              INTERNET_STATUS_REQUEST_SENT,
+                              &len, sizeof(DWORD));
+
+        if (bEndRequest)
+        {
+            DWORD dwBufferSize;
+            DWORD dwStatusCode;
+
+            INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                                INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
+    
+            if (res != ERROR_SUCCESS)
+                goto lend;
+    
+            responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
+    
+            INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                                INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
+                                sizeof(DWORD));
+
+            HTTP_ProcessCookies(lpwhr);
+
+            if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
+
+            dwBufferSize = sizeof(dwStatusCode);
+            if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
+                                    &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
+                dwStatusCode = 0;
+
+            if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
+            {
+                WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
+                dwBufferSize=sizeof(szNewLocation);
+                if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
+                    HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
+                {
+                    if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
+                    {
+                        HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
+                        lpwhr->lpszVerb = heap_strdupW(szGET);
+                    }
+                    HTTP_DrainContent(lpwhr);
+                    if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
+                    {
+                        INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
+                                              new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
+                        res = HTTP_HandleRedirect(lpwhr, new_url);
+                        if (res == ERROR_SUCCESS)
+                        {
+                            HeapFree(GetProcessHeap(), 0, requestString);
+                            loop_next = TRUE;
+                        }
+                        HeapFree( GetProcessHeap(), 0, new_url );
+                    }
+                    redirected = TRUE;
+                }
+            }
+            if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
+            {
+                WCHAR szAuthValue[2048];
+                dwBufferSize=2048;
+                if (dwStatusCode == HTTP_STATUS_DENIED)
+                {
+                    LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
+                    DWORD dwIndex = 0;
+                    while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
+                    {
+                        if (HTTP_DoAuthorization(lpwhr, szAuthValue,
+                                                 &lpwhr->pAuthInfo,
+                                                 lpwhr->lpHttpSession->lpszUserName,
+                                                 lpwhr->lpHttpSession->lpszPassword,
+                                                 Host->lpszValue))
+                        {
+                            HeapFree(GetProcessHeap(), 0, requestString);
+                            loop_next = TRUE;
+                            break;
+                        }
+                    }
+                }
+                if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
+                {
+                    DWORD dwIndex = 0;
+                    while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
+                    {
+                        if (HTTP_DoAuthorization(lpwhr, szAuthValue,
+                                                 &lpwhr->pProxyAuthInfo,
+                                                 lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
+                                                 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
+                                                 NULL))
+                        {
+                            loop_next = TRUE;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        else
+            res = ERROR_SUCCESS;
+    }
+    while (loop_next);
+
+    if(res == ERROR_SUCCESS) {
+        WCHAR url[INTERNET_MAX_URL_LENGTH];
+        WCHAR cacheFileName[MAX_PATH+1];
+        BOOL b;
+
+        b = HTTP_GetRequestURL(lpwhr, url);
+        if(!b) {
+            WARN("Could not get URL\n");
+            goto lend;
+        }
+
+        b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
+        if(b) {
+            HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
+            CloseHandle(lpwhr->hCacheFile);
+
+            lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
+            lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                      NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+            if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
+                WARN("Could not create file: %u\n", GetLastError());
+                lpwhr->hCacheFile = NULL;
+            }
+        }else {
+            WARN("Could not create cache entry: %08x\n", GetLastError());
+        }
+    }
+
+lend:
+
+    HeapFree(GetProcessHeap(), 0, requestString);
+
+    /* TODO: send notification for P3P header */
+
+    if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+    {
+        if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
+            HTTP_ReceiveRequestData(lpwhr, TRUE);
         else
-            lpwhs->lpszHostName = WININET_strdupW(hostName);
+        {
+            iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
+            iar.dwError = res;
+
+            INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                                  INTERNET_STATUS_REQUEST_COMPLETE, &iar,
+                                  sizeof(INTERNET_ASYNC_RESULT));
+        }
+    }
+
+    TRACE("<--\n");
+    return res;
+}
+
+/***********************************************************************
+ *
+ * Helper functions for the HttpSendRequest(Ex) functions
+ *
+ */
+static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
+{
+    struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
+    http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
+
+    TRACE("%p\n", lpwhr);
+
+    HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
+            req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
+            req->dwContentLength, req->bEndRequest);
+
+    HeapFree(GetProcessHeap(), 0, req->lpszHeader);
+}
+
+
+static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
+{
+    INT responseLen;
+    DWORD dwBufferSize;
+    INTERNET_ASYNC_RESULT iar;
+    DWORD res = ERROR_SUCCESS;
+
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                  INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
+
+    responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
+    if (!responseLen)
+        res = ERROR_HTTP_HEADER_NOT_FOUND;
+
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                  INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
 
-        HTTP_ProcessHeader(lpwhr, szHost, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
+    /* process cookies here. Is this right? */
+    HTTP_ProcessCookies(lpwhr);
+
+    if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
+
+    if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
+    {
+        DWORD dwCode,dwCodeLength = sizeof(DWORD);
+        if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
+            && (dwCode == 302 || dwCode == 301 || dwCode == 303))
+        {
+            WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
+            dwBufferSize=sizeof(szNewLocation);
+            if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
+            {
+                if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
+                {
+                    HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
+                    lpwhr->lpszVerb = heap_strdupW(szGET);
+                }
+                HTTP_DrainContent(lpwhr);
+                if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
+                {
+                    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
+                                          new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
+                    res = HTTP_HandleRedirect(lpwhr, new_url);
+                    if (res == ERROR_SUCCESS)
+                        res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
+                    HeapFree( GetProcessHeap(), 0, new_url );
+                }
+            }
+        }
+    }
+
+    iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
+    iar.dwError = res;
+
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                          INTERNET_STATUS_REQUEST_COMPLETE, &iar,
+                          sizeof(INTERNET_ASYNC_RESULT));
+    return res;
+}
+
+/***********************************************************************
+ *           HttpEndRequestA (WININET.@)
+ *
+ * Ends an HTTP request that was started by HttpSendRequestEx
+ *
+ * RETURNS
+ *    TRUE     if successful
+ *    FALSE    on failure
+ *
+ */
+BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
+        LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
+{
+    TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
+
+    if (lpBuffersOut)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
+}
+
+static void AsyncHttpEndRequestProc(WORKREQUEST *work)
+{
+    struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
+    http_request_t *lpwhr = (http_request_t*)work->hdr;
+
+    TRACE("%p\n", lpwhr);
+
+    HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
+}
+
+/***********************************************************************
+ *           HttpEndRequestW (WININET.@)
+ *
+ * Ends an HTTP request that was started by HttpSendRequestEx
+ *
+ * RETURNS
+ *    TRUE     if successful
+ *    FALSE    on failure
+ *
+ */
+BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
+        LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
+{
+    http_request_t *lpwhr;
+    DWORD res;
+
+    TRACE("-->\n");
+
+    if (lpBuffersOut)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    lpwhr = (http_request_t*) WININET_GetObject( hRequest );
+
+    if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
+    {
+        SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+        if (lpwhr)
+            WININET_Release( &lpwhr->hdr );
+        return FALSE;
+    }
+    lpwhr->hdr.dwFlags |= dwFlags;
 
+    if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+    {
+        WORKREQUEST work;
+        struct WORKREQ_HTTPENDREQUESTW *request;
 
-        HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
-        lpwhs->lpszUserName = NULL;
-        if (userName[0])
-            lpwhs->lpszUserName = WININET_strdupW(userName);
-        lpwhs->nServerPort = urlComponents.nPort;
+        work.asyncproc = AsyncHttpEndRequestProc;
+        work.hdr = WININET_AddRef( &lpwhr->hdr );
 
-        INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                              INTERNET_STATUS_RESOLVING_NAME,
-                              lpwhs->lpszServerName,
-                              strlenW(lpwhs->lpszServerName)+1);
+        request = &work.u.HttpEndRequestW;
+        request->dwFlags = dwFlags;
+        request->dwContext = dwContext;
 
-        if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
-                    &lpwhs->socketAddress))
-        {
-            INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
-            return FALSE;
-        }
+        INTERNET_AsyncCall(&work);
+        res = ERROR_IO_PENDING;
+    }
+    else
+        res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
 
-        inet_ntop(lpwhs->socketAddress.sin_family, &lpwhs->socketAddress.sin_addr,
-              szaddr, sizeof(szaddr));
-        INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                              INTERNET_STATUS_NAME_RESOLVED,
-                              szaddr, strlen(szaddr)+1);
+    WININET_Release( &lpwhr->hdr );
+    TRACE("%u <--\n", res);
+    if(res != ERROR_SUCCESS)
+        SetLastError(res);
+    return res == ERROR_SUCCESS;
+}
 
-        NETCON_close(&lpwhr->netConnection);
+/***********************************************************************
+ *           HttpSendRequestExA (WININET.@)
+ *
+ * Sends the specified request to the HTTP server and allows chunked
+ * transfers.
+ *
+ * RETURNS
+ *  Success: TRUE
+ *  Failure: FALSE, call GetLastError() for more information.
+ */
+BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
+                              LPINTERNET_BUFFERSA lpBuffersIn,
+                              LPINTERNET_BUFFERSA lpBuffersOut,
+                              DWORD dwFlags, DWORD_PTR dwContext)
+{
+    INTERNET_BUFFERSW BuffersInW;
+    BOOL rc = FALSE;
+    DWORD headerlen;
+    LPWSTR header = NULL;
 
-        if (!NETCON_init(&lpwhr->netConnection,lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
-            return FALSE;
-    }
+    TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
+           lpBuffersOut, dwFlags, dwContext);
 
-    HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
-    lpwhr->lpszPath=NULL;
-    if (strlenW(path))
+    if (lpBuffersIn)
     {
-        DWORD needed = 0;
-        HRESULT rc;
-
-        rc = UrlEscapeW(path, NULL, &needed, URL_ESCAPE_SPACES_ONLY);
-        if (rc != E_POINTER)
-            needed = strlenW(path)+1;
-        lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
-        rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
-                        URL_ESCAPE_SPACES_ONLY);
-        if (rc)
+        BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
+        if (lpBuffersIn->lpcszHeader)
         {
-            ERR("Unable to escape string!(%s) (%ld)\n",debugstr_w(path),rc);
-            strcpyW(lpwhr->lpszPath,path);
+            headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
+                    lpBuffersIn->dwHeadersLength,0,0);
+            header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
+            if (!(BuffersInW.lpcszHeader = header))
+            {
+                SetLastError(ERROR_OUTOFMEMORY);
+                return FALSE;
+            }
+            BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
+                    lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
+                    header, headerlen);
         }
+        else
+            BuffersInW.lpcszHeader = NULL;
+        BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
+        BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
+        BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
+        BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
+        BuffersInW.Next = NULL;
     }
 
-    return HTTP_HttpSendRequestW(lpwhr, lpszHeaders, dwHeaderLength, lpOptional,
-                                 dwOptionalLength, dwContentLength, TRUE);
+    rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
+
+    HeapFree(GetProcessHeap(),0,header);
+
+    return rc;
 }
 
 /***********************************************************************
- *           HTTP_build_req (internal)
+ *           HttpSendRequestExW (WININET.@)
  *
- *  concatenate all the strings in the request together
+ * Sends the specified request to the HTTP server and allows chunked
+ * transfers
+ *
+ * RETURNS
+ *  Success: TRUE
+ *  Failure: FALSE, call GetLastError() for more information.
  */
-static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
+BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
+                   LPINTERNET_BUFFERSW lpBuffersIn,
+                   LPINTERNET_BUFFERSW lpBuffersOut,
+                   DWORD dwFlags, DWORD_PTR dwContext)
 {
-    LPCWSTR *t;
-    LPWSTR str;
+    http_request_t *lpwhr;
+    http_session_t *lpwhs;
+    appinfo_t *hIC;
+    DWORD res;
 
-    for( t = list; *t ; t++  )
-        len += strlenW( *t );
-    len++;
+    TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
+            lpBuffersOut, dwFlags, dwContext);
 
-    str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
-    *str = 0;
+    lpwhr = (http_request_t*) WININET_GetObject( hRequest );
 
-    for( t = list; *t ; t++ )
-        strcatW( str, *t );
+    if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
+    {
+        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
+        goto lend;
+    }
 
-    return str;
-}
+    lpwhs = lpwhr->lpHttpSession;
+    assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
+    hIC = lpwhs->lpAppInfo;
+    assert(hIC->hdr.htype == WH_HINIT);
 
-static BOOL HTTP_SecureProxyConnect(LPWININETHTTPREQW lpwhr)
-{
-    LPWSTR lpszPath;
-    LPWSTR requestString;
-    INT len;
-    INT cnt;
-    INT responseLen;
-    char *ascii_req;
-    BOOL ret;
-    static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
-    static const WCHAR szFormat[] = {'%','s',':','%','d',0};
-    LPWININETHTTPSESSIONW lpwhs = (LPWININETHTTPSESSIONW)lpwhr->hdr.lpwhparent;
+    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+    {
+        WORKREQUEST workRequest;
+        struct WORKREQ_HTTPSENDREQUESTW *req;
 
-    TRACE("\n");
+        workRequest.asyncproc = AsyncHttpSendRequestProc;
+        workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
+        req = &workRequest.u.HttpSendRequestW;
+        if (lpBuffersIn)
+        {
+            DWORD size = 0;
 
-    lpszPath = HeapAlloc( GetProcessHeap(), 0, (lstrlenW( lpwhs->lpszHostName ) + 13)*sizeof(WCHAR) );
-    sprintfW( lpszPath, szFormat, lpwhs->lpszHostName, lpwhs->nHostPort );
-    requestString = HTTP_BuildHeaderRequestString( lpwhr, szConnect, lpszPath, FALSE );
-    HeapFree( GetProcessHeap(), 0, lpszPath );
+            if (lpBuffersIn->lpcszHeader)
+            {
+                if (lpBuffersIn->dwHeadersLength == ~0u)
+                    size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
+                else
+                    size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
 
-    len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
-                                NULL, 0, NULL, NULL );
-    len--; /* the nul terminator isn't needed */
-    ascii_req = HeapAlloc( GetProcessHeap(), 0, len );
-    WideCharToMultiByte( CP_ACP, 0, requestString, -1,
-                            ascii_req, len, NULL, NULL );
-    HeapFree( GetProcessHeap(), 0, requestString );
+                req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
+                memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
+            }
+            else req->lpszHeader = NULL;
 
-    TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
+            req->dwHeaderLength = size / sizeof(WCHAR);
+            req->lpOptional = lpBuffersIn->lpvBuffer;
+            req->dwOptionalLength = lpBuffersIn->dwBufferLength;
+            req->dwContentLength = lpBuffersIn->dwBufferTotal;
+        }
+        else
+        {
+            req->lpszHeader = NULL;
+            req->dwHeaderLength = 0;
+            req->lpOptional = NULL;
+            req->dwOptionalLength = 0;
+            req->dwContentLength = 0;
+        }
 
-    ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
-    HeapFree( GetProcessHeap(), 0, ascii_req );
-    if (!ret || cnt < 0)
-        return FALSE;
+        req->bEndRequest = FALSE;
 
-    responseLen = HTTP_GetResponseHeaders( lpwhr );
-    if (!responseLen)
-        return FALSE;
+        INTERNET_AsyncCall(&workRequest);
+        /*
+         * This is from windows.
+         */
+        res = ERROR_IO_PENDING;
+    }
+    else
+    {
+        if (lpBuffersIn)
+            res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
+                                        lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
+                                        lpBuffersIn->dwBufferTotal, FALSE);
+        else
+            res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
+    }
 
-    return TRUE;
+lend:
+    if ( lpwhr )
+        WININET_Release( &lpwhr->hdr );
+
+    TRACE("<---\n");
+    SetLastError(res);
+    return res == ERROR_SUCCESS;
 }
 
 /***********************************************************************
- *           HTTP_HttpSendRequestW (internal)
+ *           HttpSendRequestW (WININET.@)
  *
  * Sends the specified request to the HTTP server
  *
@@ -2243,152 +4024,184 @@ static BOOL HTTP_SecureProxyConnect(LPWININETHTTPREQW lpwhr)
  *    FALSE on failure
  *
  */
-BOOL WINAPI HTTP_HttpSendRequestW(LPWININETHTTPREQW lpwhr, LPCWSTR lpszHeaders,
-       DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
-       DWORD dwContentLength, BOOL bEndRequest)
+BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
+       DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
 {
-    INT cnt;
-    BOOL bSuccess = FALSE;
-    LPWSTR requestString = NULL;
-    INT responseLen;
-    BOOL loop_next = FALSE;
-    INTERNET_ASYNC_RESULT iar;
-    LPHTTPHEADERW Host;
+    http_request_t *lpwhr;
+    http_session_t *lpwhs = NULL;
+    appinfo_t *hIC = NULL;
+    DWORD res = ERROR_SUCCESS;
 
-    TRACE("--> %p\n", lpwhr);
-
-    assert(lpwhr->hdr.htype == WH_HHTTPREQ);
-
-    /* Clear any error information */
-    INTERNET_SetLastError(0);
+    TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
+            debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
 
-    HTTP_FixVerb(lpwhr);
-
-    /* if we are using optional stuff, we must add the fixed header of that option length */
-    if (dwContentLength > 0)
+    lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
+    if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
     {
-        static const WCHAR szContentLength[] = {
-            'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0};
-        WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \n\r */ + 20 /* int */ ];
-        sprintfW(contentLengthStr, szContentLength, dwContentLength);
-        HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_ADD);
+        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
+        goto lend;
     }
 
-    Host = HTTP_GetHeader(lpwhr,szHost);
-    do
+    lpwhs = lpwhr->lpHttpSession;
+    if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
     {
-        DWORD len;
-        char *ascii_req;
+        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
+        goto lend;
+    }
 
-        TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
-        loop_next = FALSE;
+    hIC = lpwhs->lpAppInfo;
+    if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
+    {
+        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
+        goto lend;
+    }
 
-        HTTP_FixURL(lpwhr);
+    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+    {
+        WORKREQUEST workRequest;
+        struct WORKREQ_HTTPSENDREQUESTW *req;
 
-        /* add the headers the caller supplied */
-        if( lpszHeaders && dwHeaderLength )
+        workRequest.asyncproc = AsyncHttpSendRequestProc;
+        workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
+        req = &workRequest.u.HttpSendRequestW;
+        if (lpszHeaders)
         {
-            HTTP_HttpAddRequestHeadersW(lpwhr, lpszHeaders, dwHeaderLength,
-                        HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REPLACE);
-        }
+            DWORD size;
 
-        /* if there's a proxy username and password, add it to the headers */
-        HTTP_AddProxyInfo(lpwhr);
+            if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
+            else size = dwHeaderLength * sizeof(WCHAR);
 
-        requestString = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, FALSE);
-
-        TRACE("Request header -> %s\n", debugstr_w(requestString) );
+            req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
+            memcpy(req->lpszHeader, lpszHeaders, size);
+        }
+        else
+            req->lpszHeader = 0;
+        req->dwHeaderLength = dwHeaderLength;
+        req->lpOptional = lpOptional;
+        req->dwOptionalLength = dwOptionalLength;
+        req->dwContentLength = dwOptionalLength;
+        req->bEndRequest = TRUE;
 
-        /* Send the request and store the results */
-        if (!HTTP_OpenConnection(lpwhr))
-            goto lend;
+        INTERNET_AsyncCall(&workRequest);
+        /*
+         * This is from windows.
+         */
+        res = ERROR_IO_PENDING;
+    }
+    else
+    {
+       res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
+               dwHeaderLength, lpOptional, dwOptionalLength,
+               dwOptionalLength, TRUE);
+    }
+lend:
+    if( lpwhr )
+        WININET_Release( &lpwhr->hdr );
 
-        /* send the request as ASCII, tack on the optional data */
-        if( !lpOptional )
-            dwOptionalLength = 0;
-        len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
-                                   NULL, 0, NULL, NULL );
-        ascii_req = HeapAlloc( GetProcessHeap(), 0, len + dwOptionalLength );
-        WideCharToMultiByte( CP_ACP, 0, requestString, -1,
-                             ascii_req, len, NULL, NULL );
-        if( lpOptional )
-            memcpy( &ascii_req[len-1], lpOptional, dwOptionalLength );
-        len = (len + dwOptionalLength - 1);
-        ascii_req[len] = 0;
-        TRACE("full request -> %s\n", debugstr_a(ascii_req) );
+    SetLastError(res);
+    return res == ERROR_SUCCESS;
+}
 
-        INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                              INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
+/***********************************************************************
+ *           HttpSendRequestA (WININET.@)
+ *
+ * Sends the specified request to the HTTP server
+ *
+ * RETURNS
+ *    TRUE  on success
+ *    FALSE on failure
+ *
+ */
+BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
+       DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
+{
+    BOOL result;
+    LPWSTR szHeaders=NULL;
+    DWORD nLen=dwHeaderLength;
+    if(lpszHeaders!=NULL)
+    {
+        nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
+        szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
+        MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
+    }
+    result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
+    HeapFree(GetProcessHeap(),0,szHeaders);
+    return result;
+}
 
-        NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
-        HeapFree( GetProcessHeap(), 0, ascii_req );
+/***********************************************************************
+ *           HTTPSESSION_Destroy (internal)
+ *
+ * Deallocate session handle
+ *
+ */
+static void HTTPSESSION_Destroy(object_header_t *hdr)
+{
+    http_session_t *lpwhs = (http_session_t*) hdr;
 
-        INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                              INTERNET_STATUS_REQUEST_SENT,
-                              &len, sizeof(DWORD));
+    TRACE("%p\n", lpwhs);
 
-        if (bEndRequest)
-        {
-            INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                                INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
+    WININET_Release(&lpwhs->lpAppInfo->hdr);
 
-            if (cnt < 0)
-                goto lend;
+    HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
+    HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
+    HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
+    HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
+    HeapFree(GetProcessHeap(), 0, lpwhs);
+}
 
-            responseLen = HTTP_GetResponseHeaders(lpwhr);
-            if (responseLen)
-                bSuccess = TRUE;
+static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
+{
+    switch(option) {
+    case INTERNET_OPTION_HANDLE_TYPE:
+        TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
 
-            INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                                INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
-                                sizeof(DWORD));
+        if (*size < sizeof(ULONG))
+            return ERROR_INSUFFICIENT_BUFFER;
 
-            HTTP_ProcessHeaders(lpwhr);
-        }
-        else
-            bSuccess = TRUE;
+        *size = sizeof(DWORD);
+        *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_HTTP;
+        return ERROR_SUCCESS;
     }
-    while (loop_next);
-
-lend:
 
-    HeapFree(GetProcessHeap(), 0, requestString);
+    return INET_QueryOption(option, buffer, size, unicode);
+}
 
-    /* TODO: send notification for P3P header */
+static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
+{
+    http_session_t *ses = (http_session_t*)hdr;
 
-    if(!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess && bEndRequest)
+    switch(option) {
+    case INTERNET_OPTION_USERNAME:
     {
-        DWORD dwCode,dwCodeLength=sizeof(DWORD),dwIndex=0;
-        if(HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,&dwCode,&dwCodeLength,&dwIndex) &&
-            (dwCode==302 || dwCode==301))
-        {
-            WCHAR szNewLocation[2048];
-            DWORD dwBufferSize=2048;
-            dwIndex=0;
-            if(HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,&dwIndex))
-            {
-                INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                                      INTERNET_STATUS_REDIRECT, szNewLocation,
-                                      dwBufferSize);
-                return HTTP_HandleRedirect(lpwhr, szNewLocation, lpszHeaders,
-                                           dwHeaderLength, lpOptional, dwOptionalLength,
-                                           dwContentLength);
-            }
-        }
+        HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
+        if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
+        return ERROR_SUCCESS;
+    }
+    case INTERNET_OPTION_PASSWORD:
+    {
+        HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
+        if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
+        return ERROR_SUCCESS;
+    }
+    default: break;
     }
 
-
-    iar.dwResult = (DWORD)bSuccess;
-    iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
-
-    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                          INTERNET_STATUS_REQUEST_COMPLETE, &iar,
-                          sizeof(INTERNET_ASYNC_RESULT));
-
-    TRACE("<--\n");
-    return bSuccess;
+    return ERROR_INTERNET_INVALID_OPTION;
 }
 
+static const object_vtbl_t HTTPSESSIONVtbl = {
+    HTTPSESSION_Destroy,
+    NULL,
+    HTTPSESSION_QueryOption,
+    HTTPSESSION_SetOption,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
 
 /***********************************************************************
  *           HTTP_Connect  (internal)
@@ -2400,62 +4213,63 @@ lend:
  *   NULL on failure
  *
  */
-HINTERNET HTTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
-       INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
-       LPCWSTR lpszPassword, DWORD dwFlags, DWORD dwContext,
-       DWORD dwInternalFlags)
+DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
+        INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
+        LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
+        DWORD dwInternalFlags, HINTERNET *ret)
 {
-    BOOL bSuccess = FALSE;
-    LPWININETHTTPSESSIONW lpwhs = NULL;
+    http_session_t *lpwhs = NULL;
     HINTERNET handle = NULL;
+    DWORD res = ERROR_SUCCESS;
 
     TRACE("-->\n");
 
-    assert( hIC->hdr.htype == WH_HINIT );
+    if (!lpszServerName || !lpszServerName[0])
+        return ERROR_INVALID_PARAMETER;
 
-    hIC->hdr.dwContext = dwContext;
+    assert( hIC->hdr.htype == WH_HINIT );
 
-    lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETHTTPSESSIONW));
-    if (NULL == lpwhs)
-    {
-        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
-       goto lerror;
-    }
+    lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
+    if (!lpwhs)
+        return ERROR_OUTOFMEMORY;
 
    /*
     * According to my tests. The name is not resolved until a request is sent
     */
 
     lpwhs->hdr.htype = WH_HHTTPSESSION;
-    lpwhs->hdr.lpwhparent = WININET_AddRef( &hIC->hdr );
+    lpwhs->hdr.vtbl = &HTTPSESSIONVtbl;
     lpwhs->hdr.dwFlags = dwFlags;
     lpwhs->hdr.dwContext = dwContext;
-    lpwhs->hdr.dwInternalFlags = dwInternalFlags;
-    lpwhs->hdr.dwRefCount = 1;
-    lpwhs->hdr.destroy = HTTP_CloseHTTPSessionHandle;
+    lpwhs->hdr.dwInternalFlags = dwInternalFlags | (hIC->hdr.dwInternalFlags & INET_CALLBACKW);
+    lpwhs->hdr.refs = 1;
     lpwhs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
 
+    WININET_AddRef( &hIC->hdr );
+    lpwhs->lpAppInfo = hIC;
+    list_add_head( &hIC->hdr.children, &lpwhs->hdr.entry );
+
     handle = WININET_AllocHandle( &lpwhs->hdr );
     if (NULL == handle)
     {
         ERR("Failed to alloc handle\n");
-        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
-       goto lerror;
+        res = ERROR_OUTOFMEMORY;
+        goto lerror;
     }
 
     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
-        if(strchrW(hIC->lpszProxy, ' '))
-            FIXME("Several proxies not implemented.\n");
         if(hIC->lpszProxyBypass)
             FIXME("Proxy bypass is ignored.\n");
     }
     if (lpszServerName && lpszServerName[0])
     {
-        lpwhs->lpszServerName = WININET_strdupW(lpszServerName);
-        lpwhs->lpszHostName = WININET_strdupW(lpszServerName);
+        lpwhs->lpszServerName = heap_strdupW(lpszServerName);
+        lpwhs->lpszHostName = heap_strdupW(lpszServerName);
     }
     if (lpszUserName && lpszUserName[0])
-        lpwhs->lpszUserName = WININET_strdupW(lpszUserName);
+        lpwhs->lpszUserName = heap_strdupW(lpszUserName);
+    if (lpszPassword && lpszPassword[0])
+        lpwhs->lpszPassword = heap_strdupW(lpszPassword);
     lpwhs->nServerPort = nServerPort;
     lpwhs->nHostPort = nServerPort;
 
@@ -2467,8 +4281,6 @@ HINTERNET HTTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
                               sizeof(handle));
     }
 
-    bSuccess = TRUE;
-
 lerror:
     if( lpwhs )
         WININET_Release( &lpwhs->hdr );
@@ -2479,7 +4291,10 @@ lerror:
  */
 
     TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
-    return handle;
+
+    if(res == ERROR_SUCCESS)
+        *ret = handle;
+    return res;
 }
 
 
@@ -2493,41 +4308,58 @@ lerror:
  *   TRUE  on success
  *   FALSE on failure
  */
-static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr)
+static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
 {
-    BOOL bSuccess = FALSE;
-    LPWININETHTTPSESSIONW lpwhs;
-    LPWININETAPPINFOW hIC = NULL;
-    char szaddr[32];
+    http_session_t *lpwhs;
+    appinfo_t *hIC = NULL;
+    char szaddr[INET6_ADDRSTRLEN];
+    const void *addr;
+    DWORD res = ERROR_SUCCESS;
 
     TRACE("-->\n");
 
 
-    if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
+    if (lpwhr->hdr.htype != WH_HHTTPREQ)
     {
-        INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
+        res = ERROR_INVALID_PARAMETER;
         goto lend;
     }
 
-    lpwhs = (LPWININETHTTPSESSIONW)lpwhr->hdr.lpwhparent;
+    if (NETCON_connected(&lpwhr->netConnection))
+        goto lend;
+    if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
+
+    lpwhs = lpwhr->lpHttpSession;
 
-    hIC = (LPWININETAPPINFOW) lpwhs->hdr.lpwhparent;
-    inet_ntop(lpwhs->socketAddress.sin_family, &lpwhs->socketAddress.sin_addr,
-              szaddr, sizeof(szaddr));
+    hIC = lpwhs->lpAppInfo;
+    switch (lpwhs->socketAddress.ss_family)
+    {
+    case AF_INET:
+        addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
+        break;
+    case AF_INET6:
+        addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
+        break;
+    default:
+        WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
+        return ERROR_INTERNET_NAME_NOT_RESOLVED;
+    }
+    inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
                           INTERNET_STATUS_CONNECTING_TO_SERVER,
                           szaddr,
                           strlen(szaddr)+1);
 
-    if (!NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.sin_family,
-                         SOCK_STREAM, 0))
+    res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
+    if (res != ERROR_SUCCESS)
     {
-       WARN("Socket creation failed\n");
+        WARN("Socket creation failed: %u\n", res);
         goto lend;
     }
 
-    if (!NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
-                      sizeof(lpwhs->socketAddress)))
+    res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
+                         lpwhs->sa_len);
+    if(res != ERROR_SUCCESS)
        goto lend;
 
     if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
@@ -2538,10 +4370,11 @@ static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr)
          * behaviour to be more correct and to not cause any incompatibilities
          * because using a secure connection through a proxy server is a rare
          * case that would be hard for anyone to depend on */
-        if (hIC->lpszProxy && !HTTP_SecureProxyConnect(lpwhr))
+        if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS)
             goto lend;
 
-        if (!NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName))
+        res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
+        if(res != ERROR_SUCCESS)
         {
             WARN("Couldn't connect securely to host\n");
             goto lend;
@@ -2552,11 +4385,12 @@ static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr)
                           INTERNET_STATUS_CONNECTED_TO_SERVER,
                           szaddr, strlen(szaddr)+1);
 
-    bSuccess = TRUE;
-
 lend:
-    TRACE("%d <--\n", bSuccess);
-    return bSuccess;
+    lpwhr->read_pos = lpwhr->read_size = 0;
+    lpwhr->read_chunked = FALSE;
+
+    TRACE("%d <--\n", res);
+    return res;
 }
 
 
@@ -2565,7 +4399,7 @@ lend:
  *
  * clear out any old response headers
  */
-static void HTTP_clear_response_headers( LPWININETHTTPREQW lpwhr )
+static void HTTP_clear_response_headers( http_request_t *lpwhr )
 {
     DWORD i;
 
@@ -2592,119 +4426,139 @@ static void HTTP_clear_response_headers( LPWININETHTTPREQW lpwhr )
  *   TRUE  on success
  *   FALSE on error
  */
-static INT HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr)
+static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
 {
     INT cbreaks = 0;
     WCHAR buffer[MAX_REPLY_LEN];
     DWORD buflen = MAX_REPLY_LEN;
     BOOL bSuccess = FALSE;
     INT  rc = 0;
-    static const WCHAR szCrLf[] = {'\r','\n',0};
     char bufferA[MAX_REPLY_LEN];
-    LPWSTR status_code, status_text;
+    LPWSTR status_code = NULL, status_text = NULL;
     DWORD cchMaxRawHeaders = 1024;
-    LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
+    LPWSTR lpszRawHeaders = NULL;
+    LPWSTR temp;
     DWORD cchRawHeaders = 0;
+    BOOL codeHundred = FALSE;
 
     TRACE("-->\n");
 
     /* clear old response headers (eg. from a redirect response) */
-    HTTP_clear_response_headers( lpwhr );
+    if (clear) HTTP_clear_response_headers( lpwhr );
 
     if (!NETCON_connected(&lpwhr->netConnection))
         goto lend;
 
-    /*
-     * HACK peek at the buffer
-     */
-#if 0
-    /* This is Wine code, we don't support MSG_PEEK yet so we have to do it
-       a bit different */
-    NETCON_recv(&lpwhr->netConnection, buffer, buflen, MSG_PEEK, &rc);
-#endif
+    do {
+        static const WCHAR szHundred[] = {'1','0','0',0};
+        /*
+         * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
+         */
+        buflen = MAX_REPLY_LEN;
+        if (!read_line(lpwhr, bufferA, &buflen))
+            goto lend;
+        rc += buflen;
+        MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
+        /* check is this a status code line? */
+        if (!strncmpW(buffer, g_szHttp1_0, 4))
+        {
+            /* split the version from the status code */
+            status_code = strchrW( buffer, ' ' );
+            if( !status_code )
+                goto lend;
+            *status_code++=0;
 
-    /*
-     * 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);
-    if (!NETCON_getNextLine(&lpwhr->netConnection, bufferA, &buflen))
-        goto lend;
-#if 1
-    rc = buflen;
-#endif
-    MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
+            /* split the status code from the status text */
+            status_text = strchrW( status_code, ' ' );
+            if( !status_text )
+                goto lend;
+            *status_text++=0;
 
-    /* regenerate raw headers */
-    while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
-    {
-        cchMaxRawHeaders *= 2;
-        lpszRawHeaders = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
-    }
-    memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
-    cchRawHeaders += (buflen-1);
-    memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
-    cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
-    lpszRawHeaders[cchRawHeaders] = '\0';
+            TRACE("version [%s] status code [%s] status text [%s]\n",
+               debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
 
-    /* split the version from the status code */
-    status_code = strchrW( buffer, ' ' );
-    if( !status_code )
-        goto lend;
-    *status_code++=0;
+            codeHundred = (!strcmpW(status_code, szHundred));
+        }
+        else if (!codeHundred)
+        {
+            WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
 
-    /* split the status code from the status text */
-    status_text = strchrW( status_code, ' ' );
-    if( !status_text )
-        goto lend;
-    *status_text++=0;
+            HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
+            HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
+
+            lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
+            lpwhr->lpszStatusText = heap_strdupW(szOK);
 
-    TRACE("version [%s] status code [%s] status text [%s]\n",
-         debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
+            HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
+            lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
+
+            bSuccess = TRUE;
+            goto lend;
+        }
+    } while (codeHundred);
 
+    /* Add status code */
     HTTP_ProcessHeader(lpwhr, szStatus, status_code,
             HTTP_ADDHDR_FLAG_REPLACE);
 
     HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
     HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
 
-    lpwhr->lpszVersion= WININET_strdupW(buffer);
-    lpwhr->lpszStatusText = WININET_strdupW(status_text);
+    lpwhr->lpszVersion = heap_strdupW(buffer);
+    lpwhr->lpszStatusText = heap_strdupW(status_text);
+
+    /* Restore the spaces */
+    *(status_code-1) = ' ';
+    *(status_text-1) = ' ';
+
+    /* regenerate raw headers */
+    lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
+    if (!lpszRawHeaders) goto lend;
+
+    while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
+        cchMaxRawHeaders *= 2;
+    temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
+    if (temp == NULL) goto lend;
+    lpszRawHeaders = temp;
+    memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
+    cchRawHeaders += (buflen-1);
+    memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
+    cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
+    lpszRawHeaders[cchRawHeaders] = '\0';
 
     /* Parse each response line */
     do
     {
        buflen = MAX_REPLY_LEN;
-        if (NETCON_getNextLine(&lpwhr->netConnection, bufferA, &buflen))
+        if (read_line(lpwhr, bufferA, &buflen))
         {
             LPWSTR * pFieldAndValue;
 
-#if 1
-            rc += buflen;
-#endif
             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
+
+            if (!bufferA[0]) break;
             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
 
-            while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
+            pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
+            if (pFieldAndValue)
             {
-                cchMaxRawHeaders *= 2;
-                lpszRawHeaders = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
+                while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
+                    cchMaxRawHeaders *= 2;
+                temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
+                if (temp == NULL) goto lend;
+                lpszRawHeaders = temp;
+                memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
+                cchRawHeaders += (buflen-1);
+                memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
+                cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
+                lpszRawHeaders[cchRawHeaders] = '\0';
+
+                HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
+                                   HTTP_ADDREQ_FLAG_ADD );
+
+                HTTP_FreeTokens(pFieldAndValue);
             }
-            memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
-            cchRawHeaders += (buflen-1);
-            memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
-            cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
-            lpszRawHeaders[cchRawHeaders] = '\0';
-
-            pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
-            if (!pFieldAndValue)
-                break;
-
-            HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
-                HTTP_ADDREQ_FLAG_ADD );
-
-            HTTP_FreeTokens(pFieldAndValue);
-       }
+        }
        else
        {
            cbreaks++;
@@ -2713,6 +4567,19 @@ static INT HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr)
        }
     }while(1);
 
+    /* make sure the response header is terminated with an empty line.  Some apps really
+       truly care about that empty line being there for some reason.  Just add it to the
+       header. */
+    if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
+    {
+        cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
+        temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
+        if (temp == NULL) goto lend;
+        lpszRawHeaders = temp;
+    }
+
+    memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
+
     HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
     lpwhr->lpszRawHeaders = lpszRawHeaders;
     TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
@@ -2724,30 +4591,12 @@ lend:
     if (bSuccess)
         return rc;
     else
-        return 0;
-}
-
-
-static void strip_spaces(LPWSTR start)
-{
-    LPWSTR str = start;
-    LPWSTR end;
-
-    while (*str == ' ' && *str != '\0')
-        str++;
-
-    if (str != start)
-        memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
-
-    end = start + strlenW(start) - 1;
-    while (end >= start && *end == ' ')
     {
-        *end = '\0';
-        end--;
+        HeapFree(GetProcessHeap(), 0, lpszRawHeaders);
+        return 0;
     }
 }
 
-
 /***********************************************************************
  *           HTTP_InterpretHttpHeader (internal)
  *
@@ -2810,28 +4659,21 @@ static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
  *
  */
 
-#define COALESCEFLASG (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
+#define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
 
-static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
+static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
 {
     LPHTTPHEADERW lphttpHdr = NULL;
-    BOOL bSuccess = FALSE;
     INT index = -1;
-    static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
     BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
+    DWORD res = ERROR_HTTP_INVALID_HEADER;
 
-    TRACE("--> %s: %s - 0x%08lx\n", debugstr_w(field), debugstr_w(value), dwModifier);
-
-    /* Don't let applications add Connection header to request */
-    if (strcmpW(szConnection,field)==0 && (dwModifier & HTTP_ADDHDR_FLAG_REQ))
-    {
-        return FALSE;
-    }
+    TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
 
     /* REPLACE wins out over ADD */
     if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE)
         dwModifier &= ~HTTP_ADDHDR_FLAG_ADD;
-
+    
     if (dwModifier & HTTP_ADDHDR_FLAG_ADD)
         index = -1;
     else
@@ -2840,9 +4682,7 @@ static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR v
     if (index >= 0)
     {
         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
-        {
-            return FALSE;
-        }
+            return ERROR_HTTP_INVALID_HEADER;
         lphttpHdr = &lpwhr->pCustHeaders[index];
     }
     else if (value)
@@ -2858,6 +4698,8 @@ static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR v
 
         return HTTP_InsertCustomHeader(lpwhr, &hdr);
     }
+    /* no value to delete */
+    else return ERROR_SUCCESS;
 
     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
            lphttpHdr->wFlags |= HDR_ISREQUEST;
@@ -2882,9 +4724,9 @@ static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR v
             return HTTP_InsertCustomHeader(lpwhr, &hdr);
         }
 
-        return TRUE;
+        return ERROR_SUCCESS;
     }
-    else if (dwModifier & COALESCEFLASG)
+    else if (dwModifier & COALESCEFLAGS)
     {
         LPWSTR lpsztmp;
         WCHAR ch = 0;
@@ -2905,7 +4747,7 @@ static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR v
 
         len = origlen + valuelen + ((ch > 0) ? 2 : 0);
 
-        lpsztmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,  lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
+        lpsztmp = HeapReAlloc(GetProcessHeap(), 0, lphttpHdr->lpszValue, (len+1)*sizeof(WCHAR));
         if (lpsztmp)
         {
             lphttpHdr->lpszValue = lpsztmp;
@@ -2920,97 +4762,40 @@ static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR v
 
             memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
             lphttpHdr->lpszValue[len] = '\0';
-            bSuccess = TRUE;
+            res = ERROR_SUCCESS;
         }
         else
         {
             WARN("HeapReAlloc (%d bytes) failed\n",len+1);
-            INTERNET_SetLastError(ERROR_OUTOFMEMORY);
+            res = ERROR_OUTOFMEMORY;
         }
     }
-    TRACE("<-- %d\n",bSuccess);
-    return bSuccess;
-}
-
-
-/***********************************************************************
- *           HTTP_CloseConnection (internal)
- *
- * Close socket connection
- *
- */
-static VOID HTTP_CloseConnection(LPWININETHTTPREQW lpwhr)
-{
-    LPWININETHTTPSESSIONW lpwhs = NULL;
-    LPWININETAPPINFOW hIC = NULL;
-
-    TRACE("%p\n",lpwhr);
-
-    lpwhs = (LPWININETHTTPSESSIONW) lpwhr->hdr.lpwhparent;
-    hIC = (LPWININETAPPINFOW) lpwhs->hdr.lpwhparent;
-
-    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                          INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
-
-    if (NETCON_connected(&lpwhr->netConnection))
-    {
-        NETCON_close(&lpwhr->netConnection);
-    }
-
-    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                          INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
+    TRACE("<-- %d\n", res);
+    return res;
 }
 
 
 /***********************************************************************
- *           HTTP_CloseHTTPRequestHandle (internal)
+ *           HTTP_FinishedReading (internal)
  *
- * Deallocate request handle
+ * Called when all content from server has been read by client.
  *
  */
-static void HTTP_CloseHTTPRequestHandle(LPWININETHANDLEHEADER hdr)
+static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
 {
-    DWORD i;
-    LPWININETHTTPREQW lpwhr = (LPWININETHTTPREQW) hdr;
+    BOOL keepalive = HTTP_KeepAlive(lpwhr);
 
     TRACE("\n");
 
-    if (NETCON_connected(&lpwhr->netConnection))
-        HTTP_CloseConnection(lpwhr);
-
-    HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
-    HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
-    HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
-    HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
-    HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
 
-    for (i = 0; i < lpwhr->nCustHeaders; i++)
+    if (!keepalive)
     {
-        HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
-        HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
+        HTTPREQ_CloseConnection(&lpwhr->hdr);
     }
 
-    HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
-    HeapFree(GetProcessHeap(), 0, lpwhr);
-}
-
-
-/***********************************************************************
- *           HTTP_CloseHTTPSessionHandle (internal)
- *
- * Deallocate session handle
- *
- */
-static void HTTP_CloseHTTPSessionHandle(LPWININETHANDLEHEADER hdr)
-{
-    LPWININETHTTPSESSIONW lpwhs = (LPWININETHTTPSESSIONW) hdr;
-
-    TRACE("%p\n", lpwhs);
+    /* FIXME: store data in the URL cache here */
 
-    HeapFree(GetProcessHeap(), 0, lpwhs->lpszHostName);
-    HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
-    HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
-    HeapFree(GetProcessHeap(), 0, lpwhs);
+    return TRUE;
 }
 
 
@@ -3020,33 +4805,33 @@ static void HTTP_CloseHTTPSessionHandle(LPWININETHANDLEHEADER hdr)
  * Return index of custom header from header array
  *
  */
-static INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQW lpwhr, LPCWSTR lpszField,int requested_index, BOOL request_only)
+static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
+                                     int requested_index, BOOL request_only)
 {
     DWORD index;
 
-    TRACE("%s\n", debugstr_w(lpszField));
+    TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
 
     for (index = 0; index < lpwhr->nCustHeaders; index++)
     {
-       if (!strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
-    {
-        if ((request_only &&
-                !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))||
-            (!request_only &&
-                (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST)))
+        if (strcmpiW(lpwhr->pCustHeaders[index].lpszField, lpszField))
+            continue;
+
+        if (request_only && !(lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
+            continue;
+
+        if (!request_only && (lpwhr->pCustHeaders[index].wFlags & HDR_ISREQUEST))
             continue;
 
         if (requested_index == 0)
-           break;
-        else
-            requested_index --;
-    }
+            break;
+        requested_index --;
     }
 
     if (index >= lpwhr->nCustHeaders)
        index = -1;
 
-    TRACE("Return: %ld\n", index);
+    TRACE("Return: %d\n", index);
     return index;
 }
 
@@ -3057,11 +4842,10 @@ static INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQW lpwhr, LPCWSTR lpszField,
  * Insert header into array
  *
  */
-static BOOL HTTP_InsertCustomHeader(LPWININETHTTPREQW lpwhr, LPHTTPHEADERW lpHdr)
+static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
 {
     INT count;
     LPHTTPHEADERW lph = NULL;
-    BOOL r = FALSE;
 
     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
     count = lpwhr->nCustHeaders + 1;
@@ -3070,22 +4854,17 @@ static BOOL HTTP_InsertCustomHeader(LPWININETHTTPREQW lpwhr, LPHTTPHEADERW lpHdr
     else
        lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
 
-    if (NULL != lph)
-    {
-       lpwhr->pCustHeaders = lph;
-        lpwhr->pCustHeaders[count-1].lpszField = WININET_strdupW(lpHdr->lpszField);
-        lpwhr->pCustHeaders[count-1].lpszValue = WININET_strdupW(lpHdr->lpszValue);
-        lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
-        lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
-       lpwhr->nCustHeaders++;
-        r = TRUE;
-    }
-    else
-    {
-        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
-    }
+    if (!lph)
+        return ERROR_OUTOFMEMORY;
 
-    return r;
+    lpwhr->pCustHeaders = lph;
+    lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
+    lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
+    lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
+    lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
+    lpwhr->nCustHeaders++;
+
+    return ERROR_SUCCESS;
 }
 
 
@@ -3095,7 +4874,7 @@ static BOOL HTTP_InsertCustomHeader(LPWININETHTTPREQW lpwhr, LPHTTPHEADERW lpHdr
  * Delete header from array
  *  If this function is called, the indexs may change.
  */
-static BOOL HTTP_DeleteCustomHeader(LPWININETHTTPREQW lpwhr, DWORD index)
+static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
 {
     if( lpwhr->nCustHeaders <= 0 )
         return FALSE;
@@ -3103,6 +4882,9 @@ static BOOL HTTP_DeleteCustomHeader(LPWININETHTTPREQW lpwhr, DWORD index)
         return FALSE;
     lpwhr->nCustHeaders--;
 
+    HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszField);
+    HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[index].lpszValue);
+
     memmove( &lpwhr->pCustHeaders[index], &lpwhr->pCustHeaders[index+1],
              (lpwhr->nCustHeaders - index)* sizeof(HTTPHEADERW) );
     memset( &lpwhr->pCustHeaders[lpwhr->nCustHeaders], 0, sizeof(HTTPHEADERW) );
@@ -3110,6 +4892,22 @@ static BOOL HTTP_DeleteCustomHeader(LPWININETHTTPREQW lpwhr, DWORD index)
     return TRUE;
 }
 
+
+/***********************************************************************
+ *           HTTP_VerifyValidHeader (internal)
+ *
+ * Verify the given header is not invalid for the given http request
+ *
+ */
+static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
+{
+    /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
+    if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
+        return ERROR_HTTP_INVALID_HEADER;
+
+    return ERROR_SUCCESS;
+}
+
 /***********************************************************************
  *          IsHostInProxyBypassList (@)
  *
@@ -3118,6 +4916,6 @@ static BOOL HTTP_DeleteCustomHeader(LPWININETHTTPREQW lpwhr, DWORD index)
  */
 BOOL WINAPI IsHostInProxyBypassList(DWORD flags, LPCSTR szHost, DWORD length)
 {
-   FIXME("STUB: flags=%ld host=%s length=%ld\n",flags,szHost,length);
+   FIXME("STUB: flags=%d host=%s length=%d\n",flags,szHost,length);
    return FALSE;
 }