diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c index 46590f972fd..31c7fc09713 100644 --- a/dlls/http.sys/http.c +++ b/dlls/http.sys/http.c @@ -33,164 +33,6 @@ 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_unknown_header_32 -{ - USHORT NameLength; - USHORT RawValueLength; - ULONG pName; /* char string */ - ULONG pRawValue; /* char string */ -}; - -struct http_data_chunk_32 -{ - HTTP_DATA_CHUNK_TYPE DataChunkType; - union - { - struct - { - ULONG pBuffer; /* char string */ - ULONG BufferLength; - } FromMemory; - /* for the struct size */ - struct - { - ULARGE_INTEGER StartingOffset; - ULARGE_INTEGER Length; - HANDLE FileHandle; - } FromFileHandle; - }; -}; - -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_unknown_header_64 -{ - USHORT NameLength; - USHORT RawValueLength; - ULONGLONG pName; /* char string */ - ULONGLONG pRawValue; /* char string */ -}; - -struct http_data_chunk_64 -{ - HTTP_DATA_CHUNK_TYPE DataChunkType; - union - { - struct - { - ULONGLONG pBuffer; /* char string */ - ULONG BufferLength; - } FromMemory; - /* for the struct size */ - struct - { - ULARGE_INTEGER StartingOffset; - ULARGE_INTEGER Length; - HANDLE FileHandle; - } FromFileHandle; - }; -}; - -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 = \ @@ -405,365 +247,44 @@ static void parse_header(const char *name, int *name_len, const char **value, in *value_len = p - *value + 1; } +#define http_unknown_header http_unknown_header_64 +#define http_data_chunk http_data_chunk_64 +#define http_request http_request_64 +#define complete_irp complete_irp_64 +#define POINTER ULONGLONG +#include "request.h" +#undef http_unknown_header +#undef http_data_chunk +#undef http_request +#undef complete_irp +#undef POINTER + +#define http_unknown_header http_unknown_header_32 +#define http_data_chunk http_data_chunk_32 +#define http_request http_request_32 +#define complete_irp complete_irp_32 +#define POINTER ULONG +#include "request.h" +#undef http_unknown_header +#undef http_data_chunk +#undef http_request +#undef complete_irp +#undef POINTER + static NTSTATUS complete_irp(struct connection *conn, IRP *irp) { - static const WCHAR httpW[] = {'h','t','t','p',':','/','/'}; 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); - ULONG cooked_len, host_len, abs_path_len, query_len, chunk_len = 0, offset, processed; - IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); - const DWORD output_len = stack->Parameters.DeviceIoControl.OutputBufferLength; - const char *p, *name, *value, *host, *abs_path, *query; - USHORT unk_headers_count = 0, unk_header_idx; - int name_len, value_len, len; - struct sockaddr_in addr; TRACE("Completing IRP %p.\n", irp); if (!conn->req_id) conn->req_id = ++req_id_counter; - /* First calculate the total buffer size needed for this IRP. */ - - if (conn->unk_verb_len) - irp_size += conn->unk_verb_len + 1; - irp_size += conn->url_len + 1; - - /* cooked URL */ - if (conn->url[0] == '/') - { - p = host = conn->host; - while (isgraph(*p)) ++p; - host_len = p - conn->host; - abs_path = conn->url; - abs_path_len = conn->url_len; - } - else - { - host = conn->url + 7; - abs_path = strchr(host, '/'); - host_len = abs_path - host; - abs_path_len = (conn->url + conn->url_len) - abs_path; - } - if ((query = memchr(abs_path, '?', abs_path_len))) - { - query_len = (abs_path + abs_path_len) - query; - abs_path_len = query - abs_path; - } - else - query_len = 0; - cooked_len = (7 /* scheme */ + host_len + abs_path_len + query_len) * sizeof(WCHAR); - irp_size += cooked_len + sizeof(WCHAR); - - /* addresses */ - irp_size += 2 * sizeof(addr); - - /* headers */ - p = strstr(conn->buffer, "\r\n") + 2; - while (memcmp(p, "\r\n", 2)) - { - name = p; - parse_header(name, &name_len, &value, &value_len); - if (parse_header_name(name, name_len) == HttpHeaderRequestMaximum) - { - irp_size += name_len + 1; - ++unk_headers_count; - } - irp_size += value_len + 1; - p = strstr(p, "\r\n") + 2; - } - p += 2; - if (params.bits == 32) - irp_size += unk_headers_count * sizeof(struct http_unknown_header_32); + return complete_irp_32(conn, irp); else - irp_size += unk_headers_count * sizeof(struct http_unknown_header_64); - - 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; - req->RequestId = conn->req_id; - } - else - { - struct http_request_64 *req = irp->AssociatedIrp.SystemBuffer; - req->ConnectionId = (ULONG_PTR)conn; - req->RequestId = conn->req_id; - } - return STATUS_BUFFER_OVERFLOW; - } - - if (params.bits == 32) - { - struct http_request_32 *req = irp->AssociatedIrp.SystemBuffer; - struct http_unknown_header_32 *unk_headers = NULL; - char *buffer = irp->AssociatedIrp.SystemBuffer; - struct http_data_chunk_32 *chunk = NULL; - - offset = sizeof(*req); - - req->ConnectionId = (ULONG_PTR)conn; - req->RequestId = conn->req_id; - req->UrlContext = conn->queue->context; - req->Version = conn->version; - req->Verb = conn->verb; - req->UnknownVerbLength = conn->unk_verb_len; - req->RawUrlLength = conn->url_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->pRawUrl = params.addr + offset; - memcpy(buffer + offset, conn->url, conn->url_len); - offset += conn->url_len; - buffer[offset++] = 0; - - req->CookedUrl.FullUrlLength = cooked_len; - req->CookedUrl.HostLength = host_len * sizeof(WCHAR); - req->CookedUrl.AbsPathLength = abs_path_len * sizeof(WCHAR); - req->CookedUrl.QueryStringLength = query_len * sizeof(WCHAR); - req->CookedUrl.pFullUrl = params.addr + offset; - req->CookedUrl.pHost = req->CookedUrl.pFullUrl + 7 * sizeof(WCHAR); - req->CookedUrl.pAbsPath = req->CookedUrl.pHost + host_len * sizeof(WCHAR); - if (query) - req->CookedUrl.pQueryString = req->CookedUrl.pAbsPath + abs_path_len * sizeof(WCHAR); - - memcpy(buffer + offset, httpW, sizeof(httpW)); - offset += 7 * sizeof(WCHAR); - MultiByteToWideChar(CP_ACP, 0, host, host_len, (WCHAR *)(buffer + offset), host_len * sizeof(WCHAR)); - offset += host_len * sizeof(WCHAR); - MultiByteToWideChar(CP_ACP, 0, abs_path, abs_path_len + query_len, - (WCHAR *)(buffer + offset), (abs_path_len + query_len) * sizeof(WCHAR)); - offset += (abs_path_len + query_len) * sizeof(WCHAR); - buffer[offset++] = 0; - buffer[offset++] = 0; - - req->Address.pRemoteAddress = params.addr + offset; - len = sizeof(addr); - getpeername(conn->socket, (struct sockaddr *)&addr, &len); - memcpy(buffer + offset, &addr, sizeof(addr)); - offset += sizeof(addr); - - req->Address.pLocalAddress = params.addr + offset; - len = sizeof(addr); - getsockname(conn->socket, (struct sockaddr *)&addr, &len); - memcpy(buffer + offset, &addr, sizeof(addr)); - offset += sizeof(addr); - - req->Headers.UnknownHeaderCount = unk_headers_count; - if (unk_headers_count) - { - req->Headers.pUnknownHeaders = params.addr + offset; - unk_headers = (struct http_unknown_header_32 *)(buffer + offset); - offset += unk_headers_count * sizeof(*unk_headers); - } - - unk_header_idx = 0; - p = strstr(conn->buffer, "\r\n") + 2; - while (memcmp(p, "\r\n", 2)) - { - HTTP_HEADER_ID id; - - name = p; - parse_header(name, &name_len, &value, &value_len); - if ((id = parse_header_name(name, name_len)) == HttpHeaderRequestMaximum) - { - unk_headers[unk_header_idx].NameLength = name_len; - unk_headers[unk_header_idx].RawValueLength = value_len; - unk_headers[unk_header_idx].pName = params.addr + offset; - memcpy(buffer + offset, name, name_len); - offset += name_len; - buffer[offset++] = 0; - unk_headers[unk_header_idx].pRawValue = params.addr + offset; - memcpy(buffer + offset, value, value_len); - offset += value_len; - buffer[offset++] = 0; - ++unk_header_idx; - } - else - { - req->Headers.KnownHeaders[id].RawValueLength = value_len; - req->Headers.KnownHeaders[id].pRawValue = params.addr + offset; - memcpy(buffer + offset, value, value_len); - offset += value_len; - buffer[offset++] = 0; - } - p = strstr(p, "\r\n") + 2; - } - p += 2; - - if (irp_size + sizeof(*chunk) < output_len && (params.flags & HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY)) - chunk_len = min(conn->content_len, output_len - (irp_size + sizeof(*chunk))); - if (chunk_len) - { - req->EntityChunkCount = 1; - req->pEntityChunks = params.addr + offset; - chunk = (struct http_data_chunk_32 *)(buffer + offset); - offset += sizeof(*chunk); - chunk->DataChunkType = HttpDataChunkFromMemory; - chunk->FromMemory.BufferLength = chunk_len; - chunk->FromMemory.pBuffer = params.addr + offset; - memcpy(buffer + offset, p, chunk_len); - offset += chunk_len; - - irp->IoStatus.Information = irp_size + sizeof(*chunk) + chunk_len; - } - - if (chunk_len < conn->content_len) - req->Flags |= HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS; - - req->BytesReceived = conn->req_len; - } - else - { - struct http_request_64 *req = irp->AssociatedIrp.SystemBuffer; - struct http_unknown_header_64 *unk_headers = NULL; - char *buffer = irp->AssociatedIrp.SystemBuffer; - struct http_data_chunk_64 *chunk = NULL; - - offset = sizeof(*req); - - req->ConnectionId = (ULONG_PTR)conn; - req->RequestId = conn->req_id; - req->UrlContext = conn->queue->context; - req->Version = conn->version; - req->Verb = conn->verb; - req->UnknownVerbLength = conn->unk_verb_len; - req->RawUrlLength = conn->url_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->pRawUrl = params.addr + offset; - memcpy(buffer + offset, conn->url, conn->url_len); - offset += conn->url_len; - buffer[offset++] = 0; - - req->CookedUrl.FullUrlLength = cooked_len; - req->CookedUrl.HostLength = host_len * sizeof(WCHAR); - req->CookedUrl.AbsPathLength = abs_path_len * sizeof(WCHAR); - req->CookedUrl.QueryStringLength = query_len * sizeof(WCHAR); - req->CookedUrl.pFullUrl = params.addr + offset; - req->CookedUrl.pHost = req->CookedUrl.pFullUrl + 7 * sizeof(WCHAR); - req->CookedUrl.pAbsPath = req->CookedUrl.pHost + host_len * sizeof(WCHAR); - if (query) - req->CookedUrl.pQueryString = req->CookedUrl.pAbsPath + abs_path_len * sizeof(WCHAR); - - memcpy(buffer + offset, httpW, sizeof(httpW)); - offset += 7 * sizeof(WCHAR); - MultiByteToWideChar(CP_ACP, 0, host, host_len, (WCHAR *)(buffer + offset), host_len * sizeof(WCHAR)); - offset += host_len * sizeof(WCHAR); - MultiByteToWideChar(CP_ACP, 0, abs_path, abs_path_len + query_len, - (WCHAR *)(buffer + offset), (abs_path_len + query_len) * sizeof(WCHAR)); - offset += (abs_path_len + query_len) * sizeof(WCHAR); - buffer[offset++] = 0; - buffer[offset++] = 0; - - req->Address.pRemoteAddress = params.addr + offset; - len = sizeof(addr); - getpeername(conn->socket, (struct sockaddr *)&addr, &len); - memcpy(buffer + offset, &addr, sizeof(addr)); - offset += sizeof(addr); - - req->Address.pLocalAddress = params.addr + offset; - len = sizeof(addr); - getsockname(conn->socket, (struct sockaddr *)&addr, &len); - memcpy(buffer + offset, &addr, sizeof(addr)); - offset += sizeof(addr); - - req->Headers.UnknownHeaderCount = unk_headers_count; - if (unk_headers_count) - { - req->Headers.pUnknownHeaders = params.addr + offset; - unk_headers = (struct http_unknown_header_64 *)(buffer + offset); - offset += unk_headers_count * sizeof(*unk_headers); - } - - unk_header_idx = 0; - p = strstr(conn->buffer, "\r\n") + 2; - while (memcmp(p, "\r\n", 2)) - { - HTTP_HEADER_ID id; - - name = p; - parse_header(name, &name_len, &value, &value_len); - if ((id = parse_header_name(name, name_len)) == HttpHeaderRequestMaximum) - { - unk_headers[unk_header_idx].NameLength = name_len; - unk_headers[unk_header_idx].RawValueLength = value_len; - unk_headers[unk_header_idx].pName = params.addr + offset; - memcpy(buffer + offset, name, name_len); - offset += name_len; - buffer[offset++] = 0; - unk_headers[unk_header_idx].pRawValue = params.addr + offset; - memcpy(buffer + offset, value, value_len); - offset += value_len; - buffer[offset++] = 0; - ++unk_header_idx; - } - else - { - req->Headers.KnownHeaders[id].RawValueLength = value_len; - req->Headers.KnownHeaders[id].pRawValue = params.addr + offset; - memcpy(buffer + offset, value, value_len); - offset += value_len; - buffer[offset++] = 0; - } - p = strstr(p, "\r\n") + 2; - } - p += 2; - - if (irp_size + sizeof(*chunk) < output_len && (params.flags & HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY)) - chunk_len = min(conn->content_len, output_len - (irp_size + sizeof(*chunk))); - if (chunk_len) - { - req->EntityChunkCount = 1; - req->pEntityChunks = params.addr + offset; - chunk = (struct http_data_chunk_64 *)(buffer + offset); - offset += sizeof(*chunk); - chunk->DataChunkType = HttpDataChunkFromMemory; - chunk->FromMemory.BufferLength = chunk_len; - chunk->FromMemory.pBuffer = params.addr + offset; - memcpy(buffer + offset, p, chunk_len); - offset += chunk_len; - - irp->IoStatus.Information = irp_size + sizeof(*chunk) + chunk_len; - } - - if (chunk_len < conn->content_len) - req->Flags |= HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS; - - req->BytesReceived = conn->req_len; - } - - assert(offset == irp->IoStatus.Information); - - conn->available = FALSE; - processed = conn->req_len - (conn->content_len - chunk_len); - memmove(conn->buffer, conn->buffer + processed, conn->len - processed); - conn->content_len -= chunk_len; - conn->len -= processed; - - return STATUS_SUCCESS; + return complete_irp_64(conn, irp); } /* Complete an IOCTL_HTTP_RECEIVE_REQUEST IRP if there is one to complete. */ diff --git a/dlls/http.sys/request.h b/dlls/http.sys/request.h new file mode 100644 index 00000000000..a1b77b67dc5 --- /dev/null +++ b/dlls/http.sys/request.h @@ -0,0 +1,310 @@ +/* + * Copyright 2019 Zebediah Figura + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* 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_unknown_header +{ + USHORT NameLength; + USHORT RawValueLength; + POINTER pName; /* char string */ + POINTER pRawValue; /* char string */ +}; + +struct http_data_chunk +{ + HTTP_DATA_CHUNK_TYPE DataChunkType; + union + { + struct + { + POINTER pBuffer; /* char string */ + ULONG BufferLength; + } FromMemory; + /* for the struct size */ + struct + { + ULARGE_INTEGER StartingOffset; + ULARGE_INTEGER Length; + POINTER FileHandle; + } FromFileHandle; + }; +}; + +struct http_request +{ + ULONG Flags; + HTTP_CONNECTION_ID ConnectionId; + HTTP_REQUEST_ID RequestId; + HTTP_URL_CONTEXT UrlContext; + HTTP_VERSION Version; + HTTP_VERB Verb; + USHORT UnknownVerbLength; + USHORT RawUrlLength; + POINTER pUnknownVerb; /* char string */ + POINTER pRawUrl; /* char string */ + struct + { + USHORT FullUrlLength; + USHORT HostLength; + USHORT AbsPathLength; + USHORT QueryStringLength; + POINTER pFullUrl; /* WCHAR string */ + POINTER pHost; /* pointer to above */ + POINTER pAbsPath; /* pointer to above */ + POINTER pQueryString; /* pointer to above */ + } CookedUrl; + struct + { + POINTER pRemoteAddress; /* SOCKADDR */ + POINTER pLocalAddress; /* SOCKADDR */ + } Address; + struct + { + USHORT UnknownHeaderCount; + POINTER pUnknownHeaders; /* struct http_unknown_header */ + USHORT TrailerCount; + POINTER pTrailers; /* NULL */ + struct + { + USHORT RawValueLength; + POINTER pRawValue; /* char string */ + } KnownHeaders[HttpHeaderRequestMaximum]; + } Headers; + ULONGLONG BytesReceived; + USHORT EntityChunkCount; + POINTER pEntityChunks; /* struct http_data_chunk */ + HTTP_RAW_CONNECTION_ID RawConnectionId; + POINTER pSslInfo; /* NULL (FIXME) */ + USHORT RequestInfoCount; + POINTER pRequestInfo; /* NULL (FIXME) */ +}; + +static NTSTATUS complete_irp(struct connection *conn, IRP *irp) +{ + const struct http_receive_request_params params + = *(struct http_receive_request_params *)irp->AssociatedIrp.SystemBuffer; + ULONG cooked_len, host_len, abs_path_len, query_len, chunk_len = 0, offset, processed; + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + const DWORD output_len = stack->Parameters.DeviceIoControl.OutputBufferLength; + struct http_request *req = irp->AssociatedIrp.SystemBuffer; + const char *p, *name, *value, *host, *abs_path, *query; + struct http_unknown_header *unk_headers = NULL; + char *buffer = irp->AssociatedIrp.SystemBuffer; + DWORD irp_size = sizeof(struct http_request); + USHORT unk_headers_count = 0, unk_header_idx; + struct http_data_chunk *chunk = NULL; + int name_len, value_len, len; + struct sockaddr_in addr; + + /* First calculate the total buffer size needed for this IRP. */ + + if (conn->unk_verb_len) + irp_size += conn->unk_verb_len + 1; + irp_size += conn->url_len + 1; + + /* cooked URL */ + if (conn->url[0] == '/') + { + p = host = conn->host; + while (isgraph(*p)) ++p; + host_len = p - conn->host; + abs_path = conn->url; + abs_path_len = conn->url_len; + } + else + { + host = conn->url + 7; + abs_path = strchr(host, '/'); + host_len = abs_path - host; + abs_path_len = (conn->url + conn->url_len) - abs_path; + } + if ((query = memchr(abs_path, '?', abs_path_len))) + { + query_len = (abs_path + abs_path_len) - query; + abs_path_len = query - abs_path; + } + else + query_len = 0; + cooked_len = (7 /* scheme */ + host_len + abs_path_len + query_len) * sizeof(WCHAR); + irp_size += cooked_len + sizeof(WCHAR); + + /* addresses */ + irp_size += 2 * sizeof(addr); + + /* headers */ + p = strstr(conn->buffer, "\r\n") + 2; + while (memcmp(p, "\r\n", 2)) + { + name = p; + parse_header(name, &name_len, &value, &value_len); + if (parse_header_name(name, name_len) == HttpHeaderRequestMaximum) + { + irp_size += name_len + 1; + ++unk_headers_count; + } + irp_size += value_len + 1; + p = strstr(p, "\r\n") + 2; + } + p += 2; + + irp_size += unk_headers_count * sizeof(struct http_unknown_header); + + 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) + { + req->ConnectionId = (ULONG_PTR)conn; + req->RequestId = conn->req_id; + return STATUS_BUFFER_OVERFLOW; + } + + offset = sizeof(*req); + + req->ConnectionId = (ULONG_PTR)conn; + req->RequestId = conn->req_id; + req->UrlContext = conn->queue->context; + req->Version = conn->version; + req->Verb = conn->verb; + req->UnknownVerbLength = conn->unk_verb_len; + req->RawUrlLength = conn->url_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->pRawUrl = params.addr + offset; + memcpy(buffer + offset, conn->url, conn->url_len); + offset += conn->url_len; + buffer[offset++] = 0; + + req->CookedUrl.FullUrlLength = cooked_len; + req->CookedUrl.HostLength = host_len * sizeof(WCHAR); + req->CookedUrl.AbsPathLength = abs_path_len * sizeof(WCHAR); + req->CookedUrl.QueryStringLength = query_len * sizeof(WCHAR); + req->CookedUrl.pFullUrl = params.addr + offset; + req->CookedUrl.pHost = req->CookedUrl.pFullUrl + 7 * sizeof(WCHAR); + req->CookedUrl.pAbsPath = req->CookedUrl.pHost + host_len * sizeof(WCHAR); + if (query) + req->CookedUrl.pQueryString = req->CookedUrl.pAbsPath + abs_path_len * sizeof(WCHAR); + + memcpy(buffer + offset, L"http://", sizeof(L"http://")); + offset += 7 * sizeof(WCHAR); + MultiByteToWideChar(CP_ACP, 0, host, host_len, (WCHAR *)(buffer + offset), host_len * sizeof(WCHAR)); + offset += host_len * sizeof(WCHAR); + MultiByteToWideChar(CP_ACP, 0, abs_path, abs_path_len + query_len, + (WCHAR *)(buffer + offset), (abs_path_len + query_len) * sizeof(WCHAR)); + offset += (abs_path_len + query_len) * sizeof(WCHAR); + buffer[offset++] = 0; + buffer[offset++] = 0; + + req->Address.pRemoteAddress = params.addr + offset; + len = sizeof(addr); + getpeername(conn->socket, (struct sockaddr *)&addr, &len); + memcpy(buffer + offset, &addr, sizeof(addr)); + offset += sizeof(addr); + + req->Address.pLocalAddress = params.addr + offset; + len = sizeof(addr); + getsockname(conn->socket, (struct sockaddr *)&addr, &len); + memcpy(buffer + offset, &addr, sizeof(addr)); + offset += sizeof(addr); + + req->Headers.UnknownHeaderCount = unk_headers_count; + if (unk_headers_count) + { + req->Headers.pUnknownHeaders = params.addr + offset; + unk_headers = (struct http_unknown_header *)(buffer + offset); + offset += unk_headers_count * sizeof(*unk_headers); + } + + unk_header_idx = 0; + p = strstr(conn->buffer, "\r\n") + 2; + while (memcmp(p, "\r\n", 2)) + { + HTTP_HEADER_ID id; + + name = p; + parse_header(name, &name_len, &value, &value_len); + if ((id = parse_header_name(name, name_len)) == HttpHeaderRequestMaximum) + { + unk_headers[unk_header_idx].NameLength = name_len; + unk_headers[unk_header_idx].RawValueLength = value_len; + unk_headers[unk_header_idx].pName = params.addr + offset; + memcpy(buffer + offset, name, name_len); + offset += name_len; + buffer[offset++] = 0; + unk_headers[unk_header_idx].pRawValue = params.addr + offset; + memcpy(buffer + offset, value, value_len); + offset += value_len; + buffer[offset++] = 0; + ++unk_header_idx; + } + else + { + req->Headers.KnownHeaders[id].RawValueLength = value_len; + req->Headers.KnownHeaders[id].pRawValue = params.addr + offset; + memcpy(buffer + offset, value, value_len); + offset += value_len; + buffer[offset++] = 0; + } + p = strstr(p, "\r\n") + 2; + } + p += 2; + + if (irp_size + sizeof(*chunk) < output_len && (params.flags & HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY)) + chunk_len = min(conn->content_len, output_len - (irp_size + sizeof(*chunk))); + if (chunk_len) + { + req->EntityChunkCount = 1; + req->pEntityChunks = params.addr + offset; + chunk = (struct http_data_chunk *)(buffer + offset); + offset += sizeof(*chunk); + chunk->DataChunkType = HttpDataChunkFromMemory; + chunk->FromMemory.BufferLength = chunk_len; + chunk->FromMemory.pBuffer = params.addr + offset; + memcpy(buffer + offset, p, chunk_len); + offset += chunk_len; + + irp->IoStatus.Information = irp_size + sizeof(*chunk) + chunk_len; + } + + if (chunk_len < conn->content_len) + req->Flags |= HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS; + + req->BytesReceived = conn->req_len; + + assert(offset == irp->IoStatus.Information); + + conn->available = FALSE; + processed = conn->req_len - (conn->content_len - chunk_len); + memmove(conn->buffer, conn->buffer + processed, conn->len - processed); + conn->content_len -= chunk_len; + conn->len -= processed; + + return STATUS_SUCCESS; +}