proxy.c 37.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * 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 <stdio.h>
#include "windows.h"
21
#include "winsock2.h"
22 23 24
#include "webservices.h"
#include "wine/test.h"

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
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,
45
                                    WS_XML_STRING *ns, ULONG options )
46 47 48 49 50 51 52 53
{
    memset( desc, 0, sizeof(*desc) );
    desc->size          = size;
    desc->alignment     = alignment;
    desc->fields        = fields;
    desc->fieldCount    = count;
    desc->typeLocalName = localname;
    desc->typeNs        = ns;
54
    desc->structOptions = options;
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
}

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

93 94 95 96 97
static void test_WsCreateServiceProxy(void)
{
    HRESULT hr;
    WS_SERVICE_PROXY *proxy;
    WS_SERVICE_PROXY_STATE state;
98
    ULONG value;
99 100

    hr = WsCreateServiceProxy( WS_CHANNEL_TYPE_REQUEST, WS_HTTP_CHANNEL_BINDING, NULL, NULL,
101
                               0, NULL, 0, NULL, NULL );
102
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
103 104 105

    proxy = NULL;
    hr = WsCreateServiceProxy( WS_CHANNEL_TYPE_REQUEST, WS_HTTP_CHANNEL_BINDING, NULL, NULL,
106
                               0, NULL, 0, &proxy, NULL );
107
    ok( hr == S_OK, "got %#lx\n", hr );
108 109 110 111
    ok( proxy != NULL, "proxy not set\n" );

    /* write-only property */
    value = 0xdeadbeef;
112
    hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_CALL_TIMEOUT, &value, sizeof(value), NULL );
113
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
114 115

    state = 0xdeadbeef;
116
    hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_STATE, &state, sizeof(state), NULL );
117
    ok( hr == S_OK, "got %#lx\n", hr );
118 119 120 121 122 123 124 125 126 127 128 129 130
    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 );
131
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
132 133 134

    hr = WsCreateServiceProxyFromTemplate( WS_CHANNEL_TYPE_REQUEST, NULL, 0, WS_HTTP_BINDING_TEMPLATE_TYPE,
                                           NULL, 0, NULL, 0, &proxy, NULL );
135
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
136 137 138 139 140

    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 );
141
    ok( hr == S_OK, "got %#lx\n", hr );
142 143 144 145 146 147 148 149 150
    ok( proxy != NULL, "proxy not set\n" );

    WsFreeServiceProxy( proxy );
}

static void test_WsOpenServiceProxy(void)
{
    HRESULT hr;
    WS_SERVICE_PROXY *proxy;
151
    WS_SERVICE_PROXY_STATE state;
152 153 154 155 156 157
    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 );
158
    ok( hr == S_OK, "got %#lx\n", hr );
159

160 161
    state = 0xdeadbeef;
    hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_STATE, &state, sizeof(state), NULL );
162
    ok( hr == S_OK, "got %#lx\n", hr );
163 164 165
    ok( state == WS_SERVICE_PROXY_STATE_CREATED, "got %u\n", state );

    memset( &addr, 0, sizeof(addr) );
166 167
    addr.url.length = ARRAY_SIZE( L"http://localhost/" ) - 1;
    addr.url.chars  = (WCHAR *)L"http://localhost/";
168
    hr = WsOpenServiceProxy( proxy, &addr, NULL, NULL );
169
    ok( hr == S_OK, "got %#lx\n", hr );
170

171 172
    state = 0xdeadbeef;
    hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_STATE, &state, sizeof(state), NULL );
173
    ok( hr == S_OK, "got %#lx\n", hr );
174 175
    ok( state == WS_SERVICE_PROXY_STATE_OPEN, "got %u\n", state );

176
    hr = WsCloseServiceProxy( proxy , NULL, NULL );
177
    ok( hr == S_OK, "got %#lx\n", hr );
178

179 180
    state = 0xdeadbeef;
    hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_STATE, &state, sizeof(state), NULL );
181
    ok( hr == S_OK, "got %#lx\n", hr );
182 183
    ok( state == WS_SERVICE_PROXY_STATE_CLOSED, "got %u\n", state );

