Hi.
Recently I tried to start with this interface. It looks trivial enough
but since it's first time i'm using winhttp,
there could be some problems, especially cause IXMLHTTPRequest supports
asynchronous requests.
Could someone with winhttp knowledge review this patch before it goes to
far from reality?
P.S. any comments are welcome actually.
>From 04174c73f76309be5499ed3d5da3f98849adb842 Mon Sep 17 00:00:00 2001
From: Nikolay Sivov <bungleh...@gmail.com>
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