Jacek Caban : urlmon: Move HttpProtocol:: Read implementation to generic Protocol object.

Alexandre Julliard julliard at winehq.org
Mon Mar 2 09:01:40 CST 2009


Module: wine
Branch: master
Commit: 4c129514b5aa9acc4c71e391235a2bb6ec14d462
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=4c129514b5aa9acc4c71e391235a2bb6ec14d462

Author: Jacek Caban <jacek at codeweavers.com>
Date:   Mon Mar  2 03:20:26 2009 +0100

urlmon: Move HttpProtocol::Read implementation to generic Protocol object.

---

 dlls/urlmon/http.c        |   80 +-------------------------
 dlls/urlmon/protocol.c    |  143 +++++++++++++++++++++++++++++++++++++++++++++
 dlls/urlmon/urlmon_main.h |    1 +
 3 files changed, 145 insertions(+), 79 deletions(-)

diff --git a/dlls/urlmon/http.c b/dlls/urlmon/http.c
index 5d102a6..c7d463b 100644
--- a/dlls/urlmon/http.c
+++ b/dlls/urlmon/http.c
@@ -144,14 +144,6 @@ static void HTTPPROTOCOL_ReportData(HttpProtocol *This)
     }
 }
 
-static void HTTPPROTOCOL_AllDataRead(HttpProtocol *This)
-{
-    if (!(This->base.flags & FLAG_ALL_DATA_READ))
-        This->base.flags |= FLAG_ALL_DATA_READ;
-    HTTPPROTOCOL_ReportData(This);
-    HTTPPROTOCOL_ReportResult(This, S_OK);
-}
-
 static void CALLBACK HTTPPROTOCOL_InternetStatusCallback(
     HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
     LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
@@ -735,80 +727,10 @@ static HRESULT WINAPI HttpProtocol_Read(IInternetProtocol *iface, void *pv,
         ULONG cb, ULONG *pcbRead)
 {
     HttpProtocol *This = PROTOCOL_THIS(iface);
-    ULONG read = 0, len = 0;
-    HRESULT hres = S_FALSE;
 
     TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
 
-    if (!(This->base.flags & FLAG_REQUEST_COMPLETE))
-    {
-        hres = E_PENDING;
-    }
-    else while (!(This->base.flags & FLAG_ALL_DATA_READ) &&
-                read < cb)
-    {
-        if (This->base.available_bytes == 0)
-        {
-            /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
-             * read, so clear the flag _before_ calling so it does not incorrectly get cleared
-             * after the status callback is called */
-            This->base.flags &= ~FLAG_REQUEST_COMPLETE;
-            if (!InternetQueryDataAvailable(This->base.request, &This->base.available_bytes, 0, 0))
-            {
-                if (GetLastError() == ERROR_IO_PENDING)
-                {
-                    hres = E_PENDING;
-                }
-                else
-                {
-                    WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
-                    hres = INET_E_DATA_NOT_AVAILABLE;
-                    HTTPPROTOCOL_ReportResult(This, hres);
-                }
-                goto done;
-            }
-            else if (This->base.available_bytes == 0)
-            {
-                HTTPPROTOCOL_AllDataRead(This);
-            }
-        }
-        else
-        {
-            if (!InternetReadFile(This->base.request, ((BYTE *)pv)+read,
-                                  This->base.available_bytes > cb-read ?
-                                  cb-read : This->base.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->base.current_position += len;
-                This->base.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;
-
-    if (hres != E_PENDING)
-        This->base.flags |= FLAG_REQUEST_COMPLETE;
-
-    return hres;
+    return protocol_read(&This->base, pv, cb, pcbRead);
 }
 
 static HRESULT WINAPI HttpProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
diff --git a/dlls/urlmon/protocol.c b/dlls/urlmon/protocol.c
index a2e801b..46e6264 100644
--- a/dlls/urlmon/protocol.c
+++ b/dlls/urlmon/protocol.c
@@ -23,6 +23,149 @@
 
 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         0x0001
+#define FLAG_FIRST_CONTINUE_COMPLETE  0x0002
+#define FLAG_FIRST_DATA_REPORTED      0x0004
+#define FLAG_ALL_DATA_READ            0x0008
+#define FLAG_LAST_DATA_REPORTED       0x0010
+#define FLAG_RESULT_REPORTED          0x0020
+
+static inline HRESULT report_result(Protocol *protocol, HRESULT hres)
+{
+    if (!(protocol->flags & FLAG_RESULT_REPORTED) && protocol->protocol_sink) {
+        protocol->flags |= FLAG_RESULT_REPORTED;
+        IInternetProtocolSink_ReportResult(protocol->protocol_sink, hres, 0, NULL);
+    }
+
+    return hres;
+}
+
+static void report_data(Protocol *protocol)
+{
+    DWORD bscf;
+
+    if((protocol->flags & FLAG_LAST_DATA_REPORTED) || !protocol->protocol_sink)
+        return;
+
+    if(protocol->flags & FLAG_FIRST_DATA_REPORTED) {
+        bscf = BSCF_INTERMEDIATEDATANOTIFICATION;
+    }else {
+        protocol->flags |= FLAG_FIRST_DATA_REPORTED;
+        bscf = BSCF_FIRSTDATANOTIFICATION;
+    }
+
+    if(protocol->flags & FLAG_ALL_DATA_READ && !(protocol->flags & FLAG_LAST_DATA_REPORTED)) {
+        protocol->flags |= FLAG_LAST_DATA_REPORTED;
+        bscf |= BSCF_LASTDATANOTIFICATION;
+    }
+
+    IInternetProtocolSink_ReportData(protocol->protocol_sink, bscf,
+            protocol->current_position+protocol->available_bytes,
+            protocol->content_length);
+}
+
+static void all_data_read(Protocol *protocol)
+{
+    protocol->flags |= FLAG_ALL_DATA_READ;
+
+    report_data(protocol);
+    report_result(protocol, S_OK);
+}
+
+HRESULT protocol_read(Protocol *protocol, void *buf, ULONG size, ULONG *read_ret)
+{
+    ULONG read = 0;
+    BOOL res;
+    HRESULT hres = S_FALSE;
+
+    if(!(protocol->flags & FLAG_REQUEST_COMPLETE)) {
+        *read_ret = 0;
+        return E_PENDING;
+    }
+
+    if(protocol->flags & FLAG_ALL_DATA_READ) {
+        *read_ret = 0;
+        return S_FALSE;
+    }
+
+    while(read < size) {
+        if(protocol->available_bytes) {
+            ULONG len;
+
+            res = InternetReadFile(protocol->request, ((BYTE *)buf)+read,
+                    protocol->available_bytes > size-read ? size-read : protocol->available_bytes, &len);
+            if(!res) {
+                WARN("InternetReadFile failed: %d\n", GetLastError());
+                hres = INET_E_DOWNLOAD_FAILURE;
+                report_result(protocol, hres);
+                break;
+            }
+
+            if(!len) {
+                all_data_read(protocol);
+                break;
+            }
+
+            read += len;
+            protocol->current_position += len;
+            protocol->available_bytes -= len;
+        }else {
+            /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
+             * read, so clear the flag _before_ calling so it does not incorrectly get cleared
+             * after the status callback is called */
+            protocol->flags &= ~FLAG_REQUEST_COMPLETE;
+            res = InternetQueryDataAvailable(protocol->request, &protocol->available_bytes, 0, 0);
+            if(!res) {
+                if (GetLastError() == ERROR_IO_PENDING) {
+                    hres = E_PENDING;
+                }else {
+                    WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
+                    hres = INET_E_DATA_NOT_AVAILABLE;
+                    report_result(protocol, hres);
+                }
+                break;
+            }
+
+            if(!protocol->available_bytes) {
+                all_data_read(protocol);
+                break;
+            }
+        }
+    }
+
+    *read_ret = read;
+
+    if (hres != E_PENDING)
+        protocol->flags |= FLAG_REQUEST_COMPLETE;
+    if(FAILED(hres))
+        return hres;
+
+    return read ? S_OK : S_FALSE;
+}
+
 HRESULT protocol_lock_request(Protocol *protocol)
 {
     if (!InternetLockRequestFile(protocol->request, &protocol->lock))
diff --git a/dlls/urlmon/urlmon_main.h b/dlls/urlmon/urlmon_main.h
index 6519630..a75c1ed 100644
--- a/dlls/urlmon/urlmon_main.h
+++ b/dlls/urlmon/urlmon_main.h
@@ -106,6 +106,7 @@ struct ProtocolVtbl {
     void (*close_connection)(Protocol*);
 };
 
+HRESULT protocol_read(Protocol*,void*,ULONG,ULONG*);
 HRESULT protocol_lock_request(Protocol*);
 HRESULT protocol_unlock_request(Protocol*);
 void protocol_close_connection(Protocol*);




More information about the wine-cvs mailing list