Initial implementation of IXMLHTTPRequest

Nikolay Sivov bunglehead at gmail.com
Sat Feb 6 19:25:32 CST 2010


---
 dlls/msxml3/Makefile.in    |    2 +-
 dlls/msxml3/httprequest.c  |  219 ++++++++++++++++++++++++++++++++++++++++----
 dlls/msxml3/tests/domdoc.c |   62 ++++++++++++-
 3 files changed, 261 insertions(+), 22 deletions(-)

diff --git a/dlls/msxml3/Makefile.in b/dlls/msxml3/Makefile.in
index 8997f1b..f0e2739 100644
--- a/dlls/msxml3/Makefile.in
+++ b/dlls/msxml3/Makefile.in
@@ -4,7 +4,7 @@ TOPOBJDIR = ../..
 SRCDIR    = @srcdir@
 VPATH     = @srcdir@
 MODULE    = msxml3.dll
-IMPORTS   = uuid urlmon shlwapi oleaut32 ole32 user32 advapi32 kernel32
+IMPORTS   = uuid urlmon shlwapi oleaut32 ole32 user32 advapi32 kernel32 winhttp
 EXTRALIBS = @XML2LIBS@
 EXTRAINCL = @XML2INCL@ @XSLTINCL@
 
diff --git a/dlls/msxml3/httprequest.c b/dlls/msxml3/httprequest.c
index a477753..a9cfdca 100644
--- a/dlls/msxml3/httprequest.c
+++ b/dlls/msxml3/httprequest.c
@@ -25,6 +25,8 @@
 #include "windef.h"
 #include "winbase.h"
 #include "winuser.h"
+#include "winhttp.h"
+
 #include "ole2.h"
 #include "msxml2.h"
 
@@ -40,6 +42,13 @@ typedef struct _httprequest
 {
     const struct IXMLHTTPRequestVtbl *lpVtbl;
     LONG ref;
+
+    HINTERNET session;
+    HINTERNET connection;
+    HINTERNET request;
+
+    DWORD     status; /* HTTP status code */
+    DWORD     content_length;
 } httprequest;
 
 static inline httprequest *impl_from_IXMLHTTPRequest( IXMLHTTPRequest *iface )
@@ -47,6 +56,54 @@ static inline httprequest *impl_from_IXMLHTTPRequest( IXMLHTTPRequest *iface )
     return (httprequest *)((char*)iface - FIELD_OFFSET(httprequest, lpVtbl));
 }
 
