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
23 #define WIN32_NO_STATUS
24 #include "wine/http.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
;
52 struct list entry
; /* in "connections" below */
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). */
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. */
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
93 static struct list listening_sockets
= LIST_INIT(listening_sockets
);
99 HTTP_URL_CONTEXT context
;
100 struct listening_socket
*listening_sock
;
106 LIST_ENTRY irp_queue
;
110 static struct list request_queues
= LIST_INIT(request_queues
);
112 static void accept_connection(SOCKET socket
)
114 struct connection
*conn
;
118 if ((peer
= accept(socket
, NULL
, NULL
)) == INVALID_SOCKET
)
121 if (!(conn
= heap_alloc_zero(sizeof(*conn
))))
123 ERR("Failed to allocate memory.\n");
124 shutdown(peer
, SD_BOTH
);
128 if (!(conn
->buffer
= heap_alloc(8192)))
130 ERR("Failed to allocate buffer memory.\n");
132 shutdown(peer
, SD_BOTH
);
137 WSAEventSelect(peer
, request_event
, FD_READ
| FD_CLOSE
);
138 ioctlsocket(peer
, FIONBIO
, &true);
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
);
152 static HTTP_VERB
parse_verb(const char *verb
, int len
)
154 static const char *const verbs
[] =
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
)
188 for (p
= str
; !end
|| p
< end
; ++p
)
190 if (!isgraph(*p
) || strchr("()<>@,;:\\\"/[]?={}", *p
))
196 static HTTP_HEADER_ID
parse_header_name(const char *header
, int len
)
198 static const char *const headers
[] =
233 "If-Unmodified-Since",
235 "Proxy-Authorization",
244 for (i
= 0; i
< ARRAY_SIZE(headers
); ++i
)
246 if (!strncmp(header
, headers
[i
], len
))
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
);
257 while (*p
== ' ' || *p
== '\t') ++p
;
258 ++p
; /* skip colon */
259 while (*p
== ' ' || *p
== '\t') ++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
272 #undef http_unknown_header
273 #undef http_data_chunk
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
284 #undef http_unknown_header
285 #undef http_data_chunk
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
);
298 conn
->req_id
= ++req_id_counter
;
300 if (params
.bits
== 32)
301 return complete_irp_32(conn
, irp
);
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
)
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
)
323 if (str
>= end
) return 0;
324 if (*str
++ != *expect
++) return -1;
329 static int parse_number(const char *str
, const char **endptr
, const char *end
)
332 while (str
< end
&& isdigit(*str
))
333 n
= n
* 10 + (*str
++ - '0');
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;
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] == '/')
353 if (conn_path
[conn_len
- 1] == '/')
356 if (conn_len
< queue_len
)
359 for (i
= 0; i
< queue_len
; ++i
)
361 if (queue_path
[i
] != conn_path
[i
])
363 if (queue_path
[i
] == '/')
367 if (queue_len
== conn_len
|| conn_path
[queue_len
] == '/')
373 static BOOL
host_matches(const struct url
*url
, const char *conn_host
)
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
))
389 host_len
= strchr(url
->url
+ 7, '/') - url
->url
- 7;
390 if (!memicmp(url
->url
+ 7, conn_host
, host_len
))
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
;
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, '/');
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
;
436 *ret_slash_count
= max_slash_count
;
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;
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;
462 if ((conn
->verb
= parse_verb(p
, len
)) == HttpVerbUnknown
)
463 conn
->unk_verb_len
= len
;
466 TRACE("Got verb %u (%s).\n", conn
->verb
, debugstr_an(req
, len
));
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
));
478 if ((ret
= compare_exact(p
, " HTTP/", end
)) <= 0) return ret
;
480 conn
->version
.MajorVersion
= parse_number(p
, &q
, end
);
481 if (q
>= end
) return 0;
482 if (q
== p
|| *q
!= '.') return -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;
489 if ((ret
= compare_exact(p
, "\r\n", end
)) <= 0) return ret
;
492 TRACE("Got version %hu.%hu.\n", conn
->version
.MajorVersion
, conn
->version
.MinorVersion
);
496 conn
->content_len
= 0;
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;
508 while (p
< end
&& (*p
== ' ' || *p
== '\t')) ++p
;
509 if (p
>= end
) return 0;
510 if (*p
!= ':') return -1;
512 while (p
< end
&& (*p
== ' ' || *p
== '\t')) ++p
;
514 TRACE("Got %s header.\n", debugstr_an(name
, len
));
516 if (!strncmp(name
, "Host", len
))
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
;
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
);
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
;
549 best_conn_url
= 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
);
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"};
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
)
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 */
610 TRACE("Connection was shut down by peer.\n");
612 ERR("Got error %u; shutting down connection.\n", WSAGetLastError());
613 close_connection(conn
);
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
)))
628 ioctlsocket(conn
->socket
, FIONREAD
, &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
);
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
);
646 TRACE("Received %u bytes of data.\n", len
);
648 ret
= parse_request(conn
);
652 TRACE("Request is incomplete, waiting for more data.\n");
655 WARN("Failed to parse request; shutting down connection.\n");
657 close_connection(conn
);
661 static DWORD WINAPI
request_thread_proc(void *arg
)
663 struct connection
*conn
, *cursor
;
664 struct request_queue
*queue
;
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
)
687 LeaveCriticalSection(&http_cs
);
690 TRACE("Stopping request thread.\n");
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
;
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
;
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
))))
742 return STATUS_NO_MEMORY
;
745 new_url_len
= strlen(url
);
746 if (url
[new_url_len
- 1] == '/')
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] == '/')
759 if (url_entry
->url
&& queue_url_len
== new_url_len
&& !memcmp(url_entry
->url
, url
, queue_url_len
))
761 LeaveCriticalSection(&http_cs
);
764 return STATUS_OBJECT_NAME_COLLISION
;
769 listening_sock
= get_listening_socket(addr
.sin_port
);
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
);
779 return STATUS_UNSUCCESSFUL
;
782 addr
.sin_family
= AF_INET
;
783 addr
.sin_addr
.S_un
.S_addr
= INADDR_ANY
;
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
);
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
);
813 return STATUS_OBJECT_NAME_COLLISION
;
816 if (!(listening_sock
= malloc(sizeof(struct listening_socket
))))
818 LeaveCriticalSection(&http_cs
);
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
))
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
)
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
);
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
)
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
;
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
);
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
;
965 IoMarkIrpPending(irp
);
966 InsertTailList(&queue
->irp_queue
, &irp
->Tail
.Overlay
.ListEntry
);
967 ret
= STATUS_PENDING
;
971 ret
= STATUS_CONNECTION_INVALID
;
973 LeaveCriticalSection(&http_cs
);
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
;
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");
1007 close_connection(conn
);
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
;
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
;
1048 irp
->IoStatus
.Information
= len
;
1049 ret
= STATUS_SUCCESS
;
1052 ret
= STATUS_END_OF_FILE
;
1055 ret
= STATUS_CONNECTION_INVALID
;
1057 LeaveCriticalSection(&http_cs
);
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
;
1068 switch (stack
->Parameters
.DeviceIoControl
.IoControlCode
)
1070 case IOCTL_HTTP_ADD_URL
:
1071 ret
= http_add_url(queue
, irp
);
1073 case IOCTL_HTTP_REMOVE_URL
:
1074 ret
= http_remove_url(queue
, irp
);
1076 case IOCTL_HTTP_RECEIVE_REQUEST
:
1077 ret
= http_receive_request(queue
, irp
);
1079 case IOCTL_HTTP_SEND_RESPONSE
:
1080 ret
= http_send_response(queue
, irp
);
1082 case IOCTL_HTTP_RECEIVE_BODY
:
1083 ret
= http_receive_body(queue
, irp
);
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
);
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
)
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
);
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
;
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
);
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
;
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
)
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");
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
);
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
;