184 185 186
    WsFreeServiceProxy( proxy );
}

187 188 189 190 191 192 193 194 195
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 );
196
    ok( hr == S_OK, "got %#lx\n", hr );
197 198

    hr = WsResetServiceProxy( proxy, NULL );
199
    ok( hr == S_OK, "got %#lx\n", hr );
200 201 202

    state = 0xdeadbeef;
    hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_STATE, &state, sizeof(state), NULL );
203
    ok( hr == S_OK, "got %#lx\n", hr );
204 205 206
    ok( state == WS_SERVICE_PROXY_STATE_CREATED, "got %u\n", state );

    memset( &addr, 0, sizeof(addr) );
207 208
    addr.url.length = ARRAY_SIZE( L"http://localhost/" ) - 1;
    addr.url.chars  = (WCHAR *)L"http://localhost/";
209
    hr = WsOpenServiceProxy( proxy, &addr, NULL, NULL );
210
    ok( hr == S_OK, "got %#lx\n", hr );
211 212

    hr = WsResetServiceProxy( proxy, NULL );
213
    ok( hr == WS_E_INVALID_OPERATION, "got %#lx\n", hr );
214 215

    hr = WsCloseServiceProxy( proxy , NULL, NULL );
216
    ok( hr == S_OK, "got %#lx\n", hr );
217 218

    hr = WsResetServiceProxy( proxy, NULL );
219
    ok( hr == S_OK, "got %#lx\n", hr );
220 221 222

    state = 0xdeadbeef;
    hr = WsGetServiceProxyProperty( proxy, WS_PROXY_PROPERTY_STATE, &state, sizeof(state), NULL );
223
    ok( hr == S_OK, "got %#lx\n", hr );
224 225 226 227 228
    ok( state == WS_SERVICE_PROXY_STATE_CREATED, "got %u\n", state );

    WsFreeServiceProxy( proxy );
}

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
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;

251
    memset( &addr, 0, sizeof(addr) );
252
    addr.url.length = wsprintfW( buf, L"http://127.0.0.1:%u", port );
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
    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[] =
    "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>"
    "<req_test1 xmlns=\"ns\">-1</req_test1>"
    "</s:Body></s:Envelope>";

static const char resp_test1[] =
    "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>"
    "<resp_test1 xmlns=\"ns\">-2</resp_test1>"
    "</s:Body></s:Envelope>";

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 );
281
    ok( hr == S_OK, "got %#lx\n", hr );
282 283

    hr = WsCreateMessageForChannel( channel, NULL, 0, &msg, NULL );
284
    ok( hr == S_OK, "got %#lx\n", hr );
285

286 287
    set_elem_desc( &body, &req, &ns, WS_INT32_TYPE, NULL );
    set_msg_desc( &desc, action, &body );
288
    hr = WsSendMessage( NULL, msg, &desc, WS_WRITE_REQUIRED_VALUE, &val, sizeof(val), NULL, NULL );
289
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
290 291

    hr = WsSendMessage( channel, NULL, &desc, WS_WRITE_REQUIRED_VALUE, &val, sizeof(val), NULL, NULL );
292
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
293 294

    hr = WsSendMessage( channel, msg, NULL, WS_WRITE_REQUIRED_VALUE, &val, sizeof(val), NULL, NULL );
295
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
296 297

    hr = WsSendMessage( channel, msg, &desc, WS_WRITE_REQUIRED_VALUE, &val, sizeof(val), NULL, NULL );
298
    ok( hr == S_OK, "got %#lx\n", hr );
299 300

    hr = WsCloseChannel( channel, NULL, NULL );
301
    ok( hr == S_OK, "got %#lx\n", hr );
302 303 304 305 306

    WsFreeChannel( channel );
    WsFreeMessage( msg );
}

307 308 309 310 311 312 313 314 315 316 317 318
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 );
319
    ok( hr == S_OK, "got %#lx\n", hr );
320 321

    hr = WsCreateMessageForChannel( channel, NULL, 0, &msg, NULL );
322
    ok( hr == S_OK, "got %#lx\n", hr );
323

324 325
    set_elem_desc( &body, &req, &ns, WS_INT32_TYPE, NULL );
    set_msg_desc( &desc_req, &req, &body );
