Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/http.sys/http.c | 165 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 163 insertions(+), 2 deletions(-)
diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c index 5e29db0fa7..506aefccf1 100644 --- a/dlls/http.sys/http.c +++ b/dlls/http.sys/http.c @@ -52,6 +52,13 @@ struct connection
char *buffer; unsigned int len, size; + + /* Things we already parsed out of the request header in parse_request(). */ + unsigned int req_len; + HTTP_VERB verb; + HTTP_VERSION version; + const char *url, *host; + ULONG unk_verb_len, url_len, content_len; };
static struct list connections = LIST_INIT(connections); @@ -99,19 +106,173 @@ static void accept_connection(int socket)
static void close_connection(struct connection *conn) { + heap_free(conn->buffer); shutdown(conn->socket, SD_BOTH); closesocket(conn->socket); list_remove(&conn->entry); heap_free(conn); }
+static HTTP_VERB parse_verb(const char *verb, int len) +{ + static const char *const verbs[] = + { + "OPTIONS", + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "TRACE", + "CONNECT", + "TRACK", + "MOVE", + "COPY", + "PROPFIND", + "PROPPATCH", + "MKCOL", + "LOCK", + "UNLOCK", + "SEARCH", + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(verbs); ++i) + { + if (!strncmp(verb, verbs[i], len)) + return HttpVerbOPTIONS + i; + } + return HttpVerbUnknown; +} + +/* Return the length of a token, as defined in RFC 2616 section 2.2. */ +static int parse_token(const char *str, const char *end) +{ + const char *p; + for (p = str; !end || p < end; ++p) + { + if (!isgraph(*p) || strchr("()<>@,;:\"/[]?={}", *p)) + break; + } + return p - str; +} + +/* Return 1 if str matches expect, 0 if str is incomplete, -1 if they don't match. */ +static int compare_exact(const char *str, const char *expect, const char *end) +{ + while (*expect) + { + if (str >= end) return 0; + if (*str++ != *expect++) return -1; + } + return 1; +} + +static int parse_number(const char *str, const char **endptr, const char *end) +{ + int n = 0; + while (str < end && isdigit(*str)) + n = n * 10 + (*str++ - '0'); + *endptr = str; + return n; +} + /* Upon receiving a request, parse it to ensure that it is a valid HTTP request, * and mark down some information that we will use later. Returns 1 if we parsed * a complete request, 0 if incomplete, -1 if invalid. */ static int parse_request(struct connection *conn) { - FIXME("Not implemented.\n"); - return -1; + const char *const req = conn->buffer, *const end = conn->buffer + conn->len; + const char *p = req, *q; + int len, ret; + + if (!conn->len) return 0; + + TRACE("%s\n", wine_dbgstr_an(conn->buffer, conn->len)); + + len = parse_token(p, end); + if (p + len >= end) return 0; + if (!len || p[len] != ' ') return -1; + + /* verb */ + if ((conn->verb = parse_verb(p, len)) == HttpVerbUnknown) + conn->unk_verb_len = len; + p += len + 1; + + TRACE("Got verb %u (%s).\n", conn->verb, debugstr_an(req, len)); + + /* URL */ + conn->url = p; + while (p < end && isgraph(*p)) ++p; + conn->url_len = p - conn->url; + if (p >= end) return 0; + if (!conn->url_len) return -1; + + TRACE("Got URI %s.\n", debugstr_an(conn->url, conn->url_len)); + + /* version */ + if ((ret = compare_exact(p, " HTTP/", end)) <= 0) return ret; + p += 6; + conn->version.MajorVersion = parse_number(p, &q, end); + if (q >= end) return 0; + if (q == p || *q != '.') return -1; + p = q + 1; + if (p >= end) return 0; + conn->version.MinorVersion = parse_number(p, &q, end); + if (q >= end) return 0; + if (q == p) return -1; + p = q; + if ((ret = compare_exact(p, "\r\n", end)) <= 0) return ret; + p += 2; + + TRACE("Got version %hu.%hu.\n", conn->version.MajorVersion, conn->version.MinorVersion); + + /* headers */ + conn->host = NULL; + conn->content_len = 0; + for (;;) + { + const char *name = p; + + if (!(ret = compare_exact(p, "\r\n", end))) return 0; + else if (ret > 0) break; + + len = parse_token(p, end); + if (p + len >= end) return 0; + if (!len) return -1; + p += len; + while (p < end && (*p == ' ' || *p == '\t')) ++p; + if (p >= end) return 0; + if (*p != ':') return -1; + ++p; + while (p < end && (*p == ' ' || *p == '\t')) ++p; + + TRACE("Got %s header.\n", debugstr_an(name, len)); + + if (!strncmp(name, "Host", len)) + conn->host = p; + else if (!strncmp(name, "Content-Length", len)) + { + conn->content_len = parse_number(p, &q, end); + if (q >= end) return 0; + if (q == p) return -1; + } + else if (!strncmp(name, "Transfer-Encoding", len)) + FIXME("Unhandled Transfer-Encoding header.\n"); + while (p < end && (isprint(*p) || *p == '\t')) ++p; + if ((ret = compare_exact(p, "\r\n", end)) <= 0) return ret; + p += 2; + } + p += 2; + if (conn->url[0] == '/' && !conn->host) return -1; + + if (end - p < conn->content_len) return 0; + + conn->req_len = (p - req) + conn->content_len; + + TRACE("Received a full request, length %u bytes.\n", conn->req_len); + + return 1; }
static void receive_data(struct connection *conn)
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/http.sys/http.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c index 506aefccf1..81f5d3467e 100644 --- a/dlls/http.sys/http.c +++ b/dlls/http.sys/http.c @@ -53,7 +53,10 @@ struct connection char *buffer; unsigned int len, size;
- /* Things we already parsed out of the request header in parse_request(). */ + BOOL available; + + /* Things we already parsed out of the request header in parse_request(). + * These are valid only if "available" is TRUE. */ unsigned int req_len; HTTP_VERB verb; HTTP_VERSION version; @@ -272,6 +275,11 @@ static int parse_request(struct connection *conn)
TRACE("Received a full request, length %u bytes.\n", conn->req_len);
+ /* Stop selecting on incoming data until a response is queued. */ + WSAEventSelect(conn->socket, request_event, FD_CLOSE); + + conn->available = TRUE; + return 1; }
@@ -294,6 +302,9 @@ static void receive_data(struct connection *conn) } conn->len += len;
+ if (conn->available) + return; /* waiting for an HttpReceiveHttpRequest() call */ + TRACE("Received %u bytes of data.\n", len);
if (!(ret = parse_request(conn)))
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/http.sys/http.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c index 81f5d3467e..4b83cc68d3 100644 --- a/dlls/http.sys/http.c +++ b/dlls/http.sys/http.c @@ -54,6 +54,7 @@ struct connection unsigned int len, size;
BOOL available; + struct request_queue *queue;
/* Things we already parsed out of the request header in parse_request(). * These are valid only if "available" is TRUE. */ @@ -180,12 +181,20 @@ static int parse_number(const char *str, const char **endptr, const char *end) return n; }
+static BOOL host_matches(const struct connection *conn, const struct request_queue *queue) +{ + const char *conn_host = (conn->url[0] == '/') ? conn->host : conn->url + 7; + + return !memicmp(queue->url + 7, conn_host, strlen(queue->url) - 8 /* strip final slash */); +} + /* Upon receiving a request, parse it to ensure that it is a valid HTTP request, * and mark down some information that we will use later. Returns 1 if we parsed * a complete request, 0 if incomplete, -1 if invalid. */ static int parse_request(struct connection *conn) { const char *const req = conn->buffer, *const end = conn->buffer + conn->len; + struct request_queue *queue; const char *p = req, *q; int len, ret;
@@ -275,6 +284,18 @@ static int parse_request(struct connection *conn)
TRACE("Received a full request, length %u bytes.\n", conn->req_len);
+ conn->queue = NULL; + /* Find a queue which can receive this request. */ + LIST_FOR_EACH_ENTRY(queue, &request_queues, struct request_queue, entry) + { + if (host_matches(conn, queue)) + { + TRACE("Assigning request to queue %p.\n", queue); + conn->queue = queue; + break; + } + } + /* Stop selecting on incoming data until a response is queued. */ WSAEventSelect(conn->socket, request_event, FD_CLOSE);
@@ -376,6 +397,7 @@ static NTSTATUS http_add_url(struct request_queue *queue, IRP *irp) { const struct http_add_url_params *params = irp->AssociatedIrp.SystemBuffer; struct sockaddr_in addr; + struct connection *conn; char *url, *endptr; int s, count = 0; ULONG true = 1; @@ -453,6 +475,13 @@ static NTSTATUS http_add_url(struct request_queue *queue, IRP *irp) queue->url = url; queue->context = params->context;
+ /* See if any pending requests now match this queue. */ + LIST_FOR_EACH_ENTRY(conn, &connections, struct connection, entry) + { + if (conn->available && !conn->queue && host_matches(conn, queue)) + conn->queue = queue; + } + LeaveCriticalSection(&http_cs);
return STATUS_SUCCESS;
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/http.sys/http.c | 199 +++++++++++++++++++++++++++++++++++++++++++ include/wine/http.h | 9 ++ 2 files changed, 208 insertions(+)
diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c index 4b83cc68d3..a41b464a72 100644 --- a/dlls/http.sys/http.c +++ b/dlls/http.sys/http.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <assert.h> #include "ntstatus.h" #define WIN32_NO_STATUS #include "wine/http.h" @@ -32,6 +33,108 @@ static DEVICE_OBJECT *device_obj;
WINE_DEFAULT_DEBUG_CHANNEL(http);
+/* We have to return the HTTP_REQUEST structure to userspace exactly as it will + * be consumed; httpapi has no opportunity to massage it. Since it contains + * pointers, this is somewhat nontrivial. */ + +struct http_request_32 +{ + ULONG Flags; + HTTP_CONNECTION_ID ConnectionId; + HTTP_REQUEST_ID RequestId; + HTTP_URL_CONTEXT UrlContext; + HTTP_VERSION Version; + HTTP_VERB Verb; + USHORT UnknownVerbLength; + USHORT RawUrlLength; + ULONG pUnknownVerb; /* char string */ + ULONG pRawUrl; /* char string */ + struct + { + USHORT FullUrlLength; + USHORT HostLength; + USHORT AbsPathLength; + USHORT QueryStringLength; + ULONG pFullUrl; /* WCHAR string */ + ULONG pHost; /* pointer to above */ + ULONG pAbsPath; /* pointer to above */ + ULONG pQueryString; /* pointer to above */ + } CookedUrl; + struct + { + ULONG pRemoteAddress; /* SOCKADDR */ + ULONG pLocalAddress; /* SOCKADDR */ + } Address; + struct + { + USHORT UnknownHeaderCount; + ULONG pUnknownHeaders; /* struct http_unknown_header_32 */ + USHORT TrailerCount; + ULONG pTrailers; /* NULL */ + struct + { + USHORT RawValueLength; + ULONG pRawValue; /* char string */ + } KnownHeaders[HttpHeaderRequestMaximum]; + } Headers; + ULONGLONG BytesReceived; + USHORT EntityChunkCount; + ULONG pEntityChunks; /* struct http_data_chunk_32 */ + HTTP_RAW_CONNECTION_ID RawConnectionId; + ULONG pSslInfo; /* NULL (FIXME) */ + USHORT RequestInfoCount; + ULONG pRequestInfo; /* NULL (FIXME) */ +}; + +struct http_request_64 +{ + ULONG Flags; + HTTP_CONNECTION_ID ConnectionId; + HTTP_REQUEST_ID RequestId; + HTTP_URL_CONTEXT UrlContext; + HTTP_VERSION Version; + HTTP_VERB Verb; + USHORT UnknownVerbLength; + USHORT RawUrlLength; + ULONGLONG pUnknownVerb; /* char string */ + ULONGLONG pRawUrl; /* char string */ + struct + { + USHORT FullUrlLength; + USHORT HostLength; + USHORT AbsPathLength; + USHORT QueryStringLength; + ULONGLONG pFullUrl; /* WCHAR string */ + ULONGLONG pHost; /* pointer to above */ + ULONGLONG pAbsPath; /* pointer to above */ + ULONGLONG pQueryString; /* pointer to above */ + } CookedUrl; + struct + { + ULONGLONG pRemoteAddress; /* SOCKADDR */ + ULONGLONG pLocalAddress; /* SOCKADDR */ + } Address; + struct + { + USHORT UnknownHeaderCount; + ULONGLONG pUnknownHeaders; /* struct http_unknown_header_32 */ + USHORT TrailerCount; + ULONGLONG pTrailers; /* NULL */ + struct + { + USHORT RawValueLength; + ULONGLONG pRawValue; /* char string */ + } KnownHeaders[HttpHeaderRequestMaximum]; + } Headers; + ULONGLONG BytesReceived; + USHORT EntityChunkCount; + ULONGLONG pEntityChunks; /* struct http_data_chunk_32 */ + HTTP_RAW_CONNECTION_ID RawConnectionId; + ULONGLONG pSslInfo; /* NULL (FIXME) */ + USHORT RequestInfoCount; + ULONGLONG pRequestInfo; /* NULL (FIXME) */ +}; + #define DECLARE_CRITICAL_SECTION(cs) \ static CRITICAL_SECTION cs; \ static CRITICAL_SECTION_DEBUG cs##_debug = \ @@ -161,6 +264,73 @@ static int parse_token(const char *str, const char *end) return p - str; }
+static NTSTATUS complete_irp(struct connection *conn, IRP *irp) +{ + const struct http_receive_request_params params + = *(struct http_receive_request_params *)irp->AssociatedIrp.SystemBuffer; + DWORD irp_size = (params.bits == 32) ? sizeof(struct http_request_32) : sizeof(struct http_request_64); + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + const DWORD output_len = stack->Parameters.DeviceIoControl.OutputBufferLength; + ULONG offset; + + TRACE("Completing IRP %p.\n", irp); + + /* First calculate the total buffer size needed for this IRP. */ + + TRACE("Need %u bytes, have %u.\n", irp_size, output_len); + irp->IoStatus.Information = irp_size; + + memset(irp->AssociatedIrp.SystemBuffer, 0, output_len); + + if (output_len < irp_size) + { + if (params.bits == 32) + { + struct http_request_32 *req = irp->AssociatedIrp.SystemBuffer; + req->ConnectionId = (ULONG_PTR)conn; + } + else + { + struct http_request_64 *req = irp->AssociatedIrp.SystemBuffer; + req->ConnectionId = (ULONG_PTR)conn; + } + return STATUS_BUFFER_OVERFLOW; + } + + if (params.bits == 32) + { + struct http_request_32 *req = irp->AssociatedIrp.SystemBuffer; + + offset = sizeof(*req); + + req->ConnectionId = (ULONG_PTR)conn; + req->UrlContext = conn->queue->context; + req->Version = conn->version; + req->Verb = conn->verb; + req->BytesReceived = conn->req_len; + } + else + { + struct http_request_64 *req = irp->AssociatedIrp.SystemBuffer; + + offset = sizeof(*req); + + req->ConnectionId = (ULONG_PTR)conn; + req->UrlContext = conn->queue->context; + req->Version = conn->version; + req->Verb = conn->verb; + req->BytesReceived = conn->req_len; + } + + assert(offset == irp->IoStatus.Information); + + conn->available = FALSE; + memmove(conn->buffer, conn->buffer + conn->req_len, conn->len - conn->req_len); + conn->len -= conn->req_len; + + return STATUS_SUCCESS; +} + /* Return 1 if str matches expect, 0 if str is incomplete, -1 if they don't match. */ static int compare_exact(const char *str, const char *expect, const char *end) { @@ -507,6 +677,32 @@ static NTSTATUS http_remove_url(struct request_queue *queue, IRP *irp) return STATUS_SUCCESS; }
+static NTSTATUS http_receive_request(struct request_queue *queue, IRP *irp) +{ + const struct http_receive_request_params *params = irp->AssociatedIrp.SystemBuffer; + struct connection *conn; + NTSTATUS ret; + + TRACE("addr %s, id %s, flags %#x, bits %u.\n", wine_dbgstr_longlong(params->addr), + wine_dbgstr_longlong(params->id), params->flags, params->bits); + + EnterCriticalSection(&http_cs); + + LIST_FOR_EACH_ENTRY(conn, &connections, struct connection, entry) + { + if (conn->available && conn->queue == queue) + { + ret = complete_irp(conn, irp); + LeaveCriticalSection(&http_cs); + return ret; + } + } + + LeaveCriticalSection(&http_cs); + + return STATUS_PENDING; +} + static NTSTATUS WINAPI dispatch_ioctl(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); @@ -521,6 +717,9 @@ static NTSTATUS WINAPI dispatch_ioctl(DEVICE_OBJECT *device, IRP *irp) case IOCTL_HTTP_REMOVE_URL: ret = http_remove_url(queue, irp); break; + case IOCTL_HTTP_RECEIVE_REQUEST: + ret = http_receive_request(queue, irp); + break; default: FIXME("Unhandled ioctl %#x.\n", stack->Parameters.DeviceIoControl.IoControlCode); ret = STATUS_NOT_IMPLEMENTED; diff --git a/include/wine/http.h b/include/wine/http.h index 354c289387..1145cab71a 100644 --- a/include/wine/http.h +++ b/include/wine/http.h @@ -25,6 +25,7 @@
#define IOCTL_HTTP_ADD_URL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, 0) #define IOCTL_HTTP_REMOVE_URL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, 0) +#define IOCTL_HTTP_RECEIVE_REQUEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, 0)
struct http_add_url_params { @@ -32,4 +33,12 @@ struct http_add_url_params char url[1]; };
+struct http_receive_request_params +{ + ULONGLONG addr; /* user-mode buffer address */ + HTTP_REQUEST_ID id; + ULONG flags; + ULONG bits; +}; + #endif
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/http.sys/http.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c index a41b464a72..7690867fcd 100644 --- a/dlls/http.sys/http.c +++ b/dlls/http.sys/http.c @@ -277,6 +277,9 @@ static NTSTATUS complete_irp(struct connection *conn, IRP *irp)
/* First calculate the total buffer size needed for this IRP. */
+ if (conn->unk_verb_len) + irp_size += conn->unk_verb_len + 1; + TRACE("Need %u bytes, have %u.\n", irp_size, output_len); irp->IoStatus.Information = irp_size;
@@ -300,6 +303,7 @@ static NTSTATUS complete_irp(struct connection *conn, IRP *irp) if (params.bits == 32) { struct http_request_32 *req = irp->AssociatedIrp.SystemBuffer; + char *buffer = irp->AssociatedIrp.SystemBuffer;
offset = sizeof(*req);
@@ -307,11 +311,22 @@ static NTSTATUS complete_irp(struct connection *conn, IRP *irp) req->UrlContext = conn->queue->context; req->Version = conn->version; req->Verb = conn->verb; + req->UnknownVerbLength = conn->unk_verb_len; + + if (conn->unk_verb_len) + { + req->pUnknownVerb = params.addr + offset; + memcpy(buffer + offset, conn->buffer, conn->unk_verb_len); + offset += conn->unk_verb_len; + buffer[offset++] = 0; + } + req->BytesReceived = conn->req_len; } else { struct http_request_64 *req = irp->AssociatedIrp.SystemBuffer; + char *buffer = irp->AssociatedIrp.SystemBuffer;
offset = sizeof(*req);
@@ -319,6 +334,16 @@ static NTSTATUS complete_irp(struct connection *conn, IRP *irp) req->UrlContext = conn->queue->context; req->Version = conn->version; req->Verb = conn->verb; + req->UnknownVerbLength = conn->unk_verb_len; + + if (conn->unk_verb_len) + { + req->pUnknownVerb = params.addr + offset; + memcpy(buffer + offset, conn->buffer, conn->unk_verb_len); + offset += conn->unk_verb_len; + buffer[offset++] = 0; + } + req->BytesReceived = conn->req_len; }