[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