326
    hr = WsSendMessage( channel, msg, &desc_req, WS_WRITE_REQUIRED_VALUE, &val, sizeof(val), NULL, NULL );
327
    ok( hr == S_OK, "got %#lx\n", hr );
328 329 330
    WsFreeMessage( msg );

    hr = WsCreateMessageForChannel( channel, NULL, 0, &msg, NULL );
331
    ok( hr == S_OK, "got %#lx\n", hr );
332

333 334
    set_elem_desc( &body, &resp, &ns, WS_INT32_TYPE, NULL );
    set_msg_desc( &desc_resp, &resp, &body );
335 336 337
    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 );
338
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
339 340 341

    hr = WsReceiveMessage( channel, NULL, desc, 1, WS_RECEIVE_REQUIRED_MESSAGE, WS_READ_REQUIRED_VALUE,
                           NULL, &val, sizeof(val), NULL, NULL, NULL );
342
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
343 344 345

    hr = WsReceiveMessage( channel, msg, NULL, 1, WS_RECEIVE_REQUIRED_MESSAGE, WS_READ_REQUIRED_VALUE,
                           NULL, &val, sizeof(val), NULL, NULL, NULL );
346
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
347 348 349

    hr = WsReceiveMessage( channel, msg, desc, 1, WS_RECEIVE_REQUIRED_MESSAGE, WS_READ_REQUIRED_VALUE,
                           NULL, &val, sizeof(val), NULL, NULL, NULL );
350
    ok( hr == S_OK, "got %#lx\n", hr );
351 352 353
    ok( val == -2, "got %d\n", val );

    hr = WsCloseChannel( channel, NULL, NULL );
354
    ok( hr == S_OK, "got %#lx\n", hr );
355 356 357 358 359

    WsFreeChannel( channel );
    WsFreeMessage( msg );
}

360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
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
};

380 381 382 383
static HRESULT create_proxy( int port, WS_SERVICE_PROXY **ret )
{
    WS_ENVELOPE_VERSION env_version;
    WS_ADDRESSING_VERSION addr_version;
384 385
    WS_HTTP_MESSAGE_MAPPING mapping;
    WS_CHANNEL_PROPERTY prop[3];
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
    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);

401 402 403 404 405 406 407 408 409 410 411
    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);

412 413
    *ret = NULL;
    hr = WsCreateServiceProxy( WS_CHANNEL_TYPE_REQUEST, WS_HTTP_CHANNEL_BINDING, NULL, NULL,
414
                               0, prop, ARRAY_SIZE( prop ), &proxy, NULL );
415 416
    if (hr != S_OK) return hr;

417
    memset( &addr, 0, sizeof(addr) );
418
    addr.url.length = wsprintfW( url, L"http://127.0.0.1:%u", port );
419 420 421 422 423 424 425
    addr.url.chars  = url;
    hr = WsOpenServiceProxy( proxy, &addr, NULL, NULL );
    if (hr == S_OK) *ret = proxy;
    else WsFreeServiceProxy( proxy );
    return hr;
}

426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
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 );
441
    ok( hr == S_OK, "got %#lx\n", hr );
442 443

    hr = set_output( writer );
444
    ok( hr == S_OK, "got %#lx\n", hr );
445 446

    hr = WsGetMessageProperty( msg, WS_MESSAGE_PROPERTY_HEADER_BUFFER, &buf, sizeof(buf), NULL );
447
    ok( hr == S_OK, "got %#lx\n", hr );
448 449

    hr = WsWriteXmlBuffer( writer, buf, NULL );
450
    ok( hr == S_OK, "got %#lx\n", hr );
451 452 453

    memset( &bytes, 0, sizeof(bytes) );
    hr = WsGetWriterProperty( writer, WS_XML_WRITER_PROPERTY_BYTES, &bytes, sizeof(bytes), NULL );
454
    ok( hr == S_OK, "got %#lx\n", hr );
455 456 457
    WsFreeWriter( writer );
}

458 459
static const char req_test2[] =
    "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>"
460
    "<req_test2 xmlns=\"ns\"><val>1</val><str>test</str><str>test2</str></req_test2>"
