2 * HTTP server API tests
4 * Copyright 2017 Nikolay Sivov for CodeWeavers
5 * Copyright 2019 Zebediah Figura
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #define WIN32_NO_STATUS
34 #include "wine/test.h"
36 static ULONG (WINAPI
*pHttpAddUrlToUrlGroup
)(HTTP_URL_GROUP_ID id
, const WCHAR
*url
, HTTP_URL_CONTEXT context
, ULONG reserved
);
37 static ULONG (WINAPI
*pHttpCreateServerSession
)(HTTPAPI_VERSION version
, HTTP_SERVER_SESSION_ID
*session_id
, ULONG reserved
);
38 static ULONG (WINAPI
*pHttpCreateRequestQueue
)(HTTPAPI_VERSION version
, const WCHAR
*name
, SECURITY_ATTRIBUTES
*sa
, ULONG flags
, HANDLE
*handle
);
39 static ULONG (WINAPI
*pHttpCreateUrlGroup
)(HTTP_SERVER_SESSION_ID session_id
, HTTP_URL_GROUP_ID
*group_id
, ULONG reserved
);
40 static ULONG (WINAPI
*pHttpCloseRequestQueue
)(HANDLE queue
);
41 static ULONG (WINAPI
*pHttpCloseServerSession
)(HTTP_SERVER_SESSION_ID session_id
);
42 static ULONG (WINAPI
*pHttpCloseUrlGroup
)(HTTP_URL_GROUP_ID group_id
);
43 static ULONG (WINAPI
*pHttpRemoveUrlFromUrlGroup
)(HTTP_URL_GROUP_ID id
, const WCHAR
*url
, ULONG flags
);
44 static ULONG (WINAPI
*pHttpSetUrlGroupProperty
)(HTTP_URL_GROUP_ID id
, HTTP_SERVER_PROPERTY property
, void *value
, ULONG length
);
46 static void init(void)
48 HMODULE mod
= GetModuleHandleA("httpapi.dll");
50 #define X(f) p##f = (void *)GetProcAddress(mod, #f)
51 X(HttpAddUrlToUrlGroup
);
52 X(HttpCreateRequestQueue
);
53 X(HttpCreateServerSession
);
54 X(HttpCreateUrlGroup
);
55 X(HttpCloseRequestQueue
);
56 X(HttpCloseServerSession
);
58 X(HttpRemoveUrlFromUrlGroup
);
59 X(HttpSetUrlGroupProperty
);
63 static const char simple_req
[] =
64 "GET /foobar HTTP/1.1\r\n"
65 "Host: localhost:%u\r\n"
66 "Connection: keep-alive\r\n"
67 "User-Agent: WINE\r\n"
70 static SOCKET
create_client_socket(unsigned short port
)
72 struct sockaddr_in sockaddr
=
74 .sin_family
= AF_INET
,
75 .sin_port
= htons(port
),
76 .sin_addr
.S_un
.S_addr
= inet_addr("127.0.0.1"),
78 SOCKET s
= socket(AF_INET
, SOCK_STREAM
, 0), ret
;
79 ret
= connect(s
, (struct sockaddr
*)&sockaddr
, sizeof(sockaddr
));
80 ok(!ret
, "Failed to connect socket, error %lu.\n", GetLastError());
84 /* Helper function for when we don't care about the response received. */
85 static void send_response_v1(HANDLE queue
, HTTP_REQUEST_ID id
, int s
)
87 HTTP_RESPONSE_V1 response
= {};
88 char response_buffer
[2048];
91 response
.StatusCode
= 418;
92 response
.pReason
= "I'm a teapot";
93 response
.ReasonLength
= 12;
94 ret
= HttpSendHttpResponse(queue
, id
, 0, (HTTP_RESPONSE
*)&response
, NULL
, NULL
, NULL
, 0, NULL
, NULL
);
95 ok(!ret
, "Got error %u.\n", ret
);
96 ret
= recv(s
, response_buffer
, sizeof(response_buffer
), 0);
97 ok(ret
> 0, "recv() failed.\n");
100 static unsigned short add_url_v1(HANDLE queue
)
106 for (port
= 50000; port
< 51000; ++port
)
108 swprintf(url
, ARRAY_SIZE(url
), L
"http://localhost:%u/", port
);
109 if (!(ret
= HttpAddUrl(queue
, url
, NULL
)))
111 ok(ret
== ERROR_SHARING_VIOLATION
|| ret
== ERROR_ALREADY_EXISTS
, "Failed to add %s, error %u.\n", debugstr_w(url
), ret
);
113 ok(0, "Failed to add url %s, error %u.\n", debugstr_w(url
), ret
);
117 static unsigned short add_url_v2(HTTP_URL_GROUP_ID group
)
123 for (port
= 50010; port
< 51000; ++port
)
125 swprintf(url
, ARRAY_SIZE(url
), L
"http://localhost:%u/", port
);
126 if (!(ret
= pHttpAddUrlToUrlGroup(group
, url
, 0xdeadbeef, 0)))
128 ok(ret
== ERROR_SHARING_VIOLATION
, "Failed to add %s, error %u.\n", debugstr_w(url
), ret
);
130 ok(0, "Failed to add url %s, error %u.\n", debugstr_w(url
), ret
);
134 static ULONG
remove_url_v1(HANDLE queue
, unsigned short port
)
137 swprintf(url
, ARRAY_SIZE(url
), L
"http://localhost:%u/", port
);
138 return HttpRemoveUrl(queue
, url
);
141 static ULONG
remove_url_v2(HTTP_URL_GROUP_ID group
, unsigned short port
)
144 swprintf(url
, ARRAY_SIZE(url
), L
"http://localhost:%u/", port
);
145 return pHttpRemoveUrlFromUrlGroup(group
, url
, 0);
148 static void test_v1_server(void)
150 char DECLSPEC_ALIGN(8) req_buffer
[2048], response_buffer
[2048];
151 HTTP_REQUEST_V1
*req
= (HTTP_REQUEST_V1
*)req_buffer
;
152 struct sockaddr_in sockaddr
, *sin
;
153 HTTP_RESPONSE_V1 response
= {};
154 HANDLE queue
, queue2
;
164 ovl
.hEvent
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
165 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
167 ret
= HttpCreateHttpHandle(NULL
, 0);
168 ok(ret
== ERROR_INVALID_PARAMETER
, "Unexpected error %u.\n", ret
);
170 /* Non-zero reserved parameter is accepted on XP/2k3. */
172 ret
= HttpCreateHttpHandle(&queue
, 0);
173 ok(!ret
, "Unexpected ret value %u.\n", ret
);
174 ok(!!queue
, "Unexpected handle value %p.\n", queue
);
177 ret
= HttpCreateHttpHandle(&queue2
, 0);
178 ok(!ret
, "Unexpected ret value %u.\n", ret
);
179 ok(queue2
&& queue2
!= queue
, "Unexpected handle %p.\n", queue2
);
180 ret
= CloseHandle(queue2
);
181 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
183 ret_size
= 0xdeadbeef;
184 ret
= HttpReceiveHttpRequest(NULL
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
185 ok(ret
== ERROR_INVALID_HANDLE
, "Got error %u.\n", ret
);
186 ret
= HttpReceiveHttpRequest(NULL
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), NULL
, &ovl
);
187 ok(ret
== ERROR_INVALID_HANDLE
, "Got error %u.\n", ret
);
188 ret
= HttpReceiveHttpRequest(queue
, 0xdeadbeef, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), NULL
, &ovl
);
189 ok(ret
== ERROR_CONNECTION_INVALID
, "Got error %u.\n", ret
);
190 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), NULL
, &ovl
);
191 ok(ret
== ERROR_IO_PENDING
, "Got error %u.\n", ret
);
193 SetLastError(0xdeadbeef);
194 ret
= GetOverlappedResult(queue
, &ovl
, &ret_size
, FALSE
);
195 ok(!ret
, "Expected failure.\n");
196 ok(GetLastError() == ERROR_IO_INCOMPLETE
, "Got error %lu.\n", GetLastError());
198 ret
= HttpAddUrl(NULL
, L
"http://localhost:50000/", NULL
);
199 ok(ret
== ERROR_INVALID_HANDLE
|| ret
== ERROR_INVALID_PARAMETER
/* < Vista */, "Got error %u.\n", ret
);
200 ret
= HttpAddUrl(queue
, L
"http://localhost:50000", NULL
);
201 ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
202 ret
= HttpAddUrl(queue
, L
"localhost:50000", NULL
);
203 ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
204 ret
= HttpAddUrl(queue
, L
"localhost:50000/", NULL
);
205 ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
206 ret
= HttpAddUrl(queue
, L
"http://localhost/", NULL
);
207 ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
208 ret
= HttpAddUrl(queue
, L
"http://localhost:/", NULL
);
209 ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
210 ret
= HttpAddUrl(queue
, L
"http://localhost:0/", NULL
);
211 ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
212 port
= add_url_v1(queue
);
213 swprintf(url
, ARRAY_SIZE(url
), L
"http://localhost:%u/", port
);
214 ret
= HttpAddUrl(queue
, url
, NULL
);
215 ok(ret
== ERROR_ALREADY_EXISTS
, "Got error %u.\n", ret
);
217 s
= create_client_socket(port
);
218 len
= sizeof(sockaddr
);
219 ret
= getsockname(s
, (struct sockaddr
*)&sockaddr
, &len
);
220 ok(ret
== 0, "getsockname() failed, error %u.\n", WSAGetLastError());
222 SetLastError(0xdeadbeef);
223 ret
= GetOverlappedResult(queue
, &ovl
, &ret_size
, FALSE
);
224 ok(!ret
, "Expected failure.\n");
225 ok(GetLastError() == ERROR_IO_INCOMPLETE
, "Got error %lu.\n", GetLastError());
227 sprintf(req_text
, simple_req
, port
);
228 ret
= send(s
, req_text
, strlen(req_text
), 0);
229 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
231 /* Various versions of Windows (observed on 64-bit Windows 8 and Windows 10
232 * version 1507, but probably affecting others) suffer from a bug where the
233 * kernel will report success before completely filling the buffer or
234 * reporting the correct buffer size. Wait for a short interval to work
238 ret
= GetOverlappedResult(queue
, &ovl
, &ret_size
, TRUE
);
239 ok(ret
, "Got error %lu.\n", GetLastError());
240 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
242 ok(!req
->Flags
, "Got flags %#lx.\n", req
->Flags
);
243 ok(req
->ConnectionId
, "Expected nonzero connection ID.\n");
244 ok(req
->RequestId
, "Expected nonzero connection ID.\n");
245 ok(!req
->UrlContext
, "Got URL context %s.\n", wine_dbgstr_longlong(req
->UrlContext
));
246 ok(req
->Version
.MajorVersion
== 1, "Got major version %u.\n", req
->Version
.MajorVersion
);
247 ok(req
->Version
.MinorVersion
== 1, "Got major version %u.\n", req
->Version
.MinorVersion
);
248 ok(req
->Verb
== HttpVerbGET
, "Got verb %u.\n", req
->Verb
);
249 ok(!req
->UnknownVerbLength
, "Got unknown verb length %u.\n", req
->UnknownVerbLength
);
250 ok(req
->RawUrlLength
== 7, "Got raw URL length %u.\n", req
->RawUrlLength
);
251 ok(!req
->pUnknownVerb
, "Got unknown verb %s.\n", req
->pUnknownVerb
);
252 ok(!strcmp(req
->pRawUrl
, "/foobar"), "Got raw URL %s.\n", req
->pRawUrl
);
253 ok(req
->CookedUrl
.FullUrlLength
== 58, "Got full URL length %u.\n", req
->CookedUrl
.FullUrlLength
);
254 ok(req
->CookedUrl
.HostLength
== 30, "Got host length %u.\n", req
->CookedUrl
.HostLength
);
255 ok(req
->CookedUrl
.AbsPathLength
== 14, "Got absolute path length %u.\n", req
->CookedUrl
.AbsPathLength
);
256 ok(!req
->CookedUrl
.QueryStringLength
, "Got query string length %u.\n", req
->CookedUrl
.QueryStringLength
);
257 swprintf(url
, ARRAY_SIZE(url
), L
"http://localhost:%u/foobar", port
);
258 ok(!wcscmp(req
->CookedUrl
.pFullUrl
, url
), "Got full URL %s.\n", wine_dbgstr_w(req
->CookedUrl
.pFullUrl
));
259 ok(req
->CookedUrl
.pHost
== req
->CookedUrl
.pFullUrl
+ 7, "Got host %s.\n", wine_dbgstr_w(req
->CookedUrl
.pHost
));
260 ok(req
->CookedUrl
.pAbsPath
== req
->CookedUrl
.pFullUrl
+ 22,
261 "Got absolute path %s.\n", wine_dbgstr_w(req
->CookedUrl
.pAbsPath
));
262 ok(!req
->CookedUrl
.pQueryString
, "Got query string %s.\n", wine_dbgstr_w(req
->CookedUrl
.pQueryString
));
263 ok(!memcmp(req
->Address
.pRemoteAddress
, &sockaddr
, len
), "Client addresses didn't match.\n");
264 sin
= (SOCKADDR_IN
*)req
->Address
.pLocalAddress
;
265 ok(sin
->sin_family
== AF_INET
, "Got family %u.\n", sin
->sin_family
);
266 ok(ntohs(sin
->sin_port
) == port
, "Got wrong port %u.\n", ntohs(sin
->sin_port
));
267 ok(sin
->sin_addr
.S_un
.S_addr
== inet_addr("127.0.0.1"), "Got address %08lx.\n", sin
->sin_addr
.S_un
.S_addr
);
268 ok(!req
->Headers
.UnknownHeaderCount
, "Got %u unknown headers.\n", req
->Headers
.UnknownHeaderCount
);
269 ok(!req
->Headers
.pUnknownHeaders
, "Got unknown headers %p.\n", req
->Headers
.pUnknownHeaders
);
270 for (i
= 0; i
< ARRAY_SIZE(req
->Headers
.KnownHeaders
); ++i
)
272 if (i
== HttpHeaderConnection
)
274 ok(req
->Headers
.KnownHeaders
[i
].RawValueLength
== 10, "Got length %u.\n",
275 req
->Headers
.KnownHeaders
[i
].RawValueLength
);
276 ok(!strcmp(req
->Headers
.KnownHeaders
[i
].pRawValue
, "keep-alive"),
277 "Got connection '%s'.\n", req
->Headers
.KnownHeaders
[i
].pRawValue
);
279 else if (i
== HttpHeaderHost
)
282 sprintf(expect
, "localhost:%u", port
);
283 ok(req
->Headers
.KnownHeaders
[i
].RawValueLength
== strlen(expect
), "Got length %u.\n",
284 req
->Headers
.KnownHeaders
[i
].RawValueLength
);
285 ok(!strcmp(req
->Headers
.KnownHeaders
[i
].pRawValue
, expect
),
286 "Got connection '%s'.\n", req
->Headers
.KnownHeaders
[i
].pRawValue
);
288 else if (i
== HttpHeaderUserAgent
)
290 ok(req
->Headers
.KnownHeaders
[i
].RawValueLength
== 4, "Got length %u.\n",
291 req
->Headers
.KnownHeaders
[i
].RawValueLength
);
292 ok(!strcmp(req
->Headers
.KnownHeaders
[i
].pRawValue
, "WINE"),
293 "Got connection '%s'.\n", req
->Headers
.KnownHeaders
[i
].pRawValue
);
297 ok(!req
->Headers
.KnownHeaders
[i
].RawValueLength
, "Header %#x: got length %u.\n",
298 i
, req
->Headers
.KnownHeaders
[i
].RawValueLength
);
299 ok(!req
->Headers
.KnownHeaders
[i
].pRawValue
, "Header %#x: got value '%s'.\n",
300 i
, req
->Headers
.KnownHeaders
[i
].pRawValue
);
303 ok(req
->BytesReceived
== strlen(req_text
), "Got %s bytes.\n", wine_dbgstr_longlong(req
->BytesReceived
));
304 ok(!req
->EntityChunkCount
, "Got %u entity chunks.\n", req
->EntityChunkCount
);
305 ok(!req
->pEntityChunks
, "Got entity chunks %p.\n", req
->pEntityChunks
);
306 /* req->RawConnectionId is zero until Win10 2004. */
307 ok(!req
->pSslInfo
, "Got SSL info %p.\n", req
->pSslInfo
);
309 response
.StatusCode
= 418;
310 response
.pReason
= "I'm a teapot";
311 response
.ReasonLength
= 12;
312 response
.Headers
.KnownHeaders
[HttpHeaderRetryAfter
].pRawValue
= "120";
313 response
.Headers
.KnownHeaders
[HttpHeaderRetryAfter
].RawValueLength
= 3;
314 ret
= HttpSendHttpResponse(queue
, 0xdeadbeef, 0, (HTTP_RESPONSE
*)&response
, NULL
, NULL
, NULL
, 0, &ovl
, NULL
);
315 ok(ret
== ERROR_CONNECTION_INVALID
, "Got error %u.\n", ret
);
316 ret
= HttpSendHttpResponse(queue
, req
->RequestId
, 0, (HTTP_RESPONSE
*)&response
, NULL
, NULL
, NULL
, 0, &ovl
, NULL
);
317 ok(!ret
, "Got error %u.\n", ret
);
318 ret
= GetOverlappedResult(queue
, &ovl
, &ret_size
, FALSE
);
319 ok(ret
, "Got error %lu.\n", GetLastError());
321 ret
= recv(s
, response_buffer
, sizeof(response_buffer
), 0);
322 ok(ret
== ret_size
, "Expected size %lu, got %u.\n", ret_size
, ret
);
324 if (winetest_debug
> 1)
325 trace("%.*s\n", ret
, response_buffer
);
327 ok(!strncmp(response_buffer
, "HTTP/1.1 418 I'm a teapot\r\n", 27), "Got incorrect status line.\n");
328 ok(!!strstr(response_buffer
, "\r\nRetry-After: 120\r\n"), "Missing or malformed Retry-After header.\n");
329 ok(!!strstr(response_buffer
, "\r\nDate:"), "Missing Date header.\n");
331 ret
= HttpReceiveHttpRequest(queue
, req
->RequestId
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), NULL
, &ovl
);
332 ok(ret
== ERROR_CONNECTION_INVALID
, "Got error %u.\n", ret
);
334 /* HttpReceiveHttpRequest() may return synchronously, but this cannot be
335 * reliably tested. Introducing a delay after send() and before
336 * HttpReceiveHttpRequest() confirms this. */
338 ret
= remove_url_v1(NULL
, port
);
339 ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
340 ret
= remove_url_v1(queue
, port
);
341 ok(!ret
, "Got error %u.\n", ret
);
342 ret
= remove_url_v1(queue
, port
);
343 ok(ret
== ERROR_FILE_NOT_FOUND
, "Got error %u.\n", ret
);
345 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), NULL
, &ovl
);
346 ok(ret
== ERROR_IO_PENDING
, "Got error %u.\n", ret
);
348 ret
= CancelIo(queue
);
349 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
351 ret
= WaitForSingleObject(ovl
.hEvent
, 100);
352 ok(!ret
, "Got %u.\n", ret
);
353 ret_size
= 0xdeadbeef;
354 ret
= GetOverlappedResult(queue
, &ovl
, &ret_size
, FALSE
);
355 ok(!ret
, "Expected failure.\n");
356 ok(GetLastError() == ERROR_OPERATION_ABORTED
, "Got error %lu.\n", GetLastError());
357 ok(!ret_size
, "Got size %lu.\n", ret_size
);
360 CloseHandle(ovl
.hEvent
);
361 ret
= CloseHandle(queue
);
362 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
364 ret
= HttpAddUrl(queue
, L
"http://localhost:50000/", NULL
);
365 ok(ret
== ERROR_INVALID_HANDLE
, "Got error %u.\n", ret
);
368 static void test_v1_completion_port(void)
370 char DECLSPEC_ALIGN(8) req_buffer
[2048], response_buffer
[2048];
371 HTTP_REQUEST_V1
*req
= (HTTP_REQUEST_V1
*)req_buffer
;
372 HTTP_RESPONSE_V1 response
= {};
373 unsigned short tcp_port
;
374 OVERLAPPED ovl
, *povl
;
382 ovl
.hEvent
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
384 ret
= HttpCreateHttpHandle(&queue
, 0);
385 ok(!ret
, "Got error %u.\n", ret
);
387 port
= CreateIoCompletionPort(queue
, NULL
, 123, 0);
388 ok(!!port
, "Failed to create completion port, error %lu.\n", GetLastError());
390 ret
= GetQueuedCompletionStatus(port
, &ret_size
, &key
, &povl
, 0);
391 ok(!ret
, "Expected failure.\n");
392 ok(GetLastError() == WAIT_TIMEOUT
, "Got error %lu.\n", GetLastError());
394 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), NULL
, &ovl
);
395 ok(ret
== ERROR_IO_PENDING
, "Got error %u.\n", ret
);
397 tcp_port
= add_url_v1(queue
);
398 s
= create_client_socket(tcp_port
);
400 ret
= GetQueuedCompletionStatus(port
, &ret_size
, &key
, &povl
, 0);
401 ok(!ret
, "Expected failure.\n");
402 ok(GetLastError() == WAIT_TIMEOUT
, "Got error %lu.\n", GetLastError());
404 sprintf(req_text
, simple_req
, tcp_port
);
405 ret
= send(s
, req_text
, strlen(req_text
), 0);
406 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
408 ret_size
= key
= 0xdeadbeef;
409 ret
= GetQueuedCompletionStatus(port
, &ret_size
, &key
, &povl
, 1000);
410 ok(ret
, "Got error %lu.\n", GetLastError());
411 ok(povl
== &ovl
, "OVERLAPPED pointers didn't match.\n");
412 ok(key
== 123, "Got unexpected key %Iu.\n", key
);
413 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
415 ret
= GetQueuedCompletionStatus(port
, &ret_size
, &key
, &povl
, 0);
416 ok(!ret
, "Expected failure.\n");
417 ok(GetLastError() == WAIT_TIMEOUT
, "Got error %lu.\n", GetLastError());
419 response
.StatusCode
= 418;
420 response
.pReason
= "I'm a teapot";
421 response
.ReasonLength
= 12;
422 ret
= HttpSendHttpResponse(queue
, req
->RequestId
, 0, (HTTP_RESPONSE
*)&response
, NULL
, NULL
, NULL
, 0, &ovl
, NULL
);
423 ok(!ret
, "Got error %u.\n", ret
);
425 ret_size
= key
= 0xdeadbeef;
426 ret
= GetQueuedCompletionStatus(port
, &ret_size
, &key
, &povl
, 1000);
427 ok(ret
, "Got error %lu.\n", GetLastError());
428 ok(povl
== &ovl
, "OVERLAPPED pointers didn't match.\n");
429 ok(key
== 123, "Got unexpected key %Iu.\n", key
);
431 ret
= recv(s
, response_buffer
, sizeof(response_buffer
), 0);
432 ok(ret
== ret_size
, "Expected size %lu, got %u.\n", ret_size
, ret
);
434 ret
= remove_url_v1(queue
, tcp_port
);
435 ok(!ret
, "Got error %u.\n", ret
);
438 CloseHandle(ovl
.hEvent
);
439 ret
= CloseHandle(queue
);
440 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
443 static void test_v1_multiple_requests(void)
445 char DECLSPEC_ALIGN(8) req_buffer1
[2048];
446 char DECLSPEC_ALIGN(8) req_buffer2
[2048];
447 HTTP_REQUEST_V1
*req1
= (HTTP_REQUEST_V1
*)req_buffer1
, *req2
= (HTTP_REQUEST_V1
*)req_buffer2
;
448 HTTP_RESPONSE_V1 response
= {};
449 struct sockaddr_in sockaddr
;
450 OVERLAPPED ovl1
, ovl2
;
458 ovl1
.hEvent
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
459 ovl2
.hEvent
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
461 ret
= HttpCreateHttpHandle(&queue
, 0);
462 ok(!ret
, "Got error %u.\n", ret
);
463 port
= add_url_v1(queue
);
465 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req1
, sizeof(req_buffer1
), NULL
, &ovl1
);
466 ok(ret
== ERROR_IO_PENDING
, "Got error %u.\n", ret
);
467 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req2
, sizeof(req_buffer2
), NULL
, &ovl2
);
468 ok(ret
== ERROR_IO_PENDING
, "Got error %u.\n", ret
);
470 SetLastError(0xdeadbeef);
471 ret
= GetOverlappedResult(queue
, &ovl1
, &ret_size
, FALSE
);
472 ok(!ret
, "Expected failure.\n");
473 ok(GetLastError() == ERROR_IO_INCOMPLETE
, "Got error %lu.\n", GetLastError());
475 s1
= create_client_socket(port
);
476 sprintf(req_text
, simple_req
, port
);
477 ret
= send(s1
, req_text
, strlen(req_text
), 0);
478 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
480 ret
= WaitForSingleObject(ovl1
.hEvent
, 100);
481 ok(!ret
, "Got %u.\n", ret
);
482 ret
= WaitForSingleObject(ovl2
.hEvent
, 100);
483 ok(ret
== WAIT_TIMEOUT
, "Got %u.\n", ret
);
485 s2
= create_client_socket(port
);
486 ret
= send(s2
, req_text
, strlen(req_text
), 0);
487 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
489 ret
= WaitForSingleObject(ovl1
.hEvent
, 0);
490 ok(!ret
, "Got %u.\n", ret
);
491 ret
= WaitForSingleObject(ovl2
.hEvent
, 100);
492 ok(!ret
, "Got %u.\n", ret
);
494 len
= sizeof(sockaddr
);
495 getsockname(s1
, (struct sockaddr
*)&sockaddr
, &len
);
496 ok(!memcmp(req1
->Address
.pRemoteAddress
, &sockaddr
, len
), "Client addresses didn't match.\n");
497 len
= sizeof(sockaddr
);
498 getsockname(s2
, (struct sockaddr
*)&sockaddr
, &len
);
499 ok(!memcmp(req2
->Address
.pRemoteAddress
, &sockaddr
, len
), "Client addresses didn't match.\n");
500 ok(req1
->ConnectionId
!= req2
->ConnectionId
,
501 "Expected different connection IDs, but got %s.\n", wine_dbgstr_longlong(req1
->ConnectionId
));
502 ok(req1
->RequestId
!= req2
->RequestId
,
503 "Expected different request IDs, but got %s.\n", wine_dbgstr_longlong(req1
->RequestId
));
505 response
.StatusCode
= 418;
506 response
.pReason
= "I'm a teapot";
507 response
.ReasonLength
= 12;
508 ret
= HttpSendHttpResponse(queue
, req1
->RequestId
, 0, (HTTP_RESPONSE
*)&response
, NULL
, NULL
, NULL
, 0, &ovl1
, NULL
);
509 ok(!ret
, "Got error %u.\n", ret
);
510 ret
= HttpSendHttpResponse(queue
, req2
->RequestId
, 0, (HTTP_RESPONSE
*)&response
, NULL
, NULL
, NULL
, 0, &ovl2
, NULL
);
511 ok(!ret
, "Got error %u.\n", ret
);
513 /* Test sending multiple requests from the same socket. */
515 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req1
, sizeof(req_buffer1
), NULL
, &ovl1
);
516 ok(ret
== ERROR_IO_PENDING
, "Got error %u.\n", ret
);
517 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req2
, sizeof(req_buffer2
), NULL
, &ovl2
);
518 ok(ret
== ERROR_IO_PENDING
, "Got error %u.\n", ret
);
520 ret
= send(s1
, req_text
, strlen(req_text
), 0);
521 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
522 ret
= send(s1
, req_text
, strlen(req_text
), 0);
523 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
525 ret
= WaitForSingleObject(ovl1
.hEvent
, 100);
526 ok(!ret
, "Got %u.\n", ret
);
527 ret
= WaitForSingleObject(ovl2
.hEvent
, 100);
528 ok(ret
== WAIT_TIMEOUT
, "Got %u.\n", ret
);
530 ret
= HttpSendHttpResponse(queue
, req1
->RequestId
, 0, (HTTP_RESPONSE
*)&response
, NULL
, NULL
, NULL
, 0, &ovl1
, NULL
);
531 ok(!ret
, "Got error %u.\n", ret
);
533 ret
= WaitForSingleObject(ovl2
.hEvent
, 100);
534 ok(!ret
, "Got %u.\n", ret
);
535 ok(req1
->ConnectionId
== req2
->ConnectionId
, "Expected same connection IDs.\n");
536 ok(req1
->RequestId
!= req2
->RequestId
,
537 "Expected different request IDs, but got %s.\n", wine_dbgstr_longlong(req1
->RequestId
));
539 ret
= remove_url_v1(queue
, port
);
540 ok(!ret
, "Got error %u.\n", ret
);
543 CloseHandle(ovl1
.hEvent
);
544 CloseHandle(ovl2
.hEvent
);
545 ret
= CloseHandle(queue
);
546 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
549 static void test_v1_short_buffer(void)
551 char DECLSPEC_ALIGN(8) req_buffer
[2048];
552 char DECLSPEC_ALIGN(8) req_buffer2
[2048];
553 HTTP_REQUEST_V1
*req
= (HTTP_REQUEST_V1
*)req_buffer
, *req2
= (HTTP_REQUEST_V1
*)req_buffer2
;
554 HTTP_REQUEST_ID req_id
;
563 ovl
.hEvent
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
565 ret
= HttpCreateHttpHandle(&queue
, 0);
566 ok(!ret
, "Got error %u.\n", ret
);
567 port
= add_url_v1(queue
);
569 s
= create_client_socket(port
);
570 sprintf(req_text
, simple_req
, port
);
571 ret
= send(s
, req_text
, strlen(req_text
), 0);
572 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
574 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
577 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(HTTP_REQUEST_V1
) - 1, &ret_size
, NULL
);
578 ok(ret
== ERROR_INSUFFICIENT_BUFFER
, "Got error %u.\n", ret
);
579 ok(ret_size
== 1 /* win < 10 */ || !ret_size
, "Got size %lu.\n", ret_size
);
582 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(HTTP_REQUEST_V1
), &ret_size
, NULL
);
583 ok(ret
== ERROR_MORE_DATA
, "Got error %u.\n", ret
);
584 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
586 ok(!!req
->ConnectionId
, "Got connection ID %s.\n", wine_dbgstr_longlong(req
->ConnectionId
));
587 ok(!!req
->RequestId
, "Got request ID %s.\n", wine_dbgstr_longlong(req
->RequestId
));
588 ok(!req
->Version
.MajorVersion
|| req
->Version
.MajorVersion
== 0xcccc /* < Vista */,
589 "Got major version %u.\n", req
->Version
.MajorVersion
);
590 ok(!req
->BytesReceived
|| req
->BytesReceived
== ((ULONGLONG
)0xcccccccc << 32 | 0xcccccccc) /* < Vista */,
591 "Got %s bytes.\n", wine_dbgstr_longlong(req
->BytesReceived
));
592 req_id
= req
->RequestId
;
594 /* At this point the request has been assigned a specific ID, and one cannot
595 * receive it by calling with HTTP_NULL_ID. */
596 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req2
, sizeof(req_buffer2
), NULL
, &ovl
);
597 ok(ret
== ERROR_IO_PENDING
, "Got error %u.\n", ret
);
599 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
600 ret
= HttpReceiveHttpRequest(queue
, req_id
, 0, (HTTP_REQUEST
*)req
, ret_size
- 1, &ret_size
, NULL
);
601 ok(ret
== ERROR_MORE_DATA
, "Got error %u.\n", ret
);
602 ok(req
->RequestId
== req_id
, "Got request ID %s.\n", wine_dbgstr_longlong(req
->RequestId
));
604 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
605 ret
= HttpReceiveHttpRequest(queue
, req_id
, 0, (HTTP_REQUEST
*)req
, ret_size
- 1, NULL
, NULL
);
606 ok(ret
== ERROR_MORE_DATA
, "Got error %u.\n", ret
);
607 ok(req
->RequestId
== req_id
, "Got request ID %s.\n", wine_dbgstr_longlong(req
->RequestId
));
609 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
610 ret
= HttpReceiveHttpRequest(queue
, req_id
, 0, (HTTP_REQUEST
*)req
, ret_size
, &ret_size
, NULL
);
611 ok(!ret
, "Got error %u.\n", ret
);
612 ok(req
->RequestId
== req_id
, "Got request ID %s.\n", wine_dbgstr_longlong(req
->RequestId
));
616 ret
= remove_url_v1(queue
, port
);
617 ok(!ret
, "Got error %u.\n", ret
);
619 CloseHandle(ovl
.hEvent
);
620 ret
= CloseHandle(queue
);
621 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
624 static void test_v1_entity_body(void)
626 char DECLSPEC_ALIGN(8) req_buffer
[4096], response_buffer
[2048], req_body
[2048], recv_body
[2000];
627 HTTP_REQUEST_V1
*req
= (HTTP_REQUEST_V1
*)req_buffer
;
628 HTTP_RESPONSE_V1 response
= {};
629 HTTP_DATA_CHUNK chunks
[2] = {};
639 static const char post_req
[] =
640 "POST /xyzzy HTTP/1.1\r\n"
641 "Host: localhost:%u\r\n"
642 "Connection: keep-alive\r\n"
643 "Content-Length: 5\r\n"
647 static const char post_req2
[] =
648 "POST /xyzzy HTTP/1.1\r\n"
649 "Host: localhost:%u\r\n"
650 "Connection: keep-alive\r\n"
651 "Content-Length: 2048\r\n"
654 ovl
.hEvent
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
656 for (i
= 0; i
< sizeof(req_body
); ++i
)
657 req_body
[i
] = i
/ 111;
659 ret
= HttpCreateHttpHandle(&queue
, 0);
660 ok(!ret
, "Got error %u.\n", ret
);
661 port
= add_url_v1(queue
);
663 s
= create_client_socket(port
);
664 sprintf(req_text
, post_req
, port
);
665 ret
= send(s
, req_text
, strlen(req_text
) + 1, 0);
666 ok(ret
== strlen(req_text
) + 1, "send() returned %d.\n", ret
);
667 /* Windows versions before 8 will return success, and report that an entity
668 * body exists in the Flags member, but fail to account for it in the
669 * BytesReceived member or actually copy it to the buffer, if
670 * HttpReceiveHttpRequest() is called before the kernel has finished
671 * receiving the entity body. Add a small delay to work around this. */
674 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
675 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
676 ok(!ret
, "Got error %u.\n", ret
);
677 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
678 ok(req
->Flags
== HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS
, "Got flags %#lx.\n", req
->Flags
);
679 ok(req
->BytesReceived
== strlen(req_text
) + 1, "Got %s bytes.\n", wine_dbgstr_longlong(req
->BytesReceived
));
680 ok(req
->Headers
.KnownHeaders
[HttpHeaderContentLength
].RawValueLength
== 1,
681 "Got header length %u.\n", req
->Headers
.KnownHeaders
[HttpHeaderContentLength
].RawValueLength
);
682 ok(!strcmp(req
->Headers
.KnownHeaders
[HttpHeaderContentLength
].pRawValue
, "5"),
683 "Got header value %s.\n", req
->Headers
.KnownHeaders
[HttpHeaderContentLength
].pRawValue
);
684 ok(!req
->EntityChunkCount
, "Got %u entity chunks.\n", req
->EntityChunkCount
);
685 ok(!req
->pEntityChunks
, "Got entity chunks %p.\n", req
->pEntityChunks
);
687 response
.StatusCode
= 418;
688 response
.pReason
= "I'm a teapot";
689 response
.ReasonLength
= 12;
690 response
.EntityChunkCount
= ARRAY_SIZE(chunks
);
691 response
.pEntityChunks
= chunks
;
692 chunks
[0].DataChunkType
= HttpDataChunkFromMemory
;
693 chunks
[0].FromMemory
.pBuffer
= (void *)"pong";
694 chunks
[0].FromMemory
.BufferLength
= 4;
695 chunks
[1].DataChunkType
= HttpDataChunkFromMemory
;
696 chunks
[1].FromMemory
.pBuffer
= (void *)"pang";
697 chunks
[1].FromMemory
.BufferLength
= 4;
698 ret
= HttpSendHttpResponse(queue
, req
->RequestId
, 0, (HTTP_RESPONSE
*)&response
, NULL
, NULL
, NULL
, 0, NULL
, NULL
);
699 ok(!ret
, "Got error %u.\n", ret
);
701 memset(response_buffer
, 0, sizeof(response_buffer
));
703 ret
= recv(s
, response_buffer
, sizeof(response_buffer
), 0);
704 ok(ret
> 0, "recv() failed.\n");
705 if (winetest_debug
> 1)
706 trace("%.*s\n", ret
, response_buffer
);
707 ok(!strncmp(response_buffer
, "HTTP/1.1 418 I'm a teapot\r\n", 27), "Got incorrect status line.\n");
708 ok(!!strstr(response_buffer
, "\r\nContent-Length: 8\r\n"), "Missing or malformed Content-Length header.\n");
709 ok(!!strstr(response_buffer
, "\r\nDate:"), "Missing Date header.\n");
710 ok(!memcmp(response_buffer
+ ret
- 12, "\r\n\r\npongpang", 12), "Response did not end with entity data.\n");
712 ret
= HttpReceiveHttpRequest(queue
, req
->RequestId
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
713 ok(ret
== ERROR_CONNECTION_INVALID
, "Got error %u.\n", ret
);
715 /* http won't overwrite a Content-Length header if we manually supply one,
716 * but it also won't truncate the entity body to match. It will however
717 * always write its own Date header. */
719 ret
= send(s
, req_text
, strlen(req_text
) + 1, 0);
720 ok(ret
== strlen(req_text
) + 1, "send() returned %d.\n", ret
);
721 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
722 ok(!ret
, "Got error %u.\n", ret
);
724 response
.Headers
.KnownHeaders
[HttpHeaderContentLength
].RawValueLength
= 1;
725 response
.Headers
.KnownHeaders
[HttpHeaderContentLength
].pRawValue
= "6";
726 response
.Headers
.KnownHeaders
[HttpHeaderDate
].RawValueLength
= 10;
727 response
.Headers
.KnownHeaders
[HttpHeaderDate
].pRawValue
= "yesteryear";
728 ret
= HttpSendHttpResponse(queue
, req
->RequestId
, 0, (HTTP_RESPONSE
*)&response
, NULL
, NULL
, NULL
, 0, NULL
, NULL
);
729 ok(!ret
, "Got error %u.\n", ret
);
731 ret
= recv(s
, response_buffer
, sizeof(response_buffer
), 0);
732 ok(ret
> 0, "recv() failed.\n");
733 if (winetest_debug
> 1)
734 trace("%.*s\n", ret
, response_buffer
);
735 ok(!strncmp(response_buffer
, "HTTP/1.1 418 I'm a teapot\r\n", 27), "Got incorrect status line.\n");
736 ok(!!strstr(response_buffer
, "\r\nContent-Length: 6\r\n"), "Missing or malformed Content-Length header.\n");
737 ok(!!strstr(response_buffer
, "\r\nDate:"), "Missing Date header.\n");
738 ok(!strstr(response_buffer
, "yesteryear"), "Unexpected Date value.\n");
739 ok(!memcmp(response_buffer
+ ret
- 12, "\r\n\r\npongpang", 12), "Response did not end with entity data.\n");
741 /* Test the COPY_BODY flag. */
743 ret
= send(s
, req_text
, strlen(req_text
) + 1, 0);
744 ok(ret
== strlen(req_text
) + 1, "send() returned %d.\n", ret
);
747 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY
,
748 (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
749 ok(!ret
, "Got error %u.\n", ret
);
750 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
751 ok(!req
->Flags
, "Got flags %#lx.\n", req
->Flags
);
752 ok(req
->BytesReceived
== strlen(req_text
) + 1, "Got %s bytes.\n", wine_dbgstr_longlong(req
->BytesReceived
));
753 ok(req
->Headers
.KnownHeaders
[HttpHeaderContentLength
].RawValueLength
== 1,
754 "Got header length %u.\n", req
->Headers
.KnownHeaders
[HttpHeaderContentLength
].RawValueLength
);
755 ok(!strcmp(req
->Headers
.KnownHeaders
[HttpHeaderContentLength
].pRawValue
, "5"),
756 "Got header value %s.\n", req
->Headers
.KnownHeaders
[HttpHeaderContentLength
].pRawValue
);
757 ok(req
->EntityChunkCount
== 1, "Got %u entity chunks.\n", req
->EntityChunkCount
);
758 ok(req
->pEntityChunks
[0].DataChunkType
== HttpDataChunkFromMemory
,
759 "Got chunk type %u.\n", req
->pEntityChunks
[0].DataChunkType
);
760 ok(req
->pEntityChunks
[0].FromMemory
.BufferLength
== 5,
761 "Got chunk length %lu.\n", req
->pEntityChunks
[0].FromMemory
.BufferLength
);
762 ok(!memcmp(req
->pEntityChunks
[0].FromMemory
.pBuffer
, "ping", 5),
763 "Got chunk data '%s'.\n", (char *)req
->pEntityChunks
[0].FromMemory
.pBuffer
);
765 send_response_v1(queue
, req
->RequestId
, s
);
767 sprintf(req_text
, post_req2
, port
);
768 ret
= send(s
, req_text
, strlen(req_text
), 0);
769 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
770 ret
= send(s
, req_body
, sizeof(req_body
), 0);
771 ok(ret
== sizeof(req_body
), "send() returned %d.\n", ret
);
775 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY
,
776 (HTTP_REQUEST
*)req
, 2000, &ret_size
, NULL
);
777 ok(!ret
, "Got error %u.\n", ret
);
778 ok(ret_size
== 2000, "Got size %lu.\n", ret_size
);
779 ok(req
->Flags
== HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS
, "Got flags %#lx.\n", req
->Flags
);
780 ok(req
->BytesReceived
== strlen(req_text
) + 2048, "Got %s bytes.\n", wine_dbgstr_longlong(req
->BytesReceived
));
781 ok(req
->Headers
.KnownHeaders
[HttpHeaderContentLength
].RawValueLength
== 4,
782 "Got header length %u.\n", req
->Headers
.KnownHeaders
[HttpHeaderContentLength
].RawValueLength
);
783 ok(!strcmp(req
->Headers
.KnownHeaders
[HttpHeaderContentLength
].pRawValue
, "2048"),
784 "Got header value %s.\n", req
->Headers
.KnownHeaders
[HttpHeaderContentLength
].pRawValue
);
785 ok(req
->EntityChunkCount
== 1, "Got %u entity chunks.\n", req
->EntityChunkCount
);
786 ok(req
->pEntityChunks
[0].DataChunkType
== HttpDataChunkFromMemory
,
787 "Got chunk type %u.\n", req
->pEntityChunks
[0].DataChunkType
);
788 chunk_size
= req
->pEntityChunks
[0].FromMemory
.BufferLength
;
789 ok(chunk_size
> 0 && chunk_size
< 2000, "Got chunk size %u.\n", chunk_size
);
790 ok(!memcmp(req
->pEntityChunks
[0].FromMemory
.pBuffer
, req_body
, chunk_size
), "Chunk data didn't match.\n");
792 send_response_v1(queue
, req
->RequestId
, s
);
794 /* Test HttpReceiveRequestEntityBody(). */
796 ret_size
= 0xdeadbeef;
797 ret
= HttpReceiveRequestEntityBody(NULL
, HTTP_NULL_ID
, 0, recv_body
, sizeof(recv_body
), &ret_size
, NULL
);
798 ok(ret
== ERROR_INVALID_HANDLE
, "Got error %u.\n", ret
);
799 ret
= HttpReceiveRequestEntityBody(NULL
, HTTP_NULL_ID
, 0, recv_body
, sizeof(recv_body
), NULL
, &ovl
);
800 ok(ret
== ERROR_INVALID_HANDLE
, "Got error %u.\n", ret
);
802 sprintf(req_text
, post_req
, port
);
803 ret
= send(s
, req_text
, strlen(req_text
) + 1, 0);
804 ok(ret
== strlen(req_text
) + 1, "send() returned %d.\n", ret
);
807 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
808 ok(!ret
, "Got error %u.\n", ret
);
810 ret
= HttpReceiveRequestEntityBody(queue
, HTTP_NULL_ID
, 0, recv_body
, sizeof(recv_body
), &ret_size
, NULL
);
811 ok(ret
== ERROR_CONNECTION_INVALID
, "Got error %u.\n", ret
);
813 ret_size
= 0xdeadbeef;
814 ret
= HttpReceiveRequestEntityBody(queue
, req
->RequestId
, 0, recv_body
, sizeof(recv_body
), &ret_size
, NULL
);
815 ok(!ret
, "Got error %u.\n", ret
);
816 ok(ret_size
== 5, "Got size %lu.\n", ret_size
);
817 ok(!memcmp(recv_body
, "ping", 5), "Entity body didn't match.\n");
819 ret_size
= 0xdeadbeef;
820 ret
= HttpReceiveRequestEntityBody(queue
, req
->RequestId
, 0, recv_body
, sizeof(recv_body
), &ret_size
, NULL
);
821 ok(ret
== ERROR_HANDLE_EOF
, "Got error %u.\n", ret
);
822 ok(ret_size
== 0xdeadbeef || !ret_size
/* Win10+ */, "Got size %lu.\n", ret_size
);
824 send_response_v1(queue
, req
->RequestId
, s
);
826 ret
= HttpReceiveRequestEntityBody(queue
, req
->RequestId
, 0, recv_body
, sizeof(recv_body
), &ret_size
, NULL
);
827 ok(ret
== ERROR_CONNECTION_INVALID
, "Got error %u.\n", ret
);
829 ret
= send(s
, req_text
, strlen(req_text
) + 1, 0);
830 ok(ret
== strlen(req_text
) + 1, "send() returned %d.\n", ret
);
833 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
834 ok(!ret
, "Got error %u.\n", ret
);
836 memset(recv_body
, 0xcc, sizeof(recv_body
));
837 ret_size
= 0xdeadbeef;
838 ret
= HttpReceiveRequestEntityBody(queue
, req
->RequestId
, 0, recv_body
, 2, &ret_size
, NULL
);
839 ok(!ret
, "Got error %u.\n", ret
);
840 ok(ret_size
== 2, "Got size %lu.\n", ret_size
);
841 ok(!memcmp(recv_body
, "pi", 2), "Entity body didn't match.\n");
843 ret
= HttpReceiveRequestEntityBody(queue
, req
->RequestId
, 0, recv_body
, 1, NULL
, NULL
);
844 ok(!ret
, "Got error %u.\n", ret
);
845 ok(!memcmp(recv_body
, "n", 1), "Entity body didn't match.\n");
847 ret_size
= 0xdeadbeef;
848 ret
= HttpReceiveRequestEntityBody(queue
, req
->RequestId
, 0, recv_body
, 4, &ret_size
, NULL
);
849 ok(!ret
, "Got error %u.\n", ret
);
850 ok(ret_size
== 2, "Got size %lu.\n", ret_size
);
851 ok(!memcmp(recv_body
, "g", 2), "Entity body didn't match.\n");
853 ret_size
= 0xdeadbeef;
854 ret
= HttpReceiveRequestEntityBody(queue
, req
->RequestId
, 0, recv_body
, sizeof(recv_body
), &ret_size
, NULL
);
855 ok(ret
== ERROR_HANDLE_EOF
, "Got error %u.\n", ret
);
856 ok(ret_size
== 0xdeadbeef || !ret_size
/* Win10+ */, "Got size %lu.\n", ret_size
);
858 send_response_v1(queue
, req
->RequestId
, s
);
860 ret
= send(s
, req_text
, strlen(req_text
) + 1, 0);
861 ok(ret
== strlen(req_text
) + 1, "send() returned %d.\n", ret
);
864 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
865 ok(!ret
, "Got error %u.\n", ret
);
867 memset(recv_body
, 0xcc, sizeof(recv_body
));
868 ret_size
= 0xdeadbeef;
869 ret
= HttpReceiveRequestEntityBody(queue
, req
->RequestId
, 0, recv_body
, sizeof(recv_body
), NULL
, &ovl
);
870 ok(!ret
|| ret
== ERROR_IO_PENDING
, "Got error %u.\n", ret
);
871 ret
= GetOverlappedResult(queue
, &ovl
, &ret_size
, TRUE
);
872 ok(ret
, "Got error %lu.\n", GetLastError());
873 ok(ret_size
== 5, "Got size %lu.\n", ret_size
);
874 ok(!memcmp(recv_body
, "ping", 5), "Entity body didn't match.\n");
876 ret
= HttpReceiveRequestEntityBody(queue
, req
->RequestId
, 0, recv_body
, sizeof(recv_body
), NULL
, &ovl
);
877 ok(ret
== ERROR_HANDLE_EOF
, "Got error %u.\n", ret
);
879 send_response_v1(queue
, req
->RequestId
, s
);
881 ret
= send(s
, req_text
, strlen(req_text
) + 1, 0);
882 ok(ret
== strlen(req_text
) + 1, "send() returned %d.\n", ret
);
885 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY
,
886 (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
887 ok(!ret
, "Got error %u.\n", ret
);
889 ret
= HttpReceiveRequestEntityBody(queue
, req
->RequestId
, 0, recv_body
, sizeof(recv_body
), NULL
, &ovl
);
890 ok(ret
== ERROR_HANDLE_EOF
, "Got error %u.\n", ret
);
892 send_response_v1(queue
, req
->RequestId
, s
);
894 sprintf(req_text
, post_req2
, port
);
895 ret
= send(s
, req_text
, strlen(req_text
), 0);
896 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
897 ret
= send(s
, req_body
, sizeof(req_body
), 0);
898 ok(ret
== sizeof(req_body
), "send() returned %d.\n", ret
);
902 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY
,
903 (HTTP_REQUEST
*)req
, 2000, &ret_size
, NULL
);
904 ok(!ret
, "Got error %u.\n", ret
);
905 ok(ret_size
== 2000, "Got size %lu.\n", ret_size
);
906 ok(req
->Flags
== HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS
, "Got flags %#lx.\n", req
->Flags
);
907 chunk_size
= req
->pEntityChunks
[0].FromMemory
.BufferLength
;
909 memset(recv_body
, 0xcc, sizeof(recv_body
));
910 ret_size
= 0xdeadbeef;
911 ret
= HttpReceiveRequestEntityBody(queue
, req
->RequestId
, 0, recv_body
, sizeof(recv_body
), &ret_size
, NULL
);
912 ok(!ret
, "Got error %u.\n", ret
);
913 ok(ret_size
== 2048 - chunk_size
, "Got size %lu.\n", ret_size
);
914 ok(!memcmp(recv_body
, req_body
+ chunk_size
, ret_size
), "Entity body didn't match.\n");
916 send_response_v1(queue
, req
->RequestId
, s
);
918 CloseHandle(ovl
.hEvent
);
919 ret
= remove_url_v1(queue
, port
);
920 ok(!ret
, "Got error %u.\n", ret
);
922 ret
= CloseHandle(queue
);
923 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
926 static void test_v1_bad_request(void)
928 char response_buffer
[2048];
934 ret
= HttpCreateHttpHandle(&queue
, 0);
935 ok(!ret
, "Got error %u.\n", ret
);
936 port
= add_url_v1(queue
);
938 s
= create_client_socket(port
);
939 ret
= send(s
, "foo\r\n", strlen("foo\r\n"), 0);
940 ok(ret
== strlen("foo\r\n"), "send() returned %d.\n", ret
);
942 ret
= recv(s
, response_buffer
, sizeof(response_buffer
), 0);
943 ok(ret
> 0, "recv() failed.\n");
945 if (winetest_debug
> 1)
946 trace("%.*s\n", ret
, response_buffer
);
948 ok(!strncmp(response_buffer
, "HTTP/1.1 400 Bad Request\r\n", 26), "Got incorrect status line.\n");
949 ok(!!strstr(response_buffer
, "\r\nConnection: close\r\n"), "Missing or malformed Connection header.\n");
951 ret
= send(s
, "foo\r\n", strlen("foo\r\n"), 0);
952 ok(ret
== strlen("foo\r\n"), "send() returned %d.\n", ret
);
954 WSASetLastError(0xdeadbeef);
955 ret
= recv(s
, response_buffer
, sizeof(response_buffer
), 0);
956 ok(!ret
, "Connection should be shut down.\n");
957 ok(!WSAGetLastError(), "Got error %u.\n", WSAGetLastError());
959 ret
= remove_url_v1(queue
, port
);
960 ok(!ret
, "Got error %u.\n", ret
);
962 ret
= CloseHandle(queue
);
963 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
966 static void test_v1_cooked_url(void)
968 char DECLSPEC_ALIGN(8) req_buffer
[2048];
969 HTTP_REQUEST_V1
*req
= (HTTP_REQUEST_V1
*)req_buffer
;
970 char expect
[24], req_text
[200];
978 static const char req1
[] =
979 "GET /foobar?a=b HTTP/1.1\r\n"
980 "Host: localhost:%u\r\n"
981 "Connection: keep-alive\r\n"
984 static const char req2
[] =
985 "GET http://localhost:%u/ HTTP/1.1\r\n"
987 "Connection: keep-alive\r\n"
990 ret
= HttpCreateHttpHandle(&queue
, 0);
991 ok(!ret
, "Got error %u.\n", ret
);
992 port
= add_url_v1(queue
);
994 s
= create_client_socket(port
);
995 sprintf(req_text
, req1
, port
);
996 ret
= send(s
, req_text
, strlen(req_text
), 0);
997 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
999 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
1000 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
1001 ok(!ret
, "Got error %u.\n", ret
);
1002 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
1003 ok(req
->RawUrlLength
== 11, "Got raw URL length %u.\n", req
->RawUrlLength
);
1004 ok(!strcmp(req
->pRawUrl
, "/foobar?a=b"), "Got raw URL %s.\n", req
->pRawUrl
);
1005 ok(req
->CookedUrl
.FullUrlLength
== 66, "Got full URL length %u.\n", req
->CookedUrl
.FullUrlLength
);
1006 ok(req
->CookedUrl
.HostLength
== 30, "Got host length %u.\n", req
->CookedUrl
.HostLength
);
1007 ok(req
->CookedUrl
.AbsPathLength
== 14, "Got absolute path length %u.\n", req
->CookedUrl
.AbsPathLength
);
1008 ok(req
->CookedUrl
.QueryStringLength
== 8, "Got query string length %u.\n", req
->CookedUrl
.QueryStringLength
);
1009 swprintf(expectW
, ARRAY_SIZE(expectW
), L
"http://localhost:%u/foobar?a=b", port
);
1010 ok(!wcscmp(req
->CookedUrl
.pFullUrl
, expectW
), "Expected full URL %s, got %s.\n",
1011 debugstr_w(expectW
), debugstr_w(req
->CookedUrl
.pFullUrl
));
1012 ok(req
->CookedUrl
.pHost
== req
->CookedUrl
.pFullUrl
+ 7, "Got host %s.\n", wine_dbgstr_w(req
->CookedUrl
.pHost
));
1013 ok(req
->CookedUrl
.pAbsPath
== req
->CookedUrl
.pFullUrl
+ 22,
1014 "Got absolute path %s.\n", wine_dbgstr_w(req
->CookedUrl
.pAbsPath
));
1015 ok(req
->CookedUrl
.pQueryString
== req
->CookedUrl
.pFullUrl
+ 29,
1016 "Got query string %s.\n", wine_dbgstr_w(req
->CookedUrl
.pQueryString
));
1018 send_response_v1(queue
, req
->RequestId
, s
);
1020 sprintf(req_text
, req2
, port
);
1021 ret
= send(s
, req_text
, strlen(req_text
), 0);
1022 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
1024 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
1025 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
1026 ok(!ret
, "Got error %u.\n", ret
);
1027 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
1028 ok(req
->RawUrlLength
== 23, "Got raw URL length %u.\n", req
->RawUrlLength
);
1029 sprintf(expect
, "http://localhost:%u/", port
);
1030 ok(!strcmp(req
->pRawUrl
, expect
), "Expected raw URL \"%s\", got \"%s\".\n", expect
, req
->pRawUrl
);
1031 ok(req
->CookedUrl
.FullUrlLength
== 46, "Got full URL length %u.\n", req
->CookedUrl
.FullUrlLength
);
1032 ok(req
->CookedUrl
.HostLength
== 30, "Got host length %u.\n", req
->CookedUrl
.HostLength
);
1033 ok(req
->CookedUrl
.AbsPathLength
== 2, "Got absolute path length %u.\n", req
->CookedUrl
.AbsPathLength
);
1034 ok(!req
->CookedUrl
.QueryStringLength
, "Got query string length %u.\n", req
->CookedUrl
.QueryStringLength
);
1035 swprintf(expectW
, ARRAY_SIZE(expectW
), L
"http://localhost:%u/", port
);
1036 ok(!wcscmp(req
->CookedUrl
.pFullUrl
, expectW
), "Expected full URL %s, got %s.\n",
1037 wine_dbgstr_w(expectW
), wine_dbgstr_w(req
->CookedUrl
.pFullUrl
));
1038 ok(req
->CookedUrl
.pHost
== req
->CookedUrl
.pFullUrl
+ 7, "Got host %s.\n", wine_dbgstr_w(req
->CookedUrl
.pHost
));
1039 ok(req
->CookedUrl
.pAbsPath
== req
->CookedUrl
.pFullUrl
+ 22,
1040 "Got absolute path %s.\n", wine_dbgstr_w(req
->CookedUrl
.pAbsPath
));
1041 ok(!req
->CookedUrl
.pQueryString
, "Got query string %s.\n", wine_dbgstr_w(req
->CookedUrl
.pQueryString
));
1043 send_response_v1(queue
, req
->RequestId
, s
);
1045 ret
= remove_url_v1(queue
, port
);
1046 ok(!ret
, "Got error %u.\n", ret
);
1048 ret
= CloseHandle(queue
);
1049 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
1052 static void test_v1_unknown_tokens(void)
1054 char DECLSPEC_ALIGN(8) req_buffer
[2048];
1055 HTTP_REQUEST_V1
*req
= (HTTP_REQUEST_V1
*)req_buffer
;
1056 unsigned short port
;
1063 static const char req1
[] =
1064 "xyzzy / HTTP/1.1\r\n"
1065 "Host: localhost:%u\r\n"
1066 "Connection: keep-alive\r\n"
1070 ret
= HttpCreateHttpHandle(&queue
, 0);
1071 ok(!ret
, "Got error %u.\n", ret
);
1072 port
= add_url_v1(queue
);
1074 s
= create_client_socket(port
);
1075 sprintf(req_text
, req1
, port
);
1076 ret
= send(s
, req_text
, strlen(req_text
), 0);
1077 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
1079 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
1080 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
1081 ok(!ret
, "Got error %u.\n", ret
);
1082 ok(req
->Verb
== HttpVerbUnknown
, "Got verb %u.\n", req
->Verb
);
1083 ok(req
->UnknownVerbLength
== 5, "Got unknown verb length %u.\n", req
->UnknownVerbLength
);
1084 ok(!strcmp(req
->pUnknownVerb
, "xyzzy"), "Got unknown verb %s.\n", req
->pUnknownVerb
);
1085 ok(req
->Headers
.UnknownHeaderCount
== 1, "Got %u unknown headers.\n", req
->Headers
.UnknownHeaderCount
);
1086 ok(req
->Headers
.pUnknownHeaders
[0].NameLength
== 3, "Got name length %u.\n",
1087 req
->Headers
.pUnknownHeaders
[0].NameLength
);
1088 ok(!strcmp(req
->Headers
.pUnknownHeaders
[0].pName
, "Qux"), "Got name %s.\n",
1089 req
->Headers
.pUnknownHeaders
[0].pName
);
1090 ok(req
->Headers
.pUnknownHeaders
[0].RawValueLength
== 7, "Got value length %u.\n",
1091 req
->Headers
.pUnknownHeaders
[0].RawValueLength
);
1092 ok(!strcmp(req
->Headers
.pUnknownHeaders
[0].pRawValue
, "foo baz"), "Got value %s.\n",
1093 req
->Headers
.pUnknownHeaders
[0].pRawValue
);
1095 ret
= remove_url_v1(queue
, port
);
1096 ok(!ret
, "Got error %u.\n", ret
);
1098 ret
= CloseHandle(queue
);
1099 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
1102 static void test_v1_multiple_urls(void)
1104 char DECLSPEC_ALIGN(8) req_buffer
[2048];
1105 HTTP_REQUEST_V1
*req
= (HTTP_REQUEST_V1
*)req_buffer
;
1106 unsigned short ports
[4];
1114 ret
= HttpCreateHttpHandle(&queue
, 0);
1115 ok(!ret
, "Got error %u.\n", ret
);
1117 for (i
= 0; i
< 4; ++i
)
1118 ports
[i
] = add_url_v1(queue
);
1120 for (i
= 0; i
< 4; ++i
)
1122 s
= create_client_socket(ports
[i
]);
1123 sprintf(req_text
, simple_req
, ports
[i
]);
1124 ret
= send(s
, req_text
, strlen(req_text
), 0);
1125 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
1127 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
1128 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
1129 ok(!ret
, "Got error %u.\n", ret
);
1130 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
1132 send_response_v1(queue
, req
->RequestId
, s
);
1136 ret
= CloseHandle(queue
);
1137 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
1140 static void test_v1_relative_urls(void)
1142 char DECLSPEC_ALIGN(8) req_buffer
[2048];
1143 char relative_req
[] =
1144 "GET %s HTTP/1.1\r\n"
1145 "Host: localhost:%u\r\n"
1146 "Connection: keep-alive\r\n"
1147 "User-Agent: WINE\r\n"
1149 HTTP_REQUEST_V1
*req
= (HTTP_REQUEST_V1
*)req_buffer
;
1150 unsigned short port
;
1151 WCHAR url
[50], url2
[50];
1154 HANDLE queue
, queue2
;
1158 ret
= HttpCreateHttpHandle(&queue
, 0);
1159 ok(!ret
, "Got error %u.\n", ret
);
1160 ret
= HttpCreateHttpHandle(&queue2
, 0);
1161 ok(!ret
, "Got error %u.\n", ret
);
1163 port
= add_url_v1(queue
);
1164 swprintf(url2
, ARRAY_SIZE(url2
), L
"http://localhost:%u/foobar/foo/", port
);
1165 ret
= HttpAddUrl(queue2
, url2
, NULL
);
1166 ok(!ret
, "Got error %u.\n", ret
);
1168 s
= create_client_socket(port
);
1169 sprintf(req_text
, simple_req
, port
);
1170 ret
= send(s
, req_text
, strlen(req_text
), 0);
1171 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
1173 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
1174 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
1175 ok(!ret
, "Got error %u.\n", ret
);
1176 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
1178 send_response_v1(queue
, req
->RequestId
, s
);
1180 sprintf(req_text
, relative_req
, "/foobar/foo/bar", port
);
1181 ret
= send(s
, req_text
, strlen(req_text
), 0);
1182 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
1184 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
1185 ret
= HttpReceiveHttpRequest(queue2
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
1186 ok(!ret
, "Got error %u.\n", ret
);
1187 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
1189 send_response_v1(queue2
, req
->RequestId
, s
);
1191 sprintf(req_text
, relative_req
, "/foobar/foo", port
);
1192 ret
= send(s
, req_text
, strlen(req_text
), 0);
1193 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
1195 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
1196 ret
= HttpReceiveHttpRequest(queue2
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
1197 ok(!ret
, "Got error %u.\n", ret
);
1198 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
1200 send_response_v1(queue2
, req
->RequestId
, s
);
1202 sprintf(req_text
, relative_req
, "/foobar/foo?a=b", port
);
1203 ret
= send(s
, req_text
, strlen(req_text
), 0);
1204 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
1206 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
1207 ret
= HttpReceiveHttpRequest(queue2
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
1208 ok(!ret
, "Got error %u.\n", ret
);
1209 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
1211 send_response_v1(queue2
, req
->RequestId
, s
);
1214 remove_url_v1(queue
, port
);
1215 ret
= HttpRemoveUrl(queue2
, url2
);
1216 ok(!ret
, "Got error %u.\n", ret
);
1218 swprintf(url
, ARRAY_SIZE(url
), L
"http://localhost:%u/barfoo/", port
);
1219 ret
= HttpAddUrl(queue
, url
, NULL
);
1220 ok(!ret
, "Got error %u.\n", ret
);
1221 swprintf(url2
, ARRAY_SIZE(url2
), L
"http://localhost:%u/barfoo/", port
);
1222 ret
= HttpAddUrl(queue2
, url2
, NULL
);
1223 ok(ret
== ERROR_ALREADY_EXISTS
, "Got error %u.\n", ret
);
1225 swprintf(url2
, ARRAY_SIZE(url2
), L
"http://localhost:%u/barfoo", port
);
1226 ret
= HttpAddUrl(queue2
, url2
, NULL
);
1227 ok(ret
== ERROR_ALREADY_EXISTS
, "Got error %u.\n", ret
);
1229 swprintf(url2
, ARRAY_SIZE(url2
), L
"http://localhost:%u/barfoo?a=b", port
);
1230 ret
= HttpAddUrl(queue2
, url2
, NULL
);
1231 ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
1233 ret
= CloseHandle(queue
);
1234 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
1235 ret
= CloseHandle(queue2
);
1236 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
1241 static void test_v1_urls(void)
1243 char DECLSPEC_ALIGN(8) req_buffer
[2048];
1244 HTTP_REQUEST_V1
*req
= (HTTP_REQUEST_V1
*)req_buffer
;
1245 unsigned short port
;
1253 ret
= HttpCreateHttpHandle(&queue
, 0);
1254 ok(!ret
, "Got error %u.\n", ret
);
1256 for (port
= 50000; port
< 51000; ++port
)
1258 swprintf(url
, ARRAY_SIZE(url
), L
"http://+:%u/", port
);
1259 if (!(ret
= HttpAddUrl(queue
, url
, NULL
)))
1261 if (ret
== ERROR_ACCESS_DENIED
)
1263 skip("Not enough permissions to bind to all URLs.\n");
1267 ok(ret
== ERROR_SHARING_VIOLATION
, "Failed to add %s, error %u.\n", debugstr_w(url
), ret
);
1270 ok(!ret
, "Got error %u.\n", ret
);
1272 s
= create_client_socket(port
);
1273 sprintf(req_text
, simple_req
, port
);
1274 ret
= send(s
, req_text
, strlen(req_text
), 0);
1275 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
1277 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
1278 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), &ret_size
, NULL
);
1279 ok(!ret
, "Got error %u.\n", ret
);
1280 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
1282 send_response_v1(queue
, req
->RequestId
, s
);
1284 ret
= HttpRemoveUrl(queue
, url
);
1285 ok(!ret
, "Got error %u.\n", ret
);
1287 ret
= CloseHandle(queue
);
1288 ok(ret
, "Failed to close queue handle, error %lu.\n", GetLastError());
1291 static void test_HttpCreateServerSession(void)
1293 HTTP_SERVER_SESSION_ID session
;
1294 HTTPAPI_VERSION version
;
1297 version
.HttpApiMajorVersion
= 1;
1298 version
.HttpApiMinorVersion
= 0;
1299 ret
= pHttpCreateServerSession(version
, NULL
, 0);
1300 ok(ret
== ERROR_INVALID_PARAMETER
, "Unexpected return value %u.\n", ret
);
1302 version
.HttpApiMajorVersion
= 1;
1303 version
.HttpApiMinorVersion
= 1;
1304 ret
= pHttpCreateServerSession(version
, &session
, 0);
1305 ok(ret
== ERROR_REVISION_MISMATCH
, "Unexpected return value %u.\n", ret
);
1307 version
.HttpApiMajorVersion
= 3;
1308 version
.HttpApiMinorVersion
= 0;
1309 ret
= pHttpCreateServerSession(version
, &session
, 0);
1310 ok(ret
== ERROR_REVISION_MISMATCH
, "Unexpected return value %u.\n", ret
);
1312 version
.HttpApiMajorVersion
= 2;
1313 version
.HttpApiMinorVersion
= 0;
1314 ret
= pHttpCreateServerSession(version
, &session
, 0);
1315 ok(!ret
, "Unexpected return value %u.\n", ret
);
1316 ret
= pHttpCloseServerSession(session
);
1317 ok(!ret
, "Unexpected return value %u.\n", ret
);
1319 version
.HttpApiMajorVersion
= 1;
1320 version
.HttpApiMinorVersion
= 0;
1321 ret
= pHttpCreateServerSession(version
, &session
, 0);
1322 ok(!ret
, "Unexpected return value %u.\n", ret
);
1323 ret
= pHttpCloseServerSession(session
);
1324 ok(!ret
, "Unexpected return value %u.\n", ret
);
1326 ret
= pHttpCloseServerSession(0xdead);
1327 ok(ret
== ERROR_INVALID_PARAMETER
, "Unexpected return value %u.\n", ret
);
1330 static void test_HttpCreateUrlGroup(void)
1332 HTTP_SERVER_SESSION_ID session
;
1333 HTTP_URL_GROUP_ID group_id
;
1334 HTTPAPI_VERSION version
;
1338 ret
= pHttpCreateUrlGroup(0, &group_id
, 0);
1339 ok(ret
== ERROR_INVALID_PARAMETER
, "Unexpected return value %u.\n", ret
);
1340 ok(group_id
== 1, "Unexpected group id %s.\n", wine_dbgstr_longlong(group_id
));
1342 /* Create session, url group, close session. */
1343 version
.HttpApiMajorVersion
= 1;
1344 version
.HttpApiMinorVersion
= 0;
1345 ret
= pHttpCreateServerSession(version
, &session
, 0);
1346 ok(!ret
, "Unexpected return value %u.\n", ret
);
1349 ret
= pHttpCreateUrlGroup(session
, &group_id
, 0);
1350 ok(!ret
, "Unexpected return value %u.\n", ret
);
1351 ok(group_id
!= 0, "Unexpected group id %s.\n", wine_dbgstr_longlong(group_id
));
1353 ret
= pHttpCloseServerSession(session
);
1354 ok(!ret
, "Unexpected return value %u.\n", ret
);
1356 /* Groups are closed together with their session. */
1357 ret
= pHttpCloseUrlGroup(group_id
);
1358 ok(ret
== ERROR_INVALID_PARAMETER
, "Unexpected return value %u.\n", ret
);
1360 /* Create session, url group, close group. */
1361 ret
= pHttpCreateServerSession(version
, &session
, 0);
1362 ok(!ret
, "Unexpected return value %u.\n", ret
);
1365 ret
= pHttpCreateUrlGroup(session
, &group_id
, 0);
1366 ok(!ret
, "Unexpected return value %u.\n", ret
);
1367 ok(group_id
!= 0, "Unexpected group id %s.\n", wine_dbgstr_longlong(group_id
));
1369 ret
= pHttpCloseUrlGroup(group_id
);
1370 ok(!ret
, "Unexpected return value %u.\n", ret
);
1372 ret
= pHttpCloseServerSession(session
);
1373 ok(!ret
, "Unexpected return value %u.\n", ret
);
1376 static void test_v2_bound_port(void)
1378 static const HTTPAPI_VERSION version
= {2, 0};
1379 struct sockaddr_in sockaddr
;
1380 HTTP_SERVER_SESSION_ID session
;
1381 HTTP_BINDING_INFO binding
;
1382 HTTP_URL_GROUP_ID group
;
1383 unsigned short port
;
1385 HANDLE queue
, dummy_queue
;
1389 ret
= pHttpCreateServerSession(version
, &session
, 0);
1390 ok(!ret
, "Failed to create session, error %u.\n", ret
);
1391 ret
= pHttpCreateUrlGroup(session
, &group
, 0);
1392 ok(!ret
, "Failed to create URL group, error %u.\n", ret
);
1394 ret
= pHttpCreateRequestQueue(version
, NULL
, NULL
, 0, &queue
);
1395 ok(!ret
, "Failed to create request queue, error %u.\n", ret
);
1396 ret
= pHttpCreateRequestQueue(version
, NULL
, NULL
, 0, &dummy_queue
);
1397 ok(!ret
, "Failed to create request queue, error %u.\n", ret
);
1398 binding
.Flags
.Present
= 1;
1399 binding
.RequestQueueHandle
= queue
;
1400 ret
= pHttpSetUrlGroupProperty(group
, HttpServerBindingProperty
, &binding
, sizeof(binding
));
1401 ok(!ret
, "Failed to bind request queue, error %u.\n", ret
);
1403 s2
= socket(AF_INET
, SOCK_STREAM
, 0);
1404 sockaddr
.sin_family
= AF_INET
;
1405 sockaddr
.sin_addr
.S_un
.S_addr
= inet_addr("127.0.0.1");
1406 for (port
= 50000; port
< 51000; ++port
)
1408 sockaddr
.sin_port
= htons(port
);
1409 ret
= bind(s2
, (struct sockaddr
*)&sockaddr
, sizeof(sockaddr
));
1413 ok(!ret
, "Failed to bind to port\n");
1414 swprintf(url
, ARRAY_SIZE(url
), L
"http://localhost:%u/", port
);
1415 ret
= pHttpAddUrlToUrlGroup(group
, url
, 0xdeadbeef, 0);
1416 ok(ret
== ERROR_SHARING_VIOLATION
, "Unexpected failure adding %s, error %u.\n", debugstr_w(url
), ret
);
1417 shutdown(s2
, SD_BOTH
);
1420 binding
.RequestQueueHandle
= dummy_queue
;
1421 ret
= pHttpSetUrlGroupProperty(group
, HttpServerBindingProperty
, &binding
, sizeof(binding
));
1422 ok(!ret
, "Failed to rebind request queue, error %u.\n", ret
);
1424 ret
= pHttpCloseRequestQueue(dummy_queue
);
1425 ok(!ret
, "Failed to close queue handle, error %u.\n", ret
);
1426 ret
= pHttpCloseRequestQueue(queue
);
1427 ok(!ret
, "Failed to close queue handle, error %u.\n", ret
);
1428 ret
= pHttpCloseUrlGroup(group
);
1429 ok(!ret
, "Failed to close group, error %u.\n", ret
);
1430 ret
= pHttpCloseServerSession(session
);
1431 ok(!ret
, "Failed to close group, error %u.\n", ret
);
1434 static void test_v2_queue_after_url(void)
1436 char DECLSPEC_ALIGN(8) req_buffer
[2048];
1437 HTTP_REQUEST_V2
*reqv2
= (HTTP_REQUEST_V2
*)req_buffer
;
1438 static const HTTPAPI_VERSION version
= {2, 0};
1439 HTTP_REQUEST_V1
*req
= &reqv2
->s
;
1440 HTTP_SERVER_SESSION_ID session
;
1441 HTTP_BINDING_INFO binding
;
1442 HTTP_URL_GROUP_ID group
;
1443 unsigned short port
;
1449 ret
= pHttpCreateServerSession(version
, &session
, 0);
1450 ok(!ret
, "Failed to create session, error %u.\n", ret
);
1451 ret
= pHttpCreateUrlGroup(session
, &group
, 0);
1452 ok(!ret
, "Failed to create URL group, error %u.\n", ret
);
1454 port
= add_url_v2(group
);
1456 ret
= pHttpCreateRequestQueue(version
, NULL
, NULL
, 0, &queue
);
1457 ok(!ret
, "Failed to create request queue, error %u.\n", ret
);
1458 binding
.Flags
.Present
= 1;
1459 binding
.RequestQueueHandle
= queue
;
1460 ret
= pHttpSetUrlGroupProperty(group
, HttpServerBindingProperty
, &binding
, sizeof(binding
));
1461 ok(!ret
, "Failed to bind request queue, error %u.\n", ret
);
1463 s
= create_client_socket(port
);
1465 sprintf(req_text
, simple_req
, port
);
1466 ret
= send(s
, req_text
, strlen(req_text
), 0);
1467 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
1469 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), NULL
, NULL
);
1470 ok(!ret
, "Got error %u.\n", ret
);
1472 ok(req
->BytesReceived
== strlen(req_text
), "Got %s bytes.\n", wine_dbgstr_longlong(req
->BytesReceived
));
1474 ret
= remove_url_v2(group
, port
);
1475 ok(!ret
, "Got error %u.\n", ret
);
1478 ret
= pHttpCloseRequestQueue(queue
);
1479 ok(!ret
, "Failed to close queue handle, error %u.\n", ret
);
1480 ret
= pHttpCloseUrlGroup(group
);
1481 ok(!ret
, "Failed to close group, error %u.\n", ret
);
1482 ret
= pHttpCloseServerSession(session
);
1483 ok(!ret
, "Failed to close group, error %u.\n", ret
);
1486 static void test_v2_server(void)
1488 char DECLSPEC_ALIGN(8) req_buffer
[2048], response_buffer
[2048];
1489 HTTP_REQUEST_V2
*reqv2
= (HTTP_REQUEST_V2
*)req_buffer
;
1490 static const HTTPAPI_VERSION version
= {2, 0};
1491 struct sockaddr_in sockaddr
, *sin
;
1492 HTTP_REQUEST_V1
*req
= &reqv2
->s
;
1493 HTTP_SERVER_SESSION_ID session
;
1494 HTTP_RESPONSE_V2 response
= {};
1495 HTTP_BINDING_INFO binding
;
1496 HTTP_URL_GROUP_ID group
;
1497 unsigned short port
;
1507 ovl
.hEvent
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
1508 memset(req_buffer
, 0xcc, sizeof(req_buffer
));
1510 ret
= pHttpCreateServerSession(version
, &session
, 0);
1511 ok(!ret
, "Failed to create session, error %u.\n", ret
);
1512 ret
= pHttpCreateUrlGroup(session
, &group
, 0);
1513 ok(!ret
, "Failed to create URL group, error %u.\n", ret
);
1514 ret
= pHttpCreateRequestQueue(version
, NULL
, NULL
, 0, &queue
);
1515 ok(!ret
, "Failed to create request queue, error %u.\n", ret
);
1516 binding
.Flags
.Present
= 1;
1517 binding
.RequestQueueHandle
= queue
;
1518 ret
= pHttpSetUrlGroupProperty(group
, HttpServerBindingProperty
, &binding
, sizeof(binding
));
1519 ok(!ret
, "Failed to bind request queue, error %u.\n", ret
);
1521 ret
= HttpReceiveHttpRequest(NULL
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), NULL
, &ovl
);
1522 ok(ret
== ERROR_INVALID_HANDLE
, "Got error %u.\n", ret
);
1523 ret
= HttpReceiveHttpRequest(queue
, 0xdeadbeef, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), NULL
, &ovl
);
1524 ok(ret
== ERROR_CONNECTION_INVALID
, "Got error %u.\n", ret
);
1525 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), NULL
, &ovl
);
1526 ok(ret
== ERROR_IO_PENDING
, "Got error %u.\n", ret
);
1528 SetLastError(0xdeadbeef);
1529 ret
= GetOverlappedResult(queue
, &ovl
, &ret_size
, FALSE
);
1530 ok(!ret
, "Expected failure.\n");
1531 ok(GetLastError() == ERROR_IO_INCOMPLETE
, "Got error %lu.\n", GetLastError());
1533 port
= add_url_v2(group
);
1535 ret
= pHttpAddUrlToUrlGroup(group
, L
"http://localhost:50000", 0xdeadbeef, 0);
1536 todo_wine
ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
1537 ret
= pHttpAddUrlToUrlGroup(group
, L
"localhost:50000", 0xdeadbeef, 0);
1538 todo_wine
ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
1539 ret
= pHttpAddUrlToUrlGroup(group
, L
"localhost:50000/", 0xdeadbeef, 0);
1540 todo_wine
ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
1541 ret
= pHttpAddUrlToUrlGroup(group
, L
"http://localhost/", 0xdeadbeef, 0);
1542 todo_wine
ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
1543 ret
= pHttpAddUrlToUrlGroup(group
, L
"http://localhost:/", 0xdeadbeef, 0);
1544 todo_wine
ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
1545 ret
= pHttpAddUrlToUrlGroup(group
, L
"http://localhost:0/", 0xdeadbeef, 0);
1546 todo_wine
ok(ret
== ERROR_INVALID_PARAMETER
, "Got error %u.\n", ret
);
1547 swprintf(url
, ARRAY_SIZE(url
), L
"http://localhost:%u/", port
);
1548 ret
= pHttpAddUrlToUrlGroup(group
, url
, 0xdeadbeef, 0);
1549 todo_wine
ok(ret
== ERROR_ALREADY_EXISTS
, "Got error %u.\n", ret
);
1551 s
= create_client_socket(port
);
1552 len
= sizeof(sockaddr
);
1553 ret
= getsockname(s
, (struct sockaddr
*)&sockaddr
, &len
);
1554 ok(ret
== 0, "getsockname() failed, error %u.\n", WSAGetLastError());
1556 SetLastError(0xdeadbeef);
1557 ret
= GetOverlappedResult(queue
, &ovl
, &ret_size
, FALSE
);
1558 ok(!ret
, "Expected failure.\n");
1559 ok(GetLastError() == ERROR_IO_INCOMPLETE
, "Got error %lu.\n", GetLastError());
1561 sprintf(req_text
, simple_req
, port
);
1562 ret
= send(s
, req_text
, strlen(req_text
), 0);
1563 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
1565 ret
= WaitForSingleObject(ovl
.hEvent
, 100);
1566 ok(!ret
, "Got %u.\n", ret
);
1570 ret
= GetOverlappedResult(queue
, &ovl
, &ret_size
, TRUE
);
1571 ok(ret
, "Got error %lu.\n", GetLastError());
1572 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
1574 ok(!req
->Flags
, "Got flags %#lx.\n", req
->Flags
);
1575 ok(req
->ConnectionId
, "Expected nonzero connection ID.\n");
1576 ok(req
->RequestId
, "Expected nonzero connection ID.\n");
1577 ok(req
->UrlContext
== 0xdeadbeef, "Got URL context %s.\n", wine_dbgstr_longlong(req
->UrlContext
));
1578 ok(req
->Version
.MajorVersion
== 1, "Got major version %u.\n", req
->Version
.MajorVersion
);
1579 ok(req
->Version
.MinorVersion
== 1, "Got major version %u.\n", req
->Version
.MinorVersion
);
1580 ok(req
->Verb
== HttpVerbGET
, "Got verb %u.\n", req
->Verb
);
1581 ok(!req
->UnknownVerbLength
, "Got unknown verb length %u.\n", req
->UnknownVerbLength
);
1582 ok(req
->RawUrlLength
== 7, "Got raw URL length %u.\n", req
->RawUrlLength
);
1583 ok(!req
->pUnknownVerb
, "Got unknown verb %s.\n", req
->pUnknownVerb
);
1584 ok(!strcmp(req
->pRawUrl
, "/foobar"), "Got raw URL %s.\n", req
->pRawUrl
);
1585 ok(req
->CookedUrl
.FullUrlLength
== 58, "Got full URL length %u.\n", req
->CookedUrl
.FullUrlLength
);
1586 ok(req
->CookedUrl
.HostLength
== 30, "Got host length %u.\n", req
->CookedUrl
.HostLength
);
1587 ok(req
->CookedUrl
.AbsPathLength
== 14, "Got absolute path length %u.\n", req
->CookedUrl
.AbsPathLength
);
1588 ok(!req
->CookedUrl
.QueryStringLength
, "Got query string length %u.\n", req
->CookedUrl
.QueryStringLength
);
1589 swprintf(url
, ARRAY_SIZE(url
), L
"http://localhost:%u/foobar", port
);
1590 ok(!wcscmp(req
->CookedUrl
.pFullUrl
, url
), "Expected full URL %s, got %s.\n",
1591 debugstr_w(url
), debugstr_w(req
->CookedUrl
.pFullUrl
));
1592 ok(req
->CookedUrl
.pHost
== req
->CookedUrl
.pFullUrl
+ 7, "Got host %s.\n", wine_dbgstr_w(req
->CookedUrl
.pHost
));
1593 ok(req
->CookedUrl
.pAbsPath
== req
->CookedUrl
.pFullUrl
+ 22,
1594 "Got absolute path %s.\n", wine_dbgstr_w(req
->CookedUrl
.pAbsPath
));
1595 ok(!req
->CookedUrl
.pQueryString
, "Got query string %s.\n", wine_dbgstr_w(req
->CookedUrl
.pQueryString
));
1596 ok(!memcmp(req
->Address
.pRemoteAddress
, &sockaddr
, len
), "Client addresses didn't match.\n");
1597 sin
= (SOCKADDR_IN
*)req
->Address
.pLocalAddress
;
1598 ok(sin
->sin_family
== AF_INET
, "Got family %u.\n", sin
->sin_family
);
1599 ok(ntohs(sin
->sin_port
) == port
, "Got wrong port %u.\n", ntohs(sin
->sin_port
));
1600 ok(sin
->sin_addr
.S_un
.S_addr
== inet_addr("127.0.0.1"), "Got address %08lx.\n", sin
->sin_addr
.S_un
.S_addr
);
1601 ok(!req
->Headers
.UnknownHeaderCount
, "Got %u unknown headers.\n", req
->Headers
.UnknownHeaderCount
);
1602 ok(!req
->Headers
.pUnknownHeaders
, "Got unknown headers %p.\n", req
->Headers
.pUnknownHeaders
);
1603 for (i
= 0; i
< ARRAY_SIZE(req
->Headers
.KnownHeaders
); ++i
)
1605 if (i
== HttpHeaderConnection
)
1607 ok(req
->Headers
.KnownHeaders
[i
].RawValueLength
== 10, "Got length %u.\n",
1608 req
->Headers
.KnownHeaders
[i
].RawValueLength
);
1609 ok(!strcmp(req
->Headers
.KnownHeaders
[i
].pRawValue
, "keep-alive"),
1610 "Got connection '%s'.\n", req
->Headers
.KnownHeaders
[i
].pRawValue
);
1612 else if (i
== HttpHeaderHost
)
1615 sprintf(expect
, "localhost:%u", port
);
1616 ok(req
->Headers
.KnownHeaders
[i
].RawValueLength
== strlen(expect
), "Got length %u.\n",
1617 req
->Headers
.KnownHeaders
[i
].RawValueLength
);
1618 ok(!strcmp(req
->Headers
.KnownHeaders
[i
].pRawValue
, expect
),
1619 "Got connection '%s'.\n", req
->Headers
.KnownHeaders
[i
].pRawValue
);
1621 else if (i
== HttpHeaderUserAgent
)
1623 ok(req
->Headers
.KnownHeaders
[i
].RawValueLength
== 4, "Got length %u.\n",
1624 req
->Headers
.KnownHeaders
[i
].RawValueLength
);
1625 ok(!strcmp(req
->Headers
.KnownHeaders
[i
].pRawValue
, "WINE"),
1626 "Got connection '%s'.\n", req
->Headers
.KnownHeaders
[i
].pRawValue
);
1630 ok(!req
->Headers
.KnownHeaders
[i
].RawValueLength
, "Header %#x: got length %u.\n",
1631 i
, req
->Headers
.KnownHeaders
[i
].RawValueLength
);
1632 ok(!req
->Headers
.KnownHeaders
[i
].pRawValue
, "Header %#x: got value '%s'.\n",
1633 i
, req
->Headers
.KnownHeaders
[i
].pRawValue
);
1636 ok(req
->BytesReceived
== strlen(req_text
), "Got %s bytes.\n", wine_dbgstr_longlong(req
->BytesReceived
));
1637 ok(!req
->EntityChunkCount
, "Got %u entity chunks.\n", req
->EntityChunkCount
);
1638 ok(!req
->pEntityChunks
, "Got entity chunks %p.\n", req
->pEntityChunks
);
1639 /* req->RawConnectionId is zero until Win10 2004. */
1640 ok(!req
->pSslInfo
, "Got SSL info %p.\n", req
->pSslInfo
);
1641 /* RequestInfoCount and pRequestInfo are zero until Win10 1909. */
1643 response
.s
.StatusCode
= 418;
1644 response
.s
.pReason
= "I'm a teapot";
1645 response
.s
.ReasonLength
= 12;
1646 response
.s
.Headers
.KnownHeaders
[HttpHeaderRetryAfter
].pRawValue
= "120";
1647 response
.s
.Headers
.KnownHeaders
[HttpHeaderRetryAfter
].RawValueLength
= 3;
1648 ret
= HttpSendHttpResponse(queue
, 0xdeadbeef, 0, (HTTP_RESPONSE
*)&response
, NULL
, NULL
, NULL
, 0, &ovl
, NULL
);
1649 ok(ret
== ERROR_CONNECTION_INVALID
, "Got error %u.\n", ret
);
1650 ret
= HttpSendHttpResponse(queue
, req
->RequestId
, 0, (HTTP_RESPONSE
*)&response
, NULL
, NULL
, NULL
, 0, &ovl
, NULL
);
1651 ok(!ret
, "Got error %u.\n", ret
);
1652 ret
= GetOverlappedResult(queue
, &ovl
, &ret_size
, FALSE
);
1653 ok(ret
, "Got error %lu.\n", GetLastError());
1655 ret
= recv(s
, response_buffer
, sizeof(response_buffer
), 0);
1656 ok(ret
== ret_size
, "Expected size %lu, got %u.\n", ret_size
, ret
);
1658 if (winetest_debug
> 1)
1659 trace("%.*s\n", ret
, response_buffer
);
1661 ok(!strncmp(response_buffer
, "HTTP/1.1 418 I'm a teapot\r\n", 27), "Got incorrect status line.\n");
1662 ok(!!strstr(response_buffer
, "\r\nRetry-After: 120\r\n"), "Missing or malformed Retry-After header.\n");
1663 ok(!!strstr(response_buffer
, "\r\nDate:"), "Missing Date header.\n");
1665 ret
= HttpReceiveHttpRequest(queue
, req
->RequestId
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), NULL
, &ovl
);
1666 ok(ret
== ERROR_CONNECTION_INVALID
, "Got error %u.\n", ret
);
1668 ret
= remove_url_v2(group
, port
);
1669 ok(!ret
, "Got error %u.\n", ret
);
1670 ret
= remove_url_v2(group
, port
);
1671 ok(ret
== ERROR_FILE_NOT_FOUND
, "Got error %u.\n", ret
);
1674 CloseHandle(ovl
.hEvent
);
1675 ret
= pHttpCloseRequestQueue(queue
);
1676 ok(!ret
, "Failed to close queue handle, error %u.\n", ret
);
1677 ret
= pHttpCloseUrlGroup(group
);
1678 ok(!ret
, "Failed to close group, error %u.\n", ret
);
1679 ret
= pHttpCloseServerSession(session
);
1680 ok(!ret
, "Failed to close group, error %u.\n", ret
);
1683 static void test_v2_completion_port(void)
1685 char DECLSPEC_ALIGN(8) req_buffer
[2048], response_buffer
[2048];
1686 HTTP_REQUEST_V2
*req
= (HTTP_REQUEST_V2
*)req_buffer
;
1687 static const HTTPAPI_VERSION version
= {2, 0};
1688 HTTP_SERVER_SESSION_ID session
;
1689 HTTP_RESPONSE_V2 response
= {};
1690 HTTP_BINDING_INFO binding
;
1691 HTTP_URL_GROUP_ID group
;
1692 unsigned short tcp_port
;
1693 OVERLAPPED ovl
, *povl
;
1701 ovl
.hEvent
= CreateEventA(NULL
, TRUE
, FALSE
, NULL
);
1703 ret
= pHttpCreateServerSession(version
, &session
, 0);
1704 ok(!ret
, "Failed to create session, error %u.\n", ret
);
1705 ret
= pHttpCreateUrlGroup(session
, &group
, 0);
1706 ok(!ret
, "Failed to create URL group, error %u.\n", ret
);
1707 ret
= pHttpCreateRequestQueue(version
, NULL
, NULL
, 0, &queue
);
1708 ok(!ret
, "Failed to create request queue, error %u.\n", ret
);
1709 binding
.Flags
.Present
= 1;
1710 binding
.RequestQueueHandle
= queue
;
1711 ret
= pHttpSetUrlGroupProperty(group
, HttpServerBindingProperty
, &binding
, sizeof(binding
));
1712 ok(!ret
, "Failed to bind request queue, error %u.\n", ret
);
1714 port
= CreateIoCompletionPort(queue
, NULL
, 123, 0);
1715 ok(!!port
, "Failed to create completion port, error %lu.\n", GetLastError());
1717 ret
= GetQueuedCompletionStatus(port
, &ret_size
, &key
, &povl
, 0);
1718 ok(!ret
, "Expected failure.\n");
1719 ok(GetLastError() == WAIT_TIMEOUT
, "Got error %lu.\n", GetLastError());
1721 ret
= HttpReceiveHttpRequest(queue
, HTTP_NULL_ID
, 0, (HTTP_REQUEST
*)req
, sizeof(req_buffer
), NULL
, &ovl
);
1722 ok(ret
== ERROR_IO_PENDING
, "Got error %u.\n", ret
);
1724 tcp_port
= add_url_v2(group
);
1725 s
= create_client_socket(tcp_port
);
1727 ret
= GetQueuedCompletionStatus(port
, &ret_size
, &key
, &povl
, 0);
1728 ok(!ret
, "Expected failure.\n");
1729 ok(GetLastError() == WAIT_TIMEOUT
, "Got error %lu.\n", GetLastError());
1731 sprintf(req_text
, simple_req
, tcp_port
);
1732 ret
= send(s
, req_text
, strlen(req_text
), 0);
1733 ok(ret
== strlen(req_text
), "send() returned %d.\n", ret
);
1735 ret_size
= key
= 0xdeadbeef;
1736 ret
= GetQueuedCompletionStatus(port
, &ret_size
, &key
, &povl
, 1000);
1737 ok(ret
, "Got error %lu.\n", GetLastError());
1738 ok(povl
== &ovl
, "OVERLAPPED pointers didn't match.\n");
1739 ok(key
== 123, "Got unexpected key %Iu.\n", key
);
1740 ok(ret_size
> sizeof(*req
), "Got size %lu.\n", ret_size
);
1742 ret
= GetQueuedCompletionStatus(port
, &ret_size
, &key
, &povl
, 0);
1743 ok(!ret
, "Expected failure.\n");
1744 ok(GetLastError() == WAIT_TIMEOUT
, "Got error %lu.\n", GetLastError());
1746 response
.s
.StatusCode
= 418;
1747 response
.s
.pReason
= "I'm a teapot";
1748 response
.s
.ReasonLength
= 12;
1749 ret
= HttpSendHttpResponse(queue
, req
->s
.RequestId
, 0, (HTTP_RESPONSE
*)&response
, NULL
, NULL
, NULL
, 0, &ovl
, NULL
);
1750 ok(!ret
, "Got error %u.\n", ret
);
1752 ret_size
= key
= 0xdeadbeef;
1753 ret
= GetQueuedCompletionStatus(port
, &ret_size
, &key
, &povl
, 1000);
1754 ok(ret
, "Got error %lu.\n", GetLastError());
1755 ok(povl
== &ovl
, "OVERLAPPED pointers didn't match.\n");
1756 ok(key
== 123, "Got unexpected key %Iu.\n", key
);
1758 ret
= recv(s
, response_buffer
, sizeof(response_buffer
), 0);
1759 ok(ret
== ret_size
, "Expected size %lu, got %u.\n", ret_size
, ret
);
1761 ret
= remove_url_v2(group
, tcp_port
);
1762 ok(!ret
, "Got error %u.\n", ret
);
1765 CloseHandle(ovl
.hEvent
);
1766 ret
= pHttpCloseRequestQueue(queue
);
1767 ok(!ret
, "Failed to close queue handle, error %u.\n", ret
);
1768 ret
= pHttpCloseUrlGroup(group
);
1769 ok(!ret
, "Failed to close group, error %u.\n", ret
);
1770 ret
= pHttpCloseServerSession(session
);
1771 ok(!ret
, "Failed to close group, error %u.\n", ret
);
1776 HTTPAPI_VERSION version
= { 1, 0 };
1782 WSAStartup(MAKEWORD(1,1), &wsadata
);
1784 ret
= HttpInitialize(version
, HTTP_INITIALIZE_SERVER
, NULL
);
1785 ok(!ret
, "Failed to initialize library, ret %u.\n", ret
);
1788 test_v1_completion_port();
1789 test_v1_multiple_requests();
1790 test_v1_short_buffer();
1791 test_v1_entity_body();
1792 test_v1_bad_request();
1793 test_v1_cooked_url();
1794 test_v1_unknown_tokens();
1795 test_v1_multiple_urls();
1796 test_v1_relative_urls();
1799 ret
= HttpTerminate(HTTP_INITIALIZE_SERVER
, NULL
);
1800 ok(!ret
, "Failed to terminate, ret %u.\n", ret
);
1802 version
.HttpApiMajorVersion
= 2;
1803 if (!HttpInitialize(version
, HTTP_INITIALIZE_SERVER
, NULL
))
1805 test_HttpCreateServerSession();
1806 test_HttpCreateUrlGroup();
1808 test_v2_queue_after_url();
1809 test_v2_bound_port();
1810 test_v2_completion_port();
1812 ret
= HttpTerminate(HTTP_INITIALIZE_SERVER
, NULL
);
1813 ok(!ret
, "Failed to terminate, ret %u.\n", ret
);
1816 win_skip("Version 2 is not supported.\n");