urlmon: Implement HttpProtocol.
diff --git a/dlls/urlmon/http.c b/dlls/urlmon/http.c
index f761314..c501f05 100644
--- a/dlls/urlmon/http.c
+++ b/dlls/urlmon/http.c
@@ -1,5 +1,6 @@
 /*
  * Copyright 2005 Jacek Caban
+ * Copyright 2007 Misha Koshelev
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -16,6 +17,12 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+/*
+ * TODO:
+ * - Handle redirects as native.
+ * - Add support for non-GET requests (e.g., POST).
+ */
+
 #include <stdarg.h>
 
 #define COBJMACROS
@@ -25,21 +32,177 @@
 #include "winuser.h"
 #include "ole2.h"
 #include "urlmon.h"
+#include "wininet.h"
 #include "urlmon_main.h"
 
 #include "wine/debug.h"
+#include "wine/unicode.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
 
+/* Flags are needed for, among other things, return HRESULTs from the Read function
+ * to conform to native. For example, Read returns:
+ *
+ * 1. E_PENDING if called before the request has completed,
+ *        (flags = 0)
+ * 2. S_FALSE after all data has been read and S_OK has been reported,
+ *        (flags = FLAG_REQUEST_COMPLETE | FLAG_ALL_DATA_READ | FLAG_RESULT_REPORTED)
+ * 3. INET_E_DATA_NOT_AVAILABLE if InternetQueryDataAvailable fails. The first time
+ *    this occurs, INET_E_DATA_NOT_AVAILABLE will also be reported to the sink,
+ *        (flags = FLAG_REQUEST_COMPLETE)
+ *    but upon subsequent calls to Read no reporting will take place, yet
+ *    InternetQueryDataAvailable will still be called, and, on failure,
+ *    INET_E_DATA_NOT_AVAILABLE will still be returned.
+ *        (flags = FLAG_REQUEST_COMPLETE | FLAG_RESULT_REPORTED)
+ *
+ * FLAG_FIRST_DATA_REPORTED and FLAG_LAST_DATA_REPORTED are needed for proper
+ * ReportData reporting. For example, if OnResponse returns S_OK, Continue will
+ * report BSCF_FIRSTDATANOTIFICATION, and when all data has been read Read will
+ * report BSCF_INTERMEDIATEDATANOTIFICATION|BSCF_LASTDATANOTIFICATION. However,
+ * if OnResponse does not return S_OK, Continue will not report data, and Read
+ * will report BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION when all
+ * data has been read.
+ */
+#define FLAG_REQUEST_COMPLETE 0x1
+#define FLAG_FIRST_DATA_REPORTED 0x2
+#define FLAG_ALL_DATA_READ 0x4
+#define FLAG_LAST_DATA_REPORTED 0x8
+#define FLAG_RESULT_REPORTED 0x10
+
 typedef struct {
     const IInternetProtocolVtbl *lpInternetProtocolVtbl;
     const IInternetPriorityVtbl *lpInternetPriorityVtbl;
 
+    DWORD flags;
+    IInternetProtocolSink *protocol_sink;
+    IHttpNegotiate *http_negotiate;
+    HINTERNET internet, connect, request;
+    HANDLE lock;
+    ULONG current_position, content_length, available_bytes;
     LONG priority;
 
     LONG ref;
 } HttpProtocol;
 