461 462 463 464 465 466 467
    "</s:Body></s:Envelope>";

static const char resp_test2[] =
    "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>"
    "<resp_test2 xmlns=\"ns\"><str>test</str><val>1</val><val>2</val></resp_test2>"
    "</s:Body></s:Envelope>";

468 469 470 471 472 473
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 );
474
    ok( hr == S_OK, "got %#lx\n", hr );
475 476 477 478 479 480 481 482 483 484 485 486 487
    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 );
488
    ok( hr == S_OK, "got %#lx\n", hr );
489
    ok( !wcscmp(str, L"value"), "wrong value %s\n", wine_dbgstr_w(str) );
490 491 492
    return S_OK;
}

493 494 495 496 497 498 499 500 501 502 503 504 505
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;
506 507 508
    WS_PROXY_MESSAGE_CALLBACK_CONTEXT ctx_send;
    WS_PROXY_MESSAGE_CALLBACK_CONTEXT ctx_recv;
    WS_CALL_PROPERTY prop[2];
509 510 511 512
    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;
513 514 515
    WS_FIELD_DESCRIPTION f, f2, f3, f4, *fields[2], *fields2[2];
    WS_PARAMETER_DESCRIPTION param[6];
    const void *args[6];
516 517 518 519
    WS_HEAP *heap;
    INT32 **val_ptr;
    WCHAR **str_ptr;
    ULONG *count_ptr;
520
    const WCHAR *str_array[2];
521 522
    struct input
    {
523 524 525
        INT32         val;
        ULONG         count;
        const WCHAR **str;
526 527 528 529 530 531 532 533 534
    } in;
    struct output
    {
        WCHAR *str;
        ULONG  count;
        INT32 *val;
    } out;

    hr = WsCreateHeap( 1 << 16, 0, NULL, 0, &heap, NULL );
535
    ok( hr == S_OK, "got %#lx\n", hr );
536 537

    hr = WsCall( NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL );
538
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
539 540

    hr = create_proxy( port, &proxy );
541
    ok( hr == S_OK, "got %#lx\n", hr );
542 543

    hr = WsCall( proxy, NULL, NULL, NULL, NULL, 0, NULL, NULL );
544
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
545

546 547 548
    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 );
549
    fields[0] = &f;
550 551
    fields[1] = &f4;

552
    set_struct_desc( &input_struct, sizeof(struct input), TYPE_ALIGNMENT(struct input), fields, 2, &req, &ns, 0 );
553 554
    set_elem_desc( &input_elem, &req_elem, &ns, WS_STRUCT_TYPE, &input_struct );
    set_msg_desc( &input_msg, &req_action, &input_elem );
555

556 557 558 559 560
    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;
561 562
    fields2[1] = &f3;

563
    set_struct_desc( &output_struct, sizeof(struct output), TYPE_ALIGNMENT(struct output), fields2, 2, &resp, &ns, 0 );
564 565 566 567 568 569 570 571 572 573 574
    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( &param[0], WS_PARAMETER_TYPE_NORMAL, 0, 0xffff );
    set_param_desc( &param[1], WS_PARAMETER_TYPE_ARRAY, 1, 0xffff );
    set_param_desc( &param[2], WS_PARAMETER_TYPE_ARRAY_COUNT, 1, 0xffff );
    set_param_desc( &param[3], WS_PARAMETER_TYPE_NORMAL, 0xffff, 0 );
    set_param_desc( &param[4], WS_PARAMETER_TYPE_ARRAY, 0xffff, 1 );
    set_param_desc( &param[5], WS_PARAMETER_TYPE_ARRAY_COUNT, 0xffff, 1 );

    set_op_desc( &op, &input_msg, &output_msg, 6, param );
575
    hr = WsCall( proxy, &op, NULL, NULL, NULL, 0, NULL, NULL );
576
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
577 578

    in.val   = 1;
579 580
    str_array[0] = L"test";
    str_array[1] = L"test2";
581 582 583
    in.str   = str_array;
    in.count = 2;

584
    args[0] = &in.val;
585 586
    args[1] = &in.str;
    args[2] = &in.count;
