httpapi/tests: Do not test that a local TCP port is closed.
[wine.git] / dlls / httpapi / tests / httpapi.c
blob9db21635d3a0f1ba08359ee04309682943db7e73
1 /*
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
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <wchar.h>
26 #include "ntstatus.h"
27 #define WIN32_NO_STATUS
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winnt.h"
31 #include "winternl.h"
32 #include "http.h"
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);
57 X(HttpCloseUrlGroup);
58 X(HttpRemoveUrlFromUrlGroup);
59 X(HttpSetUrlGroupProperty);
60 #undef X
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"
68 "\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());
81 return s;
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];
89 int ret;
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)
102 unsigned short port;
103 WCHAR url[50];
104 int ret;
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)))
110 return port;
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);
114 return 0;
117 static unsigned short add_url_v2(HTTP_URL_GROUP_ID group)
119 unsigned short port;
120 WCHAR url[50];
121 int ret;
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)))
127 return port;
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);
131 return 0;
134 static ULONG remove_url_v1(HANDLE queue, unsigned short port)
136 WCHAR url[50];
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)
143 WCHAR url[50];
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;
155 unsigned short port;
156 char req_text[200];
157 unsigned int i;
158 OVERLAPPED ovl;
159 DWORD ret_size;
160 WCHAR url[50];
161 int ret, len;
162 SOCKET s;
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. */
171 queue = NULL;
172 ret = HttpCreateHttpHandle(&queue, 0);
173 ok(!ret, "Unexpected ret value %u.\n", ret);
174 ok(!!queue, "Unexpected handle value %p.\n", queue);
176 queue2 = NULL;
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
235 * around this. */
236 Sleep(100);
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)
281 char expect[16];
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);
295 else
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);
359 closesocket(s);
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;
375 HANDLE queue, port;
376 char req_text[200];
377 DWORD ret_size;
378 ULONG_PTR key;
379 SOCKET s;
380 int ret;
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);
436 closesocket(s);
437 CloseHandle(port);
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;
451 unsigned short port;
452 char req_text[200];
453 DWORD ret_size;
454 SOCKET s1, s2;
455 HANDLE queue;
456 int ret, len;
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);
541 closesocket(s1);
542 closesocket(s2);
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;
555 unsigned short port;
556 char req_text[200];
557 OVERLAPPED ovl;
558 DWORD ret_size;
559 HANDLE queue;
560 SOCKET s;
561 int ret;
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));
576 ret_size = 1;
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);
581 ret_size = 1;
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));
614 CancelIo(queue);
616 ret = remove_url_v1(queue, port);
617 ok(!ret, "Got error %u.\n", ret);
618 closesocket(s);
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] = {};
630 unsigned short port;
631 int ret, chunk_size;
632 char req_text[200];
633 unsigned int i;
634 OVERLAPPED ovl;
635 DWORD ret_size;
636 HANDLE queue;
637 SOCKET s;
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"
644 "\r\n"
645 "ping";
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"
652 "\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. */
672 Sleep(100);
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);
745 Sleep(100);
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);
773 Sleep(100);
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);
805 Sleep(100);
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);
831 Sleep(100);
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);
862 Sleep(100);
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);
883 Sleep(100);
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);
900 Sleep(100);
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);
921 closesocket(s);
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];
929 unsigned short port;
930 HANDLE queue;
931 SOCKET s;
932 int ret;
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);
961 closesocket(s);
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];
971 unsigned short port;
972 WCHAR expectW[50];
973 DWORD ret_size;
974 HANDLE queue;
975 SOCKET s;
976 int ret;
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"
982 "\r\n";
984 static const char req2[] =
985 "GET http://localhost:%u/ HTTP/1.1\r\n"
986 "Host: ignored\r\n"
987 "Connection: keep-alive\r\n"
988 "\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);
1047 closesocket(s);
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;
1057 char req_text[200];
1058 DWORD ret_size;
1059 HANDLE queue;
1060 SOCKET s;
1061 int ret;
1063 static const char req1[] =
1064 "xyzzy / HTTP/1.1\r\n"
1065 "Host: localhost:%u\r\n"
1066 "Connection: keep-alive\r\n"
1067 "Qux: foo baz \r\n"
1068 "\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);
1097 closesocket(s);
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];
1107 char req_text[200];
1108 DWORD ret_size;
1109 HANDLE queue;
1110 SOCKET s;
1111 unsigned int i;
1112 int ret;
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);
1133 closesocket(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"
1148 "\r\n";
1149 HTTP_REQUEST_V1 *req = (HTTP_REQUEST_V1 *)req_buffer;
1150 unsigned short port;
1151 WCHAR url[50], url2[50];
1152 char req_text[200];
1153 DWORD ret_size;
1154 HANDLE queue, queue2;
1155 SOCKET s;
1156 int ret;
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);
1213 closesocket(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());
1238 return;
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;
1246 char req_text[200];
1247 DWORD ret_size;
1248 WCHAR url[50];
1249 HANDLE queue;
1250 SOCKET s;
1251 int ret;
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)))
1260 break;
1261 if (ret == ERROR_ACCESS_DENIED)
1263 skip("Not enough permissions to bind to all URLs.\n");
1264 CloseHandle(queue);
1265 return;
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);
1286 closesocket(s);
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;
1295 int ret;
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;
1335 int ret;
1337 group_id = 1;
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);
1348 group_id = 0;
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);
1364 group_id = 0;
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;
1384 WCHAR url[50];
1385 HANDLE queue, dummy_queue;
1386 int ret;
1387 SOCKET s2;
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));
1410 if (!ret)
1411 break;
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);
1418 closesocket(s2);
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;
1444 char req_text[100];
1445 HANDLE queue;
1446 int ret;
1447 SOCKET s;
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);
1477 closesocket(s);
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;
1498 char req_text[100];
1499 unsigned int i;
1500 OVERLAPPED ovl;
1501 DWORD ret_size;
1502 WCHAR url[50];
1503 HANDLE queue;
1504 int ret, len;
1505 SOCKET s;
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);
1568 Sleep(100);
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)
1614 char expect[16];
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);
1628 else
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);
1673 closesocket(s);
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;
1694 HANDLE queue, port;
1695 char req_text[100];
1696 DWORD ret_size;
1697 ULONG_PTR key;
1698 SOCKET s;
1699 int ret;
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);
1763 closesocket(s);
1764 CloseHandle(port);
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);
1774 START_TEST(httpapi)
1776 HTTPAPI_VERSION version = { 1, 0 };
1777 WSADATA wsadata;
1778 int ret;
1780 init();
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);
1787 test_v1_server();
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();
1797 test_v1_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();
1807 test_v2_server();
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);
1815 else
1816 win_skip("Version 2 is not supported.\n");