422 lines
12 KiB
C
422 lines
12 KiB
C
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved
|
|
|
|
#include "common.h"
|
|
|
|
//
|
|
// Global variables
|
|
//
|
|
|
|
static USHORT g_usOKCode = 200;
|
|
static CHAR g_szOKReason[] = "OK";
|
|
|
|
static USHORT g_usFileNotFoundCode = 404;
|
|
static CHAR g_szFileNotFoundReason[] = "Not Found";
|
|
static CHAR g_szFileNotFoundMessage[] = "File not found";
|
|
static CHAR g_szFileNotAccessibleMessage[] = "File could not be opened";
|
|
static CHAR g_szBadPathMessage[] = "Bad path";
|
|
|
|
static USHORT g_usBadRequestReasonCode = 400;
|
|
static CHAR g_szBadRequestReason[] = "Bad Request";
|
|
static CHAR g_szBadRequestMessage[] = "Bad request";
|
|
|
|
static USHORT g_usNotImplementedCode = 501;
|
|
static CHAR g_szNotImplementedReason[] = "Not Implemented";
|
|
static CHAR g_szNotImplementedMessage[] = "Server only supports GET";
|
|
|
|
static USHORT g_usEntityTooLargeCode = 413;
|
|
static CHAR g_szEntityTooLargeReason[] = "Request Entity Too Large";
|
|
static CHAR g_szEntityTooLargeMessage[] = "Large buffer support is not implemented";
|
|
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Retrieves the next available HTTP request from the specified request
|
|
// queue asynchronously. If HttpReceiveHttpRequest call failed inline checks
|
|
// the reason and cancels the Io if necessary. If our attempt to receive
|
|
// an HTTP Request failed with ERROR_MORE_DATA the client is misbehaving
|
|
// and we should return it error 400 back. Pretend that the call
|
|
// failed asynchronously.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// pServerContext - context for the server
|
|
//
|
|
// Io - Structure that defines the I/O object.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// N/A
|
|
//
|
|
|
|
VOID PostNewReceive(
|
|
PSERVER_CONTEXT pServerContext,
|
|
PTP_IO Io
|
|
)
|
|
{
|
|
PHTTP_IO_REQUEST pIoRequest;
|
|
ULONG Result;
|
|
|
|
pIoRequest = AllocateHttpIoRequest(pServerContext);
|
|
|
|
if (pIoRequest == NULL)
|
|
return;
|
|
|
|
StartThreadpoolIo(Io);
|
|
|
|
Result = HttpReceiveHttpRequest(
|
|
pServerContext->hRequestQueue,
|
|
HTTP_NULL_ID,
|
|
HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY,
|
|
pIoRequest->pHttpRequest,
|
|
sizeof(pIoRequest->RequestBuffer),
|
|
NULL,
|
|
&pIoRequest->ioContext.Overlapped
|
|
);
|
|
|
|
if (Result != ERROR_IO_PENDING &&
|
|
Result != NO_ERROR)
|
|
{
|
|
CancelThreadpoolIo(Io);
|
|
|
|
fprintf(stderr, "HttpReceiveHttpRequest failed, error 0x%lx\n", Result);
|
|
|
|
if (Result == ERROR_MORE_DATA)
|
|
{
|
|
ProcessReceiveAndPostResponse(pIoRequest, Io, ERROR_MORE_DATA);
|
|
}
|
|
|
|
CleanupHttpIoRequest(pIoRequest);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Completion routine for the asynchronous HttpSendHttpResponse
|
|
// call. This sample doesn't process the results of its send operations.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// IoContext - The HTTP_IO_CONTEXT tracking this operation.
|
|
//
|
|
// Io - Ignored
|
|
//
|
|
// IoResult - Ignored
|
|
//
|
|
// Return Value:
|
|
//
|
|
// N/A
|
|
//
|
|
|
|
VOID SendCompletionCallback(
|
|
PHTTP_IO_CONTEXT pIoContext,
|
|
PTP_IO Io,
|
|
ULONG IoResult
|
|
)
|
|
{
|
|
PHTTP_IO_RESPONSE pIoResponse;
|
|
|
|
UNREFERENCED_PARAMETER(IoResult);
|
|
UNREFERENCED_PARAMETER(Io);
|
|
|
|
pIoResponse = CONTAINING_RECORD(pIoContext,
|
|
HTTP_IO_RESPONSE,
|
|
ioContext);
|
|
|
|
CleanupHttpIoResponse(pIoResponse);
|
|
}
|
|
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Creates a response for a successful get, the content is served
|
|
// from a file.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// pServerContext - Pointer to the http server context structure.
|
|
//
|
|
// hFile - Handle to the specified file.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Return a pointer to the HTTP_IO_RESPONSE structure.
|
|
//
|
|
|
|
PHTTP_IO_RESPONSE CreateFileResponse(
|
|
PSERVER_CONTEXT pServerContext,
|
|
HANDLE hFile
|
|
)
|
|
{
|
|
PHTTP_IO_RESPONSE pIoResponse;
|
|
PHTTP_DATA_CHUNK pChunk;
|
|
|
|
pIoResponse = AllocateHttpIoResponse(pServerContext);
|
|
|
|
if (pIoResponse == NULL)
|
|
return NULL;
|
|
|
|
pIoResponse->HttpResponse.StatusCode = g_usOKCode;
|
|
pIoResponse->HttpResponse.pReason = g_szOKReason;
|
|
pIoResponse->HttpResponse.ReasonLength = (USHORT)strlen(g_szOKReason);
|
|
|
|
pChunk = &pIoResponse->HttpResponse.pEntityChunks[0];
|
|
pChunk->DataChunkType = HttpDataChunkFromFileHandle;
|
|
pChunk->FromFileHandle.ByteRange.Length.QuadPart = HTTP_BYTE_RANGE_TO_EOF;
|
|
pChunk->FromFileHandle.ByteRange.StartingOffset.QuadPart = 0;
|
|
pChunk->FromFileHandle.FileHandle = hFile;
|
|
|
|
return pIoResponse;
|
|
}
|
|
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Creates an http response if the requested file was not found.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// pServerContext - Pointer to the http server context structure.
|
|
//
|
|
// code - The error code to use in the response
|
|
//
|
|
// pReason - The reason string to send back to the client
|
|
//
|
|
// pMessage - The more verbose message to send back to the client
|
|
//
|
|
// Return Value:
|
|
//
|
|
// Return a pointer to the HTTP_IO_RESPONSE structure
|
|
//
|
|
|
|
PHTTP_IO_RESPONSE CreateMessageResponse(
|
|
PSERVER_CONTEXT pServerContext,
|
|
USHORT code,
|
|
PCHAR pReason,
|
|
PCHAR pMessage
|
|
)
|
|
{
|
|
PHTTP_IO_RESPONSE pIoResponse;
|
|
PHTTP_DATA_CHUNK pChunk;
|
|
|
|
pIoResponse = AllocateHttpIoResponse(pServerContext);
|
|
|
|
if (pIoResponse == NULL)
|
|
return NULL;
|
|
|
|
// Can not find the requested file
|
|
pIoResponse->HttpResponse.StatusCode = code;
|
|
pIoResponse->HttpResponse.pReason = pReason;
|
|
pIoResponse->HttpResponse.ReasonLength = (USHORT)strlen(pReason);
|
|
|
|
pChunk = &pIoResponse->HttpResponse.pEntityChunks[0];
|
|
pChunk->DataChunkType = HttpDataChunkFromMemory;
|
|
pChunk->FromMemory.pBuffer = pMessage;
|
|
pChunk->FromMemory.BufferLength = (ULONG)strlen(pMessage);
|
|
|
|
return pIoResponse;
|
|
}
|
|
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This routine processes the received request, builds an HTTP response,
|
|
// and sends it using HttpSendHttpResponse.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// IoContext - The HTTP_IO_CONTEXT tracking this operation.
|
|
//
|
|
// Io - Structure that defines the I/O object.
|
|
//
|
|
// IoResult - The result of the I/O operation. If the I/O is successful,
|
|
// this parameter is NO_ERROR. Otherwise, this parameter is one of
|
|
// the system error codes.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// N/A
|
|
//
|
|
|
|
VOID ProcessReceiveAndPostResponse(
|
|
PHTTP_IO_REQUEST pIoRequest,
|
|
PTP_IO Io,
|
|
ULONG IoResult
|
|
)
|
|
{
|
|
ULONG Result;
|
|
HANDLE hFile;
|
|
HTTP_CACHE_POLICY CachePolicy;
|
|
PHTTP_IO_RESPONSE pIoResponse;
|
|
PSERVER_CONTEXT pServerContext;
|
|
|
|
pServerContext = pIoRequest->ioContext.pServerContext;
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
switch(IoResult){
|
|
case NO_ERROR:
|
|
{
|
|
WCHAR wszFilePath[MAX_STR_SIZE];
|
|
BOOL bValidUrl;
|
|
|
|
if (pIoRequest->pHttpRequest->Verb != HttpVerbGET){
|
|
pIoResponse = CreateMessageResponse(
|
|
pServerContext,
|
|
g_usNotImplementedCode,
|
|
g_szNotImplementedReason,
|
|
g_szNotImplementedMessage);
|
|
break;
|
|
}
|
|
|
|
bValidUrl = GetFilePathName(
|
|
pServerContext->wszRootDirectory,
|
|
pIoRequest->pHttpRequest->CookedUrl.pAbsPath,
|
|
wszFilePath,
|
|
MAX_STR_SIZE);
|
|
|
|
if (bValidUrl == FALSE)
|
|
{
|
|
pIoResponse = CreateMessageResponse(
|
|
pServerContext,
|
|
g_usFileNotFoundCode,
|
|
g_szFileNotFoundReason,
|
|
g_szBadPathMessage);
|
|
break;
|
|
}
|
|
|
|
hFile = CreateFileW(
|
|
wszFilePath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
if (GetLastError() == ERROR_PATH_NOT_FOUND ||
|
|
GetLastError() == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
pIoResponse = CreateMessageResponse(
|
|
pServerContext,
|
|
g_usFileNotFoundCode,
|
|
g_szFileNotFoundReason,
|
|
g_szFileNotFoundMessage);
|
|
break;
|
|
}
|
|
|
|
pIoResponse = CreateMessageResponse(
|
|
pServerContext,
|
|
g_usFileNotFoundCode,
|
|
g_szFileNotFoundReason,
|
|
g_szFileNotAccessibleMessage);
|
|
break;
|
|
}
|
|
|
|
pIoResponse = CreateFileResponse(pServerContext, hFile);
|
|
|
|
CachePolicy.Policy = HttpCachePolicyUserInvalidates;
|
|
CachePolicy.SecondsToLive = 0;
|
|
break;
|
|
}
|
|
case ERROR_MORE_DATA:
|
|
{
|
|
pIoResponse = CreateMessageResponse(
|
|
pServerContext,
|
|
g_usEntityTooLargeCode,
|
|
g_szEntityTooLargeReason,
|
|
g_szEntityTooLargeMessage);
|
|
break;
|
|
}
|
|
default:
|
|
// If the HttpReceiveHttpRequest call failed asynchronously
|
|
// with a different error than ERROR_MORE_DATA, the error is fatal
|
|
// There's nothing this function can do
|
|
return;
|
|
}
|
|
|
|
if (pIoResponse == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
StartThreadpoolIo(Io);
|
|
|
|
Result = HttpSendHttpResponse(
|
|
pServerContext->hRequestQueue,
|
|
pIoRequest->pHttpRequest->RequestId,
|
|
0,
|
|
&pIoResponse->HttpResponse,
|
|
(hFile != INVALID_HANDLE_VALUE) ? &CachePolicy : NULL,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
&pIoResponse->ioContext.Overlapped,
|
|
NULL
|
|
);
|
|
|
|
if (Result != NO_ERROR &&
|
|
Result != ERROR_IO_PENDING)
|
|
{
|
|
CancelThreadpoolIo(Io);
|
|
|
|
fprintf(stderr, "HttpSendHttpResponse failed, error 0x%lx\n", Result);
|
|
|
|
CleanupHttpIoResponse(pIoResponse);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// Completion routine for the asynchronous HttpReceiveHttpRequest
|
|
// call. Check if the user asked us to stop the server. If not, send a
|
|
// response and post a new receive to HTTPAPI.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// IoContext - The HTTP_IO_CONTEXT tracking this operation.
|
|
//
|
|
// Io - Structure that defines the I/O object.
|
|
//
|
|
// IoResult - The result of the I/O operation. If the I/O is successful,
|
|
// this parameter is NO_ERROR. Otherwise, this parameter is one of
|
|
// the system error codes.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// N/A
|
|
//
|
|
|
|
VOID ReceiveCompletionCallback(
|
|
PHTTP_IO_CONTEXT pIoContext,
|
|
PTP_IO Io,
|
|
ULONG IoResult
|
|
)
|
|
{
|
|
PHTTP_IO_REQUEST pIoRequest;
|
|
PSERVER_CONTEXT pServerContext;
|
|
|
|
pIoRequest = CONTAINING_RECORD(pIoContext,
|
|
HTTP_IO_REQUEST,
|
|
ioContext);
|
|
|
|
pServerContext = pIoRequest->ioContext.pServerContext;
|
|
|
|
if (pServerContext->bStopServer == FALSE)
|
|
{
|
|
ProcessReceiveAndPostResponse(pIoRequest, Io, IoResult);
|
|
|
|
PostNewReceive(pServerContext, Io);
|
|
}
|
|
|
|
CleanupHttpIoRequest(pIoRequest);
|
|
}
|