>From 04174c73f76309be5499ed3d5da3f98849adb842 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Sun, 7 Feb 2010 04:25:32 +0300 Subject: Initial implementation of IXMLHTTPRequest --- 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