587 588 589

    out.str   = NULL;
    out.count = 0;
590
    out.val   = NULL;
591 592 593
    str_ptr   = &out.str;
    val_ptr   = &out.val;
    count_ptr = &out.count;
594 595 596 597

    args[3] = &str_ptr;
    args[4] = &val_ptr;
    args[5] = &count_ptr;
598

599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
    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 );
614
    ok( hr == S_OK, "got %#lx\n", hr );
615
    ok( !wcscmp( out.str, L"test" ), "wrong data\n" );
616
    ok( out.count == 2, "got %lu\n", out.count );
617 618 619 620
    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 );
621
    ok( hr == S_OK, "got %#lx\n", hr );
622 623 624 625 626

    WsFreeServiceProxy( proxy );
    WsFreeHeap( heap );
}

627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
static const char req_test3[] =
    "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>"
    "<req_test3 xmlns=\"ns\"><val>1</val></req_test3>"
    "</s:Body></s:Envelope>";

static const char resp_test3[] =
    "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body/></s:Envelope>";

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 );
662
    ok( hr == S_OK, "got %#lx\n", hr );
663 664

    hr = create_proxy( port, &proxy );
665
    ok( hr == S_OK, "got %#lx\n", hr );
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683

    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 );
684
    ok( hr == E_INVALIDARG, "got %#lx\n", hr );
685 686 687

    set_elem_desc( &output_elem, &resp_elem, &ns, WS_STRUCT_TYPE, &output_struct );
    hr = WsCall( proxy, &op, args, heap, NULL, 0, NULL, NULL );
688
    ok( hr == WS_E_INVALID_FORMAT, "got %#lx\n", hr );
689 690

    hr = WsCloseServiceProxy( proxy, NULL, NULL );
691
    ok( hr == S_OK, "got %#lx\n", hr );
692 693 694 695 696

    WsFreeServiceProxy( proxy );
    WsFreeHeap( heap );
}

697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
static const char req_test4[] =
    "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>"
    "<req_test4 xmlns=\"ns\"><str>test4</str><val>1</val><val>2</val><val>3</val></req_test4>"
    "</s:Body></s:Envelope>";

static const char resp_test4[] =
    "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>"
    "<resp_test4 xmlns=\"ns\"><str>out4</str><val>4</val><val>5</val></resp_test4>"
    "</s:Body></s:Envelope>";