+/*
+ * Helpers
+ */
+
+static void HTTPPROTOCOL_ReportResult(HttpProtocol *This, HRESULT hres)
+{
+    if (!(This->flags & FLAG_RESULT_REPORTED) &&
+        This->protocol_sink)
+    {
+        This->flags |= FLAG_RESULT_REPORTED;
+        IInternetProtocolSink_ReportResult(This->protocol_sink, hres, 0, NULL);
+    }
+}
+
+static void HTTPPROTOCOL_ReportData(HttpProtocol *This)
+{
+    DWORD bscf;
+    if (!(This->flags & FLAG_LAST_DATA_REPORTED) &&
+        This->protocol_sink)
+    {
+        if (This->flags & FLAG_FIRST_DATA_REPORTED)
+        {
+            bscf = BSCF_INTERMEDIATEDATANOTIFICATION;
+        }
+        else
+        {
+            This->flags |= FLAG_FIRST_DATA_REPORTED;
+            bscf = BSCF_FIRSTDATANOTIFICATION;
+        }
+        if (This->flags & FLAG_ALL_DATA_READ &&
+            !(This->flags & FLAG_LAST_DATA_REPORTED))
+        {
+            This->flags |= FLAG_LAST_DATA_REPORTED;
+            bscf |= BSCF_LASTDATANOTIFICATION;
+        }
+        IInternetProtocolSink_ReportData(This->protocol_sink, bscf,
+                                         This->current_position+This->available_bytes,
+                                         This->content_length);
+    }
+}
+
+static void HTTPPROTOCOL_AllDataRead(HttpProtocol *This)
+{
+    if (!(This->flags & FLAG_ALL_DATA_READ))
+        This->flags |= FLAG_ALL_DATA_READ;
+    HTTPPROTOCOL_ReportData(This);
+    HTTPPROTOCOL_ReportResult(This, S_OK);
+}
+
+static void HTTPPROTOCOL_Close(HttpProtocol *This)
+{
+    if (This->protocol_sink)
+    {
+        IInternetProtocolSink_Release(This->protocol_sink);
+        This->protocol_sink = 0;
+    }
+    if (This->http_negotiate)
+    {
+        IHttpNegotiate_Release(This->http_negotiate);
+        This->http_negotiate = 0;
+    }
+    if (This->request)
+        InternetCloseHandle(This->request);
+    CloseHandle(This->connect);
+    CloseHandle(This->internet);
+    This->request = This->connect = This->internet = 0;
+    This->flags = 0;
+}
+
+static void CALLBACK HTTPPROTOCOL_InternetStatusCallback(
+    HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
+    LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
+{
+    HttpProtocol *This = (HttpProtocol *)dwContext;
+    PROTOCOLDATA data;
+    ULONG ulStatusCode;
+
+    switch (dwInternetStatus)
+    {
+    case INTERNET_STATUS_RESOLVING_NAME:
+        ulStatusCode = BINDSTATUS_FINDINGRESOURCE;
+        break;
+    case INTERNET_STATUS_CONNECTING_TO_SERVER:
+        ulStatusCode = BINDSTATUS_CONNECTING;
+        break;
+    case INTERNET_STATUS_SENDING_REQUEST:
+        ulStatusCode = BINDSTATUS_SENDINGREQUEST;
+        break;
+    case INTERNET_STATUS_REQUEST_COMPLETE:
+        This->flags |= FLAG_REQUEST_COMPLETE;
+        /* PROTOCOLDATA same as native */
+        memset(&data, 0, sizeof(data));
+        data.dwState = 0xf1000000;
+        data.pData = (LPVOID)BINDSTATUS_DOWNLOADINGDATA;
+        IInternetProtocolSink_Switch(This->protocol_sink, &data);
+        return;
+    default:
+        WARN("Unhandled Internet status callback %d\n", dwInternetStatus);
+        return;
+    }
+
+    IInternetProtocolSink_ReportProgress(This->protocol_sink, ulStatusCode, (LPWSTR)lpvStatusInformation);
+}
+
+static inline LPWSTR strndupW(LPWSTR string, int len)
+{
+    LPWSTR ret = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
+    if (ret)
+    {
+        memcpy(ret, string, len*sizeof(WCHAR));
+        ret[len] = 0;
+    }
+    return ret;
+}
+
+/*
+ * Interface implementations
+ */
+
 #define PROTOCOL(x)  ((IInternetProtocol*)  &(x)->lpInternetProtocolVtbl)
 #define PRIORITY(x)  ((IInternetPriority*)  &(x)->lpInternetPriorityVtbl)
 
@@ -89,6 +252,7 @@
     TRACE("(%p) ref=%d\n", This, ref);
 
     if(!ref) {
+        HTTPPROTOCOL_Close(This);
         HeapFree(GetProcessHeap(), 0, This);
 
         URLMON_UnlockModule();
@@ -102,16 +266,310 @@
         DWORD grfPI, DWORD dwReserved)
 {
     HttpProtocol *This = PROTOCOL_THIS(iface);
-    FIXME("(%p)->(%s %p %p %08x %d)\n", This, debugstr_w(szUrl), pOIProtSink,
+    URL_COMPONENTSW url;
+    BINDINFO bindinfo;
+    DWORD grfBINDF = 0, len = 0;
+    ULONG num = 0;
+    IServiceProvider *service_provider = 0;
+    IHttpNegotiate2 *http_negotiate2 = 0;
+    LPWSTR host = 0, path = 0, user = 0, pass = 0, addl_header = 0;
+    BYTE security_id[512];
+    LPOLESTR user_agent, accept_mimes[257];
+    HRESULT hres;
+
+    static const WCHAR wszHttp[] = {'h','t','t','p',':'};
+    static const WCHAR wszHeaders[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',
+                                       ':',' ','g','z','i','p',',',' ','d','e','f','l','a','t','e',0};
+
+    TRACE("(%p)->(%s %p %p %08x %d)\n", This, debugstr_w(szUrl), pOIProtSink,
             pOIBindInfo, grfPI, dwReserved);
-    return E_NOTIMPL;
+
+    memset(&bindinfo, 0, sizeof(bindinfo));
+    bindinfo.cbSize = sizeof(BINDINFO);
+    hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &grfBINDF, &bindinfo);
+    if (hres != S_OK)
+    {
+        WARN("GetBindInfo failed: %08x\n", hres);
+        goto done;
+    }
+
+    if (lstrlenW(szUrl) < sizeof(wszHttp)/sizeof(WCHAR)
+        || memcmp(szUrl, wszHttp, sizeof(wszHttp)))
+    {
+        hres = MK_E_SYNTAX;
+        goto done;
+    }
+
+    memset(&url, 0, sizeof(url));
+    url.dwStructSize = sizeof(url);
+    url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = url.dwUserNameLength =
+        url.dwPasswordLength = 1;
+    if (!InternetCrackUrlW(szUrl, 0, ICU_ESCAPE, &url))
+    {
+        hres = MK_E_SYNTAX;
+        goto done;
+    }
+    host = strndupW(url.lpszHostName, url.dwHostNameLength);
+    path = strndupW(url.lpszUrlPath, url.dwUrlPathLength);
+    user = strndupW(url.lpszUserName, url.dwUserNameLength);
+    pass = strndupW(url.lpszPassword, url.dwPasswordLength);
+    if (!url.nPort)
+        url.nPort = INTERNET_DEFAULT_HTTP_PORT;
+
+    hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_USER_AGENT, &user_agent,
+                                           1, &num);
+    if (hres != S_OK || !num)
+    {
+        CHAR null_char = 0;
+        LPSTR user_agenta = NULL;
+        len = 0;
+        if ((hres = ObtainUserAgentString(0, &null_char, &len)) != E_OUTOFMEMORY)
+        {
+            WARN("ObtainUserAgentString failed: %08x\n", hres);
+        }
+        else if (!(user_agenta = HeapAlloc(GetProcessHeap(), 0, len*sizeof(CHAR))))
+        {
+            WARN("Out of memory\n");
+        }
+        else if ((hres = ObtainUserAgentString(0, user_agenta, &len)) != S_OK)
+        {
+            WARN("ObtainUserAgentString failed: %08x\n", hres);
+        }
+        else
+        {
+            if (!(user_agent = CoTaskMemAlloc((len)*sizeof(WCHAR))))
+                WARN("Out of memory\n");
+            else
+                MultiByteToWideChar(CP_ACP, 0, user_agenta, -1, user_agent, len*sizeof(WCHAR));
+        }
+        HeapFree(GetProcessHeap(), 0, user_agenta);
+    }
+
+    This->internet = InternetOpenW(user_agent, 0, NULL, NULL, INTERNET_FLAG_ASYNC);
+    if (!This->internet)
+    {
+        WARN("InternetOpen failed: %d\n", GetLastError());
+        hres = INET_E_NO_SESSION;
+        goto done;
+    }
+
+    IInternetProtocolSink_AddRef(pOIProtSink);
+    This->protocol_sink = pOIProtSink;
+
+    /* Native does not check for success of next call, so we won't either */
+    InternetSetStatusCallbackW(This->internet, HTTPPROTOCOL_InternetStatusCallback);
+
+    This->connect = InternetConnectW(This->internet, host, url.nPort, user,
+                                     pass, INTERNET_SERVICE_HTTP, 0, (DWORD)This);
+    if (!This->connect)
+    {
+        WARN("InternetConnect failed: %d\n", GetLastError());
+        hres = INET_E_CANNOT_CONNECT;
+        goto done;
+    }
+
+    num = sizeof(accept_mimes)/sizeof(accept_mimes[0])-1;
+    hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_ACCEPT_MIMES,
+                                           accept_mimes,
+                                           num, &num);
+    if (hres != S_OK)
+    {
+        WARN("GetBindString BINDSTRING_ACCEPT_MIMES failed: %08x\n", hres);
+        hres = INET_E_NO_VALID_MEDIA;
+        goto done;
+    }
+    accept_mimes[num] = 0;
+
+    This->request = HttpOpenRequestW(This->connect, NULL, path, NULL, NULL,
+                                     (LPCWSTR *)accept_mimes, 0, (DWORD)This);
+    if (!This->request)
+    {
+        WARN("HttpOpenRequest failed: %d\n", GetLastError());
+        hres = INET_E_RESOURCE_NOT_FOUND;
+        goto done;
+    }
+
+    hres = IInternetProtocolSink_QueryInterface(pOIProtSink, &IID_IServiceProvider,
+                                                (void **)&service_provider);
+    if (hres != S_OK)
+    {
+        WARN("IInternetProtocolSink_QueryInterface IID_IServiceProvider failed: %08x\n", hres);
+        goto done;
+    }
+
+    hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate,
+                                         &IID_IHttpNegotiate, (void **)&This->http_negotiate);
+    if (hres != S_OK)
+    {
+        WARN("IServiceProvider_QueryService IID_IHttpNegotiate failed: %08x\n", hres);
+        goto done;
+    }
+
+    hres = IHttpNegotiate_BeginningTransaction(This->http_negotiate, szUrl, wszHeaders,
+                                               0, &addl_header);
+    if (hres != S_OK)
+    {
+        WARN("IHttpNegotiate_BeginningTransaction failed: %08x\n", hres);
+        goto done;
+    }
+
+    hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate2,
+                                         &IID_IHttpNegotiate2, (void **)&http_negotiate2);
+    if (hres != S_OK)
+    {
+        WARN("IServiceProvider_QueryService IID_IHttpNegotiate2 failed: %08x\n", hres);
+        /* No goto done as per native */
+    }
+    else
+    {
+        len = sizeof(security_id)/sizeof(security_id[0]);
+        hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, security_id, &len, 0);
+        if (hres != S_OK)
+        {
+            WARN("IHttpNegotiate2_GetRootSecurityId failed: %08x\n", hres);
+            /* No goto done as per native */
+        }
+    }
+
+    /* FIXME: Handle security_id. Native calls undocumented function IsHostInProxyBypassList. */
+
+    if (!HttpSendRequestW(This->request, wszHeaders, lstrlenW(wszHeaders), NULL, 0) &&
+        GetLastError() != ERROR_IO_PENDING)
+    {
+        WARN("HttpSendRequest failed: %d\n", GetLastError());
+        hres = INET_E_DOWNLOAD_FAILURE;
+        goto done;
+    }
+
+    hres = S_OK;
+done:
+    if (hres != S_OK)
+    {
+        IInternetProtocolSink_ReportResult(pOIProtSink, hres, 0, NULL);
+        HTTPPROTOCOL_Close(This);
+    }
+
+    CoTaskMemFree(addl_header);
+    if (http_negotiate2)
+        IHttpNegotiate2_Release(http_negotiate2);
+    if (service_provider)
+        IServiceProvider_Release(service_provider);
+
+    while (num<sizeof(accept_mimes)/sizeof(accept_mimes[0]) &&
+           accept_mimes[num])
+        CoTaskMemFree(accept_mimes[num++]);
+    CoTaskMemFree(user_agent);
+
+    HeapFree(GetProcessHeap(), 0, pass);
+    HeapFree(GetProcessHeap(), 0, user);
+    HeapFree(GetProcessHeap(), 0, path);
+    HeapFree(GetProcessHeap(), 0, host);
+
+    ReleaseBindInfo(&bindinfo);
+
+    return hres;
 }
 
 static HRESULT WINAPI HttpProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
 {
     HttpProtocol *This = PROTOCOL_THIS(iface);
-    FIXME("(%p)->(%p)\n", This, pProtocolData);
-    return E_NOTIMPL;
+    DWORD len = sizeof(DWORD), status_code;
+    LPWSTR response_headers = 0, content_type = 0, content_length = 0;
+
+    static const WCHAR wszDefaultContentType[] =
+        {'t','e','x','t','/','h','t','m','l',0};
+
+    TRACE("(%p)->(%p)\n", This, pProtocolData);
+
+    if (!pProtocolData)
+        WARN("Expected pProtocolData to be non-NULL\n");
+    else if (!This->request)
+        WARN("Expected request to be non-NULL\n");
+    else if (!This->http_negotiate)
+        WARN("Expected IHttpNegotiate pointer to be non-NULL\n");
+    else if (!This->protocol_sink)
+        WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
+    else if (pProtocolData->pData == (LPVOID)BINDSTATUS_DOWNLOADINGDATA)
+    {
+        if (!HttpQueryInfoW(This->request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
+                            &status_code, &len, NULL))
+        {
+            WARN("HttpQueryInfo failed: %d\n", GetLastError());
+        }
+        else
+        {
+            len = 0;
+            if ((!HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
+                                 NULL) &&
+                 GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
+                !(response_headers = HeapAlloc(GetProcessHeap(), 0, len)) ||
+                !HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
+                                NULL))
+            {
+                WARN("HttpQueryInfo failed: %d\n", GetLastError());
+            }
+            else
+            {
+                HRESULT hres = IHttpNegotiate_OnResponse(This->http_negotiate, status_code,
+                                                         response_headers, NULL, NULL);
+                if (hres != S_OK)
+                {
+                    WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres);
+                    goto done;
+                }
+            }
+        }
+
+        len = 0;
+        if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL) &&
+             GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
+            !(content_type = HeapAlloc(GetProcessHeap(), 0, len)) ||
+            !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL))
+        {
+            WARN("HttpQueryInfo failed: %d\n", GetLastError());
+            IInternetProtocolSink_ReportProgress(This->protocol_sink,
+                                                 BINDSTATUS_MIMETYPEAVAILABLE,
+                                                 wszDefaultContentType);
+        }
+        else
+        {
+            IInternetProtocolSink_ReportProgress(This->protocol_sink,
+                                                 BINDSTATUS_MIMETYPEAVAILABLE,
+                                                 content_type);
+        }
+
+        len = 0;
+        if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL) &&
+             GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
+            !(content_length = HeapAlloc(GetProcessHeap(), 0, len)) ||
+            !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL))
+        {
+            WARN("HttpQueryInfo failed: %d\n", GetLastError());
+            This->content_length = 0;
+        }
+        else
+        {
+            This->content_length = atoiW(content_length);
+        }
+
+        if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
+        {
+            WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
+            HTTPPROTOCOL_ReportResult(This, INET_E_DATA_NOT_AVAILABLE);
+        }
+        else
+        {
+            HTTPPROTOCOL_ReportData(This);
+        }
+    }
+
+done:
+    HeapFree(GetProcessHeap(), 0, response_headers);
+    HeapFree(GetProcessHeap(), 0, content_type);
+    HeapFree(GetProcessHeap(), 0, content_length);
+
+    /* Returns S_OK on native */
+    return S_OK;
 }
 
 static HRESULT WINAPI HttpProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
