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
;
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
);
89 HTTP_URL_CONTEXT context
;
94 static struct list request_queues
= LIST_INIT(request_queues
);
96 static void accept_connection(SOCKET socket
)
98 struct connection
*conn
;
102 if ((peer
= accept(socket
, NULL
, NULL
)) == INVALID_SOCKET
)
105 if (!(conn
= heap_alloc_zero(sizeof(*conn
))))
107 ERR("Failed to allocate memory.\n");
108 shutdown(peer
, SD_BOTH
);
112 if (!(conn
->buffer
= heap_alloc(8192)))
114 ERR("Failed to allocate buffer memory.\n");
116 shutdown(peer
, SD_BOTH
);
121 WSAEventSelect(peer
, request_event
, FD_READ
| FD_CLOSE
);
122 ioctlsocket(peer
, FIONBIO
, &true);
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
);
136 static HTTP_VERB
parse_verb(const char *verb
, int len
)
138 static const char *const verbs
[] =
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
)
172 for (p
= str
; !end
|| p
< end
; ++p
)
174 if (!isgraph(*p
) || strchr("()<>@,;:\\\"/[]?={}", *p
))
180 static HTTP_HEADER_ID
parse_header_name(const char *header
, int len
)
182 static const char *const headers
[] =
217 "If-Unmodified-Since",
219 "Proxy-Authorization",
228 for (i
= 0; i
< ARRAY_SIZE(headers
); ++i
)
230 if (!strncmp(header
, headers
[i
], len
))
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
);
241 while (*p
== ' ' || *p
== '\t') ++p
;
242 ++p
; /* skip colon */
243 while (*p
== ' ' || *p
== '\t') ++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
256 #undef http_unknown_header
257 #undef http_data_chunk
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
268 #undef http_unknown_header
269 #undef http_data_chunk
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
);
282 conn
->req_id
= ++req_id_counter
;
284 if (params
.bits
== 32)
285 return complete_irp_32(conn
, irp
);
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
)
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
)
307 if (str
>= end
) return 0;
308 if (*str
++ != *expect
++) return -1;
313 static int parse_number(const char *str
, const char **endptr
, const char *end
)
316 while (str
< end
&& isdigit(*str
))
317 n
= n
* 10 + (*str
++ - '0');
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
;
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;
354 if ((conn
->verb
= parse_verb(p
, len
)) == HttpVerbUnknown
)
355 conn
->unk_verb_len
= len
;
358 TRACE("Got verb %u (%s).\n", conn
->verb
, debugstr_an(req
, len
));
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
));
370 if ((ret
= compare_exact(p
, " HTTP/", end
)) <= 0) return ret
;
372 conn
->version
.MajorVersion
= parse_number(p
, &q
, end
);
373 if (q
>= end
) return 0;
374 if (q
== p
|| *q
!= '.') return -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;
381 if ((ret
= compare_exact(p
, "\r\n", end
)) <= 0) return ret
;
384 TRACE("Got version %hu.%hu.\n", conn
->version
.MajorVersion
, conn
->version
.MinorVersion
);
388 conn
->content_len
= 0;
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;
400 while (p
< end
&& (*p
== ' ' || *p
== '\t')) ++p
;
401 if (p
>= end
) return 0;
402 if (*p
!= ':') return -1;
404 while (p
< end
&& (*p
== ' ' || *p
== '\t')) ++p
;
406 TRACE("Got %s header.\n", debugstr_an(name
, len
));
408 if (!strncmp(name
, "Host", len
))
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
;
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
);
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
);
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
);
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"};
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
)
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 */
492 TRACE("Connection was shut down by peer.\n");
494 ERR("Got error %u; shutting down connection.\n", WSAGetLastError());
495 close_connection(conn
);
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
)))
510 ioctlsocket(conn
->socket
, FIONREAD
, &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
);
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
);
528 TRACE("Received %u bytes of data.\n", len
);
530 ret
= parse_request(conn
);
534 TRACE("Request is incomplete, waiting for more data.\n");
537 WARN("Failed to parse request; shutting down connection.\n");
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
)
565 LeaveCriticalSection(&http_cs
);
568 TRACE("Stopping request thread.\n");
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;
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
;
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
);
612 return STATUS_OBJECT_NAME_COLLISION
;
616 FIXME("Binding to multiple URLs is not implemented.\n");
617 LeaveCriticalSection(&http_cs
);
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
);
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
);
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
);
657 return STATUS_OBJECT_NAME_COLLISION
;
660 ioctlsocket(s
, FIONBIO
, &true);
661 WSAEventSelect(s
, request_event
, FD_ACCEPT
);
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
))
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
);
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
)
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
;
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
);
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
;
757 IoMarkIrpPending(irp
);
758 InsertTailList(&queue
->irp_queue
, &irp
->Tail
.Overlay
.ListEntry
);
759 ret
= STATUS_PENDING
;
763 ret
= STATUS_CONNECTION_INVALID
;
765 LeaveCriticalSection(&http_cs
);
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
;
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");
799 close_connection(conn
);
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
;
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
;
840 irp
->IoStatus
.Information
= len
;
841 ret
= STATUS_SUCCESS
;
844 ret
= STATUS_END_OF_FILE
;
847 ret
= STATUS_CONNECTION_INVALID
;
849 LeaveCriticalSection(&http_cs
);
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
;
860 switch (stack
->Parameters
.DeviceIoControl
.IoControlCode
)
862 case IOCTL_HTTP_ADD_URL
:
863 ret
= http_add_url(queue
, irp
);
865 case IOCTL_HTTP_REMOVE_URL
:
866 ret
= http_remove_url(queue
, irp
);
868 case IOCTL_HTTP_RECEIVE_REQUEST
:
869 ret
= http_receive_request(queue
, irp
);
871 case IOCTL_HTTP_SEND_RESPONSE
:
872 ret
= http_send_response(queue
, irp
);
874 case IOCTL_HTTP_RECEIVE_BODY
:
875 ret
= http_receive_body(queue
, irp
);
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
);
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
);
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
;
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
);
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
;
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
)
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
;
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
);
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
;