static void test_inout_params( 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_test4"};
    WS_XML_STRING resp_elem = {10, (BYTE *)"resp_test4"};
    WS_XML_STRING req_action = {9, (BYTE *)"req_test4"};
    WS_XML_STRING resp_action = {10, (BYTE *)"resp_test4"};
    WS_XML_STRING val = {3, (BYTE *)"val"};
    WS_XML_STRING ns = {2, (BYTE *)"ns"};
    HRESULT hr;
    WS_SERVICE_PROXY *proxy;
    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, *fields[2];
    WS_PARAMETER_DESCRIPTION param[3];
    const void *args[3];
    WS_HEAP *heap;
    INT32 val_array[] = {1, 2, 3};
    WCHAR **str_ptr;
    INT32 **val_ptr;
    ULONG *count_ptr;
    struct data
    {
        WCHAR *str;
        ULONG  count;
        INT32 *val;
    } data;

    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, &str, &ns, WS_WSZ_TYPE, NULL,
                    FIELD_OFFSET(struct data, str), 0, 0, NULL, NULL );
    set_field_desc( &f2, WS_REPEATING_ELEMENT_FIELD_MAPPING, NULL, NULL, WS_INT32_TYPE, NULL,
                    FIELD_OFFSET(struct data, val), 0, FIELD_OFFSET(struct data, count), &val, &ns );

    fields[0] = &f;
    fields[1] = &f2;

    set_struct_desc( &input_struct, sizeof(struct data), TYPE_ALIGNMENT(struct data), 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_struct_desc( &output_struct, sizeof(struct data), TYPE_ALIGNMENT(struct data), fields, 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( &param[0], WS_PARAMETER_TYPE_NORMAL, 0, 0 );
    set_param_desc( &param[1], WS_PARAMETER_TYPE_ARRAY, 1, 1 );
    set_param_desc( &param[2], WS_PARAMETER_TYPE_ARRAY_COUNT, 1, 1 );

    set_op_desc( &op, &input_msg, &output_msg, 3, param );

    data.str   = (WCHAR *) L"test4";
    data.count = ARRAY_SIZE(val_array);
    data.val   = val_array;

    str_ptr   = &data.str;
    val_ptr   = &data.val;
    count_ptr = &data.count;

    args[0] = &str_ptr;
    args[1] = &val_ptr;
    args[2] = &count_ptr;

    hr = WsCall( proxy, &op, args, heap, NULL, 0, NULL, NULL );
    ok( hr == S_OK, "got %#lx\n", hr );
    ok( !wcscmp( data.str, L"out4" ), "wrong str\n" );
    ok( data.count == 2, "got %lu\n", data.count );
    ok( data.val[0] == 4, "got %u\n", data.val[0] );
    ok( data.val[1] == 5, "got %u\n", data.val[1] );

    hr = WsCloseServiceProxy( proxy, NULL, NULL );
    ok( hr == S_OK, "got %#lx\n", hr );

    WsFreeServiceProxy( proxy );
    WsFreeHeap( heap );
}

799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
static const char req_test5[] =
    "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>"
    "<req_test5 xmlns=\"ns\"><val>1</val></req_test5>"
    "</s:Body></s:Envelope>";

static const char resp_test5[] =
    "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>"
    "<s:Fault><faultcode>s:Client</faultcode><faultstring>OLS Exception</faultstring><detail>"
    "<ServerFault xmlns=\"http://schemas.microsoft.com/office/licensingservice/API/2012/01/ClientApi\" "
    "xmlns:a=\"http://schemas.datacontract.org/2004/07/Microsoft.Office.LicensingService\" "
    "xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><a:ErrorCode>1030</a:ErrorCode><a:Message/>"
    "<a:Url>https://portal.office.com/Account/#installs</a:Url></ServerFault></detail>"
    "</s:Fault></s:Body></s:Envelope>";

static void test_fault_response( int port )
{
    WS_XML_STRING faultcode = {6, (BYTE *)"Client"};
    WS_STRING faultstring = {13, (WCHAR *)L"OLS Exception"};
    WS_XML_STRING req = {3, (BYTE *)"req"};
    WS_XML_STRING resp = {4, (BYTE *)"resp"};
    WS_XML_STRING req_action = {9, (BYTE *)"req_test5"};
    WS_XML_STRING resp_action = {10, (BYTE *)"resp_test5"};
    WS_XML_STRING req_elem = {9, (BYTE *)"req_test5"};
    WS_XML_STRING resp_elem = {10, (BYTE *)"resp_test5"};
    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;
    WS_ERROR *error;
    WS_FAULT *fault;

    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, &output_struct );
    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 = WsCreateError( NULL, 0, &error );
    ok( hr == S_OK, "got %#lx\n", hr );

    hr = WsCall( proxy, &op, args, heap, NULL, 0, NULL, error );
    ok( hr == WS_E_ENDPOINT_FAULT_RECEIVED, "got %#lx\n", hr );

    hr = WsGetFaultErrorProperty( error, WS_FAULT_ERROR_PROPERTY_FAULT, &fault, sizeof(fault) );
    ok( hr == S_OK, "got %#lx\n", hr );
    ok( fault != NULL, "fault not set\n" );
    ok( fault->code->value.localName.length == faultcode.length, "got %lu\n", fault->code->value.localName.length );
    ok( !memcmp( fault->code->value.localName.bytes, faultcode.bytes, faultcode.length ), "wrong fault code\n" );
    ok( !fault->code->subCode, "subcode is not NULL\n" );
    ok( fault->reasonCount == 1, "got %lu\n", fault->reasonCount );
    ok( fault->reasons[0].text.length == faultstring.length, "got %lu\n", fault->reasons[0].text.length );
    ok( !memcmp( fault->reasons[0].text.chars, faultstring.chars, faultstring.length * sizeof(WCHAR) ),
        "wrong fault string\n" );
    ok( fault->detail != NULL, "fault detail not set\n" );

    hr = WsCloseServiceProxy( proxy, NULL, NULL );
    ok( hr == S_OK, "got %#lx\n", hr );

    WsFreeError( error );
    WsFreeServiceProxy( proxy );
    WsFreeHeap( heap );
}