@@ -147,8 +605,66 @@
         ULONG cb, ULONG *pcbRead)
 {
     HttpProtocol *This = PROTOCOL_THIS(iface);
-    FIXME("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
-    return E_NOTIMPL;
+    ULONG read = 0, len = 0;
+    HRESULT hres = S_FALSE;
+
+    TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
+
+    if (!(This->flags & FLAG_REQUEST_COMPLETE))
+    {
+        hres = E_PENDING;
+    }
+    else while (!(This->flags & FLAG_ALL_DATA_READ) &&
+                read < cb)
+    {
+        if (This->available_bytes == 0)
+        {
+            if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
+            {
+                WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
+                hres = INET_E_DATA_NOT_AVAILABLE;
+                HTTPPROTOCOL_ReportResult(This, hres);
+                goto done;
+            }
+            else if (This->available_bytes == 0)
+            {
+                HTTPPROTOCOL_AllDataRead(This);
+            }
+        }
+        else
+        {
+            if (!InternetReadFile(This->request, ((BYTE *)pv)+read,
+                                  This->available_bytes > cb-read ?
+                                  cb-read : This->available_bytes, &len))
+            {
+                WARN("InternetReadFile failed: %d\n", GetLastError());
+                hres = INET_E_DOWNLOAD_FAILURE;
+                HTTPPROTOCOL_ReportResult(This, hres);
+                goto done;
+            }
+            else if (len == 0)
+            {
+                HTTPPROTOCOL_AllDataRead(This);
+            }
+            else
+            {
+                read += len;
+                This->current_position += len;
+                This->available_bytes -= len;
+            }
+        }
+    }
+
+    /* Per MSDN this should be if (read == cb), but native returns S_OK
+     * if any bytes were read, so we will too */
+    if (read)
+        hres = S_OK;
+
+done:
+    if (pcbRead)
+        *pcbRead = read;
+
+    return hres;
 }
 
 static HRESULT WINAPI HttpProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
@@ -162,15 +678,29 @@
 static HRESULT WINAPI HttpProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
 {
     HttpProtocol *This = PROTOCOL_THIS(iface);
-    FIXME("(%p)->(%08x)\n", This, dwOptions);
-    return E_NOTIMPL;
+
+    TRACE("(%p)->(%08x)\n", This, dwOptions);
+
+    if (!InternetLockRequestFile(This->request, &This->lock))
+        WARN("InternetLockRequest failed: %d\n", GetLastError());
+
+    return S_OK;
 }
 
 static HRESULT WINAPI HttpProtocol_UnlockRequest(IInternetProtocol *iface)
 {
     HttpProtocol *This = PROTOCOL_THIS(iface);
-    FIXME("(%p)\n", This);
-    return E_NOTIMPL;
+
+    TRACE("(%p)\n", This);
+
+    if (This->lock)
+    {
+        if (!InternetUnlockRequestFile(This->lock))
+            WARN("InternetUnlockRequest failed: %d\n", GetLastError());
+        This->lock = 0;
+    }
+
+    return S_OK;
 }
 
 #undef PROTOCOL_THIS
@@ -253,10 +783,14 @@
 
     ret->lpInternetProtocolVtbl = &HttpProtocolVtbl;
     ret->lpInternetPriorityVtbl = &HttpPriorityVtbl;
-
-    ret->ref = 1;
-
+    ret->flags = 0;
+    ret->protocol_sink = 0;
+    ret->http_negotiate = 0;
+    ret->internet = ret->connect = ret->request = 0;
+    ret->lock = 0;
+    ret->current_position = ret->content_length = ret->available_bytes = 0;
     ret->priority = 0;
+    ret->ref = 1;
 
     *ppobj = PROTOCOL(ret);
     
diff --git a/dlls/urlmon/tests/protocol.c b/dlls/urlmon/tests/protocol.c
index 2f46b5d..366a543 100644
--- a/dlls/urlmon/tests/protocol.c
+++ b/dlls/urlmon/tests/protocol.c
@@ -1057,9 +1057,7 @@
     SET_EXPECT(GetRootSecurityId);
 
     hres = IInternetProtocol_Start(http_protocol, url, &protocol_sink, &bind_info, 0, 0);
-    todo_wine {
-        ok(hres == S_OK, "Start failed: %08x\n", hres);
-    }
+    ok(hres == S_OK, "Start failed: %08x\n", hres);
     if(FAILED(hres))
         return FALSE;