httpapi: Implement HttpSendHttpResponse().

While it's a little architecturally weird to have this part uniquely
implemented in httpapi, it's far easier to handle building the response
string here than having to marshal everything into an ioctl buffer first.

Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zebediah Figura 2019-08-28 20:45:28 -05:00 committed by Alexandre Julliard
parent 5b6cee0dbd
commit d73fc3e82a
1 changed files with 141 additions and 4 deletions

View File

@ -289,6 +289,18 @@ ULONG WINAPI HttpReceiveHttpRequest(HANDLE queue, HTTP_REQUEST_ID id, ULONG flag
return ret;
}
static void format_date(char *buffer)
{
static const char day_names[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static const char month_names[12][4] =
{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
SYSTEMTIME date;
GetSystemTime(&date);
sprintf(buffer + strlen(buffer), "Date: %s, %02u %s %u %02u:%02u:%02u GMT\r\n",
day_names[date.wDayOfWeek], date.wDay, month_names[date.wMonth - 1],
date.wYear, date.wHour, date.wMinute, date.wSecond);
}
/***********************************************************************
* HttpSendHttpResponse (HTTPAPI.@)
*/
@ -296,10 +308,135 @@ ULONG WINAPI HttpSendHttpResponse(HANDLE queue, HTTP_REQUEST_ID id, ULONG flags,
HTTP_RESPONSE *response, HTTP_CACHE_POLICY *cache_policy, ULONG *ret_size,
void *reserved1, ULONG reserved2, OVERLAPPED *ovl, HTTP_LOG_DATA *log_data)
{
FIXME("queue %p, id %s, flags %#x, response %p, cache_policy %p, "
"ret_size %p, reserved1 %p, reserved2 %#x, ovl %p, log_data %p, stub!\n",
queue, wine_dbgstr_longlong(id), flags, response, cache_policy, ret_size, reserved1, reserved2, ovl, log_data);
static const char *const header_names[] =
{
"Cache-Control",
"Connection",
"Date",
"Keep-Alive",
"Pragma",
"Trailer",
"Transfer-Encoding",
"Upgrade",
"Via",
"Warning",
"Allow",
"Content-Length",
"Content-Type",
"Content-Encoding",
"Content-Language",
"Content-Location",
"Content-MD5",
"Content-Range",
"Expires",
"Last-Modified",
"Accept-Ranges",
"Age",
"ETag",
"Location",
"Proxy-Authenticate",
"Retry-After",
"Server",
"Set-Cookie",
"Vary",
"WWW-Authenticate",
};
struct http_response *buffer;
OVERLAPPED dummy_ovl = {};
ULONG ret = ERROR_SUCCESS;
int len, body_len = 0;
char *p, dummy[12];
USHORT i;
TRACE("queue %p, id %s, flags %#x, response %p, cache_policy %p, "
"ret_size %p, reserved1 %p, reserved2 %#x, ovl %p, log_data %p.\n",
queue, wine_dbgstr_longlong(id), flags, response, cache_policy,
ret_size, reserved1, reserved2, ovl, log_data);
if (flags)
FIXME("Unhandled flags %#x.\n", flags);
if (response->s.Flags)
FIXME("Unhandled response flags %#x.\n", response->s.Flags);
if (cache_policy)
WARN("Ignoring cache_policy.\n");
if (log_data)
WARN("Ignoring log_data.\n");
len = 12 + sprintf(dummy, "%hu", response->s.StatusCode) + response->s.ReasonLength;
for (i = 0; i < response->s.EntityChunkCount; ++i)
{
if (response->s.pEntityChunks[i].DataChunkType != HttpDataChunkFromMemory)
{
FIXME("Unhandled data chunk type %u.\n", response->s.pEntityChunks[i].DataChunkType);
return ERROR_CALL_NOT_IMPLEMENTED;
}
body_len += response->s.pEntityChunks[i].FromMemory.BufferLength;
}
len += body_len;
for (i = 0; i < HttpHeaderResponseMaximum; ++i)
{
if (i == HttpHeaderDate)
len += 37;
else if (response->s.Headers.KnownHeaders[i].RawValueLength)
len += strlen(header_names[i]) + 2 + response->s.Headers.KnownHeaders[i].RawValueLength + 2;
else if (i == HttpHeaderContentLength)
{
char dummy[12];
len += strlen(header_names[i]) + 2 + sprintf(dummy, "%d", body_len) + 2;
}
}
for (i = 0; i < response->s.Headers.UnknownHeaderCount; ++i)
{
len += response->s.Headers.pUnknownHeaders[i].NameLength + 2;
len += response->s.Headers.pUnknownHeaders[i].RawValueLength + 2;
}
len += 2;
if (!(buffer = heap_alloc(offsetof(struct http_response, buffer[len]))))
return ERROR_OUTOFMEMORY;
buffer->id = id;
buffer->len = len;
sprintf(buffer->buffer, "HTTP/1.1 %u %.*s\r\n", response->s.StatusCode,
response->s.ReasonLength, response->s.pReason);
for (i = 0; i < HttpHeaderResponseMaximum; ++i)
{
const HTTP_KNOWN_HEADER *header = &response->s.Headers.KnownHeaders[i];
if (i == HttpHeaderDate)
format_date(buffer->buffer);
else if (header->RawValueLength)
sprintf(buffer->buffer + strlen(buffer->buffer), "%s: %.*s\r\n",
header_names[i], header->RawValueLength, header->pRawValue);
else if (i == HttpHeaderContentLength)
sprintf(buffer->buffer + strlen(buffer->buffer), "Content-Length: %d\r\n", body_len);
}
for (i = 0; i < response->s.Headers.UnknownHeaderCount; ++i)
{
const HTTP_UNKNOWN_HEADER *header = &response->s.Headers.pUnknownHeaders[i];
sprintf(buffer->buffer + strlen(buffer->buffer), "%.*s: %.*s\r\n", header->NameLength,
header->pName, header->RawValueLength, header->pRawValue);
}
p = buffer->buffer + strlen(buffer->buffer);
/* Don't use strcat, because this might be the end of the buffer. */
memcpy(p, "\r\n", 2);
p += 2;
for (i = 0; i < response->s.EntityChunkCount; ++i)
{
const HTTP_DATA_CHUNK *chunk = &response->s.pEntityChunks[i];
memcpy(p, chunk->FromMemory.pBuffer, chunk->FromMemory.BufferLength);
p += chunk->FromMemory.BufferLength;
}
if (!ovl)
ovl = &dummy_ovl;
if (!DeviceIoControl(queue, IOCTL_HTTP_SEND_RESPONSE, buffer,
offsetof(struct http_response, buffer[len]), NULL, 0, NULL, ovl))
ret = GetLastError();
heap_free(buffer);
return ret;
}
/***********************************************************************