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