892
static const char status_200[] = "HTTP/1.1 200 OK\r\n";
893
static const char status_500[] = "HTTP/1.1 500 Internal Server Error\r\n";
894

895 896 897 898 899
static const struct
{
    const char   *req_action;
    const char   *req_data;
    unsigned int  req_len;
900
    const char   *resp_status;
901 902 903 904 905
    const char   *resp_data;
    unsigned int  resp_len;
}
tests[] =
{
906 907 908
    { "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 },
909
    { "req_test4", req_test4, sizeof(req_test4)-1, status_200, resp_test4, sizeof(resp_test4)-1 },
910
    { "req_test5", req_test5, sizeof(req_test5)-1, status_500, resp_test5, sizeof(resp_test5)-1 },
911 912
};

913
static void send_response( int c, const char *status, const char *data, unsigned int len )
914 915
{
    static const char headers[] =
916
        "Content-Type: text/xml; charset=utf-8\r\nConnection: close\r\nMappedResponseHeader: value\r\n";
917
    static const char fmt[] =
918
        "Content-Length: %u\r\n\r\n";
919 920
    char buf[128];

921
    send( c, status, strlen(status), 0 );
922
    send( c, headers, sizeof(headers) - 1, 0 );
923
    sprintf( buf, fmt, len );
924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
    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;

987
        for (j = 0; j < ARRAY_SIZE( tests ); j++)
988 989 990 991 992 993
        {
            if (strstr( buf, tests[j].req_action ))
            {
                if (tests[j].req_data)
                {
                    int data_len = strlen( buf );
994
                    ok( tests[j].req_len == data_len, "%u: got data length %u expected %u\n",
995 996
                        j, data_len, tests[j].req_len );
                    if (tests[j].req_len == data_len)
997 998
                        ok( !memcmp( tests[j].req_data, buf, tests[j].req_len ),
                            "%u: got data '%s' expected '%s'\n", j, buf, tests[j].req_data );
999
                }
1000
                send_response( c, tests[j].resp_status, tests[j].resp_data, tests[j].resp_len );
1001
                break;
1002 1003 1004 1005 1006 1007 1008
            }
        }

        shutdown( c, 2 );
        closesocket( c );
        if (quit) break;
    }
1009
    closesocket( s );
1010 1011 1012 1013

    return 0;
}

1014 1015
START_TEST(proxy)
{
1016 1017 1018 1019 1020 1021
    WS_XML_STRING test1 = {9, (BYTE *)"req_test1"};
    WS_XML_STRING quit = {4, (BYTE *)"quit"};
    struct server_info info;
    HANDLE thread;
    DWORD ret;

1022 1023 1024
    test_WsCreateServiceProxy();
    test_WsCreateServiceProxyFromTemplate();
    test_WsOpenServiceProxy();
1025
    test_WsResetServiceProxy();
1026 1027 1028 1029

    info.port  = 7533;
    info.event = CreateEventW( NULL, 0, 0, NULL );
    thread = CreateThread( NULL, 0, server_proc, &info, 0, NULL );
1030
    ok( thread != NULL, "failed to create server thread %lu\n", GetLastError() );
1031 1032

    ret = WaitForSingleObject( info.event, 3000 );
1033
    ok(ret == WAIT_OBJECT_0, "failed to start test server %lu\n", GetLastError());
1034 1035 1036 1037 1038
    if (ret != WAIT_OBJECT_0)
    {
        CloseHandle(thread);
        return;
    }
1039 1040

    test_WsSendMessage( info.port, &test1 );
1041
    test_WsReceiveMessage( info.port );
1042
    test_WsCall( info.port );
1043
    test_empty_response( info.port );
1044
    test_inout_params( info.port );
1045
    test_fault_response( info.port );
1046

1047 1048
    test_WsSendMessage( info.port, &quit );
    WaitForSingleObject( thread, 3000 );
1049
    CloseHandle(thread);
1050
}