From 5a58080a66b245edcccfc18ebd03162d7877f3af Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Wed, 31 Oct 2018 14:21:31 +0100 Subject: [PATCH] webservices: Implement WsRequestReply. Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/webservices/channel.c | 115 +++++++++++++++++ dlls/webservices/tests/channel.c | 201 +++++++++++++++++++++--------- dlls/webservices/webservices.spec | 2 +- include/webservices.h | 3 + 4 files changed, 260 insertions(+), 61 deletions(-) diff --git a/dlls/webservices/channel.c b/dlls/webservices/channel.c index de84b0e66f3..94c3dc1e99a 100644 --- a/dlls/webservices/channel.c +++ b/dlls/webservices/channel.c @@ -2007,6 +2007,121 @@ HRESULT WINAPI WsReceiveMessage( WS_CHANNEL *handle, WS_MESSAGE *msg, const WS_M return hr; } +static HRESULT request_reply( struct channel *channel, WS_MESSAGE *request, + const WS_MESSAGE_DESCRIPTION *request_desc, WS_WRITE_OPTION write_option, + const void *request_body, ULONG request_size, WS_MESSAGE *reply, + const WS_MESSAGE_DESCRIPTION *reply_desc, WS_READ_OPTION read_option, + WS_HEAP *heap, void *value, ULONG size ) +{ + HRESULT hr; + WsInitializeMessage( request, WS_REQUEST_MESSAGE, NULL, NULL ); + if ((hr = WsAddressMessage( request, &channel->addr, NULL )) != S_OK) return hr; + if ((hr = message_set_action( request, request_desc->action )) != S_OK) return hr; + + if ((hr = init_writer( channel )) != S_OK) return hr; + if ((hr = write_message( channel, request, request_desc->bodyElementDescription, write_option, request_body, + request_size )) != S_OK) return hr; + if ((hr = send_message( channel, request )) != S_OK) return hr; + + return receive_message( channel, reply, &reply_desc, 1, WS_RECEIVE_OPTIONAL_MESSAGE, read_option, heap, + value, size, NULL ); +} + +struct request_reply +{ + struct task task; + struct channel *channel; + WS_MESSAGE *request; + const WS_MESSAGE_DESCRIPTION *request_desc; + WS_WRITE_OPTION write_option; + const void *request_body; + ULONG request_size; + WS_MESSAGE *reply; + const WS_MESSAGE_DESCRIPTION *reply_desc; + WS_READ_OPTION read_option; + WS_HEAP *heap; + void *value; + ULONG size; + WS_ASYNC_CONTEXT ctx; +}; + +static void request_reply_proc( struct task *task ) +{ + struct request_reply *r = (struct request_reply *)task; + HRESULT hr; + + hr = request_reply( r->channel, r->request, r->request_desc, r->write_option, r->request_body, r->request_size, + r->reply, r->reply_desc, r->read_option, r->heap, r->value, r->size ); + + TRACE( "calling %p(%08x)\n", r->ctx.callback, hr ); + r->ctx.callback( hr, WS_LONG_CALLBACK, r->ctx.callbackState ); + TRACE( "%p returned\n", r->ctx.callback ); +} + +static HRESULT queue_request_reply( struct channel *channel, WS_MESSAGE *request, + const WS_MESSAGE_DESCRIPTION *request_desc, WS_WRITE_OPTION write_option, + const void *request_body, ULONG request_size, WS_MESSAGE *reply, + const WS_MESSAGE_DESCRIPTION *reply_desc, WS_READ_OPTION read_option, + WS_HEAP *heap, void *value, ULONG size, const WS_ASYNC_CONTEXT *ctx ) +{ + struct request_reply *r; + + if (!(r = heap_alloc( sizeof(*r) ))) return E_OUTOFMEMORY; + r->task.proc = request_reply_proc; + r->channel = channel; + r->request = request; + r->request_desc = request_desc; + r->write_option = write_option; + r->request_body = request_body; + r->request_size = request_size; + r->reply = reply; + r->reply_desc = reply_desc; + r->read_option = read_option; + r->heap = heap; + r->value = value; + r->size = size; + r->ctx = *ctx; + return queue_task( &channel->recv_q, &r->task ); +} + +/************************************************************************** + * WsRequestReply [webservices.@] + */ +HRESULT WINAPI WsRequestReply( WS_CHANNEL *handle, WS_MESSAGE *request, const WS_MESSAGE_DESCRIPTION *request_desc, + WS_WRITE_OPTION write_option, const void *request_body, ULONG request_size, + WS_MESSAGE *reply, const WS_MESSAGE_DESCRIPTION *reply_desc, WS_READ_OPTION read_option, + WS_HEAP *heap, void *value, ULONG size, const WS_ASYNC_CONTEXT *ctx, WS_ERROR *error ) +{ + struct channel *channel = (struct channel *)handle; + HRESULT hr; + + TRACE( "%p %p %p %08x %p %u %p %p %08x %p %p %u %p %p\n", handle, request, request_desc, write_option, + request_body, request_size, reply, reply_desc, read_option, heap, value, size, ctx, error ); + if (error) FIXME( "ignoring error parameter\n" ); + if (ctx) FIXME( "ignoring ctx parameter\n" ); + + if (!channel || !request || !reply) return E_INVALIDARG; + + EnterCriticalSection( &channel->cs ); + + if (channel->magic != CHANNEL_MAGIC) + { + LeaveCriticalSection( &channel->cs ); + return E_INVALIDARG; + } + + if (ctx) + hr = queue_request_reply( channel, request, request_desc, write_option, request_body, request_size, reply, + reply_desc, read_option, heap, value, size, ctx ); + else + hr = request_reply( channel, request, request_desc, write_option, request_body, request_size, reply, + reply_desc, read_option, heap, value, size ); + + LeaveCriticalSection( &channel->cs ); + TRACE( "returning %08x\n", hr ); + return hr; +} + /************************************************************************** * WsReadMessageStart [webservices.@] */ diff --git a/dlls/webservices/tests/channel.c b/dlls/webservices/tests/channel.c index db17893138e..6a45671057d 100644 --- a/dlls/webservices/tests/channel.c +++ b/dlls/webservices/tests/channel.c @@ -433,16 +433,21 @@ static void test_WsResetListener(void) WsFreeListener( listener ); } +static const WCHAR fmt_soap_udp[] = + {'s','o','a','p','.','u','d','p',':','/','/','l','o','c','a','l','h','o','s','t',':','%','u',0}; +static const WCHAR fmt_net_tcp[] = + {'n','e','t','.','t','c','p',':','/','/','l','o','c','a','l','h','o','s','t',':','%','u',0}; + struct listener_info { int port; HANDLE wait; WS_CHANNEL_BINDING binding; WS_CHANNEL_TYPE type; - void (*test_func)( WS_CHANNEL * ); + void (*server_func)( WS_CHANNEL * ); }; -static void client_message_read_write( WS_CHANNEL *channel ) +static void server_message_read_write( WS_CHANNEL *channel ) { WS_MESSAGE *msg; HRESULT hr; @@ -476,10 +481,8 @@ static void client_message_read_write( WS_CHANNEL *channel ) WsFreeMessage( msg ); } -static void test_message_read_write( const struct listener_info *info ) +static void client_message_read_write( const struct listener_info *info ) { - static const WCHAR fmt[] = - {'s','o','a','p','.','u','d','p',':','/','/','l','o','c','a','l','h','o','s','t',':','%','u',0}; WS_ENDPOINT_ADDRESS addr; WCHAR buf[64]; WS_CHANNEL *channel; @@ -491,7 +494,7 @@ static void test_message_read_write( const struct listener_info *info ) ok( hr == S_OK, "got %08x\n", hr ); memset( &addr, 0, sizeof(addr) ); - addr.url.length = wsprintfW( buf, fmt, info->port ); + addr.url.length = wsprintfW( buf, fmt_soap_udp, info->port ); addr.url.chars = buf; hr = WsOpenChannel( channel, &addr, NULL, NULL ); ok( hr == S_OK, "got %08x\n", hr ); @@ -545,7 +548,7 @@ struct async_test HANDLE wait; }; -static void CALLBACK callback_duplex_session( HRESULT hr, WS_CALLBACK_MODEL model, void *state ) +static void CALLBACK async_callback( HRESULT hr, WS_CALLBACK_MODEL model, void *state ) { struct async_test *test = state; @@ -555,7 +558,7 @@ static void CALLBACK callback_duplex_session( HRESULT hr, WS_CALLBACK_MODEL mode SetEvent( test->wait ); } -static void client_duplex_session( WS_CHANNEL *channel ) +static void server_duplex_session( WS_CHANNEL *channel ) { WS_XML_STRING action = {6, (BYTE *)"action"}, localname = {9, (BYTE *)"localname"}, ns = {2, (BYTE *)"ns"}; WS_ELEMENT_DESCRIPTION desc_body; @@ -580,7 +583,7 @@ static void client_duplex_session( WS_CHANNEL *channel ) test.call_count = 0; test.wait = CreateEventW( NULL, FALSE, FALSE, NULL ); - ctx.callback = callback_duplex_session; + ctx.callback = async_callback; ctx.callbackState = &test; hr = WsReceiveMessage( channel, msg, desc, 1, WS_RECEIVE_OPTIONAL_MESSAGE, WS_READ_REQUIRED_VALUE, @@ -612,10 +615,8 @@ static void client_duplex_session( WS_CHANNEL *channel ) WsFreeMessage( msg2 ); } -static void test_duplex_session( const struct listener_info *info ) +static void client_duplex_session( const struct listener_info *info ) { - static const WCHAR fmt[] = - {'n','e','t','.','t','c','p',':','/','/','l','o','c','a','l','h','o','s','t',':','%','u',0}; WS_XML_STRING action = {6, (BYTE *)"action"}, localname = {9, (BYTE *)"localname"}, ns = {2, (BYTE *)"ns"}; WS_ELEMENT_DESCRIPTION desc_body; WS_MESSAGE_DESCRIPTION desc; @@ -634,7 +635,7 @@ static void test_duplex_session( const struct listener_info *info ) ok( hr == WS_E_INVALID_OPERATION, "got %08x\n", hr ); memset( &addr, 0, sizeof(addr) ); - addr.url.length = wsprintfW( buf, fmt, info->port ); + addr.url.length = wsprintfW( buf, fmt_net_tcp, info->port ); addr.url.chars = buf; hr = WsOpenChannel( channel, &addr, NULL, NULL ); ok( hr == S_OK, "got %08x\n", hr ); @@ -675,7 +676,7 @@ static void test_duplex_session( const struct listener_info *info ) WsFreeChannel( channel ); } -static void client_accept_channel( WS_CHANNEL *channel ) +static void server_accept_channel( WS_CHANNEL *channel ) { WS_XML_STRING localname = {9, (BYTE *)"localname"}, ns = {2, (BYTE *)"ns"}, action = {6, (BYTE *)"action"}; WS_ELEMENT_DESCRIPTION body; @@ -704,13 +705,9 @@ static void client_accept_channel( WS_CHANNEL *channel ) WsFreeMessage( msg ); } -static void test_WsAcceptChannel( const struct listener_info *info ) +static void client_accept_channel( const struct listener_info *info ) { - static const WCHAR fmt_tcp[] = - {'n','e','t','.','t','c','p',':','/','/','l','o','c','a','l','h','o','s','t',':','%','u',0}; - static const WCHAR fmt_udp[] = - {'s','o','a','p','.','u','d','p',':','/','/','l','o','c','a','l','h','o','s','t',':','%','u',0}; - const WCHAR *fmt = (info->binding == WS_TCP_CHANNEL_BINDING) ? fmt_tcp : fmt_udp; + const WCHAR *fmt = (info->binding == WS_TCP_CHANNEL_BINDING) ? fmt_net_tcp : fmt_soap_udp; WS_XML_STRING localname = {9, (BYTE *)"localname"}, ns = {2, (BYTE *)"ns"}, action = {6, (BYTE *)"action"}; WCHAR buf[64]; WS_LISTENER *listener; @@ -773,14 +770,104 @@ static void test_WsAcceptChannel( const struct listener_info *info ) WsFreeChannel( channel ); } +static void server_request_reply( WS_CHANNEL *channel ) +{ + WS_XML_STRING localname = {9, (BYTE *)"localname"}, ns = {2, (BYTE *)"ns"}; + WS_XML_STRING req_action = {7, (BYTE *)"request"}, reply_action= {5, (BYTE *)"reply"}; + WS_ELEMENT_DESCRIPTION body; + WS_MESSAGE_DESCRIPTION in_desc, out_desc; + const WS_MESSAGE_DESCRIPTION *desc[1]; + WS_MESSAGE *msg; + INT32 val = 0; + HRESULT hr; + + hr = WsCreateMessageForChannel( channel, NULL, 0, &msg, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + body.elementLocalName = &localname; + body.elementNs = &ns; + body.type = WS_INT32_TYPE; + body.typeDescription = NULL; + + in_desc.action = &req_action; + in_desc.bodyElementDescription = &body; + desc[0] = &in_desc; + + hr = WsReceiveMessage( channel, msg, desc, 1, WS_RECEIVE_REQUIRED_MESSAGE, WS_READ_REQUIRED_VALUE, + NULL, &val, sizeof(val), NULL, NULL, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + ok( val == -1, "got %d\n", val ); + WsFreeMessage( msg ); + + hr = WsCreateMessageForChannel( channel, NULL, 0, &msg, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + out_desc.action = &reply_action; + out_desc.bodyElementDescription = &body; + + hr = WsSendMessage( channel, msg, &out_desc, WS_WRITE_REQUIRED_VALUE, &val, sizeof(val), NULL, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + WsFreeMessage( msg ); +} + +static void client_request_reply( const struct listener_info *info ) +{ + WS_XML_STRING localname = {9, (BYTE *)"localname"}, ns = {2, (BYTE *)"ns"}; + WS_XML_STRING req_action = {7, (BYTE *)"request"}, reply_action= {5, (BYTE *)"reply"}; + WCHAR buf[64]; + WS_CHANNEL *channel; + WS_MESSAGE *req, *reply; + WS_ENDPOINT_ADDRESS addr; + WS_ELEMENT_DESCRIPTION body; + WS_MESSAGE_DESCRIPTION req_desc, reply_desc; + INT32 val_in = -1, val_out = 0; + HRESULT hr; + + hr = WsCreateChannel( info->type, info->binding, NULL, 0, NULL, &channel, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + memset( &addr, 0, sizeof(addr) ); + addr.url.length = wsprintfW( buf, fmt_net_tcp, info->port ); + addr.url.chars = buf; + hr = WsOpenChannel( channel, &addr, NULL, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = WsCreateMessageForChannel( channel, NULL, 0, &req, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = WsCreateMessageForChannel( channel, NULL, 0, &reply, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = WsInitializeMessage( req, WS_BLANK_MESSAGE, NULL, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + body.elementLocalName = &localname; + body.elementNs = &ns; + body.type = WS_INT32_TYPE; + body.typeDescription = NULL; + + req_desc.action = &req_action; + req_desc.bodyElementDescription = &body; + + reply_desc.action = &reply_action; + reply_desc.bodyElementDescription = &body; + + hr = WsRequestReply( channel, req, &req_desc, WS_WRITE_REQUIRED_VALUE, &val_in, sizeof(val_in), reply, + &reply_desc, WS_READ_REQUIRED_VALUE, NULL, &val_out, sizeof(val_out), NULL, NULL ); + ok( val_out == -1, "got %d\n", val_out ); + + hr = WsCloseChannel( channel, NULL, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + WsFreeMessage( req ); + WsFreeMessage( reply ); + WsFreeChannel( channel ); +} + static DWORD CALLBACK listener_proc( void *arg ) { - static const WCHAR fmt_tcp[] = - {'n','e','t','.','t','c','p',':','/','/','l','o','c','a','l','h','o','s','t',':','%','u',0}; - static const WCHAR fmt_udp[] = - {'s','o','a','p','.','u','d','p',':','/','/','l','o','c','a','l','h','o','s','t',':','%','u',0}; struct listener_info *info = arg; - const WCHAR *fmt = (info->binding == WS_TCP_CHANNEL_BINDING) ? fmt_tcp : fmt_udp; + const WCHAR *fmt = (info->binding == WS_TCP_CHANNEL_BINDING) ? fmt_net_tcp : fmt_soap_udp; WS_LISTENER *listener; WS_CHANNEL *channel; WCHAR buf[64]; @@ -803,7 +890,7 @@ static DWORD CALLBACK listener_proc( void *arg ) hr = WsAcceptChannel( listener, channel, NULL, NULL ); ok( hr == S_OK, "got %08x\n", hr ); - info->test_func( channel ); + info->server_func( channel ); SetEvent( info->wait ); @@ -956,6 +1043,22 @@ START_TEST(channel) struct listener_info info; HANDLE thread; HRESULT hr; + unsigned int i; + static const struct test + { + WS_CHANNEL_BINDING binding; + WS_CHANNEL_TYPE type; + void (*server_func)( WS_CHANNEL * ); + void (*client_func)( const struct listener_info * ); + } + tests[] = + { + { WS_UDP_CHANNEL_BINDING, WS_CHANNEL_TYPE_DUPLEX, server_message_read_write, client_message_read_write }, + { WS_TCP_CHANNEL_BINDING, WS_CHANNEL_TYPE_DUPLEX_SESSION, server_duplex_session, client_duplex_session }, + { WS_UDP_CHANNEL_BINDING, WS_CHANNEL_TYPE_DUPLEX, server_accept_channel, client_accept_channel }, + { WS_TCP_CHANNEL_BINDING, WS_CHANNEL_TYPE_DUPLEX_SESSION, server_accept_channel, client_accept_channel }, + { WS_TCP_CHANNEL_BINDING, WS_CHANNEL_TYPE_DUPLEX_SESSION, server_request_reply, client_request_reply }, + }; if (firewall_enabled) { @@ -979,42 +1082,20 @@ START_TEST(channel) test_WsCreateChannelForListener(); test_WsResetListener(); - info.port = 7533; - info.wait = CreateEventW( NULL, 0, 0, NULL ); - info.type = WS_CHANNEL_TYPE_DUPLEX; - info.binding = WS_UDP_CHANNEL_BINDING; - info.test_func = client_message_read_write; + info.port = 7533; + info.wait = CreateEventW( NULL, 0, 0, NULL ); - thread = start_listener( &info ); - test_message_read_write( &info ); - WaitForSingleObject( thread, 3000 ); - CloseHandle(thread); + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + info.binding = tests[i].binding; + info.type = tests[i].type; + info.server_func = tests[i].server_func; - info.type = WS_CHANNEL_TYPE_DUPLEX_SESSION; - info.binding = WS_TCP_CHANNEL_BINDING; - info.test_func = client_duplex_session; - - thread = start_listener( &info ); - test_duplex_session( &info ); - WaitForSingleObject( thread, 3000 ); - CloseHandle(thread); - - info.type = WS_CHANNEL_TYPE_DUPLEX; - info.binding = WS_UDP_CHANNEL_BINDING; - info.test_func = client_accept_channel; - - thread = start_listener( &info ); - test_WsAcceptChannel( &info ); - WaitForSingleObject( thread, 3000 ); - CloseHandle(thread); - - info.type = WS_CHANNEL_TYPE_DUPLEX_SESSION; - info.binding = WS_TCP_CHANNEL_BINDING; - - thread = start_listener( &info ); - test_WsAcceptChannel( &info ); - WaitForSingleObject( thread, 3000 ); - CloseHandle(thread); + thread = start_listener( &info ); + tests[i].client_func( &info ); + WaitForSingleObject( thread, 3000 ); + CloseHandle( thread ); + } if (firewall_enabled) set_firewall( APP_REMOVE ); } diff --git a/dlls/webservices/webservices.spec b/dlls/webservices/webservices.spec index 8ba34b42090..30c97e3473e 100644 --- a/dlls/webservices/webservices.spec +++ b/dlls/webservices/webservices.spec @@ -131,7 +131,7 @@ @ stdcall WsRemoveHeader(ptr long ptr) @ stdcall WsRemoveMappedHeader(ptr ptr ptr) @ stub WsRemoveNode -@ stub WsRequestReply +@ stdcall WsRequestReply(ptr ptr ptr long ptr long ptr ptr long ptr ptr long ptr ptr) @ stub WsRequestSecurityToken @ stdcall WsResetChannel(ptr ptr) @ stdcall WsResetError(ptr) diff --git a/include/webservices.h b/include/webservices.h index 928819fc3d3..6cafcc63d64 100644 --- a/include/webservices.h +++ b/include/webservices.h @@ -1701,6 +1701,9 @@ HRESULT WINAPI WsRemoveCustomHeader(WS_MESSAGE*, const WS_XML_STRING*, const WS_ HRESULT WINAPI WsRemoveHeader(WS_MESSAGE*, WS_HEADER_TYPE, WS_ERROR*); HRESULT WINAPI WsRemoveMappedHeader(WS_MESSAGE*, const WS_XML_STRING*, WS_ERROR*); HRESULT WINAPI WsRemoveNode(const WS_XML_NODE_POSITION*, WS_ERROR*); +HRESULT WINAPI WsRequestReply(WS_CHANNEL*, WS_MESSAGE*, const WS_MESSAGE_DESCRIPTION*, WS_WRITE_OPTION, + const void*, ULONG, WS_MESSAGE*, const WS_MESSAGE_DESCRIPTION*, WS_READ_OPTION, + WS_HEAP*, void*, ULONG, const WS_ASYNC_CONTEXT*, WS_ERROR*); HRESULT WINAPI WsResetChannel(WS_CHANNEL*, WS_ERROR*); HRESULT WINAPI WsResetMessage(WS_MESSAGE*, WS_ERROR*); HRESULT WINAPI WsResetError(WS_ERROR*);