[PATCH 3/3] webservices: Add support for mapped HTTP headers.
Hans Leidekker
hans at codeweavers.com
Wed Sep 25 07:29:58 CDT 2019
Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
dlls/webservices/channel.c | 141 ++++++++++--
dlls/webservices/msg.c | 297 +++++++++++++++++++++----
dlls/webservices/proxy.c | 2 +-
dlls/webservices/tests/proxy.c | 118 +++++++++-
dlls/webservices/webservices.spec | 2 +-
dlls/webservices/webservices_private.h | 3 +-
6 files changed, 501 insertions(+), 62 deletions(-)
diff --git a/dlls/webservices/channel.c b/dlls/webservices/channel.c
index 4afa884820..71207bee6b 100644
--- a/dlls/webservices/channel.c
+++ b/dlls/webservices/channel.c
@@ -330,6 +330,26 @@ static void reset_channel( struct channel *channel )
}
}
+static void free_header_mappings( WS_HTTP_HEADER_MAPPING **mappings, ULONG count )
+{
+ ULONG i;
+ for (i = 0; i < count; i++) heap_free( mappings[i] );
+ heap_free( mappings );
+}
+
+static void free_message_mapping( const WS_HTTP_MESSAGE_MAPPING *mapping )
+{
+ free_header_mappings( mapping->requestHeaderMappings, mapping->requestHeaderMappingCount );
+ free_header_mappings( mapping->responseHeaderMappings, mapping->responseHeaderMappingCount );
+}
+
+static void free_props( struct channel *channel )
+{
+ struct prop *prop = &channel->prop[WS_CHANNEL_PROPERTY_HTTP_MESSAGE_MAPPING];
+ WS_HTTP_MESSAGE_MAPPING *mapping = (WS_HTTP_MESSAGE_MAPPING *)prop->value;
+ free_message_mapping( mapping );
+}
+
static void free_channel( struct channel *channel )
{
reset_channel( channel );
@@ -338,6 +358,7 @@ static void free_channel( struct channel *channel )
WsFreeReader( channel->reader );
heap_free( channel->read_buf );
+ free_props( channel );
channel->send_q.cs.DebugInfo->Spare[0] = 0;
channel->recv_q.cs.DebugInfo->Spare[0] = 0;
@@ -348,6 +369,58 @@ static void free_channel( struct channel *channel )
heap_free( channel );
}
+static WS_HTTP_HEADER_MAPPING *dup_header_mapping( const WS_HTTP_HEADER_MAPPING *src )
+{
+ WS_HTTP_HEADER_MAPPING *dst;
+
+ if (!(dst = heap_alloc( sizeof(*dst) + src->headerName.length ))) return NULL;
+
+ dst->headerName.bytes = (BYTE *)(dst + 1);
+ memcpy( dst->headerName.bytes, src->headerName.bytes, src->headerName.length );
+ dst->headerName.length = src->headerName.length;
+ dst->headerMappingOptions = src->headerMappingOptions;
+ return dst;
+}
+
+static HRESULT dup_message_mapping( const WS_HTTP_MESSAGE_MAPPING *src, WS_HTTP_MESSAGE_MAPPING *dst )
+{
+ ULONG i, size;
+
+ size = src->requestHeaderMappingCount * sizeof(*dst->responseHeaderMappings);
+ if (!(dst->requestHeaderMappings = heap_alloc( size ))) return E_OUTOFMEMORY;
+
+ for (i = 0; i < src->requestHeaderMappingCount; i++)
+ {
+ if (!(dst->requestHeaderMappings[i] = dup_header_mapping( src->requestHeaderMappings[i] )))
+ {
+ free_header_mappings( dst->requestHeaderMappings, i );
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ size = src->responseHeaderMappingCount * sizeof(*dst->responseHeaderMappings);
+ if (!(dst->responseHeaderMappings = heap_alloc( size )))
+ {
+ heap_free( dst->responseHeaderMappings );
+ return E_OUTOFMEMORY;
+ }
+
+ for (i = 0; i < src->responseHeaderMappingCount; i++)
+ {
+ if (!(dst->responseHeaderMappings[i] = dup_header_mapping( src->responseHeaderMappings[i] )))
+ {
+ free_header_mappings( dst->responseHeaderMappings, i );
+ return E_OUTOFMEMORY;
+ }
+ }
+
+ dst->requestMappingOptions = src->requestMappingOptions;
+ dst->responseMappingOptions = src->responseMappingOptions;
+ dst->requestHeaderMappingCount = src->requestHeaderMappingCount;
+ dst->responseHeaderMappingCount = src->responseHeaderMappingCount;
+ return S_OK;
+}
+
static HRESULT create_channel( WS_CHANNEL_TYPE type, WS_CHANNEL_BINDING binding,
const WS_CHANNEL_PROPERTY *properties, ULONG count, struct channel **ret )
{
@@ -390,25 +463,47 @@ static HRESULT create_channel( WS_CHANNEL_TYPE type, WS_CHANNEL_BINDING binding,
for (i = 0; i < count; i++)
{
- TRACE( "property id %u value ptr %p size %u\n", properties[i].id, properties[i].value,
- properties[i].valueSize );
- if (properties[i].valueSize == sizeof(ULONG) && properties[i].value)
- TRACE( " value %08x\n", *(ULONG *)properties[i].value );
+ const WS_CHANNEL_PROPERTY *prop = &properties[i];
+
+ TRACE( "property id %u value %p size %u\n", prop->id, prop->value, prop->valueSize );
+ if (prop->valueSize == sizeof(ULONG) && prop->value) TRACE( " value %08x\n", *(ULONG *)prop->value );
- switch (properties[i].id)
+ switch (prop->id)
{
case WS_CHANNEL_PROPERTY_ENCODING:
- if (!properties[i].value || properties[i].valueSize != sizeof(channel->encoding))
+ if (!prop->value || prop->valueSize != sizeof(channel->encoding))
+ {
+ free_channel( channel );
+ return E_INVALIDARG;
+ }
+ channel->encoding = *(WS_ENCODING *)prop->value;
+ break;
+
+ case WS_CHANNEL_PROPERTY_HTTP_MESSAGE_MAPPING:
+ {
+ const WS_HTTP_MESSAGE_MAPPING *src = (WS_HTTP_MESSAGE_MAPPING *)prop->value;
+ WS_HTTP_MESSAGE_MAPPING dst;
+
+ if (!prop->value || prop->valueSize != sizeof(*src))
{
free_channel( channel );
return E_INVALIDARG;
}
- channel->encoding = *(WS_ENCODING *)properties[i].value;
+
+ if ((hr = dup_message_mapping( src, &dst )) != S_OK) return hr;
+
+ if ((hr = prop_set( channel->prop, channel->prop_count, WS_CHANNEL_PROPERTY_HTTP_MESSAGE_MAPPING, &dst,
+ sizeof(dst) )) != S_OK)
+ {
+ free_message_mapping( &dst );
+ free_channel( channel );
+ return hr;
+ }
break;
+ }
default:
- if ((hr = prop_set( channel->prop, channel->prop_count, properties[i].id, properties[i].value,
- properties[i].valueSize )) != S_OK)
+ if ((hr = prop_set( channel->prop, channel->prop_count, prop->id, prop->value, prop->valueSize )) != S_OK)
{
free_channel( channel );
return hr;
@@ -1517,13 +1612,27 @@ static HRESULT init_reader( struct channel *channel )
return WsSetInput( channel->reader, encoding, input, NULL, 0, NULL );
}
+static const WS_HTTP_MESSAGE_MAPPING *get_http_message_mapping( struct channel *channel )
+{
+ const struct prop *prop = &channel->prop[WS_CHANNEL_PROPERTY_HTTP_MESSAGE_MAPPING];
+ return (const WS_HTTP_MESSAGE_MAPPING *)prop->value;
+}
+
+static HRESULT map_http_response_headers( struct channel *channel, WS_MESSAGE *msg )
+{
+ const WS_HTTP_MESSAGE_MAPPING *mapping = get_http_message_mapping( channel );
+ return message_map_http_response_headers( msg, channel->u.http.request, mapping );
+}
+
#define INITIAL_READ_BUFFER_SIZE 4096
-static HRESULT receive_message_http( struct channel *channel )
+static HRESULT receive_message_http( struct channel *channel, WS_MESSAGE *msg )
{
DWORD len, bytes_read, offset = 0, size = INITIAL_READ_BUFFER_SIZE;
ULONG max_len;
HRESULT hr;
+ if ((hr = map_http_response_headers( channel, msg )) != S_OK) return hr;
+
prop_get( channel->prop, channel->prop_count, WS_CHANNEL_PROPERTY_MAX_BUFFERED_MESSAGE_SIZE,
&max_len, sizeof(max_len) );
@@ -1814,7 +1923,7 @@ static HRESULT receive_message_session( struct channel *channel )
return S_OK;
}
-static HRESULT receive_message_bytes( struct channel *channel )
+static HRESULT receive_message_bytes( struct channel *channel, WS_MESSAGE *msg )
{
HRESULT hr;
if ((hr = connect_channel( channel )) != S_OK) return hr;
@@ -1822,7 +1931,7 @@ static HRESULT receive_message_bytes( struct channel *channel )
switch (channel->binding)
{
case WS_HTTP_CHANNEL_BINDING:
- return receive_message_http( channel );
+ return receive_message_http( channel, msg );
case WS_TCP_CHANNEL_BINDING:
if (channel->type & WS_CHANNEL_TYPE_SESSION)
@@ -1853,7 +1962,7 @@ static HRESULT receive_message_bytes( struct channel *channel )
}
}
-HRESULT channel_receive_message( WS_CHANNEL *handle )
+HRESULT channel_receive_message( WS_CHANNEL *handle, WS_MESSAGE *msg )
{
struct channel *channel = (struct channel *)handle;
HRESULT hr;
@@ -1866,7 +1975,7 @@ HRESULT channel_receive_message( WS_CHANNEL *handle )
return E_INVALIDARG;
}
- if ((hr = receive_message_bytes( channel )) == S_OK) hr = init_reader( channel );
+ if ((hr = receive_message_bytes( channel, msg )) == S_OK) hr = init_reader( channel );
LeaveCriticalSection( &channel->cs );
return hr;
@@ -1906,7 +2015,7 @@ static HRESULT receive_message( struct channel *channel, WS_MESSAGE *msg, const
HRESULT hr;
ULONG i;
- if ((hr = receive_message_bytes( channel )) != S_OK) return hr;
+ if ((hr = receive_message_bytes( channel, msg )) != S_OK) return hr;
if ((hr = init_reader( channel )) != S_OK) return hr;
for (i = 0; i < count; i++)
@@ -2148,7 +2257,7 @@ HRESULT WINAPI WsReadMessageStart( WS_CHANNEL *handle, WS_MESSAGE *msg, const WS
return E_INVALIDARG;
}
- if ((hr = receive_message_bytes( channel )) == S_OK)
+ if ((hr = receive_message_bytes( channel, msg )) == S_OK)
{
if ((hr = init_reader( channel )) == S_OK)
hr = WsReadEnvelopeStart( msg, channel->reader, NULL, NULL, NULL );
diff --git a/dlls/webservices/msg.c b/dlls/webservices/msg.c
index d6906cb5de..a006841be6 100644
--- a/dlls/webservices/msg.c
+++ b/dlls/webservices/msg.c
@@ -652,6 +652,16 @@ static HRESULT write_headers( struct msg *msg, WS_XML_WRITER *writer, const WS_X
return WsWriteEndElement( writer, NULL ); /* </s:Header> */
}
+static ULONG count_envelope_headers( struct msg *msg )
+{
+ ULONG i, ret = 0;
+ for (i = 0; i < msg->header_count; i++)
+ {
+ if (!msg->header[i]->mapped) ret++;
+ }
+ return ret;
+}
+
static HRESULT write_headers_transport( struct msg *msg, WS_XML_WRITER *writer, const WS_XML_STRING *prefix,
const WS_XML_STRING *ns )
{
@@ -659,16 +669,19 @@ static HRESULT write_headers_transport( struct msg *msg, WS_XML_WRITER *writer,
HRESULT hr = S_OK;
ULONG i;
- if ((msg->header_count || !msg->action) &&
- (hr = WsWriteStartElement( writer, prefix, &header, ns, NULL )) != S_OK) return hr;
-
- for (i = 0; i < msg->header_count; i++)
+ if (count_envelope_headers( msg ) || !msg->action)
{
- if (msg->header[i]->mapped) continue;
- if ((hr = WsWriteXmlBuffer( writer, msg->header[i]->u.buf, NULL )) != S_OK) return hr;
+ if ((hr = WsWriteStartElement( writer, prefix, &header, ns, NULL )) != S_OK) return hr;
+
+ for (i = 0; i < msg->header_count; i++)
+ {
+ if (msg->header[i]->mapped) continue;
+ if ((hr = WsWriteXmlBuffer( writer, msg->header[i]->u.buf, NULL )) != S_OK) return hr;
+ }
+
+ hr = WsWriteEndElement( writer, NULL ); /* </s:Header> */
}
- if (msg->header_count || !msg->action) hr = WsWriteEndElement( writer, NULL ); /* </s:Header> */
return hr;
}
@@ -1435,37 +1448,14 @@ static HRESULT build_mapped_header( const WS_XML_STRING *name, WS_TYPE type, WS_
return S_OK;
}
-/**************************************************************************
- * WsAddMappedHeader [webservices.@]
- */
-HRESULT WINAPI WsAddMappedHeader( WS_MESSAGE *handle, const WS_XML_STRING *name, WS_TYPE type,
- WS_WRITE_OPTION option, const void *value, ULONG size, WS_ERROR *error )
+static HRESULT add_mapped_header( struct msg *msg, const WS_XML_STRING *name, WS_TYPE type,
+ WS_WRITE_OPTION option, const void *value, ULONG size )
{
- struct msg *msg = (struct msg *)handle;
struct header *header;
BOOL found = FALSE;
HRESULT hr;
ULONG i;
- TRACE( "%p %s %u %08x %p %u %p\n", handle, debugstr_xmlstr(name), type, option, value, size, error );
- if (error) FIXME( "ignoring error parameter\n" );
-
- if (!msg || !name) return E_INVALIDARG;
-
- EnterCriticalSection( &msg->cs );
-
- if (msg->magic != MSG_MAGIC)
- {
- LeaveCriticalSection( &msg->cs );
- return E_INVALIDARG;
- }
-
- if (msg->state < WS_MESSAGE_STATE_INITIALIZED)
- {
- hr = WS_E_INVALID_OPERATION;
- goto done;
- }
-
for (i = 0; i < msg->header_count; i++)
{
if (msg->header[i]->type || !msg->header[i]->mapped) continue;
@@ -1478,18 +1468,44 @@ HRESULT WINAPI WsAddMappedHeader( WS_MESSAGE *handle, const WS_XML_STRING *name,
if (!found)
{
- if ((hr = grow_header_array( msg, msg->header_count + 1 )) != S_OK) goto done;
+ if ((hr = grow_header_array( msg, msg->header_count + 1 )) != S_OK) return hr;
i = msg->header_count;
}
- if ((hr = build_mapped_header( name, type, option, value, size, &header )) != S_OK) goto done;
+ if ((hr = build_mapped_header( name, type, option, value, size, &header )) != S_OK) return hr;
if (!found) msg->header_count++;
else free_header( msg->header[i] );
msg->header[i] = header;
+ return S_OK;
+}
+
+/**************************************************************************
+ * WsAddMappedHeader [webservices.@]
+ */
+HRESULT WINAPI WsAddMappedHeader( WS_MESSAGE *handle, const WS_XML_STRING *name, WS_TYPE type,
+ WS_WRITE_OPTION option, const void *value, ULONG size, WS_ERROR *error )
+{
+ struct msg *msg = (struct msg *)handle;
+ HRESULT hr;
+
+ TRACE( "%p %s %u %08x %p %u %p\n", handle, debugstr_xmlstr(name), type, option, value, size, error );
+ if (error) FIXME( "ignoring error parameter\n" );
+
+ if (!msg || !name) return E_INVALIDARG;
+
+ EnterCriticalSection( &msg->cs );
+
+ if (msg->magic != MSG_MAGIC)
+ {
+ LeaveCriticalSection( &msg->cs );
+ return E_INVALIDARG;
+ }
+
+ if (msg->state < WS_MESSAGE_STATE_INITIALIZED) hr = WS_E_INVALID_OPERATION;
+ else hr = add_mapped_header( msg, name, type, option, value, size );
-done:
LeaveCriticalSection( &msg->cs );
TRACE( "returning %08x\n", hr );
return hr;
@@ -1536,6 +1552,111 @@ HRESULT WINAPI WsRemoveMappedHeader( WS_MESSAGE *handle, const WS_XML_STRING *na
return hr;
}
+static HRESULT xmlstring_to_wsz( const WS_XML_STRING *str, WS_HEAP *heap, WCHAR **ret )
+{
+ int len = MultiByteToWideChar( CP_UTF8, 0, (char *)str->bytes, str->length, NULL, 0 );
+ if (!(*ret = ws_alloc( heap, (len + 1) * sizeof(WCHAR) ))) return WS_E_QUOTA_EXCEEDED;
+ MultiByteToWideChar( CP_UTF8, 0, (char *)str->bytes, str->length, *ret, len );
+ (*ret)[len] = 0;
+ return S_OK;
+}
+
+static HRESULT get_header_value_wsz( struct header *header, WS_READ_OPTION option, WS_HEAP *heap, WCHAR **ret,
+ ULONG size )
+{
+ WCHAR *str = NULL;
+ HRESULT hr;
+
+ if (header && (hr = xmlstring_to_wsz( header->u.text, heap, &str )) != S_OK) return hr;
+
+ switch (option)
+ {
+ case WS_READ_REQUIRED_POINTER:
+ if (!str && !(str = ws_alloc_zero( heap, sizeof(*str) ))) return WS_E_QUOTA_EXCEEDED;
+ /* fall through */
+
+ case WS_READ_OPTIONAL_POINTER:
+ case WS_READ_NILLABLE_POINTER:
+ if (size != sizeof(str)) return E_INVALIDARG;
+ *ret = str;
+ break;
+
+ default:
+ FIXME( "read option %u not supported\n", option );
+ return E_NOTIMPL;
+ }
+
+ return S_OK;
+}
+
+static HRESULT get_mapped_header( struct msg *msg, const WS_XML_STRING *name, WS_TYPE type, WS_READ_OPTION option,
+ WS_HEAP *heap, void *value, ULONG size )
+{
+ struct header *header = NULL;
+ HRESULT hr;
+ ULONG i;
+
+ for (i = 0; i < msg->header_count; i++)
+ {
+ if (msg->header[i]->type || !msg->header[i]->mapped) continue;
+ if (WsXmlStringEquals( name, &msg->header[i]->name, NULL ) == S_OK)
+ {
+ header = msg->header[i];
+ break;
+ }
+ }
+
+ switch (type)
+ {
+ case WS_WSZ_TYPE:
+ hr = get_header_value_wsz( header, option, heap, value, size );
+ break;
+
+ default:
+ FIXME( "type %u not supported\n", option );
+ return WS_E_NOT_SUPPORTED;
+ }
+
+ return hr;
+}
+
+/**************************************************************************
+ * WsGetMappedHeader [webservices.@]
+ */
+HRESULT WINAPI WsGetMappedHeader( WS_MESSAGE *handle, const WS_XML_STRING *name, WS_REPEATING_HEADER_OPTION option,
+ ULONG index, WS_TYPE type, WS_READ_OPTION read_option, WS_HEAP *heap, void *value,
+ ULONG size, WS_ERROR *error )
+{
+ struct msg *msg = (struct msg *)handle;
+ HRESULT hr;
+
+ TRACE( "%p %s %u %u %u %u %p %p %u %p\n", handle, debugstr_xmlstr(name), option, index, type, read_option,
+ heap, value, size, error );
+ if (error) FIXME( "ignoring error parameter\n" );
+ if (option != WS_SINGLETON_HEADER)
+ {
+ FIXME( "option %u not supported\n", option );
+ return E_NOTIMPL;
+ }
+
+ if (!msg || !name) return E_INVALIDARG;
+
+ EnterCriticalSection( &msg->cs );
+
+ if (msg->magic != MSG_MAGIC)
+ {
+ LeaveCriticalSection( &msg->cs );
+ return E_INVALIDARG;
+ }
+
+ if (msg->state < WS_MESSAGE_STATE_INITIALIZED) hr = WS_E_INVALID_OPERATION;
+ else hr = get_mapped_header( msg, name, type, read_option, heap, value, size );
+
+ LeaveCriticalSection( &msg->cs );
+ TRACE( "returning %08x\n", hr );
+ return hr;
+}
+
static HRESULT write_custom_header( WS_XML_WRITER *writer, const WS_XML_STRING *name, const WS_XML_STRING *ns,
WS_TYPE type, const void *desc, WS_WRITE_OPTION option, const void *value,
ULONG size )
@@ -1717,10 +1838,12 @@ HRESULT WINAPI WsRemoveCustomHeader( WS_MESSAGE *handle, const WS_XML_STRING *na
static WCHAR *build_http_header( const WCHAR *name, const WCHAR *value, ULONG *ret_len )
{
int len_name = lstrlenW( name ), len_value = lstrlenW( value );
- WCHAR *ret = heap_alloc( (len_name + len_value) * sizeof(WCHAR) );
+ WCHAR *ret = heap_alloc( (len_name + len_value + 2) * sizeof(WCHAR) );
if (!ret) return NULL;
memcpy( ret, name, len_name * sizeof(WCHAR) );
+ ret[len_name++] = ':';
+ ret[len_name++] = ' ';
memcpy( ret + len_name, value, len_value * sizeof(WCHAR) );
*ret_len = len_name + len_value;
return ret;
@@ -1732,10 +1855,48 @@ static inline HRESULT insert_http_header( HINTERNET req, const WCHAR *header, UL
return HRESULT_FROM_WIN32( GetLastError() );
}
+static WCHAR *from_xml_string( const WS_XML_STRING *str )
+{
+ WCHAR *ret;
+ int len = MultiByteToWideChar( CP_UTF8, 0, (char *)str->bytes, str->length, NULL, 0 );
+ if (!(ret = heap_alloc( (len + 1) * sizeof(*ret) ))) return NULL;
+ MultiByteToWideChar( CP_UTF8, 0, (char *)str->bytes, str->length, ret, len );
+ ret[len] = 0;
+ return ret;
+}
+
+static HRESULT insert_mapped_headers( struct msg *msg, HINTERNET req )
+{
+ WCHAR *name, *value, *header;
+ ULONG i, len;
+ HRESULT hr;
+
+ for (i = 0; i < msg->header_count; i++)
+ {
+ if (!msg->header[i]->mapped) continue;
+
+ if (!(name = from_xml_string( &msg->header[i]->name ))) return E_OUTOFMEMORY;
+ if (!(value = from_xml_string( msg->header[i]->u.text )))
+ {
+ heap_free( name );
+ return E_OUTOFMEMORY;
+ }
+ header = build_http_header( name, value, &len );
+ heap_free( name );
+ heap_free( value );
+ if (!header) return E_OUTOFMEMORY;
+
+ hr = insert_http_header( req, header, len, WINHTTP_ADDREQ_FLAG_ADD|WINHTTP_ADDREQ_FLAG_REPLACE );
+ heap_free( header );
+ if (hr != S_OK) return hr;
+ }
+ return S_OK;
+}
+
HRESULT message_insert_http_headers( WS_MESSAGE *handle, HINTERNET req )
{
static const WCHAR contenttypeW[] =
- {'C','o','n','t','e','n','t','-','T','y','p','e',':',' ',0};
+ {'C','o','n','t','e','n','t','-','T','y','p','e',0};
static const WCHAR soapxmlW[] =
{'a','p','p','l','i','c','a','t','i','o','n','/','s','o','a','p','+','x','m','l',0};
static const WCHAR textxmlW[] =
@@ -1785,7 +1946,7 @@ HRESULT message_insert_http_headers( WS_MESSAGE *handle, HINTERNET req )
{
case WS_ENVELOPE_VERSION_SOAP_1_1:
{
- static const WCHAR soapactionW[] = {'S','O','A','P','A','c','t','i','o','n',':',' ',0};
+ static const WCHAR soapactionW[] = {'S','O','A','P','A','c','t','i','o','n',0};
if (!(len = MultiByteToWideChar( CP_UTF8, 0, (char *)msg->action->bytes, msg->action->length, NULL, 0 )))
break;
@@ -1832,6 +1993,8 @@ HRESULT message_insert_http_headers( WS_MESSAGE *handle, HINTERNET req )
hr = E_NOTIMPL;
}
+ if (hr == S_OK) hr = insert_mapped_headers( msg, req );
+
done:
heap_free( header );
LeaveCriticalSection( &msg->cs );
@@ -1839,6 +2002,64 @@ done:
return hr;
}
+static HRESULT map_http_response_headers( struct msg *msg, HINTERNET req, const WS_HTTP_MESSAGE_MAPPING *mapping )
+{
+ ULONG i;
+ for (i = 0; i < mapping->responseHeaderMappingCount; i++)
+ {
+ WCHAR *name, *value;
+ DWORD size;
+
+ if (!(name = from_xml_string( &mapping->responseHeaderMappings[i]->headerName ))) return E_OUTOFMEMORY;
+
+ if (!WinHttpQueryHeaders( req, WINHTTP_QUERY_CUSTOM, name, NULL, &size, NULL ) &&
+ GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ HRESULT hr;
+ if (!(value = heap_alloc( size )))
+ {
+ heap_free( name );
+ return E_OUTOFMEMORY;
+ }
+ if (!WinHttpQueryHeaders( req, WINHTTP_QUERY_CUSTOM, name, value, &size, NULL ))
+ {
+ heap_free( name );
+ return HRESULT_FROM_WIN32( GetLastError() );
+ }
+ hr = add_mapped_header( msg, &mapping->responseHeaderMappings[i]->headerName, WS_WSZ_TYPE,
+ WS_WRITE_REQUIRED_POINTER, &value, sizeof(value) );
+ heap_free( value );
+ if (hr != S_OK)
+ {
+ heap_free( name );
+ return hr;
+ }
+ }
+ heap_free( name );
+ }
+ return S_OK;
+}
+
+HRESULT message_map_http_response_headers( WS_MESSAGE *handle, HINTERNET req, const WS_HTTP_MESSAGE_MAPPING *mapping )
+{
+ struct msg *msg = (struct msg *)handle;
+ HRESULT hr;
+
+ EnterCriticalSection( &msg->cs );
+
+ if (msg->magic != MSG_MAGIC)
+ {
+ LeaveCriticalSection( &msg->cs );
+ return E_INVALIDARG;
+ }
+
+ hr = map_http_response_headers( msg, req, mapping );
+
+ LeaveCriticalSection( &msg->cs );
+ TRACE( "returning %08x\n", hr );
+ return hr;
+}
+
void message_set_send_context( WS_MESSAGE *handle, const WS_PROXY_MESSAGE_CALLBACK_CONTEXT *ctx )
{
struct msg *msg = (struct msg *)handle;
diff --git a/dlls/webservices/proxy.c b/dlls/webservices/proxy.c
index b07b3c7a26..b6278a6354 100644
--- a/dlls/webservices/proxy.c
+++ b/dlls/webservices/proxy.c
@@ -455,7 +455,7 @@ static HRESULT receive_message( WS_CHANNEL *channel, WS_MESSAGE *msg, WS_MESSAGE
HRESULT hr;
if ((hr = message_set_action( msg, desc->action )) != S_OK) return hr;
- if ((hr = channel_receive_message( channel )) != S_OK) return hr;
+ if ((hr = channel_receive_message( channel, msg )) != S_OK) return hr;
if ((hr = channel_get_reader( channel, &reader )) != S_OK) return hr;
return read_message( msg, reader, heap, desc->bodyElementDescription, params, count, args );
}
diff --git a/dlls/webservices/tests/proxy.c b/dlls/webservices/tests/proxy.c
index 3fe92367e9..07610ac70e 100644
--- a/dlls/webservices/tests/proxy.c
+++ b/dlls/webservices/tests/proxy.c
@@ -361,13 +361,34 @@ static void test_WsReceiveMessage( int port )
WsFreeMessage( msg );
}
+static WS_HTTP_HEADER_MAPPING mapped_request_header =
+{
+ {19, (BYTE *)"MappedRequestHeader"}
+};
+
+static WS_HTTP_HEADER_MAPPING *request_header_mappings[] =
+{
+ &mapped_request_header
+};
+
+static WS_HTTP_HEADER_MAPPING mapped_response_header =
+{
+ {20, (BYTE *)"MappedResponseHeader"}
+};
+
+static WS_HTTP_HEADER_MAPPING *response_header_mappings[] =
+{
+ &mapped_response_header
+};
+
static HRESULT create_proxy( int port, WS_SERVICE_PROXY **ret )
{
static const WCHAR fmt[] =
{'h','t','t','p',':','/','/','1','2','7','.','0','.','0','.','1',':','%','u','/',0};
WS_ENVELOPE_VERSION env_version;
WS_ADDRESSING_VERSION addr_version;
- WS_CHANNEL_PROPERTY prop[2];
+ WS_HTTP_MESSAGE_MAPPING mapping;
+ WS_CHANNEL_PROPERTY prop[3];
WS_ENDPOINT_ADDRESS addr;
WS_SERVICE_PROXY *proxy;
WCHAR url[64];
@@ -383,6 +404,17 @@ static HRESULT create_proxy( int port, WS_SERVICE_PROXY **ret )
prop[1].value = &addr_version;
prop[1].valueSize = sizeof(addr_version);
+ mapping.requestMappingOptions = 0;
+ mapping.responseMappingOptions = 0;
+ mapping.requestHeaderMappings = request_header_mappings;
+ mapping.requestHeaderMappingCount = ARRAY_SIZE(request_header_mappings);
+ mapping.responseHeaderMappings = response_header_mappings;
+ mapping.responseHeaderMappingCount = ARRAY_SIZE(response_header_mappings);
+
+ prop[2].id = WS_CHANNEL_PROPERTY_HTTP_MESSAGE_MAPPING;
+ prop[2].value = &mapping;
+ prop[2].valueSize = sizeof(mapping);
+
*ret = NULL;
hr = WsCreateServiceProxy( WS_CHANNEL_TYPE_REQUEST, WS_HTTP_CHANNEL_BINDING, NULL, NULL,
0, prop, ARRAY_SIZE( prop ), &proxy, NULL );
@@ -397,6 +429,38 @@ static HRESULT create_proxy( int port, WS_SERVICE_PROXY **ret )
return hr;
}
+static HRESULT set_output( WS_XML_WRITER *writer )
+{
+ WS_XML_WRITER_TEXT_ENCODING text = {{ WS_XML_WRITER_ENCODING_TYPE_TEXT }, WS_CHARSET_UTF8 };
+ WS_XML_WRITER_BUFFER_OUTPUT buf = {{ WS_XML_WRITER_OUTPUT_TYPE_BUFFER }};
+ return WsSetOutput( writer, &text.encoding, &buf.output, NULL, 0, NULL );
+}
+
+static void check_output_headers( WS_MESSAGE *msg )
+{
+ WS_XML_WRITER *writer;
+ WS_XML_BUFFER *buf;
+ WS_BYTES bytes;
+ HRESULT hr;
+
+ hr = WsCreateWriter( NULL, 0, &writer, NULL );
+ ok( hr == S_OK, "got %08x\n", hr );
+
+ hr = set_output( writer );
+ ok( hr == S_OK, "got %08x\n", hr );
+
+ hr = WsGetMessageProperty( msg, WS_MESSAGE_PROPERTY_HEADER_BUFFER, &buf, sizeof(buf), NULL );
+ ok( hr == S_OK, "got %08x\n", hr );
+
+ hr = WsWriteXmlBuffer( writer, buf, NULL );
+ ok( hr == S_OK, "got %08x\n", hr );
+
+ memset( &bytes, 0, sizeof(bytes) );
+ hr = WsGetWriterProperty( writer, WS_XML_WRITER_PROPERTY_BYTES, &bytes, sizeof(bytes), NULL );
+ ok( hr == S_OK, "got %08x\n", hr );
+ WsFreeWriter( writer );
+}
+
static const char req_test2[] =
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>"
"<req_test2 xmlns=\"ns\"><val>1</val><str>test</str><str>test2</str></req_test2>"
@@ -407,6 +471,32 @@ static const char resp_test2[] =
"<resp_test2 xmlns=\"ns\"><str>test</str><val>1</val><val>2</val></resp_test2>"
"</s:Body></s:Envelope>";
+static HRESULT CALLBACK send_callback( WS_MESSAGE *msg, WS_HEAP *heap, void *state, WS_ERROR *error )
+{
+ static const WS_XML_STRING header = {19, (BYTE *)"MappedRequestHeader"}, value = {5, (BYTE *)"value"};
+ HRESULT hr;
+
+ hr = WsAddMappedHeader( msg, &header, WS_XML_STRING_TYPE, WS_WRITE_REQUIRED_VALUE, &value, sizeof(value), NULL );
+ ok( hr == S_OK, "got %08x\n", hr );
+ check_output_headers( msg );
+ return S_OK;
+}
+
+static HRESULT CALLBACK recv_callback( WS_MESSAGE *msg, WS_HEAP *heap, void *state, WS_ERROR *error )
+{
+ static const WS_XML_STRING header = {20, (BYTE *)"MappedResponseHeader"};
+ static const WCHAR valueW[] = {'v','a','l','u','e',0};
+ WCHAR *str;
+ HRESULT hr;
+
+ check_output_headers( msg );
+ hr = WsGetMappedHeader( msg, &header, WS_SINGLETON_HEADER, 0, WS_WSZ_TYPE, WS_READ_OPTIONAL_POINTER, heap,
+ &str, sizeof(str), NULL );
+ ok( hr == S_OK, "got %08x\n", hr );
+ ok( !lstrcmpW(str, valueW), "wrong value %s\n", wine_dbgstr_w(str) );
+ return S_OK;
+}
+
static void test_WsCall( int port )
{
static const WCHAR testW[] = {'t','e','s','t',0}, test2W[] = {'t','e','s','t','2',0};
@@ -421,6 +511,9 @@ static void test_WsCall( int port )
WS_XML_STRING ns = {2, (BYTE *)"ns"};
HRESULT hr;
WS_SERVICE_PROXY *proxy;
+ WS_PROXY_MESSAGE_CALLBACK_CONTEXT ctx_send;
+ WS_PROXY_MESSAGE_CALLBACK_CONTEXT ctx_recv;
+ WS_CALL_PROPERTY prop[2];
WS_OPERATION_DESCRIPTION op;
WS_MESSAGE_DESCRIPTION input_msg, output_msg;
WS_ELEMENT_DESCRIPTION input_elem, output_elem;
@@ -511,7 +604,21 @@ static void test_WsCall( int port )
args[4] = &val_ptr;
args[5] = &count_ptr;
- hr = WsCall( proxy, &op, args, heap, NULL, 0, NULL, NULL );
+ ctx_send.callback = send_callback;
+ ctx_send.state = NULL;
+
+ prop[0].id = WS_CALL_PROPERTY_SEND_MESSAGE_CONTEXT;
+ prop[0].value = &ctx_send;
+ prop[0].valueSize = sizeof(ctx_send);
+
+ ctx_recv.callback = recv_callback;
+ ctx_recv.state = NULL;
+
+ prop[1].id = WS_CALL_PROPERTY_RECEIVE_MESSAGE_CONTEXT;
+ prop[1].value = &ctx_recv;
+ prop[1].valueSize = sizeof(ctx_recv);
+
+ hr = WsCall( proxy, &op, args, heap, prop, ARRAY_SIZE(prop), NULL, NULL );
ok( hr == S_OK, "got %08x\n", hr );
ok( !lstrcmpW( out.str, testW ), "wrong data\n" );
ok( out.count == 2, "got %u\n", out.count );
@@ -616,7 +723,7 @@ tests[] =
static void send_response( int c, const char *status, const char *data, unsigned int len )
{
static const char headers[] =
- "Content-Type: text/xml; charset=utf-8\r\nConnection: close\r\n";
+ "Content-Type: text/xml; charset=utf-8\r\nConnection: close\r\nMappedResponseHeader: value\r\n";
static const char fmt[] =
"Content-Length: %u\r\n\r\n";
char buf[128];
@@ -694,10 +801,11 @@ static DWORD CALLBACK server_proc( void *arg )
if (tests[j].req_data)
{
int data_len = strlen( buf );
- ok( tests[j].req_len == data_len, "%u: unexpected data length %u %u\n",
+ ok( tests[j].req_len == data_len, "%u: got data length %u expected %u\n",
j, data_len, tests[j].req_len );
if (tests[j].req_len == data_len)
- ok( !memcmp( tests[j].req_data, buf, tests[j].req_len ), "%u: unexpected data %s\n", j, buf );
+ ok( !memcmp( tests[j].req_data, buf, tests[j].req_len ),
+ "%u: got data '%s' expected '%s'\n", j, buf, tests[j].req_data );
}
send_response( c, tests[j].resp_status, tests[j].resp_data, tests[j].resp_len );
break;
diff --git a/dlls/webservices/webservices.spec b/dlls/webservices/webservices.spec
index c8db3a0375..3fe1f4d427 100644
--- a/dlls/webservices/webservices.spec
+++ b/dlls/webservices/webservices.spec
@@ -70,7 +70,7 @@
@ stub WsGetHeaderAttributes
@ stdcall WsGetHeapProperty(ptr long ptr long ptr)
@ stdcall WsGetListenerProperty(ptr long ptr long ptr)
-@ stub WsGetMappedHeader
+@ stdcall WsGetMappedHeader(ptr ptr long long long long ptr ptr long ptr)
@ stdcall WsGetMessageProperty(ptr long ptr long ptr)
@ stub WsGetMetadataEndpoints
@ stub WsGetMetadataProperty
diff --git a/dlls/webservices/webservices_private.h b/dlls/webservices/webservices_private.h
index 6a6f65dbec..9ca470c82b 100644
--- a/dlls/webservices/webservices_private.h
+++ b/dlls/webservices/webservices_private.h
@@ -161,9 +161,10 @@ void message_set_receive_context( WS_MESSAGE *, const WS_PROXY_MESSAGE_CALLBACK_
void message_do_send_callback( WS_MESSAGE * ) DECLSPEC_HIDDEN;
void message_do_receive_callback( WS_MESSAGE * ) DECLSPEC_HIDDEN;
HRESULT message_insert_http_headers( WS_MESSAGE *, HINTERNET ) DECLSPEC_HIDDEN;
+HRESULT message_map_http_response_headers( WS_MESSAGE *, HINTERNET, const WS_HTTP_MESSAGE_MAPPING * ) DECLSPEC_HIDDEN;
HRESULT channel_send_message( WS_CHANNEL *, WS_MESSAGE * ) DECLSPEC_HIDDEN;
-HRESULT channel_receive_message( WS_CHANNEL * ) DECLSPEC_HIDDEN;
+HRESULT channel_receive_message( WS_CHANNEL *, WS_MESSAGE * ) DECLSPEC_HIDDEN;
HRESULT channel_get_reader( WS_CHANNEL *, WS_XML_READER ** ) DECLSPEC_HIDDEN;
HRESULT parse_url( const WS_STRING *, WS_URL_SCHEME_TYPE *, WCHAR **, USHORT * ) DECLSPEC_HIDDEN;
--
2.20.1
More information about the wine-devel
mailing list