/* * Copyright 2016 Hans Leidekker for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include "windows.h" #include "winsock2.h" #include "webservices.h" #include "wine/test.h" static inline void set_field_desc( WS_FIELD_DESCRIPTION *desc, WS_FIELD_MAPPING mapping, WS_XML_STRING *localname, WS_XML_STRING *ns, WS_TYPE type, void *type_desc, ULONG offset, ULONG options, ULONG count_offset, WS_XML_STRING *item_localname, WS_XML_STRING *item_ns ) { memset( desc, 0, sizeof(*desc) ); desc->mapping = mapping; desc->localName = localname; desc->ns = ns; desc->type = type; desc->typeDescription = type_desc; desc->offset = offset; desc->options = options; desc->countOffset = count_offset; desc->itemLocalName = item_localname; desc->itemNs = item_ns; } static inline void set_struct_desc( WS_STRUCT_DESCRIPTION *desc, ULONG size, ULONG alignment, WS_FIELD_DESCRIPTION **fields, ULONG count, WS_XML_STRING *localname, WS_XML_STRING *ns, ULONG options ) { memset( desc, 0, sizeof(*desc) ); desc->size = size; desc->alignment = alignment; desc->fields = fields; desc->fieldCount = count; desc->typeLocalName = localname; desc->typeNs = ns; desc->structOptions = options; } static inline void set_elem_desc( WS_ELEMENT_DESCRIPTION *desc, WS_XML_STRING *localname, WS_XML_STRING *ns, WS_TYPE type, void *type_desc ) { desc->elementLocalName = localname; desc->elementNs = ns; desc->type = type; desc->typeDescription = type_desc; } static inline void set_msg_desc( WS_MESSAGE_DESCRIPTION *desc, WS_XML_STRING *action, WS_ELEMENT_DESCRIPTION *elem_desc ) { desc->action = action; desc->bodyElementDescription = elem_desc; } static inline void set_param_desc( WS_PARAMETER_DESCRIPTION *desc, WS_PARAMETER_TYPE type, USHORT input_index, USHORT output_index ) { desc->parameterType = type; desc->inputMessageIndex = input_index; desc->outputMessageIndex = output_index; } static inline void set_op_desc( WS_OPERATION_DESCRIPTION *desc, WS_MESSAGE_DESCRIPTION *input_msg, WS_MESSAGE_DESCRIPTION *output_msg, ULONG count, WS_PARAMETER_DESCRIPTION *param_desc ) { memset( desc, 0, sizeof(*desc) ); desc->versionInfo = 1; desc->inputMessageDescription = input_msg; desc->outputMessageDescription = output_msg; desc->parameterCount = count; desc->parameterDescription = param_desc; } static void test_WsCreateServiceProxy(void) { HRESULT hr; WS_SERVICE_PROXY *proxy; WS_SERVICE_PROXY_STATE state; ULONG value; hr = WsCreateServiceProxy( WS_CHANNEL_TYPE_REQUEST, WS_HTTP_CHANNEL_BINDING, NULL, NULL, 0, NULL, 0, NULL, NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); proxy = NULL; hr = WsCreateServiceProxy( WS_CHANNEL_TYPE_REQUEST, WS_HTTP_CHANNEL_BINDING, NULL, NULL, 0, NULL, 0, &proxy, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); ok( proxy != NULL, "proxy not set\n" ); /* write-only property */ value = 0xdeadbeef; hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_CALL_TIMEOUT, &value, sizeof(value), NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); state = 0xdeadbeef; hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_STATE, &state, sizeof(state), NULL ); ok( hr == S_OK, "got %#lx\n", hr ); ok( state == WS_SERVICE_PROXY_STATE_CREATED, "got %u\n", state ); WsFreeServiceProxy( proxy ); } static void test_WsCreateServiceProxyFromTemplate(void) { HRESULT hr; WS_SERVICE_PROXY *proxy; WS_HTTP_POLICY_DESCRIPTION policy; hr = WsCreateServiceProxyFromTemplate( WS_CHANNEL_TYPE_REQUEST, NULL, 0, WS_HTTP_BINDING_TEMPLATE_TYPE, NULL, 0, NULL, 0, NULL, NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); hr = WsCreateServiceProxyFromTemplate( WS_CHANNEL_TYPE_REQUEST, NULL, 0, WS_HTTP_BINDING_TEMPLATE_TYPE, NULL, 0, NULL, 0, &proxy, NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); memset( &policy, 0, sizeof(policy) ); proxy = NULL; hr = WsCreateServiceProxyFromTemplate( WS_CHANNEL_TYPE_REQUEST, NULL, 0, WS_HTTP_BINDING_TEMPLATE_TYPE, NULL, 0, &policy, sizeof(policy), &proxy, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); ok( proxy != NULL, "proxy not set\n" ); WsFreeServiceProxy( proxy ); } static void test_WsOpenServiceProxy(void) { HRESULT hr; WS_SERVICE_PROXY *proxy; WS_SERVICE_PROXY_STATE state; WS_HTTP_POLICY_DESCRIPTION policy; WS_ENDPOINT_ADDRESS addr; memset( &policy, 0, sizeof(policy) ); hr = WsCreateServiceProxyFromTemplate( WS_CHANNEL_TYPE_REQUEST, NULL, 0, WS_HTTP_BINDING_TEMPLATE_TYPE, NULL, 0, &policy, sizeof(policy), &proxy, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); state = 0xdeadbeef; hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_STATE, &state, sizeof(state), NULL ); ok( hr == S_OK, "got %#lx\n", hr ); ok( state == WS_SERVICE_PROXY_STATE_CREATED, "got %u\n", state ); memset( &addr, 0, sizeof(addr) ); addr.url.length = ARRAY_SIZE( L"http://localhost/" ) - 1; addr.url.chars = (WCHAR *)L"http://localhost/"; hr = WsOpenServiceProxy( proxy, &addr, NULL, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); state = 0xdeadbeef; hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_STATE, &state, sizeof(state), NULL ); ok( hr == S_OK, "got %#lx\n", hr ); ok( state == WS_SERVICE_PROXY_STATE_OPEN, "got %u\n", state ); hr = WsCloseServiceProxy( proxy , NULL, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); state = 0xdeadbeef; hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_STATE, &state, sizeof(state), NULL ); ok( hr == S_OK, "got %#lx\n", hr ); ok( state == WS_SERVICE_PROXY_STATE_CLOSED, "got %u\n", state ); WsFreeServiceProxy( proxy ); } static void test_WsResetServiceProxy(void) { HRESULT hr; WS_SERVICE_PROXY *proxy; WS_ENDPOINT_ADDRESS addr; WS_SERVICE_PROXY_STATE state; hr = WsCreateServiceProxy( WS_CHANNEL_TYPE_REQUEST, WS_HTTP_CHANNEL_BINDING, NULL, NULL, 0, NULL, 0, &proxy, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); hr = WsResetServiceProxy( proxy, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); state = 0xdeadbeef; hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_STATE, &state, sizeof(state), NULL ); ok( hr == S_OK, "got %#lx\n", hr ); ok( state == WS_SERVICE_PROXY_STATE_CREATED, "got %u\n", state ); memset( &addr, 0, sizeof(addr) ); addr.url.length = ARRAY_SIZE( L"http://localhost/" ) - 1; addr.url.chars = (WCHAR *)L"http://localhost/"; hr = WsOpenServiceProxy( proxy, &addr, NULL, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); hr = WsResetServiceProxy( proxy, NULL ); ok( hr == WS_E_INVALID_OPERATION, "got %#lx\n", hr ); hr = WsCloseServiceProxy( proxy , NULL, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); hr = WsResetServiceProxy( proxy, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); state = 0xdeadbeef; hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_STATE, &state, sizeof(state), NULL ); ok( hr == S_OK, "got %#lx\n", hr ); ok( state == WS_SERVICE_PROXY_STATE_CREATED, "got %u\n", state ); WsFreeServiceProxy( proxy ); } static HRESULT create_channel( int port, WS_CHANNEL **ret ) { WS_CHANNEL_PROPERTY prop[2]; WS_ENVELOPE_VERSION env_version = WS_ENVELOPE_VERSION_SOAP_1_1; WS_ADDRESSING_VERSION addr_version = WS_ADDRESSING_VERSION_TRANSPORT; WS_CHANNEL *channel; WS_ENDPOINT_ADDRESS addr; WCHAR buf[64]; HRESULT hr; prop[0].id = WS_CHANNEL_PROPERTY_ENVELOPE_VERSION; prop[0].value = &env_version; prop[0].valueSize = sizeof(env_version); prop[1].id = WS_CHANNEL_PROPERTY_ADDRESSING_VERSION; prop[1].value = &addr_version; prop[1].valueSize = sizeof(addr_version); *ret = NULL; hr = WsCreateChannel( WS_CHANNEL_TYPE_REQUEST, WS_HTTP_CHANNEL_BINDING, prop, 2, NULL, &channel, NULL ); if (hr != S_OK) return hr; memset( &addr, 0, sizeof(addr) ); addr.url.length = wsprintfW( buf, L"http://127.0.0.1:%u", port ); addr.url.chars = buf; hr = WsOpenChannel( channel, &addr, NULL, NULL ); if (hr == S_OK) *ret = channel; else WsFreeChannel( channel ); return hr; } static const char req_test1[] = "" "-1" ""; static const char resp_test1[] = "" "-2" ""; static void test_WsSendMessage( int port, WS_XML_STRING *action ) { WS_XML_STRING req = {9, (BYTE *)"req_test1"}, ns = {2, (BYTE *)"ns"}; WS_CHANNEL *channel; WS_MESSAGE *msg; WS_ELEMENT_DESCRIPTION body; WS_MESSAGE_DESCRIPTION desc; INT32 val = -1; HRESULT hr; hr = create_channel( port, &channel ); ok( hr == S_OK, "got %#lx\n", hr ); hr = WsCreateMessageForChannel( channel, NULL, 0, &msg, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); set_elem_desc( &body, &req, &ns, WS_INT32_TYPE, NULL ); set_msg_desc( &desc, action, &body ); hr = WsSendMessage( NULL, msg, &desc, WS_WRITE_REQUIRED_VALUE, &val, sizeof(val), NULL, NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); hr = WsSendMessage( channel, NULL, &desc, WS_WRITE_REQUIRED_VALUE, &val, sizeof(val), NULL, NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); hr = WsSendMessage( channel, msg, NULL, WS_WRITE_REQUIRED_VALUE, &val, sizeof(val), NULL, NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); hr = WsSendMessage( channel, msg, &desc, WS_WRITE_REQUIRED_VALUE, &val, sizeof(val), NULL, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); hr = WsCloseChannel( channel, NULL, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); WsFreeChannel( channel ); WsFreeMessage( msg ); } static void test_WsReceiveMessage( int port ) { WS_XML_STRING req = {9, (BYTE *)"req_test1"}, resp = {10, (BYTE *)"resp_test1"}, ns = {2, (BYTE *)"ns"}; WS_CHANNEL *channel; WS_MESSAGE *msg; WS_ELEMENT_DESCRIPTION body; WS_MESSAGE_DESCRIPTION desc_req, desc_resp; const WS_MESSAGE_DESCRIPTION *desc[1]; INT32 val = -1; HRESULT hr; hr = create_channel( port, &channel ); ok( hr == S_OK, "got %#lx\n", hr ); hr = WsCreateMessageForChannel( channel, NULL, 0, &msg, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); set_elem_desc( &body, &req, &ns, WS_INT32_TYPE, NULL ); set_msg_desc( &desc_req, &req, &body ); hr = WsSendMessage( channel, msg, &desc_req, WS_WRITE_REQUIRED_VALUE, &val, sizeof(val), NULL, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); WsFreeMessage( msg ); hr = WsCreateMessageForChannel( channel, NULL, 0, &msg, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); set_elem_desc( &body, &resp, &ns, WS_INT32_TYPE, NULL ); set_msg_desc( &desc_resp, &resp, &body ); desc[0] = &desc_resp; hr = WsReceiveMessage( NULL, msg, desc, 1, WS_RECEIVE_REQUIRED_MESSAGE, WS_READ_REQUIRED_VALUE, NULL, &val, sizeof(val), NULL, NULL, NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); hr = WsReceiveMessage( channel, NULL, desc, 1, WS_RECEIVE_REQUIRED_MESSAGE, WS_READ_REQUIRED_VALUE, NULL, &val, sizeof(val), NULL, NULL, NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); hr = WsReceiveMessage( channel, msg, NULL, 1, WS_RECEIVE_REQUIRED_MESSAGE, WS_READ_REQUIRED_VALUE, NULL, &val, sizeof(val), NULL, NULL, NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); 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 %#lx\n", hr ); ok( val == -2, "got %d\n", val ); hr = WsCloseChannel( channel, NULL, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); WsFreeChannel( channel ); 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 ) { WS_ENVELOPE_VERSION env_version; WS_ADDRESSING_VERSION addr_version; WS_HTTP_MESSAGE_MAPPING mapping; WS_CHANNEL_PROPERTY prop[3]; WS_ENDPOINT_ADDRESS addr; WS_SERVICE_PROXY *proxy; WCHAR url[64]; HRESULT hr; env_version = WS_ENVELOPE_VERSION_SOAP_1_1; prop[0].id = WS_CHANNEL_PROPERTY_ENVELOPE_VERSION; prop[0].value = &env_version; prop[0].valueSize = sizeof(env_version); addr_version = WS_ADDRESSING_VERSION_TRANSPORT; prop[1].id = WS_CHANNEL_PROPERTY_ADDRESSING_VERSION; 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 ); if (hr != S_OK) return hr; memset( &addr, 0, sizeof(addr) ); addr.url.length = wsprintfW( url, L"http://127.0.0.1:%u", port ); addr.url.chars = url; hr = WsOpenServiceProxy( proxy, &addr, NULL, NULL ); if (hr == S_OK) *ret = proxy; else WsFreeServiceProxy( proxy ); 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 %#lx\n", hr ); hr = set_output( writer ); ok( hr == S_OK, "got %#lx\n", hr ); hr = WsGetMessageProperty( msg, WS_MESSAGE_PROPERTY_HEADER_BUFFER, &buf, sizeof(buf), NULL ); ok( hr == S_OK, "got %#lx\n", hr ); hr = WsWriteXmlBuffer( writer, buf, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); memset( &bytes, 0, sizeof(bytes) ); hr = WsGetWriterProperty( writer, WS_XML_WRITER_PROPERTY_BYTES, &bytes, sizeof(bytes), NULL ); ok( hr == S_OK, "got %#lx\n", hr ); WsFreeWriter( writer ); } static const char req_test2[] = "" "1testtest2" ""; static const char resp_test2[] = "" "test12" ""; 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 %#lx\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"}; 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 %#lx\n", hr ); ok( !wcscmp(str, L"value"), "wrong value %s\n", wine_dbgstr_w(str) ); return S_OK; } static void test_WsCall( int port ) { WS_XML_STRING str = {3, (BYTE *)"str"}; WS_XML_STRING req = {3, (BYTE *)"req"}; WS_XML_STRING resp = {4, (BYTE *)"resp"}; WS_XML_STRING req_elem = {9, (BYTE *)"req_test2"}; WS_XML_STRING resp_elem = {10, (BYTE *)"resp_test2"}; WS_XML_STRING req_action = {9, (BYTE *)"req_test2"}; WS_XML_STRING resp_action = {10, (BYTE *)"resp_test2"}; WS_XML_STRING val = {3, (BYTE *)"val"}; 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; WS_STRUCT_DESCRIPTION input_struct, output_struct; WS_FIELD_DESCRIPTION f, f2, f3, f4, *fields[2], *fields2[2]; WS_PARAMETER_DESCRIPTION param[6]; const void *args[6]; WS_HEAP *heap; INT32 **val_ptr; WCHAR **str_ptr; ULONG *count_ptr; const WCHAR *str_array[2]; struct input { INT32 val; ULONG count; const WCHAR **str; } in; struct output { WCHAR *str; ULONG count; INT32 *val; } out; hr = WsCreateHeap( 1 << 16, 0, NULL, 0, &heap, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); hr = WsCall( NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); hr = create_proxy( port, &proxy ); ok( hr == S_OK, "got %#lx\n", hr ); hr = WsCall( proxy, NULL, NULL, NULL, NULL, 0, NULL, NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); set_field_desc( &f, WS_ELEMENT_FIELD_MAPPING, &val, &ns, WS_INT32_TYPE, NULL, 0, 0, 0, NULL, NULL ); set_field_desc( &f4, WS_REPEATING_ELEMENT_FIELD_MAPPING, NULL, NULL, WS_WSZ_TYPE, NULL, FIELD_OFFSET(struct input, str), 0, FIELD_OFFSET(struct input, count), &str, &ns ); fields[0] = &f; fields[1] = &f4; set_struct_desc( &input_struct, sizeof(struct input), TYPE_ALIGNMENT(struct input), fields, 2, &req, &ns, 0 ); set_elem_desc( &input_elem, &req_elem, &ns, WS_STRUCT_TYPE, &input_struct ); set_msg_desc( &input_msg, &req_action, &input_elem ); set_field_desc( &f2, WS_ELEMENT_FIELD_MAPPING, &str, &ns, WS_WSZ_TYPE, NULL, FIELD_OFFSET(struct output, str), 0, 0, NULL, NULL ); set_field_desc( &f3, WS_REPEATING_ELEMENT_FIELD_MAPPING, NULL, NULL, WS_INT32_TYPE, NULL, FIELD_OFFSET(struct output, val), 0, FIELD_OFFSET(struct output, count), &val, &ns ); fields2[0] = &f2; fields2[1] = &f3; set_struct_desc( &output_struct, sizeof(struct output), TYPE_ALIGNMENT(struct output), fields2, 2, &resp, &ns, 0 ); set_elem_desc( &output_elem, &resp_elem, &ns, WS_STRUCT_TYPE, &output_struct ); set_msg_desc( &output_msg, &resp_action, &output_elem ); set_param_desc( ¶m[0], WS_PARAMETER_TYPE_NORMAL, 0, 0xffff ); set_param_desc( ¶m[1], WS_PARAMETER_TYPE_ARRAY, 1, 0xffff ); set_param_desc( ¶m[2], WS_PARAMETER_TYPE_ARRAY_COUNT, 1, 0xffff ); set_param_desc( ¶m[3], WS_PARAMETER_TYPE_NORMAL, 0xffff, 0 ); set_param_desc( ¶m[4], WS_PARAMETER_TYPE_ARRAY, 0xffff, 1 ); set_param_desc( ¶m[5], WS_PARAMETER_TYPE_ARRAY_COUNT, 0xffff, 1 ); set_op_desc( &op, &input_msg, &output_msg, 6, param ); hr = WsCall( proxy, &op, NULL, NULL, NULL, 0, NULL, NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); in.val = 1; str_array[0] = L"test"; str_array[1] = L"test2"; in.str = str_array; in.count = 2; args[0] = &in.val; args[1] = &in.str; args[2] = &in.count; out.str = NULL; out.count = 0; out.val = NULL; str_ptr = &out.str; val_ptr = &out.val; count_ptr = &out.count; args[3] = &str_ptr; args[4] = &val_ptr; args[5] = &count_ptr; 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 %#lx\n", hr ); ok( !wcscmp( out.str, L"test" ), "wrong data\n" ); ok( out.count == 2, "got %lu\n", out.count ); ok( out.val[0] == 1, "got %u\n", out.val[0] ); ok( out.val[1] == 2, "got %u\n", out.val[1] ); hr = WsCloseServiceProxy( proxy, NULL, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); WsFreeServiceProxy( proxy ); WsFreeHeap( heap ); } static const char req_test3[] = "" "1" ""; static const char resp_test3[] = ""; static void test_empty_response( int port ) { WS_XML_STRING req = {3, (BYTE *)"req"}; WS_XML_STRING resp = {4, (BYTE *)"resp"}; WS_XML_STRING req_action = {9, (BYTE *)"req_test3"}; WS_XML_STRING resp_action = {10, (BYTE *)"resp_test3"}; WS_XML_STRING req_elem = {9, (BYTE *)"req_test3"}; WS_XML_STRING resp_elem = {10, (BYTE *)"resp_test3"}; WS_XML_STRING ns = {2, (BYTE *)"ns"}; WS_XML_STRING ns2 = {0, (BYTE *)""}; WS_XML_STRING val = {3, (BYTE *)"val"}; HRESULT hr; WS_SERVICE_PROXY *proxy; WS_FIELD_DESCRIPTION f, *fields[1]; WS_STRUCT_DESCRIPTION input_struct, output_struct; WS_ELEMENT_DESCRIPTION input_elem, output_elem; WS_MESSAGE_DESCRIPTION input_msg, output_msg; WS_PARAMETER_DESCRIPTION param[1]; WS_OPERATION_DESCRIPTION op; const void *args[1]; WS_HEAP *heap; struct input { INT32 val; } in; hr = WsCreateHeap( 1 << 16, 0, NULL, 0, &heap, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); hr = create_proxy( port, &proxy ); ok( hr == S_OK, "got %#lx\n", hr ); set_field_desc( &f, WS_ELEMENT_FIELD_MAPPING, &val, &ns, WS_INT32_TYPE, NULL, 0, 0, 0, NULL, NULL ); fields[0] = &f; set_struct_desc( &input_struct, sizeof(struct input), TYPE_ALIGNMENT(struct input), fields, 1, &req, &ns, 0 ); set_elem_desc( &input_elem, &req_elem, &ns, WS_STRUCT_TYPE, &input_struct ); set_msg_desc( &input_msg, &req_action, &input_elem ); set_struct_desc( &output_struct, 0, 1, NULL, 0, &resp, &ns2, 0x6 ); set_elem_desc( &output_elem, &resp_elem, &ns, WS_STRUCT_TYPE, NULL ); set_msg_desc( &output_msg, &resp_action, &output_elem ); set_param_desc( param, WS_PARAMETER_TYPE_NORMAL, 0, 0xffff ); set_op_desc( &op, &input_msg, &output_msg, 1, param ); in.val = 1; args[0] = &in.val; hr = WsCall( proxy, &op, args, heap, NULL, 0, NULL, NULL ); ok( hr == E_INVALIDARG, "got %#lx\n", hr ); set_elem_desc( &output_elem, &resp_elem, &ns, WS_STRUCT_TYPE, &output_struct ); hr = WsCall( proxy, &op, args, heap, NULL, 0, NULL, NULL ); ok( hr == WS_E_INVALID_FORMAT, "got %#lx\n", hr ); hr = WsCloseServiceProxy( proxy, NULL, NULL ); ok( hr == S_OK, "got %#lx\n", hr ); WsFreeServiceProxy( proxy ); WsFreeHeap( heap ); } static const char status_200[] = "HTTP/1.1 200 OK\r\n"; static const struct { const char *req_action; const char *req_data; unsigned int req_len; const char *resp_status; const char *resp_data; unsigned int resp_len; } tests[] = { { "req_test1", req_test1, sizeof(req_test1)-1, status_200, resp_test1, sizeof(resp_test1)-1 }, { "req_test2", req_test2, sizeof(req_test2)-1, status_200, resp_test2, sizeof(resp_test2)-1 }, { "req_test3", req_test3, sizeof(req_test3)-1, status_200, resp_test3, sizeof(resp_test3)-1 }, }; 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\nMappedResponseHeader: value\r\n"; static const char fmt[] = "Content-Length: %u\r\n\r\n"; char buf[128]; send( c, status, strlen(status), 0 ); send( c, headers, sizeof(headers) - 1, 0 ); sprintf( buf, fmt, len ); send( c, buf, strlen(buf), 0 ); send( c, data, len, 0 ); } struct server_info { HANDLE event; int port; }; static DWORD CALLBACK server_proc( void *arg ) { struct server_info *info = arg; int len, res, c = -1, i, j, on = 1, quit; WSADATA wsa; SOCKET s; struct sockaddr_in sa; char buf[1024]; const char *p; WSAStartup( MAKEWORD(1,1), &wsa ); if ((s = socket( AF_INET, SOCK_STREAM, 0 )) == INVALID_SOCKET) return 1; setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on) ); memset( &sa, 0, sizeof(sa) ); sa.sin_family = AF_INET; sa.sin_port = htons( info->port ); sa.sin_addr.S_un.S_addr = inet_addr( "127.0.0.1" ); if (bind( s, (struct sockaddr *)&sa, sizeof(sa) ) < 0) return 1; listen( s, 0 ); SetEvent( info->event ); for (;;) { c = accept( s, NULL, NULL ); buf[0] = 0; for (i = 0; i < sizeof(buf) - 1; i++) { if ((res = recv( c, &buf[i], 1, 0 )) != 1) break; if (i < 4) continue; if (buf[i - 2] == '\n' && buf[i] == '\n' && buf[i - 3] == '\r' && buf[i - 1] == '\r') break; } buf[i] = 0; quit = strstr( buf, "SOAPAction: \"quit\"" ) != NULL; len = 0; if ((p = strstr( buf, "Content-Length: " ))) { p += strlen( "Content-Length: " ); while (isdigit( *p )) { len *= 10; len += *p++ - '0'; } } for (i = 0; i < len; i++) { if ((res = recv( c, &buf[i], 1, 0 )) != 1) break; } buf[i] = 0; for (j = 0; j < ARRAY_SIZE( tests ); j++) { if (strstr( buf, tests[j].req_action )) { if (tests[j].req_data) { int data_len = strlen( buf ); 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: 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; } } shutdown( c, 2 ); closesocket( c ); if (quit) break; } closesocket( s ); return 0; } START_TEST(proxy) { WS_XML_STRING test1 = {9, (BYTE *)"req_test1"}; WS_XML_STRING quit = {4, (BYTE *)"quit"}; struct server_info info; HANDLE thread; DWORD ret; test_WsCreateServiceProxy(); test_WsCreateServiceProxyFromTemplate(); test_WsOpenServiceProxy(); test_WsResetServiceProxy(); info.port = 7533; info.event = CreateEventW( NULL, 0, 0, NULL ); thread = CreateThread( NULL, 0, server_proc, &info, 0, NULL ); ok( thread != NULL, "failed to create server thread %lu\n", GetLastError() ); ret = WaitForSingleObject( info.event, 3000 ); ok(ret == WAIT_OBJECT_0, "failed to start test server %lu\n", GetLastError()); if (ret != WAIT_OBJECT_0) { CloseHandle(thread); return; } test_WsSendMessage( info.port, &test1 ); test_WsReceiveMessage( info.port ); test_WsCall( info.port ); test_empty_response( info.port ); test_WsSendMessage( info.port, &quit ); WaitForSingleObject( thread, 3000 ); CloseHandle(thread); }