dmime/tests: Test tempo track Play and DMUS_PMSGT_TEMPO messages.
[wine.git] / dlls / http.sys / http.c
blob52887259aa72980c4aacc8e02c150951a678d443
1 /*
2 * HTTP server driver
4 * Copyright 2019 Zebediah Figura
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <assert.h>
22 #include "ntstatus.h"
23 #define WIN32_NO_STATUS
24 #include "wine/http.h"
25 #include "winternl.h"
26 #include "ddk/wdm.h"
27 #include "wine/debug.h"
28 #include "wine/list.h"
30 static HANDLE directory_obj;
31 static DEVICE_OBJECT *device_obj;
33 WINE_DEFAULT_DEBUG_CHANNEL(http);
35 #define DECLARE_CRITICAL_SECTION(cs) \
36 static CRITICAL_SECTION cs; \
37 static CRITICAL_SECTION_DEBUG cs##_debug = \
38 { 0, 0, &cs, { &cs##_debug.ProcessLocksList, &cs##_debug.ProcessLocksList }, \
39 0, 0, { (DWORD_PTR)(__FILE__ ": " # cs) }}; \
40 static CRITICAL_SECTION cs = { &cs##_debug, -1, 0, 0, 0, 0 };
42 DECLARE_CRITICAL_SECTION(http_cs);
44 static HANDLE request_thread, request_event;
45 static BOOL thread_stop;
47 static HTTP_REQUEST_ID req_id_counter;
49 struct connection
51 struct list entry; /* in "connections" below */
53 SOCKET socket;
55 char *buffer;
56 unsigned int len, size;
58 /* If there is a request fully received and waiting to be read, the
59 * "available" parameter will be TRUE. Either there is no queue matching
60 * the URL of this request yet ("queue" is NULL), there is a queue but no
61 * IRPs have arrived for this request yet ("queue" is non-NULL and "req_id"
62 * is HTTP_NULL_ID), or an IRP has arrived but did not provide a large
63 * enough buffer to read the whole request ("queue" is non-NULL and
64 * "req_id" is not HTTP_NULL_ID).
66 * If "available" is FALSE, either we are waiting for a new request
67 * ("req_id" is HTTP_NULL_ID), or we are waiting for the user to send a
68 * response ("req_id" is not HTTP_NULL_ID). */
69 BOOL available;
70 struct request_queue *queue;
71 HTTP_REQUEST_ID req_id;
72 HTTP_URL_CONTEXT context;
74 /* Things we already parsed out of the request header in parse_request().
75 * These are valid only if "available" is TRUE. */
76 unsigned int req_len;
77 HTTP_VERB verb;
78 HTTP_VERSION version;
79 const char *url, *host;
80 ULONG unk_verb_len, url_len, content_len;
83 static struct list connections = LIST_INIT(connections);
85 struct listening_socket
87 struct list entry;
88 unsigned short port;
89 SOCKET socket;
92 static struct list listening_sockets = LIST_INIT(listening_sockets);
94 struct url
96 struct list entry;
97 char *url;
98 HTTP_URL_CONTEXT context;
99 struct listening_socket *listening_sock;
102 struct request_queue
104 struct list entry;
105 LIST_ENTRY irp_queue;
106 struct list urls;
109 static struct list request_queues = LIST_INIT(request_queues);
111 static void accept_connection(SOCKET socket)
113 struct connection *conn;
114 ULONG true = 1;
115 SOCKET peer;
117 if ((peer = accept(socket, NULL, NULL)) == INVALID_SOCKET)
118 return;
120 if (!(conn = calloc(1, sizeof(*conn))))
122 ERR("Failed to allocate memory.\n");
123 shutdown(peer, SD_BOTH);
124 closesocket(peer);
125 return;
127 if (!(conn->buffer = malloc(8192)))
129 ERR("Failed to allocate buffer memory.\n");
130 free(conn);
131 shutdown(peer, SD_BOTH);
132 closesocket(peer);
133 return;
135 conn->size = 8192;
136 WSAEventSelect(peer, request_event, FD_READ | FD_CLOSE);
137 ioctlsocket(peer, FIONBIO, &true);
138 conn->socket = peer;
139 list_add_head(&connections, &conn->entry);
142 static void close_connection(struct connection *conn)
144 free(conn->buffer);
145 shutdown(conn->socket, SD_BOTH);
146 closesocket(conn->socket);
147 list_remove(&conn->entry);
148 free(conn);
151 static HTTP_VERB parse_verb(const char *verb, int len)
153 static const char *const verbs[] =
155 "OPTIONS",
156 "GET",
157 "HEAD",
158 "POST",
159 "PUT",
160 "DELETE",
161 "TRACE",
162 "CONNECT",
163 "TRACK",
164 "MOVE",
165 "COPY",
166 "PROPFIND",
167 "PROPPATCH",
168 "MKCOL",
169 "LOCK",
170 "UNLOCK",
171 "SEARCH",
173 unsigned int i;
175 for (i = 0; i < ARRAY_SIZE(verbs); ++i)
177 if (!strncmp(verb, verbs[i], len))
178 return HttpVerbOPTIONS + i;
180 return HttpVerbUnknown;
183 /* Return the length of a token, as defined in RFC 2616 section 2.2. */
184 static int parse_token(const char *str, const char *end)
186 const char *p;
187 for (p = str; !end || p < end; ++p)
189 if (!isgraph(*p) || strchr("()<>@,;:\\\"/[]?={}", *p))
190 break;
192 return p - str;
195 static HTTP_HEADER_ID parse_header_name(const char *header, int len)
197 static const char *const headers[] =
199 "Cache-Control",
200 "Connection",
201 "Date",
202 "Keep-Alive",
203 "Pragma",
204 "Trailer",
205 "Transfer-Encoding",
206 "Upgrade",
207 "Via",
208 "Warning",
209 "Allow",
210 "Content-Length",
211 "Content-Type",
212 "Content-Encoding",
213 "Content-Language",
214 "Content-Location",
215 "Content-MD5",
216 "Content-Range",
217 "Expires",
218 "Last-Modified",
219 "Accept",
220 "Accept-Charset",
221 "Accept-Encoding",
222 "Accept-Language",
223 "Authorization",
224 "Cookie",
225 "Expect",
226 "From",
227 "Host",
228 "If-Match",
229 "If-Modified-Since",
230 "If-None-Match",
231 "If-Range",
232 "If-Unmodified-Since",
233 "Max-Forwards",
234 "Proxy-Authorization",
235 "Referer",
236 "Range",
237 "TE",
238 "Translate",
239 "User-Agent",
241 unsigned int i;
243 for (i = 0; i < ARRAY_SIZE(headers); ++i)
245 if (!strncmp(header, headers[i], len))
246 return i;
248 return HttpHeaderRequestMaximum;
251 static void parse_header(const char *name, int *name_len, const char **value, int *value_len)
253 const char *p = name;
254 *name_len = parse_token(name, NULL);
255 p += *name_len;
256 while (*p == ' ' || *p == '\t') ++p;
257 ++p; /* skip colon */
258 while (*p == ' ' || *p == '\t') ++p;
259 *value = p;
260 while (isprint(*p) || *p == '\t') ++p;
261 while (isspace(*p)) --p; /* strip trailing LWS */
262 *value_len = p - *value + 1;
265 #define http_unknown_header http_unknown_header_64
266 #define http_data_chunk http_data_chunk_64
267 #define http_request http_request_64
268 #define complete_irp complete_irp_64
269 #define POINTER ULONGLONG
270 #include "request.h"
271 #undef http_unknown_header
272 #undef http_data_chunk
273 #undef http_request
274 #undef complete_irp
275 #undef POINTER
277 #define http_unknown_header http_unknown_header_32
278 #define http_data_chunk http_data_chunk_32
279 #define http_request http_request_32
280 #define complete_irp complete_irp_32
281 #define POINTER ULONG
282 #include "request.h"
283 #undef http_unknown_header
284 #undef http_data_chunk
285 #undef http_request
286 #undef complete_irp
287 #undef POINTER
289 static NTSTATUS complete_irp(struct connection *conn, IRP *irp)
291 const struct http_receive_request_params params
292 = *(struct http_receive_request_params *)irp->AssociatedIrp.SystemBuffer;
294 TRACE("Completing IRP %p.\n", irp);
296 if (!conn->req_id)
297 conn->req_id = ++req_id_counter;
299 if (params.bits == 32)
300 return complete_irp_32(conn, irp);
301 else
302 return complete_irp_64(conn, irp);
305 /* Complete an IOCTL_HTTP_RECEIVE_REQUEST IRP if there is one to complete. */
306 static void try_complete_irp(struct connection *conn)
308 LIST_ENTRY *entry;
309 if (conn->queue && (entry = RemoveHeadList(&conn->queue->irp_queue)) != &conn->queue->irp_queue)
311 IRP *irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
312 irp->IoStatus.Status = complete_irp(conn, irp);
313 IoCompleteRequest(irp, IO_NO_INCREMENT);
317 /* Return 1 if str matches expect, 0 if str is incomplete, -1 if they don't match. */
318 static int compare_exact(const char *str, const char *expect, const char *end)
320 while (*expect)
322 if (str >= end) return 0;
323 if (*str++ != *expect++) return -1;
325 return 1;
328 static int parse_number(const char *str, const char **endptr, const char *end)
330 int n = 0;
331 while (str < end && isdigit(*str))
332 n = n * 10 + (*str++ - '0');
333 *endptr = str;
334 return n;
338 /* 0 means not a match, 1 and higher means a match, higher means more paths matched. */
339 static unsigned int compare_paths(const char *queue_path, const char *conn_path, size_t conn_len)
341 const char *question_mark;
342 unsigned int i, cnt = 1;
343 size_t queue_len;
345 queue_len = strlen(queue_path);
347 if ((question_mark = memchr(conn_path, '?', conn_len)))
348 conn_len = question_mark - conn_path;
350 if (queue_path[queue_len - 1] == '/')
351 queue_len--;
352 if (conn_path[conn_len - 1] == '/')
353 conn_len--;
355 if (conn_len < queue_len)
356 return 0;
358 for (i = 0; i < queue_len; ++i)
360 if (queue_path[i] != conn_path[i])
361 return 0;
362 if (queue_path[i] == '/')
363 cnt++;
366 if (queue_len == conn_len || conn_path[queue_len] == '/')
367 return cnt;
368 else
369 return 0;
372 static BOOL host_matches(const struct url *url, const char *conn_host)
374 size_t host_len;
376 if (!url->url)
377 return FALSE;
379 if (url->url[7] == '+')
381 const char *queue_port = strchr(url->url + 7, ':');
382 host_len = strchr(queue_port, '/') - queue_port - 1;
383 if (!strncmp(queue_port, strchr(conn_host, ':'), host_len))
384 return TRUE;
386 else
388 host_len = strchr(url->url + 7, '/') - url->url - 7;
389 if (!memicmp(url->url + 7, conn_host, host_len))
390 return TRUE;
393 return FALSE;
396 static struct url *url_matches(const struct connection *conn, const struct request_queue *queue,
397 unsigned int *ret_slash_count)
399 const char *queue_path, *conn_host, *conn_path;
400 unsigned int max_slash_count = 0, slash_count;
401 size_t conn_path_len;
402 struct url *url, *ret = NULL;
404 if (conn->url[0] == '/')
406 conn_host = conn->host;
407 conn_path = conn->url;
408 conn_path_len = conn->url_len;
411 else
413 conn_host = conn->url + 7;
414 conn_path = strchr(conn_host, '/');
415 conn_path_len = (conn->url + conn->url_len) - conn_path;
418 LIST_FOR_EACH_ENTRY(url, &queue->urls, struct url, entry)
420 if (host_matches(url, conn_host))
422 queue_path = strchr(url->url + 7, '/');
423 if (!queue_path)
424 continue;
425 slash_count = compare_paths(queue_path, conn_path, conn_path_len);
426 if (slash_count > max_slash_count)
428 max_slash_count = slash_count;
429 ret = url;
434 if (ret_slash_count)
435 *ret_slash_count = max_slash_count;
437 return ret;
440 /* Upon receiving a request, parse it to ensure that it is a valid HTTP request,
441 * and mark down some information that we will use later. Returns 1 if we parsed
442 * a complete request, 0 if incomplete, -1 if invalid. */
443 static int parse_request(struct connection *conn)
445 const char *const req = conn->buffer, *const end = conn->buffer + conn->len;
446 struct request_queue *queue, *best_queue = NULL;
447 struct url *conn_url, *best_conn_url = NULL;
448 const char *p = req, *q;
449 unsigned int slash_count, best_slash_count = 0;
450 int len, ret;
452 if (!conn->len) return 0;
454 TRACE("%s\n", wine_dbgstr_an(conn->buffer, conn->len));
456 len = parse_token(p, end);
457 if (p + len >= end) return 0;
458 if (!len || p[len] != ' ') return -1;
460 /* verb */
461 if ((conn->verb = parse_verb(p, len)) == HttpVerbUnknown)
462 conn->unk_verb_len = len;
463 p += len + 1;
465 TRACE("Got verb %u (%s).\n", conn->verb, debugstr_an(req, len));
467 /* URL */
468 conn->url = p;
469 while (p < end && isgraph(*p)) ++p;
470 conn->url_len = p - conn->url;
471 if (p >= end) return 0;
472 if (!conn->url_len) return -1;
474 TRACE("Got URI %s.\n", debugstr_an(conn->url, conn->url_len));
476 /* version */
477 if ((ret = compare_exact(p, " HTTP/", end)) <= 0) return ret;
478 p += 6;
479 conn->version.MajorVersion = parse_number(p, &q, end);
480 if (q >= end) return 0;
481 if (q == p || *q != '.') return -1;
482 p = q + 1;
483 if (p >= end) return 0;
484 conn->version.MinorVersion = parse_number(p, &q, end);
485 if (q >= end) return 0;
486 if (q == p) return -1;
487 p = q;
488 if ((ret = compare_exact(p, "\r\n", end)) <= 0) return ret;
489 p += 2;
491 TRACE("Got version %hu.%hu.\n", conn->version.MajorVersion, conn->version.MinorVersion);
493 /* headers */
494 conn->host = NULL;
495 conn->content_len = 0;
496 for (;;)
498 const char *name = p;
500 if (!(ret = compare_exact(p, "\r\n", end))) return 0;
501 else if (ret > 0) break;
503 len = parse_token(p, end);
504 if (p + len >= end) return 0;
505 if (!len) return -1;
506 p += len;
507 while (p < end && (*p == ' ' || *p == '\t')) ++p;
508 if (p >= end) return 0;
509 if (*p != ':') return -1;
510 ++p;
511 while (p < end && (*p == ' ' || *p == '\t')) ++p;
513 TRACE("Got %s header.\n", debugstr_an(name, len));
515 if (!strncmp(name, "Host", len))
516 conn->host = p;
517 else if (!strncmp(name, "Content-Length", len))
519 conn->content_len = parse_number(p, &q, end);
520 if (q >= end) return 0;
521 if (q == p) return -1;
523 else if (!strncmp(name, "Transfer-Encoding", len))
524 FIXME("Unhandled Transfer-Encoding header.\n");
525 while (p < end && (isprint(*p) || *p == '\t')) ++p;
526 if ((ret = compare_exact(p, "\r\n", end)) <= 0) return ret;
527 p += 2;
529 p += 2;
530 if (conn->url[0] == '/' && !conn->host) return -1;
532 if (end - p < conn->content_len) return 0;
534 conn->req_len = (p - req) + conn->content_len;
536 TRACE("Received a full request, length %u bytes.\n", conn->req_len);
538 conn->queue = NULL;
539 /* Find a queue which can receive this request. */
540 LIST_FOR_EACH_ENTRY(queue, &request_queues, struct request_queue, entry)
542 if ((conn_url = url_matches(conn, queue, &slash_count)))
544 if (slash_count > best_slash_count)
546 best_slash_count = slash_count;
547 best_queue = queue;
548 best_conn_url = conn_url;
553 if (best_conn_url)
555 TRACE("Assigning request to queue %p.\n", best_queue);
556 conn->queue = best_queue;
557 conn->context = best_conn_url->context;
560 /* Stop selecting on incoming data until a response is queued. */
561 WSAEventSelect(conn->socket, request_event, FD_CLOSE);
563 conn->available = TRUE;
564 try_complete_irp(conn);
566 return 1;
569 static void format_date(char *buffer)
571 static const char day_names[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
572 static const char month_names[12][4] =
573 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
574 SYSTEMTIME date;
575 GetSystemTime(&date);
576 sprintf(buffer + strlen(buffer), "Date: %s, %02u %s %u %02u:%02u:%02u GMT\r\n",
577 day_names[date.wDayOfWeek], date.wDay, month_names[date.wMonth - 1],
578 date.wYear, date.wHour, date.wMinute, date.wSecond);
581 /* Send a 400 Bad Request response. */
582 static void send_400(struct connection *conn)
584 static const char response_header[] = "HTTP/1.1 400 Bad Request\r\n";
585 static const char response_body[] =
586 "Content-Type: text/html; charset=utf-8\r\n"
587 "Content-Language: en\r\n"
588 "Connection: close\r\n";
589 char buffer[sizeof(response_header) + sizeof(response_body) + 37];
591 strcpy(buffer, response_header);
592 format_date(buffer + strlen(buffer));
593 strcat(buffer, response_body);
594 if (send(conn->socket, buffer, strlen(buffer), 0) < 0)
595 ERR("Failed to send 400 response, error %u.\n", WSAGetLastError());
598 static void receive_data(struct connection *conn)
600 int len, ret;
602 /* We might be waiting for an IRP, but always call recv() anyway, since we
603 * might have been woken up by the socket closing. */
604 if ((len = recv(conn->socket, conn->buffer + conn->len, conn->size - conn->len, 0)) <= 0)
606 if (WSAGetLastError() == WSAEWOULDBLOCK)
607 return; /* nothing to receive */
608 else if (!len)
609 TRACE("Connection was shut down by peer.\n");
610 else
611 ERR("Got error %u; shutting down connection.\n", WSAGetLastError());
612 close_connection(conn);
613 return;
615 conn->len += len;
617 if (conn->available)
618 return; /* waiting for an HttpReceiveHttpRequest() call */
619 if (conn->req_id != HTTP_NULL_ID)
620 return; /* waiting for an HttpSendHttpResponse() call */
622 TRACE("Received %u bytes of data.\n", len);
624 if (!(ret = parse_request(conn)))
626 ULONG available;
627 ioctlsocket(conn->socket, FIONREAD, &available);
628 if (available)
630 TRACE("%lu more bytes of data available, trying with larger buffer.\n", available);
631 if (!(conn->buffer = realloc(conn->buffer, conn->len + available)))
633 ERR("Failed to allocate %lu bytes of memory.\n", conn->len + available);
634 close_connection(conn);
635 return;
637 conn->size = conn->len + available;
639 if ((len = recv(conn->socket, conn->buffer + conn->len, conn->size - conn->len, 0)) < 0)
641 ERR("Got error %u; shutting down connection.\n", WSAGetLastError());
642 close_connection(conn);
643 return;
645 TRACE("Received %u bytes of data.\n", len);
646 conn->len += len;
647 ret = parse_request(conn);
650 if (!ret)
651 TRACE("Request is incomplete, waiting for more data.\n");
652 else if (ret < 0)
654 WARN("Failed to parse request; shutting down connection.\n");
655 send_400(conn);
656 close_connection(conn);
660 static DWORD WINAPI request_thread_proc(void *arg)
662 struct connection *conn, *cursor;
663 struct request_queue *queue;
664 struct url *url;
666 TRACE("Starting request thread.\n");
668 while (!WaitForSingleObject(request_event, INFINITE))
670 EnterCriticalSection(&http_cs);
672 LIST_FOR_EACH_ENTRY(queue, &request_queues, struct request_queue, entry)
674 LIST_FOR_EACH_ENTRY(url, &queue->urls, struct url, entry)
676 if (url->listening_sock && url->listening_sock->socket != -1)
677 accept_connection(url->listening_sock->socket);
681 LIST_FOR_EACH_ENTRY_SAFE(conn, cursor, &connections, struct connection, entry)
683 receive_data(conn);
686 LeaveCriticalSection(&http_cs);
689 TRACE("Stopping request thread.\n");
691 return 0;
694 static struct listening_socket *get_listening_socket(unsigned short port)
696 struct listening_socket *listening_sock;
698 LIST_FOR_EACH_ENTRY(listening_sock, &listening_sockets, struct listening_socket, entry)
700 if (listening_sock->port == port)
701 return listening_sock;
704 return NULL;
707 static NTSTATUS http_add_url(struct request_queue *queue, IRP *irp)
709 const struct http_add_url_params *params = irp->AssociatedIrp.SystemBuffer;
710 struct request_queue *queue_entry;
711 struct sockaddr_in addr;
712 struct connection *conn;
713 struct url *url_entry, *new_entry;
714 struct listening_socket *listening_sock;
715 char *url, *endptr;
716 size_t queue_url_len, new_url_len;
717 ULONG true = 1, value;
718 SOCKET s = INVALID_SOCKET;
720 TRACE("host %s, context %s.\n", debugstr_a(params->url), wine_dbgstr_longlong(params->context));
722 if (!strncmp(params->url, "https://", 8))
724 FIXME("HTTPS is not implemented.\n");
725 return STATUS_NOT_IMPLEMENTED;
727 else if (strncmp(params->url, "http://", 7) || !strchr(params->url + 7, ':'))
728 return STATUS_INVALID_PARAMETER;
729 if (!(addr.sin_port = htons(strtol(strchr(params->url + 7, ':') + 1, &endptr, 10))) || *endptr != '/')
730 return STATUS_INVALID_PARAMETER;
731 if (strchr(params->url, '?'))
732 return STATUS_INVALID_PARAMETER;
734 if (!(url = strdup(params->url)))
735 return STATUS_NO_MEMORY;
737 if (!(new_entry = malloc(sizeof(struct url))))
739 free(url);
740 return STATUS_NO_MEMORY;
743 new_url_len = strlen(url);
744 if (url[new_url_len - 1] == '/')
745 new_url_len--;
747 EnterCriticalSection(&http_cs);
749 LIST_FOR_EACH_ENTRY(queue_entry, &request_queues, struct request_queue, entry)
751 LIST_FOR_EACH_ENTRY(url_entry, &queue_entry->urls, struct url, entry)
753 queue_url_len = strlen(url_entry->url);
754 if (url_entry->url[queue_url_len - 1] == '/')
755 queue_url_len--;
757 if (url_entry->url && queue_url_len == new_url_len && !memcmp(url_entry->url, url, queue_url_len))
759 LeaveCriticalSection(&http_cs);
760 free(url);
761 free(new_entry);
762 return STATUS_OBJECT_NAME_COLLISION;
767 listening_sock = get_listening_socket(addr.sin_port);
769 if (!listening_sock)
771 if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
773 ERR("Failed to create socket, error %u.\n", WSAGetLastError());
774 LeaveCriticalSection(&http_cs);
775 free(url);
776 free(new_entry);
777 return STATUS_UNSUCCESSFUL;
780 addr.sin_family = AF_INET;
781 addr.sin_addr.S_un.S_addr = INADDR_ANY;
782 value = 1;
783 setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&value, sizeof(value));
784 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
786 LeaveCriticalSection(&http_cs);
787 closesocket(s);
788 free(url);
789 free(new_entry);
790 if (WSAGetLastError() == WSAEADDRINUSE)
792 WARN("Address %s is already in use.\n", debugstr_a(params->url));
793 return STATUS_SHARING_VIOLATION;
795 else if (WSAGetLastError() == WSAEACCES)
797 WARN("Not enough permissions to bind to address %s.\n", debugstr_a(params->url));
798 return STATUS_ACCESS_DENIED;
800 ERR("Failed to bind socket, error %u.\n", WSAGetLastError());
801 return STATUS_UNSUCCESSFUL;
804 if (listen(s, SOMAXCONN) == -1)
806 ERR("Failed to listen to port %u, error %u.\n", addr.sin_port, WSAGetLastError());
807 LeaveCriticalSection(&http_cs);
808 closesocket(s);
809 free(url);
810 free(new_entry);
811 return STATUS_OBJECT_NAME_COLLISION;
814 if (!(listening_sock = malloc(sizeof(struct listening_socket))))
816 LeaveCriticalSection(&http_cs);
817 closesocket(s);
818 free(url);
819 free(new_entry);
820 return STATUS_NO_MEMORY;
822 listening_sock->port = addr.sin_port;
823 listening_sock->socket = s;
824 list_add_head(&listening_sockets, &listening_sock->entry);
826 ioctlsocket(s, FIONBIO, &true);
827 WSAEventSelect(s, request_event, FD_ACCEPT);
830 new_entry->url = url;
831 new_entry->context = params->context;
832 new_entry->listening_sock = listening_sock;
833 list_add_head(&queue->urls, &new_entry->entry);
835 /* See if any pending requests now match this queue. */
836 LIST_FOR_EACH_ENTRY(conn, &connections, struct connection, entry)
838 if (conn->available && !conn->queue && url_matches(conn, queue, NULL))
840 conn->queue = queue;
841 conn->context = params->context;
842 try_complete_irp(conn);
846 LeaveCriticalSection(&http_cs);
848 return STATUS_SUCCESS;
851 static BOOL is_listening_socket_used(const struct listening_socket *listening_sock)
853 struct request_queue *queue_entry;
854 struct url *url_entry;
856 LIST_FOR_EACH_ENTRY(queue_entry, &request_queues, struct request_queue, entry)
858 LIST_FOR_EACH_ENTRY(url_entry, &queue_entry->urls, struct url, entry)
860 if (listening_sock == url_entry->listening_sock)
862 return TRUE;
867 return FALSE;
870 static NTSTATUS http_remove_url(struct request_queue *queue, IRP *irp)
872 const char *url = irp->AssociatedIrp.SystemBuffer;
873 struct url *url_entry;
875 TRACE("host %s.\n", debugstr_a(url));
877 EnterCriticalSection(&http_cs);
879 LIST_FOR_EACH_ENTRY(url_entry, &queue->urls, struct url, entry)
881 if (url_entry->url && !strcmp(url, url_entry->url))
883 free(url_entry->url);
884 url_entry->url = NULL;
886 if (!is_listening_socket_used(url_entry->listening_sock))
888 shutdown(url_entry->listening_sock->socket, SD_BOTH);
889 closesocket(url_entry->listening_sock->socket);
890 list_remove(&url_entry->listening_sock->entry);
891 free(url_entry->listening_sock);
893 url_entry->listening_sock = NULL;
895 list_remove(&url_entry->entry);
896 free(url_entry);
898 LeaveCriticalSection(&http_cs);
899 return STATUS_SUCCESS;
903 LeaveCriticalSection(&http_cs);
904 return STATUS_OBJECT_NAME_NOT_FOUND;
907 static struct connection *get_connection(HTTP_REQUEST_ID req_id)
909 struct connection *conn;
911 LIST_FOR_EACH_ENTRY(conn, &connections, struct connection, entry)
913 if (conn->req_id == req_id)
914 return conn;
916 return NULL;
919 static void WINAPI http_receive_request_cancel(DEVICE_OBJECT *device, IRP *irp)
921 TRACE("device %p, irp %p.\n", device, irp);
923 IoReleaseCancelSpinLock(irp->CancelIrql);
925 EnterCriticalSection(&http_cs);
926 RemoveEntryList(&irp->Tail.Overlay.ListEntry);
927 LeaveCriticalSection(&http_cs);
929 irp->IoStatus.Status = STATUS_CANCELLED;
930 IoCompleteRequest(irp, IO_NO_INCREMENT);
933 static NTSTATUS http_receive_request(struct request_queue *queue, IRP *irp)
935 const struct http_receive_request_params *params = irp->AssociatedIrp.SystemBuffer;
936 struct connection *conn;
937 NTSTATUS ret;
939 TRACE("addr %s, id %s, flags %#lx, bits %lu.\n", wine_dbgstr_longlong(params->addr),
940 wine_dbgstr_longlong(params->id), params->flags, params->bits);
942 EnterCriticalSection(&http_cs);
944 if ((conn = get_connection(params->id)) && conn->available && conn->queue == queue)
946 ret = complete_irp(conn, irp);
947 LeaveCriticalSection(&http_cs);
948 return ret;
951 if (params->id == HTTP_NULL_ID)
953 TRACE("Queuing IRP %p.\n", irp);
955 IoSetCancelRoutine(irp, http_receive_request_cancel);
956 if (irp->Cancel && !IoSetCancelRoutine(irp, NULL))
958 /* The IRP was canceled before we set the cancel routine. */
959 ret = STATUS_CANCELLED;
961 else
963 IoMarkIrpPending(irp);
964 InsertTailList(&queue->irp_queue, &irp->Tail.Overlay.ListEntry);
965 ret = STATUS_PENDING;
968 else
969 ret = STATUS_CONNECTION_INVALID;
971 LeaveCriticalSection(&http_cs);
973 return ret;
976 static NTSTATUS http_send_response(struct request_queue *queue, IRP *irp)
978 const struct http_response *response = irp->AssociatedIrp.SystemBuffer;
979 struct connection *conn;
981 TRACE("id %s, len %d.\n", wine_dbgstr_longlong(response->id), response->len);
983 EnterCriticalSection(&http_cs);
985 if ((conn = get_connection(response->id)))
987 if (send(conn->socket, response->buffer, response->len, 0) >= 0)
989 if (conn->content_len)
991 /* Discard whatever entity body is left. */
992 memmove(conn->buffer, conn->buffer + conn->content_len, conn->len - conn->content_len);
993 conn->len -= conn->content_len;
996 conn->queue = NULL;
997 conn->req_id = HTTP_NULL_ID;
998 WSAEventSelect(conn->socket, request_event, FD_READ | FD_CLOSE);
999 irp->IoStatus.Information = response->len;
1000 /* We might have another request already in the buffer. */
1001 if (parse_request(conn) < 0)
1003 WARN("Failed to parse request; shutting down connection.\n");
1004 send_400(conn);
1005 close_connection(conn);
1008 else
1010 ERR("Got error %u; shutting down connection.\n", WSAGetLastError());
1011 close_connection(conn);
1014 LeaveCriticalSection(&http_cs);
1015 return STATUS_SUCCESS;
1018 LeaveCriticalSection(&http_cs);
1019 return STATUS_CONNECTION_INVALID;
1022 static NTSTATUS http_receive_body(struct request_queue *queue, IRP *irp)
1024 const struct http_receive_body_params *params = irp->AssociatedIrp.SystemBuffer;
1025 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
1026 const DWORD output_len = stack->Parameters.DeviceIoControl.OutputBufferLength;
1027 struct connection *conn;
1028 NTSTATUS ret;
1030 TRACE("id %s, bits %lu.\n", wine_dbgstr_longlong(params->id), params->bits);
1032 EnterCriticalSection(&http_cs);
1034 if ((conn = get_connection(params->id)))
1036 TRACE("%lu bits remaining.\n", conn->content_len);
1038 if (conn->content_len)
1040 ULONG len = min(conn->content_len, output_len);
1041 memcpy(irp->AssociatedIrp.SystemBuffer, conn->buffer, len);
1042 memmove(conn->buffer, conn->buffer + len, conn->len - len);
1043 conn->content_len -= len;
1044 conn->len -= len;
1046 irp->IoStatus.Information = len;
1047 ret = STATUS_SUCCESS;
1049 else
1050 ret = STATUS_END_OF_FILE;
1052 else
1053 ret = STATUS_CONNECTION_INVALID;
1055 LeaveCriticalSection(&http_cs);
1057 return ret;
1060 static NTSTATUS WINAPI dispatch_ioctl(DEVICE_OBJECT *device, IRP *irp)
1062 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
1063 struct request_queue *queue = stack->FileObject->FsContext;
1064 NTSTATUS ret;
1066 switch (stack->Parameters.DeviceIoControl.IoControlCode)
1068 case IOCTL_HTTP_ADD_URL:
1069 ret = http_add_url(queue, irp);
1070 break;
1071 case IOCTL_HTTP_REMOVE_URL:
1072 ret = http_remove_url(queue, irp);
1073 break;
1074 case IOCTL_HTTP_RECEIVE_REQUEST:
1075 ret = http_receive_request(queue, irp);
1076 break;
1077 case IOCTL_HTTP_SEND_RESPONSE:
1078 ret = http_send_response(queue, irp);
1079 break;
1080 case IOCTL_HTTP_RECEIVE_BODY:
1081 ret = http_receive_body(queue, irp);
1082 break;
1083 default:
1084 FIXME("Unhandled ioctl %#lx.\n", stack->Parameters.DeviceIoControl.IoControlCode);
1085 ret = STATUS_NOT_IMPLEMENTED;
1088 if (ret != STATUS_PENDING)
1090 irp->IoStatus.Status = ret;
1091 IoCompleteRequest(irp, IO_NO_INCREMENT);
1093 return ret;
1096 static NTSTATUS WINAPI dispatch_create(DEVICE_OBJECT *device, IRP *irp)
1098 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
1099 struct request_queue *queue;
1101 if (!(queue = calloc(1, sizeof(*queue))))
1102 return STATUS_NO_MEMORY;
1103 list_init(&queue->urls);
1104 stack->FileObject->FsContext = queue;
1105 InitializeListHead(&queue->irp_queue);
1107 EnterCriticalSection(&http_cs);
1108 list_add_head(&request_queues, &queue->entry);
1109 LeaveCriticalSection(&http_cs);
1111 TRACE("Created queue %p.\n", queue);
1113 irp->IoStatus.Status = STATUS_SUCCESS;
1114 IoCompleteRequest(irp, IO_NO_INCREMENT);
1115 return STATUS_SUCCESS;
1118 static void close_queue(struct request_queue *queue)
1120 struct url *url, *url_next;
1121 struct listening_socket *listening_sock, *listening_sock_next;
1123 EnterCriticalSection(&http_cs);
1124 list_remove(&queue->entry);
1126 LIST_FOR_EACH_ENTRY_SAFE(url, url_next, &queue->urls, struct url, entry)
1128 free(url->url);
1129 free(url);
1132 LIST_FOR_EACH_ENTRY_SAFE(listening_sock, listening_sock_next, &listening_sockets, struct listening_socket, entry)
1134 shutdown(listening_sock->socket, SD_BOTH);
1135 closesocket(listening_sock->socket);
1136 list_remove(&listening_sock->entry);
1137 free(listening_sock);
1140 free(queue);
1142 LeaveCriticalSection(&http_cs);
1145 static NTSTATUS WINAPI dispatch_close(DEVICE_OBJECT *device, IRP *irp)
1147 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
1148 struct request_queue *queue = stack->FileObject->FsContext;
1149 LIST_ENTRY *entry;
1151 TRACE("Closing queue %p.\n", queue);
1153 EnterCriticalSection(&http_cs);
1155 while ((entry = queue->irp_queue.Flink) != &queue->irp_queue)
1157 IRP *queued_irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
1158 IoCancelIrp(queued_irp);
1161 LeaveCriticalSection(&http_cs);
1163 close_queue(queue);
1165 irp->IoStatus.Status = STATUS_SUCCESS;
1166 IoCompleteRequest(irp, IO_NO_INCREMENT);
1167 return STATUS_SUCCESS;
1170 static void WINAPI unload(DRIVER_OBJECT *driver)
1172 struct request_queue *queue, *queue_next;
1173 struct connection *conn, *conn_next;
1175 thread_stop = TRUE;
1176 SetEvent(request_event);
1177 WaitForSingleObject(request_thread, INFINITE);
1178 CloseHandle(request_thread);
1179 CloseHandle(request_event);
1181 LIST_FOR_EACH_ENTRY_SAFE(conn, conn_next, &connections, struct connection, entry)
1183 close_connection(conn);
1186 LIST_FOR_EACH_ENTRY_SAFE(queue, queue_next, &request_queues, struct request_queue, entry)
1188 close_queue(queue);
1191 WSACleanup();
1193 IoDeleteDevice(device_obj);
1194 NtClose(directory_obj);
1197 NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *path)
1199 OBJECT_ATTRIBUTES attr = {sizeof(attr)};
1200 UNICODE_STRING device_http = RTL_CONSTANT_STRING(L"\\Device\\Http");
1201 UNICODE_STRING device_http_req_queue = RTL_CONSTANT_STRING(L"\\Device\\Http\\ReqQueue");
1202 WSADATA wsadata;
1203 NTSTATUS ret;
1205 TRACE("driver %p, path %s.\n", driver, debugstr_w(path->Buffer));
1207 attr.ObjectName = &device_http;
1208 if ((ret = NtCreateDirectoryObject(&directory_obj, 0, &attr)) && ret != STATUS_OBJECT_NAME_COLLISION)
1209 ERR("Failed to create \\Device\\Http directory, status %#lx.\n", ret);
1211 if ((ret = IoCreateDevice(driver, 0, &device_http_req_queue, FILE_DEVICE_UNKNOWN, 0, FALSE, &device_obj)))
1213 ERR("Failed to create request queue device, status %#lx.\n", ret);
1214 NtClose(directory_obj);
1215 return ret;
1218 driver->MajorFunction[IRP_MJ_CREATE] = dispatch_create;
1219 driver->MajorFunction[IRP_MJ_CLOSE] = dispatch_close;
1220 driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = dispatch_ioctl;
1221 driver->DriverUnload = unload;
1223 WSAStartup(MAKEWORD(1,1), &wsadata);
1225 request_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1226 request_thread = CreateThread(NULL, 0, request_thread_proc, NULL, 0, NULL);
1228 return STATUS_SUCCESS;