ntdll: Use cpu_info to check for AVX availability.
[wine.git] / dlls / http.sys / http.c
blobe655edfeea295cae7bd0ab5ec94095d556bc41e4
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;
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 request_queue
87 struct list entry;
88 LIST_ENTRY irp_queue;
89 HTTP_URL_CONTEXT context;
90 char *url;
91 SOCKET socket;
94 static struct list request_queues = LIST_INIT(request_queues);
96 static void accept_connection(SOCKET socket)
98 struct connection *conn;
99 ULONG true = 1;
100 SOCKET peer;
102 if ((peer = accept(socket, NULL, NULL)) == INVALID_SOCKET)
103 return;
105 if (!(conn = heap_alloc_zero(sizeof(*conn))))
107 ERR("Failed to allocate memory.\n");
108 shutdown(peer, SD_BOTH);
109 closesocket(peer);
110 return;
112 if (!(conn->buffer = heap_alloc(8192)))
114 ERR("Failed to allocate buffer memory.\n");
115 heap_free(conn);
116 shutdown(peer, SD_BOTH);
117 closesocket(peer);
118 return;
120 conn->size = 8192;
121 WSAEventSelect(peer, request_event, FD_READ | FD_CLOSE);
122 ioctlsocket(peer, FIONBIO, &true);
123 conn->socket = peer;
124 list_add_head(&connections, &conn->entry);
127 static void close_connection(struct connection *conn)
129 heap_free(conn->buffer);
130 shutdown(conn->socket, SD_BOTH);
131 closesocket(conn->socket);
132 list_remove(&conn->entry);
133 heap_free(conn);
136 static HTTP_VERB parse_verb(const char *verb, int len)
138 static const char *const verbs[] =
140 "OPTIONS",
141 "GET",
142 "HEAD",
143 "POST",
144 "PUT",
145 "DELETE",
146 "TRACE",
147 "CONNECT",
148 "TRACK",
149 "MOVE",
150 "COPY",
151 "PROPFIND",
152 "PROPPATCH",
153 "MKCOL",
154 "LOCK",
155 "UNLOCK",
156 "SEARCH",
158 unsigned int i;
160 for (i = 0; i < ARRAY_SIZE(verbs); ++i)
162 if (!strncmp(verb, verbs[i], len))
163 return HttpVerbOPTIONS + i;
165 return HttpVerbUnknown;
168 /* Return the length of a token, as defined in RFC 2616 section 2.2. */
169 static int parse_token(const char *str, const char *end)
171 const char *p;
172 for (p = str; !end || p < end; ++p)
174 if (!isgraph(*p) || strchr("()<>@,;:\\\"/[]?={}", *p))
175 break;
177 return p - str;
180 static HTTP_HEADER_ID parse_header_name(const char *header, int len)
182 static const char *const headers[] =
184 "Cache-Control",
185 "Connection",
186 "Date",
187 "Keep-Alive",
188 "Pragma",
189 "Trailer",
190 "Transfer-Encoding",
191 "Upgrade",
192 "Via",
193 "Warning",
194 "Allow",
195 "Content-Length",
196 "Content-Type",
197 "Content-Encoding",
198 "Content-Language",
199 "Content-Location",
200 "Content-MD5",
201 "Content-Range",
202 "Expires",
203 "Last-Modified",
204 "Accept",
205 "Accept-Charset",
206 "Accept-Encoding",
207 "Accept-Language",
208 "Authorization",
209 "Cookie",
210 "Expect",
211 "From",
212 "Host",
213 "If-Match",
214 "If-Modified-Since",
215 "If-None-Match",
216 "If-Range",
217 "If-Unmodified-Since",
218 "Max-Forwards",
219 "Proxy-Authorization",
220 "Referer",
221 "Range",
222 "TE",
223 "Translate",
224 "User-Agent",
226 unsigned int i;
228 for (i = 0; i < ARRAY_SIZE(headers); ++i)
230 if (!strncmp(header, headers[i], len))
231 return i;
233 return HttpHeaderRequestMaximum;
236 static void parse_header(const char *name, int *name_len, const char **value, int *value_len)
238 const char *p = name;
239 *name_len = parse_token(name, NULL);
240 p += *name_len;
241 while (*p == ' ' || *p == '\t') ++p;
242 ++p; /* skip colon */
243 while (*p == ' ' || *p == '\t') ++p;
244 *value = p;
245 while (isprint(*p) || *p == '\t') ++p;
246 while (isspace(*p)) --p; /* strip trailing LWS */
247 *value_len = p - *value + 1;
250 #define http_unknown_header http_unknown_header_64
251 #define http_data_chunk http_data_chunk_64
252 #define http_request http_request_64
253 #define complete_irp complete_irp_64
254 #define POINTER ULONGLONG
255 #include "request.h"
256 #undef http_unknown_header
257 #undef http_data_chunk
258 #undef http_request
259 #undef complete_irp
260 #undef POINTER
262 #define http_unknown_header http_unknown_header_32
263 #define http_data_chunk http_data_chunk_32
264 #define http_request http_request_32
265 #define complete_irp complete_irp_32
266 #define POINTER ULONG
267 #include "request.h"
268 #undef http_unknown_header
269 #undef http_data_chunk
270 #undef http_request
271 #undef complete_irp
272 #undef POINTER
274 static NTSTATUS complete_irp(struct connection *conn, IRP *irp)
276 const struct http_receive_request_params params
277 = *(struct http_receive_request_params *)irp->AssociatedIrp.SystemBuffer;
279 TRACE("Completing IRP %p.\n", irp);
281 if (!conn->req_id)
282 conn->req_id = ++req_id_counter;
284 if (params.bits == 32)
285 return complete_irp_32(conn, irp);
286 else
287 return complete_irp_64(conn, irp);
290 /* Complete an IOCTL_HTTP_RECEIVE_REQUEST IRP if there is one to complete. */
291 static void try_complete_irp(struct connection *conn)
293 LIST_ENTRY *entry;
294 if (conn->queue && (entry = RemoveHeadList(&conn->queue->irp_queue)) != &conn->queue->irp_queue)
296 IRP *irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
297 irp->IoStatus.Status = complete_irp(conn, irp);
298 IoCompleteRequest(irp, IO_NO_INCREMENT);
302 /* Return 1 if str matches expect, 0 if str is incomplete, -1 if they don't match. */
303 static int compare_exact(const char *str, const char *expect, const char *end)
305 while (*expect)
307 if (str >= end) return 0;
308 if (*str++ != *expect++) return -1;
310 return 1;
313 static int parse_number(const char *str, const char **endptr, const char *end)
315 int n = 0;
316 while (str < end && isdigit(*str))
317 n = n * 10 + (*str++ - '0');
318 *endptr = str;
319 return n;
322 static BOOL host_matches(const struct connection *conn, const struct request_queue *queue)
324 const char *conn_host = (conn->url[0] == '/') ? conn->host : conn->url + 7;
326 if (queue->url[7] == '+')
328 const char *queue_port = strchr(queue->url + 7, ':');
329 return !strncmp(queue_port, strchr(conn_host, ':'), strlen(queue_port) - 1 /* strip final slash */);
332 return !memicmp(queue->url + 7, conn_host, strlen(queue->url) - 8 /* strip final slash */);
335 /* Upon receiving a request, parse it to ensure that it is a valid HTTP request,
336 * and mark down some information that we will use later. Returns 1 if we parsed
337 * a complete request, 0 if incomplete, -1 if invalid. */
338 static int parse_request(struct connection *conn)
340 const char *const req = conn->buffer, *const end = conn->buffer + conn->len;
341 struct request_queue *queue;
342 const char *p = req, *q;
343 int len, ret;
345 if (!conn->len) return 0;
347 TRACE("%s\n", wine_dbgstr_an(conn->buffer, conn->len));
349 len = parse_token(p, end);
350 if (p + len >= end) return 0;
351 if (!len || p[len] != ' ') return -1;
353 /* verb */
354 if ((conn->verb = parse_verb(p, len)) == HttpVerbUnknown)
355 conn->unk_verb_len = len;
356 p += len + 1;
358 TRACE("Got verb %u (%s).\n", conn->verb, debugstr_an(req, len));
360 /* URL */
361 conn->url = p;
362 while (p < end && isgraph(*p)) ++p;
363 conn->url_len = p - conn->url;
364 if (p >= end) return 0;
365 if (!conn->url_len) return -1;
367 TRACE("Got URI %s.\n", debugstr_an(conn->url, conn->url_len));
369 /* version */
370 if ((ret = compare_exact(p, " HTTP/", end)) <= 0) return ret;
371 p += 6;
372 conn->version.MajorVersion = parse_number(p, &q, end);
373 if (q >= end) return 0;
374 if (q == p || *q != '.') return -1;
375 p = q + 1;
376 if (p >= end) return 0;
377 conn->version.MinorVersion = parse_number(p, &q, end);
378 if (q >= end) return 0;
379 if (q == p) return -1;
380 p = q;
381 if ((ret = compare_exact(p, "\r\n", end)) <= 0) return ret;
382 p += 2;
384 TRACE("Got version %hu.%hu.\n", conn->version.MajorVersion, conn->version.MinorVersion);
386 /* headers */
387 conn->host = NULL;
388 conn->content_len = 0;
389 for (;;)
391 const char *name = p;
393 if (!(ret = compare_exact(p, "\r\n", end))) return 0;
394 else if (ret > 0) break;
396 len = parse_token(p, end);
397 if (p + len >= end) return 0;
398 if (!len) return -1;
399 p += len;
400 while (p < end && (*p == ' ' || *p == '\t')) ++p;
401 if (p >= end) return 0;
402 if (*p != ':') return -1;
403 ++p;
404 while (p < end && (*p == ' ' || *p == '\t')) ++p;
406 TRACE("Got %s header.\n", debugstr_an(name, len));
408 if (!strncmp(name, "Host", len))
409 conn->host = p;
410 else if (!strncmp(name, "Content-Length", len))
412 conn->content_len = parse_number(p, &q, end);
413 if (q >= end) return 0;
414 if (q == p) return -1;
416 else if (!strncmp(name, "Transfer-Encoding", len))
417 FIXME("Unhandled Transfer-Encoding header.\n");
418 while (p < end && (isprint(*p) || *p == '\t')) ++p;
419 if ((ret = compare_exact(p, "\r\n", end)) <= 0) return ret;
420 p += 2;
422 p += 2;
423 if (conn->url[0] == '/' && !conn->host) return -1;
425 if (end - p < conn->content_len) return 0;
427 conn->req_len = (p - req) + conn->content_len;
429 TRACE("Received a full request, length %u bytes.\n", conn->req_len);
431 conn->queue = NULL;
432 /* Find a queue which can receive this request. */
433 LIST_FOR_EACH_ENTRY(queue, &request_queues, struct request_queue, entry)
435 if (host_matches(conn, queue))
437 TRACE("Assigning request to queue %p.\n", queue);
438 conn->queue = queue;
439 break;
443 /* Stop selecting on incoming data until a response is queued. */
444 WSAEventSelect(conn->socket, request_event, FD_CLOSE);
446 conn->available = TRUE;
447 try_complete_irp(conn);
449 return 1;
452 static void format_date(char *buffer)
454 static const char day_names[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
455 static const char month_names[12][4] =
456 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
457 SYSTEMTIME date;
458 GetSystemTime(&date);
459 sprintf(buffer + strlen(buffer), "Date: %s, %02u %s %u %02u:%02u:%02u GMT\r\n",
460 day_names[date.wDayOfWeek], date.wDay, month_names[date.wMonth - 1],
461 date.wYear, date.wHour, date.wMinute, date.wSecond);
464 /* Send a 400 Bad Request response. */
465 static void send_400(struct connection *conn)
467 static const char response_header[] = "HTTP/1.1 400 Bad Request\r\n";
468 static const char response_body[] =
469 "Content-Type: text/html; charset=utf-8\r\n"
470 "Content-Language: en\r\n"
471 "Connection: close\r\n";
472 char buffer[sizeof(response_header) + sizeof(response_body) + 37];
474 strcpy(buffer, response_header);
475 format_date(buffer + strlen(buffer));
476 strcat(buffer, response_body);
477 if (send(conn->socket, buffer, strlen(buffer), 0) < 0)
478 ERR("Failed to send 400 response, error %u.\n", WSAGetLastError());
481 static void receive_data(struct connection *conn)
483 int len, ret;
485 /* We might be waiting for an IRP, but always call recv() anyway, since we
486 * might have been woken up by the socket closing. */
487 if ((len = recv(conn->socket, conn->buffer + conn->len, conn->size - conn->len, 0)) <= 0)
489 if (WSAGetLastError() == WSAEWOULDBLOCK)
490 return; /* nothing to receive */
491 else if (!len)
492 TRACE("Connection was shut down by peer.\n");
493 else
494 ERR("Got error %u; shutting down connection.\n", WSAGetLastError());
495 close_connection(conn);
496 return;
498 conn->len += len;
500 if (conn->available)
501 return; /* waiting for an HttpReceiveHttpRequest() call */
502 if (conn->req_id != HTTP_NULL_ID)
503 return; /* waiting for an HttpSendHttpResponse() call */
505 TRACE("Received %u bytes of data.\n", len);
507 if (!(ret = parse_request(conn)))
509 ULONG available;
510 ioctlsocket(conn->socket, FIONREAD, &available);
511 if (available)
513 TRACE("%u more bytes of data available, trying with larger buffer.\n", available);
514 if (!(conn->buffer = heap_realloc(conn->buffer, conn->len + available)))
516 ERR("Failed to allocate %u bytes of memory.\n", conn->len + available);
517 close_connection(conn);
518 return;
520 conn->size = conn->len + available;
522 if ((len = recv(conn->socket, conn->buffer + conn->len, conn->size - conn->len, 0)) < 0)
524 ERR("Got error %u; shutting down connection.\n", WSAGetLastError());
525 close_connection(conn);
526 return;
528 TRACE("Received %u bytes of data.\n", len);
529 conn->len += len;
530 ret = parse_request(conn);
533 if (!ret)
534 TRACE("Request is incomplete, waiting for more data.\n");
535 else if (ret < 0)
537 WARN("Failed to parse request; shutting down connection.\n");
538 send_400(conn);
539 close_connection(conn);
543 static DWORD WINAPI request_thread_proc(void *arg)
545 struct connection *conn, *cursor;
546 struct request_queue *queue;
548 TRACE("Starting request thread.\n");
550 while (!WaitForSingleObject(request_event, INFINITE))
552 EnterCriticalSection(&http_cs);
554 LIST_FOR_EACH_ENTRY(queue, &request_queues, struct request_queue, entry)
556 if (queue->socket != -1)
557 accept_connection(queue->socket);
560 LIST_FOR_EACH_ENTRY_SAFE(conn, cursor, &connections, struct connection, entry)
562 receive_data(conn);
565 LeaveCriticalSection(&http_cs);
568 TRACE("Stopping request thread.\n");
570 return 0;
573 static NTSTATUS http_add_url(struct request_queue *queue, IRP *irp)
575 const struct http_add_url_params *params = irp->AssociatedIrp.SystemBuffer;
576 struct sockaddr_in addr;
577 struct connection *conn;
578 unsigned int count = 0;
579 char *url, *endptr;
580 ULONG true = 1;
581 const char *p;
582 SOCKET s;
584 TRACE("host %s, context %s.\n", debugstr_a(params->url), wine_dbgstr_longlong(params->context));
586 if (!strncmp(params->url, "https://", 8))
588 FIXME("HTTPS is not implemented.\n");
589 return STATUS_NOT_IMPLEMENTED;
591 else if (strncmp(params->url, "http://", 7) || !strchr(params->url + 7, ':')
592 || params->url[strlen(params->url) - 1] != '/')
593 return STATUS_INVALID_PARAMETER;
594 if (!(addr.sin_port = htons(strtol(strchr(params->url + 7, ':') + 1, &endptr, 10))) || *endptr != '/')
595 return STATUS_INVALID_PARAMETER;
597 if (!(url = heap_alloc(strlen(params->url)+1)))
598 return STATUS_NO_MEMORY;
599 strcpy(url, params->url);
601 for (p = url; *p; ++p)
602 if (*p == '/') ++count;
603 if (count > 3)
604 FIXME("Binding to relative URIs is not implemented; binding to all URIs instead.\n");
606 EnterCriticalSection(&http_cs);
608 if (queue->url && !strcmp(queue->url, url))
610 LeaveCriticalSection(&http_cs);
611 heap_free(url);
612 return STATUS_OBJECT_NAME_COLLISION;
614 else if (queue->url)
616 FIXME("Binding to multiple URLs is not implemented.\n");
617 LeaveCriticalSection(&http_cs);
618 heap_free(url);
619 return STATUS_NOT_IMPLEMENTED;
622 if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
624 ERR("Failed to create socket, error %u.\n", WSAGetLastError());
625 LeaveCriticalSection(&http_cs);
626 heap_free(url);
627 return STATUS_UNSUCCESSFUL;
630 addr.sin_family = AF_INET;
631 addr.sin_addr.S_un.S_addr = INADDR_ANY;
632 if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
634 LeaveCriticalSection(&http_cs);
635 closesocket(s);
636 heap_free(url);
637 if (WSAGetLastError() == WSAEADDRINUSE)
639 WARN("Address %s is already in use.\n", debugstr_a(params->url));
640 return STATUS_SHARING_VIOLATION;
642 else if (WSAGetLastError() == WSAEACCES)
644 WARN("Not enough permissions to bind to address %s.\n", debugstr_a(params->url));
645 return STATUS_ACCESS_DENIED;
647 ERR("Failed to bind socket, error %u.\n", WSAGetLastError());
648 return STATUS_UNSUCCESSFUL;
651 if (listen(s, SOMAXCONN) == -1)
653 ERR("Failed to listen to port %u, error %u.\n", addr.sin_port, WSAGetLastError());
654 LeaveCriticalSection(&http_cs);
655 closesocket(s);
656 heap_free(url);
657 return STATUS_OBJECT_NAME_COLLISION;
660 ioctlsocket(s, FIONBIO, &true);
661 WSAEventSelect(s, request_event, FD_ACCEPT);
662 queue->socket = s;
663 queue->url = url;
664 queue->context = params->context;
666 /* See if any pending requests now match this queue. */
667 LIST_FOR_EACH_ENTRY(conn, &connections, struct connection, entry)
669 if (conn->available && !conn->queue && host_matches(conn, queue))
671 conn->queue = queue;
672 try_complete_irp(conn);
676 LeaveCriticalSection(&http_cs);
678 return STATUS_SUCCESS;
681 static NTSTATUS http_remove_url(struct request_queue *queue, IRP *irp)
683 const char *url = irp->AssociatedIrp.SystemBuffer;
685 TRACE("host %s.\n", debugstr_a(url));
687 EnterCriticalSection(&http_cs);
689 if (!queue->url || strcmp(url, queue->url))
691 LeaveCriticalSection(&http_cs);
692 return STATUS_OBJECT_NAME_NOT_FOUND;
694 heap_free(queue->url);
695 queue->url = NULL;
697 LeaveCriticalSection(&http_cs);
698 return STATUS_SUCCESS;
701 static struct connection *get_connection(HTTP_REQUEST_ID req_id)
703 struct connection *conn;
705 LIST_FOR_EACH_ENTRY(conn, &connections, struct connection, entry)
707 if (conn->req_id == req_id)
708 return conn;
710 return NULL;
713 static void WINAPI http_receive_request_cancel(DEVICE_OBJECT *device, IRP *irp)
715 TRACE("device %p, irp %p.\n", device, irp);
717 IoReleaseCancelSpinLock(irp->CancelIrql);
719 EnterCriticalSection(&http_cs);
720 RemoveEntryList(&irp->Tail.Overlay.ListEntry);
721 LeaveCriticalSection(&http_cs);
723 irp->IoStatus.Status = STATUS_CANCELLED;
724 IoCompleteRequest(irp, IO_NO_INCREMENT);
727 static NTSTATUS http_receive_request(struct request_queue *queue, IRP *irp)
729 const struct http_receive_request_params *params = irp->AssociatedIrp.SystemBuffer;
730 struct connection *conn;
731 NTSTATUS ret;
733 TRACE("addr %s, id %s, flags %#x, bits %u.\n", wine_dbgstr_longlong(params->addr),
734 wine_dbgstr_longlong(params->id), params->flags, params->bits);
736 EnterCriticalSection(&http_cs);
738 if ((conn = get_connection(params->id)) && conn->available && conn->queue == queue)
740 ret = complete_irp(conn, irp);
741 LeaveCriticalSection(&http_cs);
742 return ret;
745 if (params->id == HTTP_NULL_ID)
747 TRACE("Queuing IRP %p.\n", irp);
749 IoSetCancelRoutine(irp, http_receive_request_cancel);
750 if (irp->Cancel && !IoSetCancelRoutine(irp, NULL))
752 /* The IRP was canceled before we set the cancel routine. */
753 ret = STATUS_CANCELLED;
755 else
757 IoMarkIrpPending(irp);
758 InsertTailList(&queue->irp_queue, &irp->Tail.Overlay.ListEntry);
759 ret = STATUS_PENDING;
762 else
763 ret = STATUS_CONNECTION_INVALID;
765 LeaveCriticalSection(&http_cs);
767 return ret;
770 static NTSTATUS http_send_response(struct request_queue *queue, IRP *irp)
772 const struct http_response *response = irp->AssociatedIrp.SystemBuffer;
773 struct connection *conn;
775 TRACE("id %s, len %d.\n", wine_dbgstr_longlong(response->id), response->len);
777 EnterCriticalSection(&http_cs);
779 if ((conn = get_connection(response->id)))
781 if (send(conn->socket, response->buffer, response->len, 0) >= 0)
783 if (conn->content_len)
785 /* Discard whatever entity body is left. */
786 memmove(conn->buffer, conn->buffer + conn->content_len, conn->len - conn->content_len);
787 conn->len -= conn->content_len;
790 conn->queue = NULL;
791 conn->req_id = HTTP_NULL_ID;
792 WSAEventSelect(conn->socket, request_event, FD_READ | FD_CLOSE);
793 irp->IoStatus.Information = response->len;
794 /* We might have another request already in the buffer. */
795 if (parse_request(conn) < 0)
797 WARN("Failed to parse request; shutting down connection.\n");
798 send_400(conn);
799 close_connection(conn);
802 else
804 ERR("Got error %u; shutting down connection.\n", WSAGetLastError());
805 close_connection(conn);
808 LeaveCriticalSection(&http_cs);
809 return STATUS_SUCCESS;
812 LeaveCriticalSection(&http_cs);
813 return STATUS_CONNECTION_INVALID;
816 static NTSTATUS http_receive_body(struct request_queue *queue, IRP *irp)
818 const struct http_receive_body_params *params = irp->AssociatedIrp.SystemBuffer;
819 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
820 const DWORD output_len = stack->Parameters.DeviceIoControl.OutputBufferLength;
821 struct connection *conn;
822 NTSTATUS ret;
824 TRACE("id %s, bits %u.\n", wine_dbgstr_longlong(params->id), params->bits);
826 EnterCriticalSection(&http_cs);
828 if ((conn = get_connection(params->id)))
830 TRACE("%u bits remaining.\n", conn->content_len);
832 if (conn->content_len)
834 ULONG len = min(conn->content_len, output_len);
835 memcpy(irp->AssociatedIrp.SystemBuffer, conn->buffer, len);
836 memmove(conn->buffer, conn->buffer + len, conn->len - len);
837 conn->content_len -= len;
838 conn->len -= len;
840 irp->IoStatus.Information = len;
841 ret = STATUS_SUCCESS;
843 else
844 ret = STATUS_END_OF_FILE;
846 else
847 ret = STATUS_CONNECTION_INVALID;
849 LeaveCriticalSection(&http_cs);
851 return ret;
854 static NTSTATUS WINAPI dispatch_ioctl(DEVICE_OBJECT *device, IRP *irp)
856 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
857 struct request_queue *queue = stack->FileObject->FsContext;
858 NTSTATUS ret;
860 switch (stack->Parameters.DeviceIoControl.IoControlCode)
862 case IOCTL_HTTP_ADD_URL:
863 ret = http_add_url(queue, irp);
864 break;
865 case IOCTL_HTTP_REMOVE_URL:
866 ret = http_remove_url(queue, irp);
867 break;
868 case IOCTL_HTTP_RECEIVE_REQUEST:
869 ret = http_receive_request(queue, irp);
870 break;
871 case IOCTL_HTTP_SEND_RESPONSE:
872 ret = http_send_response(queue, irp);
873 break;
874 case IOCTL_HTTP_RECEIVE_BODY:
875 ret = http_receive_body(queue, irp);
876 break;
877 default:
878 FIXME("Unhandled ioctl %#x.\n", stack->Parameters.DeviceIoControl.IoControlCode);
879 ret = STATUS_NOT_IMPLEMENTED;
882 if (ret != STATUS_PENDING)
884 irp->IoStatus.Status = ret;
885 IoCompleteRequest(irp, IO_NO_INCREMENT);
887 return ret;
890 static NTSTATUS WINAPI dispatch_create(DEVICE_OBJECT *device, IRP *irp)
892 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
893 struct request_queue *queue;
895 if (!(queue = heap_alloc_zero(sizeof(*queue))))
896 return STATUS_NO_MEMORY;
897 stack->FileObject->FsContext = queue;
898 InitializeListHead(&queue->irp_queue);
900 EnterCriticalSection(&http_cs);
901 list_add_head(&request_queues, &queue->entry);
902 LeaveCriticalSection(&http_cs);
904 TRACE("Created queue %p.\n", queue);
906 irp->IoStatus.Status = STATUS_SUCCESS;
907 IoCompleteRequest(irp, IO_NO_INCREMENT);
908 return STATUS_SUCCESS;
911 static void close_queue(struct request_queue *queue)
913 EnterCriticalSection(&http_cs);
914 list_remove(&queue->entry);
915 if (queue->socket != -1)
917 shutdown(queue->socket, SD_BOTH);
918 closesocket(queue->socket);
920 LeaveCriticalSection(&http_cs);
922 heap_free(queue->url);
923 heap_free(queue);
926 static NTSTATUS WINAPI dispatch_close(DEVICE_OBJECT *device, IRP *irp)
928 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
929 struct request_queue *queue = stack->FileObject->FsContext;
930 LIST_ENTRY *entry;
932 TRACE("Closing queue %p.\n", queue);
934 EnterCriticalSection(&http_cs);
936 while ((entry = queue->irp_queue.Flink) != &queue->irp_queue)
938 IRP *queued_irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
939 IoCancelIrp(queued_irp);
942 LeaveCriticalSection(&http_cs);
944 close_queue(queue);
946 irp->IoStatus.Status = STATUS_SUCCESS;
947 IoCompleteRequest(irp, IO_NO_INCREMENT);
948 return STATUS_SUCCESS;
951 static void WINAPI unload(DRIVER_OBJECT *driver)
953 struct request_queue *queue, *queue_next;
954 struct connection *conn, *conn_next;
956 thread_stop = TRUE;
957 SetEvent(request_event);
958 WaitForSingleObject(request_thread, INFINITE);
959 CloseHandle(request_thread);
960 CloseHandle(request_event);
962 LIST_FOR_EACH_ENTRY_SAFE(conn, conn_next, &connections, struct connection, entry)
964 close_connection(conn);
967 LIST_FOR_EACH_ENTRY_SAFE(queue, queue_next, &request_queues, struct request_queue, entry)
969 close_queue(queue);
972 WSACleanup();
974 IoDeleteDevice(device_obj);
975 NtClose(directory_obj);
978 NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *path)
980 OBJECT_ATTRIBUTES attr = {sizeof(attr)};
981 UNICODE_STRING string;
982 WSADATA wsadata;
983 NTSTATUS ret;
985 TRACE("driver %p, path %s.\n", driver, debugstr_w(path->Buffer));
987 RtlInitUnicodeString(&string, L"\\Device\\Http");
988 attr.ObjectName = &string;
989 if ((ret = NtCreateDirectoryObject(&directory_obj, 0, &attr)) && ret != STATUS_OBJECT_NAME_COLLISION)
990 ERR("Failed to create \\Device\\Http directory, status %#x.\n", ret);
992 RtlInitUnicodeString(&string, L"\\Device\\Http\\ReqQueue");
993 if ((ret = IoCreateDevice(driver, 0, &string, FILE_DEVICE_UNKNOWN, 0, FALSE, &device_obj)))
995 ERR("Failed to create request queue device, status %#x.\n", ret);
996 NtClose(directory_obj);
997 return ret;
1000 driver->MajorFunction[IRP_MJ_CREATE] = dispatch_create;
1001 driver->MajorFunction[IRP_MJ_CLOSE] = dispatch_close;
1002 driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = dispatch_ioctl;
1003 driver->DriverUnload = unload;
1005 WSAStartup(MAKEWORD(1,1), &wsadata);
1007 request_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1008 request_thread = CreateThread(NULL, 0, request_thread_proc, NULL, 0, NULL);
1010 return STATUS_SUCCESS;