webservices: Add support for mapped HTTP headers.

Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
stable
Hans Leidekker 2019-09-25 14:29:58 +02:00 committed by Alexandre Julliard
parent 7236d982c9
commit 02962d92c4
6 changed files with 505 additions and 66 deletions

View File

@ -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];
switch (properties[i].id)
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 (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 *)properties[i].value;
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;
}
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 );

View File

@ -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,6 +1448,39 @@ static HRESULT build_mapped_header( const WS_XML_STRING *name, WS_TYPE type, WS_
return S_OK;
}
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 header *header;
BOOL found = FALSE;
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)
{
found = TRUE;
break;
}
}
if (!found)
{
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) return hr;
if (!found) msg->header_count++;
else free_header( msg->header[i] );
msg->header[i] = header;
return S_OK;
}
/**************************************************************************
* WsAddMappedHeader [webservices.@]
*/
@ -1442,10 +1488,7 @@ HRESULT WINAPI WsAddMappedHeader( WS_MESSAGE *handle, const WS_XML_STRING *name,
WS_WRITE_OPTION option, const void *value, ULONG size, WS_ERROR *error )
{
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" );
@ -1460,36 +1503,9 @@ HRESULT WINAPI WsAddMappedHeader( WS_MESSAGE *handle, const WS_XML_STRING *name,
return E_INVALIDARG;
}
if (msg->state < WS_MESSAGE_STATE_INITIALIZED)
{
hr = WS_E_INVALID_OPERATION;
goto done;
}
if (msg->state < WS_MESSAGE_STATE_INITIALIZED) hr = WS_E_INVALID_OPERATION;
else hr = add_mapped_header( msg, name, type, option, value, size );
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)
{
found = TRUE;
break;
}
}
if (!found)
{
if ((hr = grow_header_array( msg, msg->header_count + 1 )) != S_OK) goto done;
i = msg->header_count;
}
if ((hr = build_mapped_header( name, type, option, value, size, &header )) != S_OK) goto done;
if (!found) msg->header_count++;
else free_header( msg->header[i] );
msg->header[i] = header;
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;

View File

@ -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 );
}

View File

@ -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;

View File

@ -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

View File

@ -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;