+static inline void httprequest_cleanup( httprequest *This )
+{
+    if ( This->request )    WinHttpCloseHandle( This->request );
+    if ( This->connection ) WinHttpCloseHandle( This->connection );
+    if ( This->session )    WinHttpCloseHandle( This->session );
+    This->request = This->connection = This->session = NULL;
+    This->status = 0;
+    This->content_length = 0;
+}
+
+static void CALLBACK httprequest_callback(HINTERNET handle,
+                                          DWORD_PTR context,
+                                          DWORD     status,
+                                          LPVOID    status_info,
+                                          DWORD     info_len)
+{
+    TRACE("status=0x%08x\n", status);
+
+    switch (status)
+    {
+        case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
+        case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:
+            WinHttpReceiveResponse(handle, NULL);
+            break;
+
+        case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:
+        {
+            httprequest *This = (httprequest*)context;
+            static DWORD size = sizeof(DWORD);
+
+            /* update HTTP status code */
+            WinHttpQueryHeaders(handle,
+                                WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
+                                WINHTTP_HEADER_NAME_BY_INDEX,
+                                &This->status,
+                                &size,
+                                WINHTTP_NO_HEADER_INDEX);
+            /* update content length */
+            WinHttpQueryHeaders(handle,
+                                WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER,
+                                WINHTTP_HEADER_NAME_BY_INDEX,
+                                &This->content_length,
+                                &size,
+                                WINHTTP_NO_HEADER_INDEX);
+       }
+    }
+}
+
 static HRESULT WINAPI httprequest_QueryInterface(IXMLHTTPRequest *iface, REFIID riid, void **ppvObject)
 {
     httprequest *This = impl_from_IXMLHTTPRequest( iface );
@@ -83,6 +140,7 @@ static ULONG WINAPI httprequest_Release(IXMLHTTPRequest *iface)
     ref = InterlockedDecrement( &This->ref );
     if ( ref == 0 )
     {
+        httprequest_cleanup( This );
         heap_free( This );
     }
 
@@ -158,14 +216,96 @@ static HRESULT WINAPI httprequest_Invoke(IXMLHTTPRequest *iface, DISPID dispIdMe
     return hr;
 }
 
-static HRESULT WINAPI httprequest_open(IXMLHTTPRequest *iface, BSTR bstrMethod, BSTR bstrUrl,
-        VARIANT varAsync, VARIANT bstrUser, VARIANT bstrPassword)
+static HRESULT WINAPI httprequest_open(IXMLHTTPRequest *iface, BSTR method, BSTR url,
+        VARIANT v_async, VARIANT user, VARIANT password)
 {
     httprequest *This = impl_from_IXMLHTTPRequest( iface );
+    URL_COMPONENTS uc;
+    BSTR name, verb;
+    VARIANT b_async;
+    DWORD flags;
+    HRESULT hr;
 
-    FIXME("stub (%p)\n", This);
+    TRACE("(%p)->(%s, %s)\n", This, debugstr_w(method), debugstr_w(url));
 
-    return E_NOTIMPL;
+    if (!method || !url) return E_INVALIDARG;
+
+    VariantInit(&b_async);
+    if ((hr = VariantChangeType(&b_async, &v_async, 0, VT_BOOL)) == S_OK)
+        flags = V_BOOL(&b_async) ? WINHTTP_FLAG_ASYNC : 0;
+    else
+    {
+        FIXME("failed to coerce to VT_BOOL, vt=%d, %x. Assuming not async.\n",
+                                                            V_VT(&v_async), hr);
+        flags = 0;
+    }
+
+    httprequest_cleanup(This);
+
+    This->session = WinHttpOpen(NULL,
+                                WINHTTP_ACCESS_TYPE_NO_PROXY,
+                                WINHTTP_NO_PROXY_NAME,
+                                WINHTTP_NO_PROXY_BYPASS,
+                                flags);
+    if (!This->session)
+    {
+        WARN("failed to create http session handle\n");
+        return E_FAIL;
+    }
+
+    memset(&uc, 0, sizeof(uc));
+    uc.dwStructSize = sizeof(uc);
+    if (!WinHttpCrackUrl(url, SysStringLen(url), 0, &uc))
+    {
+        WinHttpCloseHandle(This->session);
+        This->session = NULL;
+
+        WARN("failed to crack url, %d\n", GetLastError());
+        return E_FAIL;
+    }
+
+    name = SysAllocStringLen(uc.lpszHostName, uc.dwHostNameLength);
+    if (!(This->connection = WinHttpConnect(This->session, name,
+                                            uc.nPort, 0)))
+    {
+        SysFreeString(name);
+        WinHttpCloseHandle(This->session);
+        This->session = NULL;
+        return E_FAIL;      
+    }
+
+    SysFreeString(name);
+    name = SysAllocStringLen(uc.lpszUrlPath, uc.dwUrlPathLength);
+
+    /* should support cased verbs too here */
+    verb = SysAllocString(method);
+    CharUpperBuffW(verb, SysStringLen(verb));
+
+    if (!(This->request = WinHttpOpenRequest(This->connection, verb, name, NULL,
+                                             NULL, WINHTTP_DEFAULT_ACCEPT_TYPES, 0)))
+    {
+        WinHttpCloseHandle(This->connection);
+        WinHttpCloseHandle(This->session);
+        This->connection = This->session = NULL;
+        SysFreeString(verb);
+        SysFreeString(name);
+        return E_FAIL;
+    }
+
+    SysFreeString(verb);
+    SysFreeString(name);
+
+    if (V_VT(&user) == VT_BSTR && V_VT(&password) == VT_BSTR &&
+        SysStringLen(V_BSTR(&user)))
+    {
+        WinHttpSetCredentials(This->request,
+                              WINHTTP_AUTH_TARGET_SERVER,
+                              WINHTTP_AUTH_SCHEME_BASIC,
+                              V_BSTR(&user), V_BSTR(&password),
+                              0);
+    }
+
+    return S_OK;
 }
 
 static HRESULT WINAPI httprequest_setRequestHeader(IXMLHTTPRequest *iface, BSTR bstrHeader, BSTR bstrValue)
@@ -195,31 +335,60 @@ static HRESULT WINAPI httprequest_getAllResponseHeaders(IXMLHTTPRequest *iface,
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI httprequest_send(IXMLHTTPRequest *iface, VARIANT varBody)
+static HRESULT WINAPI httprequest_send(IXMLHTTPRequest *iface, VARIANT body)
 {
     httprequest *This = impl_from_IXMLHTTPRequest( iface );
+    BOOL ret;
 
-    FIXME("stub (%p)\n", This);
+    TRACE("(%p)\n", This);
 
-    return E_NOTIMPL;
+    if (!This->request) return S_FALSE;
+
+    if (V_VT(&body) != VT_BSTR)
+    {
+         FIXME("only VT_BSTR supported, got %d\n", V_VT(&body));
+         return E_FAIL;
+    }
+
+    WinHttpSetStatusCallback(This->request,
+                             httprequest_callback,
+                             WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS,
+                             0);
+
+    ret = WinHttpSendRequest(This->request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, V_BSTR(&body),
+                             (SysStringLen(V_BSTR(&body))+1)*sizeof(WCHAR),
+                             (SysStringLen(V_BSTR(&body))+1)*sizeof(WCHAR),
+                             (DWORD_PTR)This);
+
+    return ret ? S_OK : S_FALSE;
 }
 
 static HRESULT WINAPI httprequest_abort(IXMLHTTPRequest *iface)
 {
     httprequest *This = impl_from_IXMLHTTPRequest( iface );
 
-    FIXME("stub (%p)\n", This);
+    TRACE("(%p)\n", This);
 
-    return E_NOTIMPL;
+    httprequest_cleanup(This);
+
+    return S_OK;
 }
 
-static HRESULT WINAPI httprequest_get_status(IXMLHTTPRequest *iface, LONG *plStatus)
+static HRESULT WINAPI httprequest_get_status(IXMLHTTPRequest *iface, LONG *status)
 {
     httprequest *This = impl_from_IXMLHTTPRequest( iface );
 
-    FIXME("stub %p %p\n", This, plStatus);
+    TRACE("(%p)->(%p)\n", This, status);
 
-    return E_NOTIMPL;
+    if (!status) return E_INVALIDARG;
+
+    if (This->request)
+    {
+        *status = This->status;
+        return S_OK;
+    }
+    else
+        return E_FAIL;
 }
 
 static HRESULT WINAPI httprequest_get_statusText(IXMLHTTPRequest *iface, BSTR *pbstrStatus)
@@ -240,22 +409,35 @@ static HRESULT WINAPI httprequest_get_responseXML(IXMLHTTPRequest *iface, IDispa
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI httprequest_get_responseText(IXMLHTTPRequest *iface, BSTR *pbstrBody)
+static HRESULT WINAPI httprequest_get_responseText(IXMLHTTPRequest *iface, BSTR *body)
 {
     httprequest *This = impl_from_IXMLHTTPRequest( iface );
 
-    FIXME("stub %p %p\n", This, pbstrBody);
+    FIXME("(%p)->(%p): stub\n", This, body);
 
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI httprequest_get_responseBody(IXMLHTTPRequest *iface, VARIANT *pvarBody)
+static HRESULT WINAPI httprequest_get_responseBody(IXMLHTTPRequest *iface, VARIANT *body)
 {
     httprequest *This = impl_from_IXMLHTTPRequest( iface );
+    SAFEARRAY *array;
+    void *data;
+    BOOL ret;
 
-    FIXME("stub %p %p\n", This, pvarBody);
+    TRACE("(%p)->(%p)\n", This, body);
 
-    return E_NOTIMPL;
+    array = SafeArrayCreateVector(VT_UI1, 0, This->content_length);
+    if (!array) return E_FAIL;
+
+    V_VT(body)    = VT_ARRAY | VT_UI1;
+    V_ARRAY(body) = array;
+
+    SafeArrayAccessData(array, &data);
+    ret = WinHttpReadData(This->request, data, This->content_length, NULL);
+    SafeArrayUnaccessData(array);
+
+    return ret ? S_OK : S_FALSE;
 }
 
 static HRESULT WINAPI httprequest_get_responseStream(IXMLHTTPRequest *iface, VARIANT *pvarBody)
@@ -323,6 +505,9 @@ HRESULT XMLHTTPRequest_create(IUnknown *pUnkOuter, LPVOID *ppObj)
 
     req->lpVtbl = &dimimpl_vtbl;
     req->ref = 1;
+    req->session = req->connection = req->request = NULL;
+    req->status = 0;
+    req->content_length = 0;
 
     *ppObj = &req->lpVtbl;
 
diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c
index bc052be..ef048bf 100644
--- a/dlls/msxml3/tests/domdoc.c
+++ b/dlls/msxml3/tests/domdoc.c
@@ -23,6 +23,7 @@
 #define COBJMACROS
 
 #include "windows.h"
+#include "winhttp.h"
 #include "ole2.h"
 #include "xmldom.h"
 #include "msxml2.h"
@@ -2316,6 +2317,8 @@ static void test_XMLHTTP(void)
     VARIANT dummy;
     VARIANT varfalse;
     VARIANT varbody;
+    LONG status;
+
     HRESULT hr = CoCreateInstance(&CLSID_XMLHTTPRequest, NULL,
                                   CLSCTX_INPROC_SERVER, &IID_IXMLHttpRequest,
                                   (void **)&pXMLHttpRequest);
@@ -2323,22 +2326,59 @@ static void test_XMLHTTP(void)
     if (hr != S_OK)
         return;
 
+    /* abort before open */
+    hr = IXMLHttpRequest_abort(pXMLHttpRequest);
+    ok(hr == S_OK, "IXMLHttpRequest_abort should have succeeded instead of failing with 0x%08x\n", hr);
+
+    hr = IXMLHttpRequest_get_status(pXMLHttpRequest, NULL);
+    ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr);
+
+    /* initial status value */
+    status = -1;
+    hr = IXMLHttpRequest_get_status(pXMLHttpRequest, &status);
+    ok(hr == E_FAIL, "Expected E_FAIL, got 0x%08x\n", hr);
+    ok(status == -1, "Expected -1, got %d\n", status);
+
     VariantInit(&dummy);
     V_VT(&dummy) = VT_ERROR;
     V_ERROR(&dummy) = DISP_E_MEMBERNOTFOUND;
     VariantInit(&varfalse);
-    V_VT(&varfalse) = VT_BOOL;
-    V_BOOL(&varfalse) = VARIANT_FALSE;
     V_VT(&varbody) = VT_BSTR;
     V_BSTR(&varbody) = SysAllocString(wszBody);
 
+    /* not bool type for async flag is acceptable */
+    V_VT(&varfalse) = VT_I4;
+    V_I4(&varfalse) = 0;
+    hr = IXMLHttpRequest_open(pXMLHttpRequest, wszPOST, wszUrl, varfalse, dummy, dummy);
+    ok(hr == S_OK, "IXMLHttpRequest_open should have succeeded instead of failing with 0x%08x\n", hr);
+
+    V_VT(&varfalse) = VT_BSTR;
+    V_BSTR(&varfalse) = wszUrl;
     hr = IXMLHttpRequest_open(pXMLHttpRequest, wszPOST, wszUrl, varfalse, dummy, dummy);
-    todo_wine ok(hr == S_OK, "IXMLHttpRequest_open should have succeeded instead of failing with 0x%08x\n", hr);
+    ok(hr == S_OK, "IXMLHttpRequest_open should have succeeded instead of failing with 0x%08x\n", hr);
+
+    V_VT(&varfalse) = VT_BOOL;
+    V_BOOL(&varfalse) = VARIANT_FALSE;
+
+    /* NULL for command is not accepted */
+    hr = IXMLHttpRequest_open(pXMLHttpRequest, NULL, wszUrl, varfalse, dummy, dummy);
+    ok(hr == E_INVALIDARG, "IXMLHttpRequest_open should fail with E_INVALIDARG, got 0x%08x\n", hr);
+    /* url can't be NULL too */
+    hr = IXMLHttpRequest_open(pXMLHttpRequest, wszPOST, NULL, varfalse, dummy, dummy);
+    ok(hr == E_INVALIDARG, "IXMLHttpRequest_open should fail with E_INVALIDARG, got 0x%08x\n", hr);
+
+    hr = IXMLHttpRequest_open(pXMLHttpRequest, wszPOST, wszUrl, varfalse, dummy, dummy);
+    ok(hr == S_OK, "IXMLHttpRequest_open should have succeeded instead of failing with 0x%08x\n", hr);
 
     hr = IXMLHttpRequest_send(pXMLHttpRequest, varbody);
-    todo_wine ok(hr == S_OK, "IXMLHttpRequest_send should have succeeded instead of failing with 0x%08x\n", hr);
+    ok(hr == S_OK, "IXMLHttpRequest_send should have succeeded instead of failing with 0x%08x\n", hr);
     VariantClear(&varbody);
 
+    status = -1;
+    hr = IXMLHttpRequest_get_status(pXMLHttpRequest, &status);
+    ok(hr == S_OK, "IXMLHttpRequest_get_status should have succeeded instead of failing with 0x%08x\n", hr);
+    ok(status == HTTP_STATUS_OK, "Expected HTTP_STATUS_OK, got %d\n", status);
+
     hr = IXMLHttpRequest_get_responseText(pXMLHttpRequest, &bstrResponse);
     todo_wine ok(hr == S_OK, "IXMLHttpRequest_get_responseText should have succeeded instead of failing with 0x%08x\n", hr);
     /* the server currently returns "FAILED" because the Content-Type header is
@@ -2349,6 +2389,20 @@ static void test_XMLHTTP(void)
         SysFreeString(bstrResponse);
     }
 
+    hr = IXMLHttpRequest_get_responseBody(pXMLHttpRequest, &varbody);
+    ok(hr == S_OK, "IXMLHttpRequest_get_responseText should have succeeded instead of failing with 0x%08x\n", hr);
+    if(hr == S_OK)
+    {
+        static const char failed[] = "FAILED";
+        void *data;
+
+        /* raw data to be returned here */
+        SafeArrayAccessData(V_ARRAY(&varbody), &data);
+        ok(!memcmp(data, failed, sizeof(failed)-1), "Wrong data returned\n");
+        SafeArrayUnaccessData(V_ARRAY(&varbody));
+        VariantClear(&varbody);
+    }
+
     IXMLHttpRequest_Release(pXMLHttpRequest);
 }
 
-- 
1.5.6.5


--------------040000000600070704000800--



More information about the wine-devel mailing list