imm32: Ignore some messages in ImmTranslateMessage.
[wine.git] / dlls / http.sys / http.c
blobc2234aa14ad5a969acb2f9cbc1f25b72548abe2e
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/heap.h"
29 #include "wine/list.h"
31 static HANDLE directory_obj;
32 static DEVICE_OBJECT *device_obj;
34 WINE_DEFAULT_DEBUG_CHANNEL(http);
36 #define DECLARE_CRITICAL_SECTION(cs) \
37 static CRITICAL_SECTION cs; \
38 static CRITICAL_SECTION_DEBUG cs##_debug = \
39 { 0, 0, &cs, { &cs##_debug.ProcessLocksList, &cs##_debug.ProcessLocksList }, \
40 0, 0, { (DWORD_PTR)(__FILE__ ": " # cs) }}; \
41 static CRITICAL_SECTION cs = { &cs##_debug, -1, 0, 0, 0, 0 };
43 DECLARE_CRITICAL_SECTION(http_cs);
45 static HANDLE request_thread, request_event;
46 static BOOL thread_stop;
48 static HTTP_REQUEST_ID req_id_counter;
50 struct connection
52 struct list entry; /* in "connections" below */
54 SOCKET socket;
56 char *buffer;
57 unsigned int len, size;
59 /* If there is a request fully received and waiting to be read, the
60 * "available" parameter will be TRUE. Either there is no queue matching
61 * the URL of this request yet ("queue" is NULL), there is a queue but no
62 * IRPs have arrived for this request yet ("queue" is non-NULL and "req_id"
63 * is HTTP_NULL_ID), or an IRP has arrived but did not provide a large
64 * enough buffer to read the whole request ("queue" is non-NULL and
65 * "req_id" is not HTTP_NULL_ID).
67 * If "available" is FALSE, either we are waiting for a new request
68 * ("req_id" is HTTP_NULL_ID), or we are waiting for the user to send a
69 * response ("req_id" is not HTTP_NULL_ID). */
70 BOOL available;
71 struct request_queue *queue;
72 HTTP_REQUEST_ID req_id;
73 HTTP_URL_CONTEXT context;
75 /* Things we already parsed out of the request header in parse_request().
76 * These are valid only if "available" is TRUE. */
77 unsigned int req_len;
78 HTTP_VERB verb;
79 HTTP_VERSION version;
80 const char *url, *host;
81 ULONG unk_verb_len, url_len, content_len;
84 static struct list connections = LIST_INIT(connections);
86 struct listening_socket
88 struct list entry;
89 unsigned short port;
90 SOCKET socket;
93 static struct list listening_sockets = LIST_INIT(listening_sockets);
95 struct url
97 struct list entry;
98 char *url;
99 HTTP_URL_CONTEXT context;
100 struct listening_socket *listening_sock;
103 struct request_queue
105 struct list entry;
106 LIST_ENTRY irp_queue;
107 struct list urls;
110 static struct list request_queues = LIST_INIT(request_queues);
112 static void accept_connection(SOCKET socket)
114 struct connection *conn;
115 ULONG true = 1;
116 SOCKET peer;
118 if ((peer = accept(socket, NULL, NULL)) == INVALID_SOCKET)
119 return;
121 if (!(conn = heap_alloc_zero(sizeof(*conn))))
123 ERR("Failed to allocate memory.\n");
124 shutdown(peer, SD_BOTH);
125 closesocket(peer);
126 return;
128 if (!(conn->buffer = heap_alloc(8192)))
130 ERR("Failed to allocate buffer memory.\n");
131 heap_free(conn);
132 shutdown(peer, SD_BOTH);
133 closesocket(peer);
134 return;
136 conn->size = 8192;
137 WSAEventSelect(peer, request_event, FD_READ | FD_CLOSE);
138 ioctlsocket(peer, FIONBIO, &true);
139 conn->socket = peer;
140 list_add_head(&connections, &conn->entry);
143 static void close_connection(struct connection *conn)
145 heap_free(conn->buffer);
146 shutdown(conn->socket, SD_BOTH);
147 closesocket(conn->socket);
148 list_remove(&conn->entry);
149 heap_free(conn);
152 static HTTP_VERB parse_verb(const char *verb, int len)
154 static const char *const verbs[] =
156 "OPTIONS",
157 "GET",
158 "HEAD",
159 "POST",
160 "PUT",
161 "DELETE",
162 "TRACE",
163 "CONNECT",
164 "TRACK",
165 "MOVE",
166 "COPY",
167 "PROPFIND",
168 "PROPPATCH",
169 "MKCOL",
170 "LOCK",
171 "UNLOCK",
172 "SEARCH",
174 unsigned int i;
176 for (i = 0; i < ARRAY_SIZE(verbs); ++i)
178 if (!strncmp(verb, verbs[i], len))
179 return HttpVerbOPTIONS + i;
181 return HttpVerbUnknown;
184 /* Return the length of a token, as defined in RFC 2616 section 2.2. */
185 static int parse_token(const char *str, const char *end)
187 const char *p;
188 for (p = str; !end || p < end; ++p)
190 if (!isgraph(*p) || strchr("()<>@,;:\\\"/[]?={}", *p))
191 break;
193 return p - str;
196 static HTTP_HEADER_ID parse_header_name(const char *header, int len)
198 static const char *const headers[] =
200 "Cache-Control",
201 "Connection",
202 "Date",
203 "Keep-Alive",
204 "Pragma",
205 "Trailer",
206 "Transfer-Encoding",
207 "Upgrade",
208 "Via",
209 "Warning",
210 "Allow",
211 "Content-Length",
212 "Content-Type",
213 "Content-Encoding",
214 "Content-Language",
215 "Content-Location",
216 "Content-MD5",
217 "Content-Range",
218 "Expires",
219 "Last-Modified",
220 "Accept",
221 "Accept-Charset",
222 "Accept-Encoding",
223 "Accept-Language",
224 "Authorization",
225 "Cookie",
226 "Expect",
227 "From",
228 "Host",
229 "If-Match",
230 "If-Modified-Since",
231 "If-None-Match",
232 "If-Range",
233 "If-Unmodified-Since",
234 "Max-Forwards",
235 "Proxy-Authorization",
236 "Referer",
237 "Range",
238 "TE",
239 "Translate",
240 "User-Agent",
242 unsigned int i;
244 for (i = 0; i < ARRAY_SIZE(headers); ++i)
246 if (!strncmp(header, headers[i], len))
247 return i;
249 return HttpHeaderRequestMaximum;
252 static void parse_header(const char *name, int *name_len, const char **value, int *value_len)
254 const char *p = name;
255 *name_len = parse_token(name, NULL);
256 p += *name_len;
257 while (*p == ' ' || *p == '\t') ++p;
258 ++p; /* skip colon */
259 while (*p == ' ' || *p == '\t') ++p;
260 *value = p;
261 while (isprint(*p) || *p == '\t') ++p;
262 while (isspace(*p)) --p; /* strip trailing LWS */
263 *value_len = p - *value + 1;
266 #define http_unknown_header http_unknown_header_64
267 #define http_data_chunk http_data_chunk_64
268 #define http_request http_request_64
269 #define complete_irp complete_irp_64
270 #define POINTER ULONGLONG
271 #include "request.h"
272 #undef http_unknown_header
273 #undef http_data_chunk
274 #undef http_request
275 #undef complete_irp
276 #undef POINTER
278 #define http_unknown_header http_unknown_header_32
279 #define http_data_chunk http_data_chunk_32
280 #define http_request http_request_32
281 #define complete_irp complete_irp_32
282 #define POINTER ULONG
283 #include "request.h"
284 #undef http_unknown_header
285 #undef http_data_chunk
286 #undef http_request
287 #undef complete_irp
288 #undef POINTER
290 static NTSTATUS complete_irp(struct connection *conn, IRP *irp)
292 const struct http_receive_request_params params
293 = *(struct http_receive_request_params *)irp->AssociatedIrp.SystemBuffer;
295 TRACE("Completing IRP %p.\n", irp);
297 if (!conn->req_id)
298 conn->req_id = ++req_id_counter;
300 if (params.bits == 32)
301 return complete_irp_32(conn, irp);
302 else
303 return complete_irp_64(conn, irp);
306 /* Complete an IOCTL_HTTP_RECEIVE_REQUEST IRP if there is one to complete. */
307 static void try_complete_irp(struct connection *conn)
309 LIST_ENTRY *entry;
310 if (conn->queue && (entry = RemoveHeadList(&conn->queue->irp_queue)) != &conn->queue->irp_queue)
312 IRP *irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
313 irp->IoStatus.Status = complete_irp(conn, irp);
314 IoCompleteRequest(irp, IO_NO_INCREMENT);
318 /* Return 1 if str matches expect, 0 if str is incomplete, -1 if they don't match. */
319 static int compare_exact(const char *str, const char *expect, const char *end)
321 while (*expect)
323 if (str >= end) return 0;
324 if (*str++ != *expect++) return -1;
326 return 1;
329 static int parse_number(const char *str, const char **endptr, const char *end)
331 int n = 0;
332 while (str < end && isdigit(*str))
333 n = n * 10 + (*str++ - '0');
334 *endptr = str;
335 return n;
339 /* 0 means not a match, 1 and higher means a match, higher means more paths matched. */
340 static unsigned int compare_paths(const char *queue_path, const char *conn_path, size_t conn_len)
342 const char *question_mark;
343 unsigned int i, cnt = 1;
344 size_t queue_len;
346 queue_len = strlen(queue_path);
348 if ((question_mark = memchr(conn_path, '?', conn_len)))
349 conn_len = question_mark - conn_path;
351 if (queue_path[queue_len - 1] == '/')
352 queue_len--;
353 if (conn_path[conn_len - 1] == '/')
354 conn_len--;
356 if (conn_len < queue_len)
357 return 0;
359 for (i = 0; i < queue_len; ++i)
361 if (queue_path[i] != conn_path[i])
362 return 0;
363 if (queue_path[i] == '/')
364 cnt++;
367 if (queue_len == conn_len || conn_path[queue_len] == '/')
368 return cnt;
369 else
370 return 0;
373 static BOOL host_matches(const struct url *url, const char *conn_host)
375 size_t host_len;
377 if (!url->url)
378 return FALSE;
380 if (url->url[7] == '+')
382 const char *queue_port = strchr(url->url + 7, ':');
383 host_len = strchr(queue_port, '/') - queue_port - 1;
384 if (!strncmp(queue_port, strchr(conn_host, ':'), host_len))
385 return TRUE;
387 else
389 host_len = strchr(url->url + 7, '/') - url->url - 7;
390 if (!memicmp(url->url + 7, conn_host, host_len))
391 return TRUE;
394 return FALSE;
397 static struct url *url_matches(const struct connection *conn, const struct request_queue *queue,
398 unsigned int *ret_slash_count)
400 const char *queue_path, *conn_host, *conn_path;
401 unsigned int max_slash_count = 0, slash_count;
402 size_t conn_path_len;
403 struct url *url, *ret = NULL;
405 if (conn->url[0] == '/')
407 conn_host = conn->host;
408 conn_path = conn->url;
409 conn_path_len = conn->url_len;
412 else
414 conn_host = conn->url + 7;
415 conn_path = strchr(conn_host, '/');
416 conn_path_len = (conn->url + conn->url_len) - conn_path;
419 LIST_FOR_EACH_ENTRY(url, &queue->urls, struct url, entry)
421 if (host_matches(url, conn_host))
423 queue_path = strchr(url->url + 7, '/');
424 if (!queue_path)
425 continue;
426 slash_count = compare_paths(queue_path, conn_path, conn_path_len);
427 if (slash_count > max_slash_count)
429 max_slash_count = slash_count;
430 ret = url;
435 if (ret_slash_count)
436 *ret_slash_count = max_slash_count;
438 return ret;
441 /* Upon receiving a request, parse it to ensure that it is a valid HTTP request,
442 * and mark down some information that we will use later. Returns 1 if we parsed
443 * a complete request, 0 if incomplete, -1 if invalid. */
444 static int parse_request(struct connection *conn)
446 const char *const req = conn->buffer, *const end = conn->buffer + conn->len;
447 struct request_queue *queue, *best_queue = NULL;
448 struct url *conn_url, *best_conn_url = NULL;
449 const char *p = req, *q;
450 unsigned int slash_count, best_slash_count = 0;
451 int len, ret;
453 if (!conn->len) return 0;
455 TRACE("%s\n", wine_dbgstr_an(conn->buffer, conn->len));
457 len = parse_token(p, end);
458 if (p + len >= end) return 0;
459 if (!len || p[len] != ' ') return -1;
461 /* verb */
462 if ((conn->verb = parse_verb(p, len)) == HttpVerbUnknown)
463 conn->unk_verb_len = len;
464 p += len + 1;
466 TRACE("Got verb %u (%s).\n", conn->verb, debugstr_an(req, len));
468 /* URL */
469 conn->url = p;
470 while (p < end && isgraph(*p)) ++p;
471 conn->url_len = p - conn->url;
472 if (p >= end) return 0;
473 if (!conn->url_len) return -1;
475 TRACE("Got URI %s.\n", debugstr_an(conn->url, conn->url_len));
477 /* version */
478 if ((ret = compare_exact(p, " HTTP/", end)) <= 0) return ret;
479 p += 6;
480 conn->version.MajorVersion = parse_number(p, &q, end);
481 if (q >= end) return 0;
482 if (q == p || *q != '.') return -1;
483 p = q + 1;
484 if (p >= end) return 0;
485 conn->version.MinorVersion = parse_number(p, &q, end);
486 if (q >= end) return 0;
487 if (q == p) return -1;
488 p = q;
489 if ((ret = compare_exact(p, "\r\n", end)) <= 0) return ret;
490 p += 2;
492 TRACE("Got version %hu.%hu.\n", conn->version.MajorVersion, conn->version.MinorVersion);
494 /* headers */
495 conn->host = NULL;
496 conn->content_len = 0;
497 for (;;)
499 const char *name = p;
501 if (!(ret = compare_exact(p, "\r\n", end))) return 0;
502 else if (ret > 0) break;
504 len = parse_token(p, end);
505 if (p + len >= end) return 0;
506 if (!len) return -1;
507 p += len;
508 while (p < end && (*p == ' ' || *p == '\t')) ++p;
509 if (p >= end) return 0;
510 if (*p != ':') return -1;
511 ++p;
512 while (p < end && (*p == ' ' || *p == '\t')) ++p;
514 TRACE("Got %s header.\n", debugstr_an(name, len));
516 if (!strncmp(name, "Host", len))
517 conn->host = p;
518 else if (!strncmp(name, "Content-Length", len))
520 conn->content_len = parse_number(p, &q, end);
521 if (q >= end) return 0;
522 if (q == p) return -1;
524 else if (!strncmp(name, "Transfer-Encoding", len))
525 FIXME("Unhandled Transfer-Encoding header.\n");
526 while (p < end && (isprint(*p) || *p == '\t')) ++p;
527 if ((ret = compare_exact(p, "\r\n", end)) <= 0) return ret;
528 p += 2;
530 p += 2;
531 if (conn->url[0] == '/' && !conn->host) return -1;
533 if (end - p < conn->content_len) return 0;
535 conn->req_len = (p - req) + conn->content_len;
537 TRACE("Received a full request, length %u bytes.\n", conn->req_len);
539 conn->queue = NULL;
540 /* Find a queue which can receive this request. */
541 LIST_FOR_EACH_ENTRY(queue, &request_queues, struct request_queue, entry)
543 if ((conn_url = url_matches(conn, queue, &slash_count)))
545 if (slash_count > best_slash_count)
547 best_slash_count = slash_count;
548 best_queue = queue;
549 best_conn_url = conn_url;
554 if (best_conn_url)
556 TRACE("Assigning request to queue %p.\n", best_queue);
557 conn->queue = best_queue;
558 conn->context = best_conn_url->context;
561 /* Stop selecting on incoming data until a response is queued. */
562 WSAEventSelect(conn->socket, request_event, FD_CLOSE);
564 conn->available = TRUE;
565 try_complete_irp(conn);
567 return 1;
570 static void format_date(char *buffer)
572 static const char day_names[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
573 static const char month_names[12][4] =
574 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
575 SYSTEMTIME date;
576 GetSystemTime(&date);
577 sprintf(buffer + strlen(buffer), "Date: %s, %02u %s %u %02u:%02u:%02u GMT\r\n",
578 day_names[date.wDayOfWeek], date.wDay, month_names[date.wMonth - 1],
579 date.wYear, date.wHour, date.wMinute, date.wSecond);
582 /* Send a 400 Bad Request response. */
583 static void send_400(struct connection *conn)
585 static const char response_header[] = "HTTP/1.1 400 Bad Request\r\n";
586 static const char response_body[] =
587 "Content-Type: text/html; charset=utf-8\r\n"
588 "Content-Language: en\r\n"
589 "Connection: close\r\n";
590 char buffer[sizeof(response_header) + sizeof(response_body) + 37];
592 strcpy(buffer, response_header);
593 format_date(buffer + strlen(buffer));
594 strcat(buffer, response_body);
595 if (send(conn->socket, buffer, strlen(buffer), 0) < 0)
596 ERR("Failed to send 400 response, error %u.\n", WSAGetLastError());
599 static void receive_data(struct connection *conn)
601 int len, ret;
603 /* We might be waiting for an IRP, but always call recv() anyway, since we
604 * might have been woken up by the socket closing. */
605 if ((len = recv(conn->socket, conn->buffer + conn->len, conn->size - conn->len, 0)) <= 0)
607 if (WSAGetLastError() == WSAEWOULDBLOCK)
608 return; /* nothing to receive */
609 else if (!len)
610 TRACE("Connection was shut down by peer.\n");
611 else
612 ERR("Got error %u; shutting down connection.\n", WSAGetLastError());
613 close_connection(conn);
614 return;
616 conn->len += len;
618 if (conn->available)
619 return; /* waiting for an HttpReceiveHttpRequest() call */
620 if (conn->req_id != HTTP_NULL_ID)
621 return; /* waiting for an HttpSendHttpResponse() call */
623 TRACE("Received %u bytes of data.\n", len);
625 if (!(ret = parse_request(conn)))
627 ULONG available;
628 ioctlsocket(conn->socket, FIONREAD, &available);
629 if (available)
631 TRACE("%lu more bytes of data available, trying with larger buffer.\n", available);
632 if (!(conn->buffer = heap_realloc(conn->buffer, conn->len + available)))
634 ERR("Failed to allocate %lu bytes of memory.\n", conn->len + available);
635 close_connection(conn);
636 return;
638 conn->size = conn->len + available;
640 if ((len = recv(conn->socket, conn->buffer + conn->len, conn->size - conn->len, 0)) < 0)
642 ERR("Got error %u; shutting down connection.\n", WSAGetLastError());
643 close_connection(conn);
644 return;
646 TRACE("Received %u bytes of data.\n", len);
647 conn->len += len;
648 ret = parse_request(conn);
651 if (!ret)
652 TRACE("Request is incomplete, waiting for more data.\n");
653 else if (ret < 0)
655 WARN("Failed to parse request; shutting down connection.\n");
656 send_400(conn);
657 close_connection(conn);
661 static DWORD WINAPI request_thread_proc(void *arg)
663 struct connection *conn, *cursor;
664 struct request_queue *queue;
665 struct url *url;
667 TRACE("Starting request thread.\n");
669 while (!WaitForSingleObject(request_event, INFINITE))
671 EnterCriticalSection(&http_cs);
673 LIST_FOR_EACH_ENTRY(queue, &request_queues, struct request_queue, entry)
675 LIST_FOR_EACH_ENTRY(url, &queue->urls, struct url, entry)
677 if (url->listening_sock && url->listening_sock->socket != -1)
678 accept_connection(url->listening_sock->socket);
682 LIST_FOR_EACH_ENTRY_SAFE(conn, cursor, &connections, struct connection, entry)
684 receive_data(conn);
687 LeaveCriticalSection(&http_cs);
690 TRACE("Stopping request thread.\n");
692 return 0;
695 static struct listening_socket *get_listening_socket(unsigned short port)
697 struct listening_socket *listening_sock;
699 LIST_FOR_EACH_ENTRY(listening_sock, &listening_sockets, struct listening_socket, entry)
701 if (listening_sock->port == port)
702 return listening_sock;
705 return NULL;
708 static NTSTATUS http_add_url(struct request_queue *queue, IRP *irp)
710 const struct http_add_url_params *params = irp->AssociatedIrp.SystemBuffer;
711 struct request_queue *queue_entry;
712 struct sockaddr_in addr;
713 struct connection *conn;
714 struct url *url_entry, *new_entry;
715 struct listening_socket *listening_sock;
716 char *url, *endptr;
717 size_t queue_url_len, new_url_len;
718 ULONG true = 1, value;
719 SOCKET s = INVALID_SOCKET;
721 TRACE("host %s, context %s.\n", debugstr_a(params->url), wine_dbgstr_longlong(params->context));
723 if (!strncmp(params->url, "https://", 8))
725 FIXME("HTTPS is not implemented.\n");
726 return STATUS_NOT_IMPLEMENTED;
728 else if (strncmp(params->url, "http://", 7) || !strchr(params->url + 7, ':'))
729 return STATUS_INVALID_PARAMETER;
730 if (!(addr.sin_port = htons(strtol(strchr(params->url + 7, ':') + 1, &endptr, 10))) || *endptr != '/')
731 return STATUS_INVALID_PARAMETER;
732 if (strchr(params->url, '?'))
733 return STATUS_INVALID_PARAMETER;
735 if (!(url = malloc(strlen(params->url)+1)))
736 return STATUS_NO_MEMORY;
737 strcpy(url, params->url);
739 if (!(new_entry = malloc(sizeof(struct url))))
741 free(url);
742 return STATUS_NO_MEMORY;
745 new_url_len = strlen(url);
746 if (url[new_url_len - 1] == '/')
747 new_url_len--;
749 EnterCriticalSection(&http_cs);
751 LIST_FOR_EACH_ENTRY(queue_entry, &request_queues, struct request_queue, entry)
753 LIST_FOR_EACH_ENTRY(url_entry, &queue_entry->urls, struct url, entry)
755 queue_url_len = strlen(url_entry->url);
756 if (url_entry->url[queue_url_len - 1] == '/')
757 queue_url_len--;
759 if (url_entry->url && queue_url_len == new_url_len && !memcmp(url_entry->url, url, queue_url_len))
761 LeaveCriticalSection(&http_cs);
762 free(url);
763 free(new_entry);
764 return STATUS_OBJECT_NAME_COLLISION;
769 listening_sock = get_listening_socket(addr.sin_port);
771 if (!listening_sock)
773 if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
775 ERR("Failed to create socket, error %u.\n", WSAGetLastError());
776 LeaveCriticalSection(&http_cs);
777 free(url);
778 free(new_entry);
779 return STATUS_UNSUCCESSFUL;
782 addr.sin_family = AF_INET;
783 addr.sin_addr.S_un.S_addr = INADDR_ANY;
784 value = 1;
785 setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&value, sizeof(value));
786 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
788 LeaveCriticalSection(&http_cs);
789 closesocket(s);
790 free(url);
791 free(new_entry);
792 if (WSAGetLastError() == WSAEADDRINUSE)
794 WARN("Address %s is already in use.\n", debugstr_a(params->url));
795 return STATUS_SHARING_VIOLATION;
797 else if (WSAGetLastError() == WSAEACCES)
799 WARN("Not enough permissions to bind to address %s.\n", debugstr_a(params->url));
800 return STATUS_ACCESS_DENIED;
802 ERR("Failed to bind socket, error %u.\n", WSAGetLastError());
803 return STATUS_UNSUCCESSFUL;
806 if (listen(s, SOMAXCONN) == -1)
808 ERR("Failed to listen to port %u, error %u.\n", addr.sin_port, WSAGetLastError());
809 LeaveCriticalSection(&http_cs);
810 closesocket(s);
811 free(url);
812 free(new_entry);
813 return STATUS_OBJECT_NAME_COLLISION;
816 if (!(listening_sock = malloc(sizeof(struct listening_socket))))
818 LeaveCriticalSection(&http_cs);
819 closesocket(s);
820 free(url);
821 free(new_entry);
822 return STATUS_NO_MEMORY;
824 listening_sock->port = addr.sin_port;
825 listening_sock->socket = s;
826 list_add_head(&listening_sockets, &listening_sock->entry);
828 ioctlsocket(s, FIONBIO, &true);
829 WSAEventSelect(s, request_event, FD_ACCEPT);
832 new_entry->url = url;
833 new_entry->context = params->context;
834 new_entry->listening_sock = listening_sock;
835 list_add_head(&queue->urls, &new_entry->entry);
837 /* See if any pending requests now match this queue. */
838 LIST_FOR_EACH_ENTRY(conn, &connections, struct connection, entry)
840 if (conn->available && !conn->queue && url_matches(conn, queue, NULL))
842 conn->queue = queue;
843 conn->context = params->context;
844 try_complete_irp(conn);
848 LeaveCriticalSection(&http_cs);
850 return STATUS_SUCCESS;
853 static BOOL is_listening_socket_used(const struct listening_socket *listening_sock)
855 struct request_queue *queue_entry;
856 struct url *url_entry;
858 LIST_FOR_EACH_ENTRY(queue_entry, &request_queues, struct request_queue, entry)
860 LIST_FOR_EACH_ENTRY(url_entry, &queue_entry->urls, struct url, entry)
862 if (listening_sock == url_entry->listening_sock)
864 return TRUE;
869 return FALSE;
872 static NTSTATUS http_remove_url(struct request_queue *queue, IRP *irp)
874 const char *url = irp->AssociatedIrp.SystemBuffer;
875 struct url *url_entry;
877 TRACE("host %s.\n", debugstr_a(url));
879 EnterCriticalSection(&http_cs);
881 LIST_FOR_EACH_ENTRY(url_entry, &queue->urls, struct url, entry)
883 if (url_entry->url && !strcmp(url, url_entry->url))
885 free(url_entry->url);
886 url_entry->url = NULL;
888 if (!is_listening_socket_used(url_entry->listening_sock))
890 shutdown(url_entry->listening_sock->socket, SD_BOTH);
891 closesocket(url_entry->listening_sock->socket);
892 list_remove(&url_entry->listening_sock->entry);
893 free(url_entry->listening_sock);
895 url_entry->listening_sock = NULL;
897 list_remove(&url_entry->entry);
898 free(url_entry);
900 LeaveCriticalSection(&http_cs);
901 return STATUS_SUCCESS;
905 LeaveCriticalSection(&http_cs);
906 return STATUS_OBJECT_NAME_NOT_FOUND;
909 static struct connection *get_connection(HTTP_REQUEST_ID req_id)
911 struct connection *conn;
913 LIST_FOR_EACH_ENTRY(conn, &connections, struct connection, entry)
915 if (conn->req_id == req_id)
916 return conn;
918 return NULL;
921 static void WINAPI http_receive_request_cancel(DEVICE_OBJECT *device, IRP *irp)
923 TRACE("device %p, irp %p.\n", device, irp);
925 IoReleaseCancelSpinLock(irp->CancelIrql);
927 EnterCriticalSection(&http_cs);
928 RemoveEntryList(&irp->Tail.Overlay.ListEntry);
929 LeaveCriticalSection(&http_cs);
931 irp->IoStatus.Status = STATUS_CANCELLED;
932 IoCompleteRequest(irp, IO_NO_INCREMENT);
935 static NTSTATUS http_receive_request(struct request_queue *queue, IRP *irp)
937 const struct http_receive_request_params *params = irp->AssociatedIrp.SystemBuffer;
938 struct connection *conn;
939 NTSTATUS ret;
941 TRACE("addr %s, id %s, flags %#lx, bits %lu.\n", wine_dbgstr_longlong(params->addr),
942 wine_dbgstr_longlong(params->id), params->flags, params->bits);
944 EnterCriticalSection(&http_cs);
946 if ((conn = get_connection(params->id)) && conn->available && conn->queue == queue)
948 ret = complete_irp(conn, irp);
949 LeaveCriticalSection(&http_cs);
950 return ret;
953 if (params->id == HTTP_NULL_ID)
955 TRACE("Queuing IRP %p.\n", irp);
957 IoSetCancelRoutine(irp, http_receive_request_cancel);
958 if (irp->Cancel && !IoSetCancelRoutine(irp, NULL))
960 /* The IRP was canceled before we set the cancel routine. */
961 ret = STATUS_CANCELLED;
963 else
965 IoMarkIrpPending(irp);
966 InsertTailList(&queue->irp_queue, &irp->Tail.Overlay.ListEntry);
967 ret = STATUS_PENDING;
970 else
971 ret = STATUS_CONNECTION_INVALID;
973 LeaveCriticalSection(&http_cs);
975 return ret;
978 static NTSTATUS http_send_response(struct request_queue *queue, IRP *irp)
980 const struct http_response *response = irp->AssociatedIrp.SystemBuffer;
981 struct connection *conn;
983 TRACE("id %s, len %d.\n", wine_dbgstr_longlong(response->id), response->len);
985 EnterCriticalSection(&http_cs);
987 if ((conn = get_connection(response->id)))
989 if (send(conn->socket, response->buffer, response->len, 0) >= 0)
991 if (conn->content_len)
993 /* Discard whatever entity body is left. */
994 memmove(conn->buffer, conn->buffer + conn->content_len, conn->len - conn->content_len);
995 conn->len -= conn->content_len;
998 conn->queue = NULL;
999 conn->req_id = HTTP_NULL_ID;
1000 WSAEventSelect(conn->socket, request_event, FD_READ | FD_CLOSE);
1001 irp->IoStatus.Information = response->len;
1002 /* We might have another request already in the buffer. */
1003 if (parse_request(conn) < 0)
1005 WARN("Failed to parse request; shutting down connection.\n");
1006 send_400(conn);
1007 close_connection(conn);
1010 else
1012 ERR("Got error %u; shutting down connection.\n", WSAGetLastError());
1013 close_connection(conn);
1016 LeaveCriticalSection(&http_cs);
1017 return STATUS_SUCCESS;
1020 LeaveCriticalSection(&http_cs);
1021 return STATUS_CONNECTION_INVALID;
1024 static NTSTATUS http_receive_body(struct request_queue *queue, IRP *irp)
1026 const struct http_receive_body_params *params = irp->AssociatedIrp.SystemBuffer;
1027 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
1028 const DWORD output_len = stack->Parameters.DeviceIoControl.OutputBufferLength;
1029 struct connection *conn;
1030 NTSTATUS ret;
1032 TRACE("id %s, bits %lu.\n", wine_dbgstr_longlong(params->id), params->bits);
1034 EnterCriticalSection(&http_cs);
1036 if ((conn = get_connection(params->id)))
1038 TRACE("%lu bits remaining.\n", conn->content_len);
1040 if (conn->content_len)
1042 ULONG len = min(conn->content_len, output_len);
1043 memcpy(irp->AssociatedIrp.SystemBuffer, conn->buffer, len);
1044 memmove(conn->buffer, conn->buffer + len, conn->len - len);
1045 conn->content_len -= len;
1046 conn->len -= len;
1048 irp->IoStatus.Information = len;
1049 ret = STATUS_SUCCESS;
1051 else
1052 ret = STATUS_END_OF_FILE;
1054 else
1055 ret = STATUS_CONNECTION_INVALID;
1057 LeaveCriticalSection(&http_cs);
1059 return ret;
1062 static NTSTATUS WINAPI dispatch_ioctl(DEVICE_OBJECT *device, IRP *irp)
1064 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
1065 struct request_queue *queue = stack->FileObject->FsContext;
1066 NTSTATUS ret;
1068 switch (stack->Parameters.DeviceIoControl.IoControlCode)
1070 case IOCTL_HTTP_ADD_URL:
1071 ret = http_add_url(queue, irp);
1072 break;
1073 case IOCTL_HTTP_REMOVE_URL:
1074 ret = http_remove_url(queue, irp);
1075 break;
1076 case IOCTL_HTTP_RECEIVE_REQUEST:
1077 ret = http_receive_request(queue, irp);
1078 break;
1079 case IOCTL_HTTP_SEND_RESPONSE:
1080 ret = http_send_response(queue, irp);
1081 break;
1082 case IOCTL_HTTP_RECEIVE_BODY:
1083 ret = http_receive_body(queue, irp);
1084 break;
1085 default:
1086 FIXME("Unhandled ioctl %#lx.\n", stack->Parameters.DeviceIoControl.IoControlCode);
1087 ret = STATUS_NOT_IMPLEMENTED;
1090 if (ret != STATUS_PENDING)
1092 irp->IoStatus.Status = ret;
1093 IoCompleteRequest(irp, IO_NO_INCREMENT);
1095 return ret;
1098 static NTSTATUS WINAPI dispatch_create(DEVICE_OBJECT *device, IRP *irp)
1100 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
1101 struct request_queue *queue;
1103 if (!(queue = calloc(1, sizeof(*queue))))
1104 return STATUS_NO_MEMORY;
1105 list_init(&queue->urls);
1106 stack->FileObject->FsContext = queue;
1107 InitializeListHead(&queue->irp_queue);
1109 EnterCriticalSection(&http_cs);
1110 list_add_head(&request_queues, &queue->entry);
1111 LeaveCriticalSection(&http_cs);
1113 TRACE("Created queue %p.\n", queue);
1115 irp->IoStatus.Status = STATUS_SUCCESS;
1116 IoCompleteRequest(irp, IO_NO_INCREMENT);
1117 return STATUS_SUCCESS;
1120 static void close_queue(struct request_queue *queue)
1122 struct url *url, *url_next;
1123 struct listening_socket *listening_sock, *listening_sock_next;
1125 EnterCriticalSection(&http_cs);
1126 list_remove(&queue->entry);
1128 LIST_FOR_EACH_ENTRY_SAFE(url, url_next, &queue->urls, struct url, entry)
1130 free(url->url);
1131 free(url);
1134 LIST_FOR_EACH_ENTRY_SAFE(listening_sock, listening_sock_next, &listening_sockets, struct listening_socket, entry)
1136 shutdown(listening_sock->socket, SD_BOTH);
1137 closesocket(listening_sock->socket);
1138 list_remove(&listening_sock->entry);
1139 free(listening_sock);
1142 free(queue);
1144 LeaveCriticalSection(&http_cs);
1147 static NTSTATUS WINAPI dispatch_close(DEVICE_OBJECT *device, IRP *irp)
1149 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
1150 struct request_queue *queue = stack->FileObject->FsContext;
1151 LIST_ENTRY *entry;
1153 TRACE("Closing queue %p.\n", queue);
1155 EnterCriticalSection(&http_cs);
1157 while ((entry = queue->irp_queue.Flink) != &queue->irp_queue)
1159 IRP *queued_irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
1160 IoCancelIrp(queued_irp);
1163 LeaveCriticalSection(&http_cs);
1165 close_queue(queue);
1167 irp->IoStatus.Status = STATUS_SUCCESS;
1168 IoCompleteRequest(irp, IO_NO_INCREMENT);
1169 return STATUS_SUCCESS;
1172 static void WINAPI unload(DRIVER_OBJECT *driver)
1174 struct request_queue *queue, *queue_next;
1175 struct connection *conn, *conn_next;
1177 thread_stop = TRUE;
1178 SetEvent(request_event);
1179 WaitForSingleObject(request_thread, INFINITE);
1180 CloseHandle(request_thread);
1181 CloseHandle(request_event);
1183 LIST_FOR_EACH_ENTRY_SAFE(conn, conn_next, &connections, struct connection, entry)
1185 close_connection(conn);
1188 LIST_FOR_EACH_ENTRY_SAFE(queue, queue_next, &request_queues, struct request_queue, entry)
1190 close_queue(queue);
1193 WSACleanup();
1195 IoDeleteDevice(device_obj);
1196 NtClose(directory_obj);
1199 NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *path)
1201 OBJECT_ATTRIBUTES attr = {sizeof(attr)};
1202 UNICODE_STRING device_http = RTL_CONSTANT_STRING(L"\\Device\\Http");
1203 UNICODE_STRING device_http_req_queue = RTL_CONSTANT_STRING(L"\\Device\\Http\\ReqQueue");
1204 WSADATA wsadata;
1205 NTSTATUS ret;
1207 TRACE("driver %p, path %s.\n", driver, debugstr_w(path->Buffer));
1209 attr.ObjectName = &device_http;
1210 if ((ret = NtCreateDirectoryObject(&directory_obj, 0, &attr)) && ret != STATUS_OBJECT_NAME_COLLISION)
1211 ERR("Failed to create \\Device\\Http directory, status %#lx.\n", ret);
1213 if ((ret = IoCreateDevice(driver, 0, &device_http_req_queue, FILE_DEVICE_UNKNOWN, 0, FALSE, &device_obj)))
1215 ERR("Failed to create request queue device, status %#lx.\n", ret);
1216 NtClose(directory_obj);
1217 return ret;
1220 driver->MajorFunction[IRP_MJ_CREATE] = dispatch_create;
1221 driver->MajorFunction[IRP_MJ_CLOSE] = dispatch_close;
1222 driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = dispatch_ioctl;
1223 driver->DriverUnload = unload;
1225 WSAStartup(MAKEWORD(1,1), &wsadata);
1227 request_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1228 request_thread = CreateThread(NULL, 0, request_thread_proc, NULL, 0, NULL);
1230 return STATUS_SUCCESS;