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/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
;
51 struct list entry
; /* in "connections" below */
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). */
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. */
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
92 static struct list listening_sockets
= LIST_INIT(listening_sockets
);
98 HTTP_URL_CONTEXT context
;
99 struct listening_socket
*listening_sock
;
105 LIST_ENTRY irp_queue
;
109 static struct list request_queues
= LIST_INIT(request_queues
);
111 static void accept_connection(SOCKET socket
)
113 struct connection
*conn
;
117 if ((peer
= accept(socket
, NULL
, NULL
)) == INVALID_SOCKET
)
120 if (!(conn
= calloc(1, sizeof(*conn
))))
122 ERR("Failed to allocate memory.\n");
123 shutdown(peer
, SD_BOTH
);
127 if (!(conn
->buffer
= malloc(8192)))
129 ERR("Failed to allocate buffer memory.\n");
131 shutdown(peer
, SD_BOTH
);
136 WSAEventSelect(peer
, request_event
, FD_READ
| FD_CLOSE
);
137 ioctlsocket(peer
, FIONBIO
, &true);
139 list_add_head(&connections
, &conn
->entry
);
142 static void close_connection(struct connection
*conn
)
145 shutdown(conn
->socket
, SD_BOTH
);
146 closesocket(conn
->socket
);
147 list_remove(&conn
->entry
);
151 static HTTP_VERB
parse_verb(const char *verb
, int len
)
153 static const char *const verbs
[] =
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
)
187 for (p
= str
; !end
|| p
< end
; ++p
)
189 if (!isgraph(*p
) || strchr("()<>@,;:\\\"/[]?={}", *p
))
195 static HTTP_HEADER_ID
parse_header_name(const char *header
, int len
)
197 static const char *const headers
[] =
232 "If-Unmodified-Since",
234 "Proxy-Authorization",
243 for (i
= 0; i
< ARRAY_SIZE(headers
); ++i
)
245 if (!strncmp(header
, headers
[i
], len
))
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
);
256 while (*p
== ' ' || *p
== '\t') ++p
;
257 ++p
; /* skip colon */
258 while (*p
== ' ' || *p
== '\t') ++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
271 #undef http_unknown_header
272 #undef http_data_chunk
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
283 #undef http_unknown_header
284 #undef http_data_chunk
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
);
297 conn
->req_id
= ++req_id_counter
;
299 if (params
.bits
== 32)
300 return complete_irp_32(conn
, irp
);
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
)
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
)
322 if (str
>= end
) return 0;
323 if (*str
++ != *expect
++) return -1;
328 static int parse_number(const char *str
, const char **endptr
, const char *end
)
331 while (str
< end
&& isdigit(*str
))
332 n
= n
* 10 + (*str
++ - '0');
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;
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] == '/')
352 if (conn_path
[conn_len
- 1] == '/')
355 if (conn_len
< queue_len
)
358 for (i
= 0; i
< queue_len
; ++i
)
360 if (queue_path
[i
] != conn_path
[i
])
362 if (queue_path
[i
] == '/')
366 if (queue_len
== conn_len
|| conn_path
[queue_len
] == '/')
372 static BOOL
host_matches(const struct url
*url
, const char *conn_host
)
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
))
388 host_len
= strchr(url
->url
+ 7, '/') - url
->url
- 7;
389 if (!memicmp(url
->url
+ 7, conn_host
, host_len
))
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
;
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, '/');
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
;
435 *ret_slash_count
= max_slash_count
;
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;
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;
461 if ((conn
->verb
= parse_verb(p
, len
)) == HttpVerbUnknown
)
462 conn
->unk_verb_len
= len
;
465 TRACE("Got verb %u (%s).\n", conn
->verb
, debugstr_an(req
, len
));
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
));
477 if ((ret
= compare_exact(p
, " HTTP/", end
)) <= 0) return ret
;
479 conn
->version
.MajorVersion
= parse_number(p
, &q
, end
);
480 if (q
>= end
) return 0;
481 if (q
== p
|| *q
!= '.') return -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;
488 if ((ret
= compare_exact(p
, "\r\n", end
)) <= 0) return ret
;
491 TRACE("Got version %hu.%hu.\n", conn
->version
.MajorVersion
, conn
->version
.MinorVersion
);
495 conn
->content_len
= 0;
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;
507 while (p
< end
&& (*p
== ' ' || *p
== '\t')) ++p
;
508 if (p
>= end
) return 0;
509 if (*p
!= ':') return -1;
511 while (p
< end
&& (*p
== ' ' || *p
== '\t')) ++p
;
513 TRACE("Got %s header.\n", debugstr_an(name
, len
));
515 if (!strncmp(name
, "Host", len
))
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
;
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
);
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
;
548 best_conn_url
= 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
);
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"};
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
)
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 */
609 TRACE("Connection was shut down by peer.\n");
611 ERR("Got error %u; shutting down connection.\n", WSAGetLastError());
612 close_connection(conn
);
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
)))
627 ioctlsocket(conn
->socket
, FIONREAD
, &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
);
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
);
645 TRACE("Received %u bytes of data.\n", len
);
647 ret
= parse_request(conn
);
651 TRACE("Request is incomplete, waiting for more data.\n");
654 WARN("Failed to parse request; shutting down connection.\n");
656 close_connection(conn
);
660 static DWORD WINAPI
request_thread_proc(void *arg
)
662 struct connection
*conn
, *cursor
;
663 struct request_queue
*queue
;
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
)
686 LeaveCriticalSection(&http_cs
);
689 TRACE("Stopping request thread.\n");
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
;
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
;
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
))))
740 return STATUS_NO_MEMORY
;
743 new_url_len
= strlen(url
);
744 if (url
[new_url_len
- 1] == '/')
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] == '/')
757 if (url_entry
->url
&& queue_url_len
== new_url_len
&& !memcmp(url_entry
->url
, url
, queue_url_len
))
759 LeaveCriticalSection(&http_cs
);
762 return STATUS_OBJECT_NAME_COLLISION
;
767 listening_sock
= get_listening_socket(addr
.sin_port
);
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
);
777 return STATUS_UNSUCCESSFUL
;
780 addr
.sin_family
= AF_INET
;
781 addr
.sin_addr
.S_un
.S_addr
= INADDR_ANY
;
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
);
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
);
811 return STATUS_OBJECT_NAME_COLLISION
;
814 if (!(listening_sock
= malloc(sizeof(struct listening_socket
))))
816 LeaveCriticalSection(&http_cs
);
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
))
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
)
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
);
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
)
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
;
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
);
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
;
963 IoMarkIrpPending(irp
);
964 InsertTailList(&queue
->irp_queue
, &irp
->Tail
.Overlay
.ListEntry
);
965 ret
= STATUS_PENDING
;
969 ret
= STATUS_CONNECTION_INVALID
;
971 LeaveCriticalSection(&http_cs
);
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
;
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");
1005 close_connection(conn
);
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
;
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
;
1046 irp
->IoStatus
.Information
= len
;
1047 ret
= STATUS_SUCCESS
;
1050 ret
= STATUS_END_OF_FILE
;
1053 ret
= STATUS_CONNECTION_INVALID
;
1055 LeaveCriticalSection(&http_cs
);
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
;
1066 switch (stack
->Parameters
.DeviceIoControl
.IoControlCode
)
1068 case IOCTL_HTTP_ADD_URL
:
1069 ret
= http_add_url(queue
, irp
);
1071 case IOCTL_HTTP_REMOVE_URL
:
1072 ret
= http_remove_url(queue
, irp
);
1074 case IOCTL_HTTP_RECEIVE_REQUEST
:
1075 ret
= http_receive_request(queue
, irp
);
1077 case IOCTL_HTTP_SEND_RESPONSE
:
1078 ret
= http_send_response(queue
, irp
);
1080 case IOCTL_HTTP_RECEIVE_BODY
:
1081 ret
= http_receive_body(queue
, irp
);
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
);
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
)
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
);
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
;
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
);
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
;
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
)
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");
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
);
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
;