2637 lines
73 KiB
C++
2637 lines
73 KiB
C++
#include "..\precomp.hxx"
|
|
|
|
// Just to be aware of the FORWARDING_HANDLER object size.
|
|
C_ASSERT(sizeof(FORWARDING_HANDLER) <= 632);
|
|
|
|
#define DEF_MAX_FORWARDS 32
|
|
#define HEX_TO_ASCII(c) ((CHAR)(((c) < 10) ? ((c) + '0') : ((c) + 'a' - 10)))
|
|
#define BUFFER_SIZE (8192UL)
|
|
#define ENTITY_BUFFER_SIZE (6 + BUFFER_SIZE + 2)
|
|
|
|
#define FORWARDING_HANDLER_SIGNATURE ((DWORD)'FHLR')
|
|
#define FORWARDING_HANDLER_SIGNATURE_FREE ((DWORD)'fhlr')
|
|
|
|
STRA FORWARDING_HANDLER::sm_pStra502ErrorMsg;
|
|
ALLOC_CACHE_HANDLER * FORWARDING_HANDLER::sm_pAlloc = NULL;
|
|
TRACE_LOG * FORWARDING_HANDLER::sm_pTraceLog = NULL;
|
|
PROTOCOL_CONFIG FORWARDING_HANDLER::sm_ProtocolConfig;
|
|
RESPONSE_HEADER_HASH * FORWARDING_HANDLER::sm_pResponseHeaderHash = NULL;
|
|
|
|
FORWARDING_HANDLER::FORWARDING_HANDLER(
|
|
_In_ IHttpContext *pW3Context,
|
|
_In_ HTTP_MODULE_ID *pModuleId,
|
|
_In_ APPLICATION *pApplication
|
|
) : REQUEST_HANDLER(pW3Context, pModuleId, pApplication),
|
|
m_Signature(FORWARDING_HANDLER_SIGNATURE),
|
|
m_RequestStatus(FORWARDER_START),
|
|
m_fHandleClosedDueToClient(FALSE),
|
|
m_fResponseHeadersReceivedAndSet(FALSE),
|
|
m_fDoReverseRewriteHeaders(FALSE),
|
|
m_fFinishRequest(FALSE),
|
|
m_fHasError(FALSE),
|
|
m_pszHeaders(NULL),
|
|
m_cchHeaders(0),
|
|
m_BytesToReceive(0),
|
|
m_BytesToSend(0),
|
|
m_fWebSocketEnabled(FALSE),
|
|
m_pWebSocket(NULL)
|
|
{
|
|
DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO,
|
|
"FORWARDING_HANDLER::FORWARDING_HANDLER");
|
|
}
|
|
|
|
FORWARDING_HANDLER::~FORWARDING_HANDLER(
|
|
)
|
|
{
|
|
//
|
|
// Destructor has started.
|
|
//
|
|
m_Signature = FORWARDING_HANDLER_SIGNATURE_FREE;
|
|
|
|
DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO,
|
|
"FORWARDING_HANDLER::~FORWARDING_HANDLER");
|
|
//
|
|
// RemoveRequest() should already have been called and m_pDisconnect
|
|
// has been freed or m_pDisconnect was never initialized.
|
|
//
|
|
// Disconnect notification cleanup would happen first, before
|
|
// the FORWARDING_HANDLER instance got removed from m_pSharedhandler list.
|
|
// The m_pServer cleanup would happen afterwards, since there may be a
|
|
// call pending from SHARED_HANDLER to FORWARDING_HANDLER::SetStatusAndHeaders()
|
|
//
|
|
DBG_ASSERT(m_pDisconnect == NULL);
|
|
|
|
if (m_pDisconnect != NULL)
|
|
{
|
|
m_pDisconnect->ResetHandler();
|
|
m_pDisconnect = NULL;
|
|
}
|
|
|
|
FreeResponseBuffers();
|
|
|
|
if (m_pWebSocket)
|
|
{
|
|
m_pWebSocket->Terminate();
|
|
m_pWebSocket = NULL;
|
|
}
|
|
}
|
|
|
|
__override
|
|
REQUEST_NOTIFICATION_STATUS
|
|
FORWARDING_HANDLER::OnExecuteRequestHandler()
|
|
{
|
|
REQUEST_NOTIFICATION_STATUS retVal = RQ_NOTIFICATION_CONTINUE;
|
|
HRESULT hr = S_OK;
|
|
BOOL fRequestLocked = FALSE;
|
|
BOOL fHandleSet = FALSE;
|
|
BOOL fFailedToStartKestrel = FALSE;
|
|
BOOL fSecure = FALSE;
|
|
HINTERNET hConnect = NULL;
|
|
IHttpRequest *pRequest = m_pW3Context->GetRequest();
|
|
IHttpResponse *pResponse = m_pW3Context->GetResponse();
|
|
IHttpConnection *pClientConnection = NULL;
|
|
OUT_OF_PROCESS_APPLICATION *pApplication = NULL;
|
|
PROTOCOL_CONFIG *pProtocol = &sm_ProtocolConfig;
|
|
SERVER_PROCESS *pServerProcess = NULL;
|
|
|
|
USHORT cchHostName = 0;
|
|
|
|
STACK_STRU(strDestination, 32);
|
|
STACK_STRU(strUrl, 2048);
|
|
STACK_STRU(struEscapedUrl, 2048);
|
|
|
|
// override Protocol related config from aspNetCore config
|
|
pProtocol->OverrideConfig(m_pApplication->QueryConfig());
|
|
|
|
// check connection
|
|
pClientConnection = m_pW3Context->GetConnection();
|
|
if (pClientConnection == NULL ||
|
|
!pClientConnection->IsConnected())
|
|
{
|
|
hr = HRESULT_FROM_WIN32(WSAECONNRESET);
|
|
goto Failure;
|
|
}
|
|
|
|
pApplication = static_cast<OUT_OF_PROCESS_APPLICATION*> (m_pApplication);
|
|
if (pApplication == NULL)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto Failure;
|
|
}
|
|
|
|
hr = pApplication->GetProcess(&pServerProcess);
|
|
if (FAILED(hr))
|
|
{
|
|
fFailedToStartKestrel = TRUE;
|
|
goto Failure;
|
|
}
|
|
|
|
if (pServerProcess == NULL)
|
|
{
|
|
fFailedToStartKestrel = TRUE;
|
|
hr = HRESULT_FROM_WIN32(ERROR_CREATE_FAILED);
|
|
goto Failure;
|
|
}
|
|
|
|
if (pServerProcess->QueryWinHttpConnection() == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE);
|
|
goto Failure;
|
|
}
|
|
|
|
hConnect = pServerProcess->QueryWinHttpConnection()->QueryHandle();
|
|
|
|
m_pszOriginalHostHeader = pRequest->GetHeader(HttpHeaderHost, &cchHostName);
|
|
//
|
|
// parse original url
|
|
//
|
|
if (FAILED(hr = UTILITY::SplitUrl(pRequest->GetRawHttpRequest()->CookedUrl.pFullUrl,
|
|
&fSecure,
|
|
&strDestination,
|
|
&strUrl)))
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
if (FAILED(hr = UTILITY::EscapeAbsPath(pRequest, &struEscapedUrl)))
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
m_fDoReverseRewriteHeaders = pProtocol->QueryReverseRewriteHeaders();
|
|
|
|
m_cMinBufferLimit = pProtocol->QueryMinResponseBuffer();
|
|
|
|
//
|
|
// Mark request as websocket if upgrade header is present.
|
|
//
|
|
if (pApplication->QueryConfig()->QueryWebSocketEnabled())
|
|
{
|
|
USHORT cchHeader = 0;
|
|
PCSTR pszWebSocketHeader = pRequest->GetHeader("Upgrade", &cchHeader);
|
|
if (cchHeader == 9 && _stricmp(pszWebSocketHeader, "websocket") == 0)
|
|
{
|
|
m_fWebSocketEnabled = TRUE;
|
|
}
|
|
}
|
|
|
|
hr = CreateWinHttpRequest(pRequest,
|
|
pProtocol,
|
|
hConnect,
|
|
&struEscapedUrl,
|
|
pServerProcess);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
// Set client disconnect callback contract with IIS
|
|
m_pDisconnect = static_cast<ASYNC_DISCONNECT_CONTEXT *>(
|
|
pClientConnection->GetModuleContextContainer()->
|
|
GetConnectionModuleContext(m_pModuleId));
|
|
if (m_pDisconnect == NULL)
|
|
{
|
|
m_pDisconnect = new ASYNC_DISCONNECT_CONTEXT();
|
|
if (m_pDisconnect == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
|
|
hr = pClientConnection->GetModuleContextContainer()->
|
|
SetConnectionModuleContext(m_pDisconnect,
|
|
m_pModuleId);
|
|
DBG_ASSERT(hr != HRESULT_FROM_WIN32(ERROR_ALREADY_ASSIGNED));
|
|
if (FAILED(hr))
|
|
{
|
|
goto Failure;
|
|
}
|
|
}
|
|
|
|
m_pDisconnect->SetHandler(this);
|
|
fHandleSet = TRUE;
|
|
|
|
// require lock as client disconnect callback may happen
|
|
AcquireSRWLockShared(&m_RequestLock);
|
|
fRequestLocked = TRUE;
|
|
|
|
//
|
|
// Remember the handler being processed in the current thread
|
|
// before staring a WinHTTP operation.
|
|
//
|
|
DBG_ASSERT(fRequestLocked);
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == NULL);
|
|
TlsSetValue(g_dwTlsIndex, this);
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == this);
|
|
|
|
if (m_hRequest == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(WSAECONNRESET);
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Begins normal request handling. Send request to server.
|
|
//
|
|
m_RequestStatus = FORWARDER_SENDING_REQUEST;
|
|
|
|
//
|
|
// Calculate the bytes to receive from the content length.
|
|
//
|
|
DWORD cbContentLength = 0;
|
|
PCSTR pszContentLength = pRequest->GetHeader(HttpHeaderContentLength);
|
|
if (pszContentLength != NULL)
|
|
{
|
|
cbContentLength = m_BytesToReceive = atol(pszContentLength);
|
|
if (m_BytesToReceive == INFINITE)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(WSAECONNRESET);
|
|
goto Failure;
|
|
}
|
|
}
|
|
else if (pRequest->GetHeader(HttpHeaderTransferEncoding) != NULL)
|
|
{
|
|
m_BytesToReceive = INFINITE;
|
|
}
|
|
|
|
if (m_fWebSocketEnabled)
|
|
{
|
|
//
|
|
// Set the upgrade flag for a websocket request.
|
|
//
|
|
if (!WinHttpSetOption(m_hRequest,
|
|
WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET,
|
|
NULL,
|
|
0))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
m_cchLastSend = m_cchHeaders;
|
|
|
|
//FREB log
|
|
if (ANCMEvents::ANCM_REQUEST_FORWARD_START::IsEnabled(m_pW3Context->GetTraceContext()))
|
|
{
|
|
ANCMEvents::ANCM_REQUEST_FORWARD_START::RaiseEvent(
|
|
m_pW3Context->GetTraceContext(),
|
|
NULL);
|
|
}
|
|
|
|
if (!WinHttpSendRequest(m_hRequest,
|
|
m_pszHeaders,
|
|
m_cchHeaders,
|
|
NULL,
|
|
0,
|
|
cbContentLength,
|
|
reinterpret_cast<DWORD_PTR>(static_cast<PVOID>(this))))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO,
|
|
"FORWARDING_HANDLER::OnExecuteRequestHandler, Send request failed");
|
|
|
|
// FREB log
|
|
if (ANCMEvents::ANCM_REQUEST_FORWARD_FAIL::IsEnabled(m_pW3Context->GetTraceContext()))
|
|
{
|
|
ANCMEvents::ANCM_REQUEST_FORWARD_FAIL::RaiseEvent(
|
|
m_pW3Context->GetTraceContext(),
|
|
NULL,
|
|
hr);
|
|
}
|
|
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Async WinHTTP operation is in progress. Release this thread meanwhile,
|
|
// OnWinHttpCompletion method should resume the work by posting an IIS completion.
|
|
//
|
|
retVal = RQ_NOTIFICATION_PENDING;
|
|
goto Finished;
|
|
|
|
Failure:
|
|
m_RequestStatus = FORWARDER_DONE;
|
|
|
|
//disbale client disconnect callback
|
|
if (m_pDisconnect != NULL)
|
|
{
|
|
if (fHandleSet)
|
|
{
|
|
m_pDisconnect->ResetHandler();
|
|
}
|
|
m_pDisconnect->CleanupStoredContext();
|
|
m_pDisconnect = NULL;
|
|
}
|
|
|
|
pResponse->DisableKernelCache();
|
|
pResponse->GetRawHttpResponse()->EntityChunkCount = 0;
|
|
if (hr == HRESULT_FROM_WIN32(WSAECONNRESET))
|
|
{
|
|
pResponse->SetStatus(400, "Bad Request", 0, hr);
|
|
}
|
|
else if (fFailedToStartKestrel && !m_pApplication->QueryConfig()->QueryDisableStartUpErrorPage())
|
|
{
|
|
HTTP_DATA_CHUNK DataChunk;
|
|
pResponse->SetStatus(502, "Bad Gateway", 5, hr, NULL, TRUE);
|
|
pResponse->SetHeader("Content-Type",
|
|
"text/html",
|
|
(USHORT)strlen("text/html"),
|
|
FALSE
|
|
);
|
|
|
|
DataChunk.DataChunkType = HttpDataChunkFromMemory;
|
|
DataChunk.FromMemory.pBuffer = (PVOID)sm_pStra502ErrorMsg.QueryStr();
|
|
DataChunk.FromMemory.BufferLength = sm_pStra502ErrorMsg.QueryCB();
|
|
pResponse->WriteEntityChunkByReference(&DataChunk);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// default error behavior
|
|
//
|
|
pResponse->SetStatus(502, "Bad Gateway", 3, hr);
|
|
}
|
|
//
|
|
// Finish the request on failure.
|
|
//
|
|
retVal = RQ_NOTIFICATION_FINISH_REQUEST;
|
|
|
|
Finished:
|
|
if (fRequestLocked)
|
|
{
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == this);
|
|
TlsSetValue(g_dwTlsIndex, NULL);
|
|
ReleaseSRWLockShared(&m_RequestLock);
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == NULL);
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
__override
|
|
REQUEST_NOTIFICATION_STATUS
|
|
FORWARDING_HANDLER::OnAsyncCompletion(
|
|
DWORD cbCompletion,
|
|
HRESULT hrCompletionStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle the completion from IIS and continue the execution
|
|
of this request based on the current state.
|
|
|
|
Arguments:
|
|
|
|
cbCompletion - Number of bytes associated with this completion
|
|
dwCompletionStatus - the win32 status associated with this completion
|
|
|
|
Return Value:
|
|
|
|
REQUEST_NOTIFICATION_STATUS
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
REQUEST_NOTIFICATION_STATUS retVal = RQ_NOTIFICATION_PENDING;
|
|
BOOL fLocked = FALSE;
|
|
BOOL fClientError = FALSE;
|
|
BOOL fClosed = FALSE;
|
|
|
|
DBG_ASSERT(m_pW3Context != NULL);
|
|
__analysis_assume(m_pW3Context != NULL);
|
|
|
|
//
|
|
// Take a reference so that object does not go away as a result of
|
|
// async completion.
|
|
//
|
|
ReferenceRequestHandler();
|
|
|
|
if (sm_pTraceLog != NULL)
|
|
{
|
|
WriteRefTraceLogEx(sm_pTraceLog,
|
|
m_cRefs,
|
|
this,
|
|
"FORWARDING_HANDLER::OnAsyncCompletion Enter",
|
|
reinterpret_cast<PVOID>(static_cast<DWORD_PTR>(cbCompletion)),
|
|
reinterpret_cast<PVOID>(static_cast<DWORD_PTR>(hrCompletionStatus)));
|
|
}
|
|
|
|
if (TlsGetValue(g_dwTlsIndex) != this)
|
|
{
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == NULL);
|
|
AcquireSRWLockShared(&m_RequestLock);
|
|
TlsSetValue(g_dwTlsIndex, this);
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == this);
|
|
|
|
fLocked = TRUE;
|
|
}
|
|
|
|
if (m_hRequest == NULL)
|
|
{
|
|
// Request is Done
|
|
if (m_fFinishRequest)
|
|
{
|
|
if (m_fHasError)
|
|
{
|
|
retVal = RQ_NOTIFICATION_FINISH_REQUEST;
|
|
}
|
|
else
|
|
{
|
|
retVal = RQ_NOTIFICATION_CONTINUE;
|
|
}
|
|
goto Finished;
|
|
}
|
|
|
|
fClientError = m_fHandleClosedDueToClient;
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// Begins normal completion handling. There is already a shared acquired
|
|
// for protecting the WinHTTP request handle from being closed.
|
|
//
|
|
switch (m_RequestStatus)
|
|
{
|
|
case FORWARDER_RECEIVED_WEBSOCKET_RESPONSE:
|
|
DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO,
|
|
"FORWARDING_HANDLER::OnAsyncCompletion, Send completed for 101 response");
|
|
//
|
|
// This should be the write completion of the 101 response.
|
|
//
|
|
m_pWebSocket = new WEBSOCKET_HANDLER();
|
|
if (m_pWebSocket == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Finished;
|
|
}
|
|
|
|
hr = m_pWebSocket->ProcessRequest(this, m_pW3Context, m_hRequest);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// WebSocket upgrade is successful. Close the WinHttpRequest Handle
|
|
//
|
|
fClosed = WinHttpCloseHandle(m_hRequest);
|
|
|
|
DBG_ASSERT(fClosed);
|
|
if (fClosed)
|
|
{
|
|
m_hRequest = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Failure;
|
|
}
|
|
break;
|
|
|
|
case FORWARDER_RECEIVING_RESPONSE:
|
|
//
|
|
// This is a completion of a write (send) to http.sys, abort in case of
|
|
// failure, if there is more data available from WinHTTP, read it
|
|
// or else ask if there is more.
|
|
//
|
|
if (FAILED(hrCompletionStatus))
|
|
{
|
|
hr = hrCompletionStatus;
|
|
fClientError = TRUE;
|
|
goto Failure;
|
|
}
|
|
|
|
hr = OnReceivingResponse();
|
|
if (FAILED(hr))
|
|
{
|
|
goto Failure;
|
|
}
|
|
break;
|
|
|
|
case FORWARDER_SENDING_REQUEST:
|
|
hr = OnSendingRequest(cbCompletion,
|
|
hrCompletionStatus,
|
|
&fClientError);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Failure;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
DBG_ASSERT(m_RequestStatus == FORWARDER_DONE);
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// Either OnReceivingResponse or OnSendingRequest initiated an
|
|
// async WinHTTP operation, release this thread meanwhile,
|
|
// OnWinHttpCompletion method should resume the work by posting an IIS completion.
|
|
//
|
|
retVal = RQ_NOTIFICATION_PENDING;
|
|
goto Finished;
|
|
|
|
Failure:
|
|
|
|
m_fHasError = TRUE;
|
|
m_RequestStatus = FORWARDER_DONE;
|
|
|
|
//disbale client disconnect callback
|
|
if (m_pDisconnect != NULL)
|
|
{
|
|
m_pDisconnect->ResetHandler();
|
|
m_pDisconnect = NULL;
|
|
}
|
|
|
|
//
|
|
// Do the right thing based on where the error originated from.
|
|
//
|
|
IHttpResponse *pResponse = m_pW3Context->GetResponse();
|
|
pResponse->DisableKernelCache();
|
|
pResponse->GetRawHttpResponse()->EntityChunkCount = 0;
|
|
|
|
if (fClientError)
|
|
{
|
|
if (!m_fResponseHeadersReceivedAndSet)
|
|
{
|
|
pResponse->SetStatus(400, "Bad Request", 0, HRESULT_FROM_WIN32(WSAECONNRESET));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Response headers from origin server were
|
|
// already received and set for the current response.
|
|
// Honor the response status.
|
|
//
|
|
}
|
|
}
|
|
else
|
|
{
|
|
STACK_STRU(strDescription, 128);
|
|
|
|
pResponse->SetStatus(502, "Bad Gateway", 3, hr);
|
|
|
|
if (!(hr > HRESULT_FROM_WIN32(WINHTTP_ERROR_BASE) &&
|
|
hr <= HRESULT_FROM_WIN32(WINHTTP_ERROR_LAST)) ||
|
|
#pragma prefast (suppress : __WARNING_FUNCTION_NEEDS_REVIEW, "Function and parameters reviewed.")
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
|
|
g_hWinHttpModule,
|
|
HRESULT_CODE(hr),
|
|
0,
|
|
strDescription.QueryStr(),
|
|
strDescription.QuerySizeCCH(),
|
|
NULL) == 0)
|
|
{
|
|
LoadString(g_hAspNetCoreModule,
|
|
IDS_SERVER_ERROR,
|
|
strDescription.QueryStr(),
|
|
strDescription.QuerySizeCCH());
|
|
}
|
|
|
|
(VOID)strDescription.SyncWithBuffer();
|
|
if (strDescription.QueryCCH() != 0)
|
|
{
|
|
pResponse->SetErrorDescription(
|
|
strDescription.QueryStr(),
|
|
strDescription.QueryCCH(),
|
|
FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finish the request on failure.
|
|
// Let IIS pipeline continue only after receiving handle close callback
|
|
// from WinHttp. This ensures no more callback from WinHttp
|
|
//
|
|
if (m_hRequest != NULL)
|
|
{
|
|
if (WinHttpCloseHandle(m_hRequest))
|
|
{
|
|
m_hRequest = NULL;
|
|
}
|
|
else
|
|
{
|
|
// Failed to close the handle
|
|
// which should never happen as we registered a callback with WinHttp
|
|
// For this unexpected failure, let conitnue IIS pipeline
|
|
/* retVal = RQ_NOTIFICATION_FINISH_REQUEST;
|
|
DebugBreak();*/
|
|
}
|
|
}
|
|
retVal = RQ_NOTIFICATION_PENDING;
|
|
|
|
Finished:
|
|
if (fLocked)
|
|
{
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == this);
|
|
TlsSetValue(g_dwTlsIndex, NULL);
|
|
ReleaseSRWLockShared(&m_RequestLock);
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == NULL);
|
|
}
|
|
|
|
DereferenceRequestHandler();
|
|
//
|
|
// No code after this point, as the handle might be gone
|
|
//
|
|
return retVal;
|
|
}
|
|
|
|
// static
|
|
HRESULT
|
|
FORWARDING_HANDLER::StaticInitialize(
|
|
BOOL fEnableReferenceCountTracing
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Global initialization routine for FORWARDING_HANDLERs
|
|
|
|
Arguments:
|
|
|
|
fEnableReferenceCountTracing - True if ref count tracing should be use.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
sm_pAlloc = new ALLOC_CACHE_HANDLER;
|
|
if (sm_pAlloc == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Finished;
|
|
}
|
|
|
|
hr = sm_pAlloc->Initialize(sizeof(FORWARDING_HANDLER),
|
|
64); // nThreshold
|
|
if (FAILED(hr))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
sm_pResponseHeaderHash = new RESPONSE_HEADER_HASH;
|
|
if (sm_pResponseHeaderHash == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Finished;
|
|
}
|
|
|
|
hr = sm_pResponseHeaderHash->Initialize();
|
|
if (FAILED(hr))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
// Initialize PROTOCOL_CONFIG
|
|
hr = sm_ProtocolConfig.Initialize();
|
|
if (FAILED(hr))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
if (fEnableReferenceCountTracing)
|
|
{
|
|
sm_pTraceLog = CreateRefTraceLog(10000, 0);
|
|
}
|
|
|
|
sm_pStra502ErrorMsg.Copy(
|
|
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"> \
|
|
<html xmlns=\"http://www.w3.org/1999/xhtml\"> \
|
|
<head> \
|
|
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" /> \
|
|
<title> IIS 502.5 Error </title><style type=\"text/css\"></style></head> \
|
|
<body> <div id = \"content\"> \
|
|
<div class = \"content-container\"><h3> HTTP Error 502.5 - Process Failure </h3></div> \
|
|
<div class = \"content-container\"> \
|
|
<fieldset> <h4> Common causes of this issue: </h4> \
|
|
<ul><li> The application process failed to start </li> \
|
|
<li> The application process started but then stopped </li> \
|
|
<li> The application process started but failed to listen on the configured port </li></ul></fieldset> \
|
|
</div> \
|
|
<div class = \"content-container\"> \
|
|
<fieldset><h4> Troubleshooting steps: </h4> \
|
|
<ul><li> Check the system event log for error messages </li> \
|
|
<li> Enable logging the application process' stdout messages </li> \
|
|
<li> Attach a debugger to the application process and inspect </li></ul></fieldset> \
|
|
<fieldset><h4> For more information visit: \
|
|
<a href=\"https://go.microsoft.com/fwlink/?linkid=808681\"> <cite> https://go.microsoft.com/fwlink/?LinkID=808681 </cite></a></h4> \
|
|
</fieldset> \
|
|
</div> \
|
|
</div></body></html>");
|
|
|
|
Finished:
|
|
if (FAILED(hr))
|
|
{
|
|
StaticTerminate();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//static
|
|
VOID
|
|
FORWARDING_HANDLER::StaticTerminate()
|
|
{
|
|
sm_pStra502ErrorMsg.Reset();
|
|
|
|
if (sm_pResponseHeaderHash != NULL)
|
|
{
|
|
sm_pResponseHeaderHash->Clear();
|
|
delete sm_pResponseHeaderHash;
|
|
sm_pResponseHeaderHash = NULL;
|
|
}
|
|
|
|
if (sm_pTraceLog != NULL)
|
|
{
|
|
DestroyRefTraceLog(sm_pTraceLog);
|
|
sm_pTraceLog = NULL;
|
|
}
|
|
|
|
if (sm_pAlloc != NULL)
|
|
{
|
|
delete sm_pAlloc;
|
|
sm_pAlloc = NULL;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
FORWARDING_HANDLER::GetHeaders(
|
|
_In_ const PROTOCOL_CONFIG * pProtocol,
|
|
_In_ BOOL fForwardWindowsAuthToken,
|
|
_In_ SERVER_PROCESS* pServerProcess,
|
|
_Out_ PCWSTR * ppszHeaders,
|
|
_Inout_ DWORD * pcchHeaders
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PCSTR pszCurrentHeader;
|
|
PCSTR ppHeadersToBeRemoved;
|
|
PCSTR pszFinalHeader;
|
|
USHORT cchCurrentHeader;
|
|
DWORD cchFinalHeader;
|
|
BOOL fSecure = FALSE; // dummy. Used in SplitUrl. Value will not be used
|
|
// as ANCM always use http protocol to communicate with backend
|
|
STRU struDestination;
|
|
STRU struUrl;
|
|
STACK_STRA(strTemp, 64);
|
|
HTTP_REQUEST_HEADERS *pHeaders;
|
|
IHttpRequest *pRequest = m_pW3Context->GetRequest();
|
|
MULTISZA mszMsAspNetCoreHeaders;
|
|
|
|
//
|
|
// We historically set the host section in request url to the new host header
|
|
// this is wrong but Kestrel has dependency on it.
|
|
// should change it in the future
|
|
//
|
|
if (!pProtocol->QueryPreserveHostHeader())
|
|
{
|
|
if (FAILED(hr = UTILITY::SplitUrl(pRequest->GetRawHttpRequest()->CookedUrl.pFullUrl,
|
|
&fSecure,
|
|
&struDestination,
|
|
&struUrl)) ||
|
|
FAILED(hr = strTemp.CopyW(struDestination.QueryStr())) ||
|
|
FAILED(hr = pRequest->SetHeader(HttpHeaderHost,
|
|
strTemp.QueryStr(),
|
|
static_cast<USHORT>(strTemp.QueryCCH()),
|
|
TRUE))) // fReplace
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
//
|
|
// Strip all headers starting with MS-ASPNETCORE.
|
|
// These headers are generated by the asp.net core module and
|
|
// passed to the process it creates.
|
|
//
|
|
|
|
pHeaders = &m_pW3Context->GetRequest()->GetRawHttpRequest()->Headers;
|
|
for (DWORD i = 0; i<pHeaders->UnknownHeaderCount; i++)
|
|
{
|
|
if (_strnicmp(pHeaders->pUnknownHeaders[i].pName, "MS-ASPNETCORE", 13) == 0)
|
|
{
|
|
mszMsAspNetCoreHeaders.Append(pHeaders->pUnknownHeaders[i].pName, (DWORD)pHeaders->pUnknownHeaders[i].NameLength);
|
|
}
|
|
}
|
|
|
|
ppHeadersToBeRemoved = mszMsAspNetCoreHeaders.First();
|
|
|
|
//
|
|
// iterate the list of headers to be removed and delete them from the request.
|
|
//
|
|
|
|
while (ppHeadersToBeRemoved != NULL)
|
|
{
|
|
m_pW3Context->GetRequest()->DeleteHeader(ppHeadersToBeRemoved);
|
|
ppHeadersToBeRemoved = mszMsAspNetCoreHeaders.Next(ppHeadersToBeRemoved);
|
|
}
|
|
|
|
if (pServerProcess->QueryGuid() != NULL)
|
|
{
|
|
hr = m_pW3Context->GetRequest()->SetHeader("MS-ASPNETCORE-TOKEN",
|
|
pServerProcess->QueryGuid(),
|
|
(USHORT)strlen(pServerProcess->QueryGuid()),
|
|
TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (fForwardWindowsAuthToken &&
|
|
(_wcsicmp(m_pW3Context->GetUser()->GetAuthenticationType(), L"negotiate") == 0 ||
|
|
_wcsicmp(m_pW3Context->GetUser()->GetAuthenticationType(), L"ntlm") == 0))
|
|
{
|
|
if (m_pW3Context->GetUser()->GetPrimaryToken() != NULL &&
|
|
m_pW3Context->GetUser()->GetPrimaryToken() != INVALID_HANDLE_VALUE)
|
|
{
|
|
HANDLE hTargetTokenHandle = NULL;
|
|
hr = pServerProcess->SetWindowsAuthToken(m_pW3Context->GetUser()->GetPrimaryToken(),
|
|
&hTargetTokenHandle);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// set request header with target token value
|
|
//
|
|
CHAR pszHandleStr[16] = { 0 };
|
|
if (_ui64toa_s((UINT64)hTargetTokenHandle, pszHandleStr, 16, 16) != 0)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
return hr;
|
|
}
|
|
|
|
hr = m_pW3Context->GetRequest()->SetHeader("MS-ASPNETCORE-WINAUTHTOKEN",
|
|
pszHandleStr,
|
|
(USHORT)strlen(pszHandleStr),
|
|
TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pProtocol->QueryXForwardedForName()->IsEmpty())
|
|
{
|
|
strTemp.Reset();
|
|
|
|
pszCurrentHeader = pRequest->GetHeader(pProtocol->QueryXForwardedForName()->QueryStr(), &cchCurrentHeader);
|
|
if (pszCurrentHeader != NULL)
|
|
{
|
|
if (FAILED(hr = strTemp.Copy(pszCurrentHeader, cchCurrentHeader)) ||
|
|
FAILED(hr = strTemp.Append(", ", 2)))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr = m_pW3Context->GetServerVariable("REMOTE_ADDR",
|
|
&pszFinalHeader,
|
|
&cchFinalHeader)))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if (pRequest->GetRawHttpRequest()->Address.pRemoteAddress->sa_family == AF_INET6)
|
|
{
|
|
if (FAILED(hr = strTemp.Append("[", 1)) ||
|
|
FAILED(hr = strTemp.Append(pszFinalHeader, cchFinalHeader)) ||
|
|
FAILED(hr = strTemp.Append("]", 1)))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FAILED(hr = strTemp.Append(pszFinalHeader, cchFinalHeader)))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (pProtocol->QueryIncludePortInXForwardedFor())
|
|
{
|
|
if (FAILED(hr = m_pW3Context->GetServerVariable("REMOTE_PORT",
|
|
&pszFinalHeader,
|
|
&cchFinalHeader)))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if (FAILED(hr = strTemp.Append(":", 1)) ||
|
|
FAILED(hr = strTemp.Append(pszFinalHeader, cchFinalHeader)))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr = pRequest->SetHeader(pProtocol->QueryXForwardedForName()->QueryStr(),
|
|
strTemp.QueryStr(),
|
|
static_cast<USHORT>(strTemp.QueryCCH()),
|
|
TRUE))) // fReplace
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (!pProtocol->QuerySslHeaderName()->IsEmpty())
|
|
{
|
|
const HTTP_SSL_INFO *pSslInfo = pRequest->GetRawHttpRequest()->pSslInfo;
|
|
LPSTR pszScheme = "http";
|
|
if (pSslInfo != NULL)
|
|
{
|
|
pszScheme = "https";
|
|
}
|
|
|
|
strTemp.Reset();
|
|
|
|
pszCurrentHeader = pRequest->GetHeader(pProtocol->QuerySslHeaderName()->QueryStr(), &cchCurrentHeader);
|
|
if (pszCurrentHeader != NULL)
|
|
{
|
|
if (FAILED(hr = strTemp.Copy(pszCurrentHeader, cchCurrentHeader)) ||
|
|
FAILED(hr = strTemp.Append(", ", 2)))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr = strTemp.Append(pszScheme)))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if (FAILED(pRequest->SetHeader(pProtocol->QuerySslHeaderName()->QueryStr(),
|
|
strTemp.QueryStr(),
|
|
(USHORT)strTemp.QueryCCH(),
|
|
TRUE)))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (!pProtocol->QueryClientCertName()->IsEmpty())
|
|
{
|
|
if (pRequest->GetRawHttpRequest()->pSslInfo == NULL ||
|
|
pRequest->GetRawHttpRequest()->pSslInfo->pClientCertInfo == NULL)
|
|
{
|
|
pRequest->DeleteHeader(pProtocol->QueryClientCertName()->QueryStr());
|
|
}
|
|
else
|
|
{
|
|
// Resize the buffer large enough to hold the encoded certificate info
|
|
if (FAILED(hr = strTemp.Resize(
|
|
1 + (pRequest->GetRawHttpRequest()->pSslInfo->pClientCertInfo->CertEncodedSize + 2) / 3 * 4)))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
Base64Encode(
|
|
pRequest->GetRawHttpRequest()->pSslInfo->pClientCertInfo->pCertEncoded,
|
|
pRequest->GetRawHttpRequest()->pSslInfo->pClientCertInfo->CertEncodedSize,
|
|
strTemp.QueryStr(),
|
|
strTemp.QuerySize(),
|
|
NULL);
|
|
strTemp.SyncWithBuffer();
|
|
|
|
if (FAILED(hr = pRequest->SetHeader(
|
|
pProtocol->QueryClientCertName()->QueryStr(),
|
|
strTemp.QueryStr(),
|
|
static_cast<USHORT>(strTemp.QueryCCH()),
|
|
TRUE))) // fReplace
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remove the connection header
|
|
//
|
|
if (!m_fWebSocketEnabled)
|
|
{
|
|
pRequest->DeleteHeader(HttpHeaderConnection);
|
|
}
|
|
|
|
//
|
|
// Get all the headers to send to the client
|
|
//
|
|
hr = m_pW3Context->GetServerVariable("ALL_RAW",
|
|
ppszHeaders,
|
|
pcchHeaders);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
FORWARDING_HANDLER::CreateWinHttpRequest(
|
|
_In_ const IHttpRequest * pRequest,
|
|
_In_ const PROTOCOL_CONFIG * pProtocol,
|
|
_In_ HINTERNET hConnect,
|
|
_Inout_ STRU * pstrUrl,
|
|
// _In_ ASPNETCORE_CONFIG* pAspNetCoreConfig,
|
|
_In_ SERVER_PROCESS* pServerProcess
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PCWSTR pszVersion = NULL;
|
|
PCSTR pszVerb;
|
|
STACK_STRU(strVerb, 32);
|
|
|
|
//
|
|
// Create the request handle for this request (leave some fields blank,
|
|
// we will fill them when sending the request)
|
|
//
|
|
pszVerb = pRequest->GetHttpMethod();
|
|
if (FAILED(hr = strVerb.CopyA(pszVerb)))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
//pszVersion = pProtocol->QueryVersion();
|
|
if (pszVersion == NULL)
|
|
{
|
|
DWORD cchUnused;
|
|
hr = m_pW3Context->GetServerVariable(
|
|
"HTTP_VERSION",
|
|
&pszVersion,
|
|
&cchUnused);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
m_hRequest = WinHttpOpenRequest(hConnect,
|
|
strVerb.QueryStr(),
|
|
pstrUrl->QueryStr(),
|
|
pszVersion,
|
|
WINHTTP_NO_REFERER,
|
|
WINHTTP_DEFAULT_ACCEPT_TYPES,
|
|
WINHTTP_FLAG_ESCAPE_DISABLE_QUERY
|
|
| g_OptionalWinHttpFlags);
|
|
if (m_hRequest == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Finished;
|
|
}
|
|
|
|
if (!WinHttpSetTimeouts(m_hRequest,
|
|
pProtocol->QueryTimeout(),
|
|
pProtocol->QueryTimeout(),
|
|
pProtocol->QueryTimeout(),
|
|
pProtocol->QueryTimeout()))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Finished;
|
|
}
|
|
|
|
DWORD dwResponseBufferLimit = pProtocol->QueryResponseBufferLimit();
|
|
if (!WinHttpSetOption(m_hRequest,
|
|
WINHTTP_OPTION_MAX_RESPONSE_DRAIN_SIZE,
|
|
&dwResponseBufferLimit,
|
|
sizeof(dwResponseBufferLimit)))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Finished;
|
|
}
|
|
|
|
DWORD dwMaxHeaderSize = pProtocol->QueryMaxResponseHeaderSize();
|
|
if (!WinHttpSetOption(m_hRequest,
|
|
WINHTTP_OPTION_MAX_RESPONSE_HEADER_SIZE,
|
|
&dwMaxHeaderSize,
|
|
sizeof(dwMaxHeaderSize)))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Finished;
|
|
}
|
|
|
|
DWORD dwOption = WINHTTP_DISABLE_COOKIES;
|
|
|
|
dwOption |= WINHTTP_DISABLE_AUTHENTICATION;
|
|
|
|
if (!pProtocol->QueryDoKeepAlive())
|
|
{
|
|
dwOption |= WINHTTP_DISABLE_KEEP_ALIVE;
|
|
}
|
|
if (!WinHttpSetOption(m_hRequest,
|
|
WINHTTP_OPTION_DISABLE_FEATURE,
|
|
&dwOption,
|
|
sizeof(dwOption)))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Finished;
|
|
}
|
|
|
|
if (WinHttpSetStatusCallback(m_hRequest,
|
|
FORWARDING_HANDLER::OnWinHttpCompletion,
|
|
(WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS |
|
|
WINHTTP_CALLBACK_FLAG_HANDLES |
|
|
WINHTTP_CALLBACK_STATUS_SENDING_REQUEST),
|
|
NULL) == WINHTTP_INVALID_STATUS_CALLBACK)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Finished;
|
|
}
|
|
|
|
hr = GetHeaders(pProtocol,
|
|
m_pApplication->QueryConfig()->QueryForwardWindowsAuthToken(),
|
|
pServerProcess,
|
|
&m_pszHeaders,
|
|
&m_cchHeaders);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
Finished:
|
|
|
|
return hr;
|
|
}
|
|
|
|
VOID
|
|
FORWARDING_HANDLER::OnWinHttpCompletion(
|
|
HINTERNET hRequest,
|
|
DWORD_PTR dwContext,
|
|
DWORD dwInternetStatus,
|
|
LPVOID lpvStatusInformation,
|
|
DWORD dwStatusInformationLength
|
|
)
|
|
{
|
|
FORWARDING_HANDLER * pThis = static_cast<FORWARDING_HANDLER *>(reinterpret_cast<PVOID>(dwContext));
|
|
if (pThis == NULL)
|
|
{
|
|
//error happened, nothing can be done here
|
|
return;
|
|
}
|
|
DBG_ASSERT(pThis->m_Signature == FORWARDING_HANDLER_SIGNATURE);
|
|
pThis->OnWinHttpCompletionInternal(hRequest,
|
|
dwInternetStatus,
|
|
lpvStatusInformation,
|
|
dwStatusInformationLength);
|
|
}
|
|
|
|
VOID
|
|
FORWARDING_HANDLER::OnWinHttpCompletionInternal(
|
|
_In_ HINTERNET hRequest,
|
|
_In_ DWORD dwInternetStatus,
|
|
_In_ LPVOID lpvStatusInformation,
|
|
_In_ DWORD dwStatusInformationLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completion call associated with a WinHTTP operation
|
|
|
|
Arguments:
|
|
|
|
hRequest - The winhttp request handle associated with this completion
|
|
dwInternetStatus - enum specifying what the completion is for
|
|
lpvStatusInformation - completion specific information
|
|
dwStatusInformationLength - length of the above information
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fLockAcquired = FALSE;
|
|
BOOL fClientError = FALSE;
|
|
BOOL fAnotherCompletionExpected = FALSE;
|
|
BOOL fDoPostCompletion = FALSE;
|
|
BOOL fEndRequest = (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING);
|
|
|
|
DBG_ASSERT(m_pW3Context != NULL);
|
|
__analysis_assume(m_pW3Context != NULL);
|
|
IHttpResponse * pResponse = m_pW3Context->GetResponse();
|
|
|
|
// Reference the request handler to prevent it from being released prematurely
|
|
ReferenceRequestHandler();
|
|
|
|
UNREFERENCED_PARAMETER(dwStatusInformationLength);
|
|
|
|
if (sm_pTraceLog != NULL)
|
|
{
|
|
WriteRefTraceLogEx(sm_pTraceLog,
|
|
m_cRefs,
|
|
this,
|
|
"FORWARDING_HANDLER::OnWinHttpCompletionInternal Enter",
|
|
reinterpret_cast<PVOID>(static_cast<DWORD_PTR>(dwInternetStatus)),
|
|
NULL);
|
|
}
|
|
|
|
//FREB log
|
|
if (ANCMEvents::ANCM_WINHTTP_CALLBACK::IsEnabled(m_pW3Context->GetTraceContext()))
|
|
{
|
|
ANCMEvents::ANCM_WINHTTP_CALLBACK::RaiseEvent(
|
|
m_pW3Context->GetTraceContext(),
|
|
NULL,
|
|
dwInternetStatus);
|
|
}
|
|
|
|
//
|
|
// ReadLock on the winhttp handle to protect from a client disconnect/
|
|
// server stop closing the handle while we are using it.
|
|
//
|
|
// WinHttp can call async completion on the same thread/stack, so
|
|
// we have to account for that and not try to take the lock again,
|
|
// otherwise, we could end up in a deadlock.
|
|
//
|
|
|
|
if (TlsGetValue(g_dwTlsIndex) != this)
|
|
{
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == NULL);
|
|
|
|
AcquireSRWLockShared(&m_RequestLock);
|
|
TlsSetValue(g_dwTlsIndex, this);
|
|
fLockAcquired = TRUE;
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == this);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO,
|
|
"FORWARDING_HANDLER::OnWinHttpCompletionInternal %x -- %d --%p\n", dwInternetStatus, GetCurrentThreadId(), m_pW3Context);
|
|
#endif // DEBUG
|
|
|
|
if (!fEndRequest)
|
|
{
|
|
if (!m_pW3Context->GetConnection()->IsConnected())
|
|
{
|
|
hr = ERROR_CONNECTION_ABORTED;
|
|
fClientError = m_fHandleClosedDueToClient = TRUE;
|
|
fAnotherCompletionExpected = TRUE;
|
|
goto Failure;
|
|
}
|
|
}
|
|
|
|
//
|
|
// In case of websocket, http request handle (m_hRequest) will be closed immediately after upgrading success
|
|
// This close will trigger a callback with WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING
|
|
// As m_RequestStatus is FORWARDER_RECEIVED_WEBSOCKET_RESPONSE, this callback will be skipped.
|
|
// When WebSocket handle (m_pWebsocket) gets closed, another winhttp handle close callback will be triggered
|
|
// This callback will be captured and then notify IIS pipeline to continue
|
|
// This ensures no request leaks
|
|
//
|
|
if (m_RequestStatus == FORWARDER_RECEIVED_WEBSOCKET_RESPONSE)
|
|
{
|
|
fAnotherCompletionExpected = TRUE;
|
|
if (m_pWebSocket == NULL)
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
switch (dwInternetStatus)
|
|
{
|
|
case WINHTTP_CALLBACK_STATUS_SHUTDOWN_COMPLETE:
|
|
m_pWebSocket->OnWinHttpShutdownComplete();
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
|
|
m_pWebSocket->OnWinHttpSendComplete(
|
|
(WINHTTP_WEB_SOCKET_STATUS*)lpvStatusInformation
|
|
);
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
|
|
m_pWebSocket->OnWinHttpReceiveComplete(
|
|
(WINHTTP_WEB_SOCKET_STATUS*)lpvStatusInformation
|
|
);
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
|
|
m_pWebSocket->OnWinHttpIoError(
|
|
(WINHTTP_WEB_SOCKET_ASYNC_RESULT*)lpvStatusInformation
|
|
);
|
|
break;
|
|
}
|
|
goto Finished;
|
|
}
|
|
|
|
switch (dwInternetStatus)
|
|
{
|
|
case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
|
|
case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
|
|
hr = OnWinHttpCompletionSendRequestOrWriteComplete(hRequest,
|
|
dwInternetStatus,
|
|
&fClientError,
|
|
&fAnotherCompletionExpected);
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
|
|
hr = OnWinHttpCompletionStatusHeadersAvailable(hRequest,
|
|
&fAnotherCompletionExpected);
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
|
|
hr = OnWinHttpCompletionStatusDataAvailable(hRequest,
|
|
*reinterpret_cast<const DWORD *>(lpvStatusInformation), // dwBytes
|
|
&fAnotherCompletionExpected);
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
|
|
hr = OnWinHttpCompletionStatusReadComplete(pResponse,
|
|
dwStatusInformationLength,
|
|
&fAnotherCompletionExpected);
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
|
|
hr = HRESULT_FROM_WIN32(static_cast<const WINHTTP_ASYNC_RESULT *>(lpvStatusInformation)->dwError);
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
|
|
//
|
|
// This is a notification, not a completion. This notifiation happens
|
|
// during the Send Request operation.
|
|
//
|
|
fAnotherCompletionExpected = TRUE;
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:
|
|
//
|
|
// Need to ignore this event. We get it as a side-effect of registering
|
|
// for WINHTTP_CALLBACK_STATUS_SENDING_REQUEST (which we actually need).
|
|
//
|
|
hr = S_OK;
|
|
fAnotherCompletionExpected = TRUE;
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
|
|
if (ANCMEvents::ANCM_REQUEST_FORWARD_END::IsEnabled(m_pW3Context->GetTraceContext()))
|
|
{
|
|
ANCMEvents::ANCM_REQUEST_FORWARD_END::RaiseEvent(
|
|
m_pW3Context->GetTraceContext(),
|
|
NULL);
|
|
}
|
|
if (m_RequestStatus != FORWARDER_DONE || m_fHandleClosedDueToClient)
|
|
{
|
|
hr = ERROR_CONNECTION_ABORTED;
|
|
fClientError = m_fHandleClosedDueToClient;
|
|
}
|
|
m_hRequest = NULL;
|
|
fAnotherCompletionExpected = FALSE;
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED:
|
|
hr = ERROR_CONNECTION_ABORTED;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// E_UNEXPECTED is rarely used, if seen means that this condition may been occurred.
|
|
//
|
|
DBG_ASSERT(FALSE);
|
|
hr = E_UNEXPECTED;
|
|
if (sm_pTraceLog != NULL)
|
|
{
|
|
WriteRefTraceLogEx(sm_pTraceLog,
|
|
m_cRefs,
|
|
this,
|
|
"FORWARDING_HANDLER::OnWinHttpCompletionInternal Unexpected WinHTTP Status",
|
|
reinterpret_cast<PVOID>(static_cast<DWORD_PTR>(dwInternetStatus)),
|
|
NULL);
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Handle failure code for switch statement above.
|
|
//
|
|
if (FAILED(hr))
|
|
{
|
|
goto Failure;
|
|
}
|
|
|
|
//
|
|
// WinHTTP completion handled successfully.
|
|
//
|
|
goto Finished;
|
|
|
|
Failure:
|
|
|
|
if (!m_fHasError)
|
|
{
|
|
m_RequestStatus = FORWARDER_DONE;
|
|
m_fHasError = TRUE;
|
|
|
|
pResponse->DisableKernelCache();
|
|
pResponse->GetRawHttpResponse()->EntityChunkCount = 0;
|
|
|
|
if (hr == HRESULT_FROM_WIN32(ERROR_WINHTTP_INVALID_SERVER_RESPONSE))
|
|
{
|
|
m_fResetConnection = TRUE;
|
|
}
|
|
|
|
if (fClientError || m_fHandleClosedDueToClient)
|
|
{
|
|
if (!m_fResponseHeadersReceivedAndSet)
|
|
{
|
|
pResponse->SetStatus(400, "Bad Request", 0, HRESULT_FROM_WIN32(WSAECONNRESET));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Response headers from origin server were
|
|
// already received and set for the current response.
|
|
// Honor the response status.
|
|
//
|
|
}
|
|
}
|
|
else
|
|
{
|
|
STACK_STRU(strDescription, 128);
|
|
|
|
pResponse->SetStatus(502, "Bad Gateway", 3, hr);
|
|
|
|
if (!(hr > HRESULT_FROM_WIN32(WINHTTP_ERROR_BASE) &&
|
|
hr <= HRESULT_FROM_WIN32(WINHTTP_ERROR_LAST)) ||
|
|
#pragma prefast (suppress : __WARNING_FUNCTION_NEEDS_REVIEW, "Function and parameters reviewed.")
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
|
|
g_hWinHttpModule,
|
|
HRESULT_CODE(hr),
|
|
0,
|
|
strDescription.QueryStr(),
|
|
strDescription.QuerySizeCCH(),
|
|
NULL) == 0)
|
|
{
|
|
LoadString(g_hAspNetCoreModule,
|
|
IDS_SERVER_ERROR,
|
|
strDescription.QueryStr(),
|
|
strDescription.QuerySizeCCH());
|
|
}
|
|
|
|
strDescription.SyncWithBuffer();
|
|
if (strDescription.QueryCCH() != 0)
|
|
{
|
|
pResponse->SetErrorDescription(
|
|
strDescription.QueryStr(),
|
|
strDescription.QueryCCH(),
|
|
FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// FREB log
|
|
if (ANCMEvents::ANCM_REQUEST_FORWARD_FAIL::IsEnabled(m_pW3Context->GetTraceContext()))
|
|
{
|
|
ANCMEvents::ANCM_REQUEST_FORWARD_FAIL::RaiseEvent(
|
|
m_pW3Context->GetTraceContext(),
|
|
NULL,
|
|
hr);
|
|
}
|
|
|
|
Finished:
|
|
|
|
if (fLockAcquired)
|
|
{
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == this);
|
|
TlsSetValue(g_dwTlsIndex, NULL);
|
|
ReleaseSRWLockShared(&m_RequestLock);
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == NULL);
|
|
}
|
|
|
|
if (m_RequestStatus == FORWARDER_DONE)
|
|
{
|
|
//disbale client disconnect callback
|
|
if (m_pDisconnect != NULL)
|
|
{
|
|
m_pDisconnect->ResetHandler();
|
|
m_pDisconnect = NULL;
|
|
}
|
|
|
|
if (m_hRequest != NULL)
|
|
{
|
|
WinHttpSetStatusCallback(m_hRequest,
|
|
FORWARDING_HANDLER::OnWinHttpCompletion,
|
|
WINHTTP_CALLBACK_FLAG_HANDLES,
|
|
NULL);
|
|
if (WinHttpCloseHandle(m_hRequest))
|
|
{
|
|
m_hRequest = NULL;
|
|
}
|
|
else
|
|
{
|
|
// unexpected WinHttp error, log it
|
|
/*DebugBreak();
|
|
m_RequestStatus = FORWARDER_FINISH_REQUEST;
|
|
fDoPostCompletion = TRUE;*/
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the request is a websocket request, initiate cleanup.
|
|
//
|
|
if (m_pWebSocket != NULL)
|
|
{
|
|
m_pWebSocket->TerminateRequest();
|
|
}
|
|
|
|
if (fEndRequest)
|
|
{
|
|
// only postCompletion after WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING
|
|
// so that no further WinHttp callback will be called
|
|
// in case of websocket, m_hRequest has already been closed after upgrade
|
|
// websocket will handle completion
|
|
m_fFinishRequest = TRUE;
|
|
fDoPostCompletion = TRUE;
|
|
}
|
|
}
|
|
//
|
|
// Completion may had been already posted to IIS if an async
|
|
// operation was started in this method (either WinHTTP or IIS e.g. ReadyEntityBody)
|
|
// If fAnotherCompletionExpected is false, this method must post the completion.
|
|
//
|
|
else if (!fAnotherCompletionExpected)
|
|
{
|
|
//
|
|
// Since we use TLS to guard WinHttp operation, call PostCompletion instead of
|
|
// IndicateCompletion to allow cleaning up the TLS before thread reuse.
|
|
//
|
|
fDoPostCompletion = TRUE;
|
|
}
|
|
|
|
DereferenceRequestHandler();
|
|
if (fDoPostCompletion)
|
|
{
|
|
m_pW3Context->PostCompletion(0);
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
FORWARDING_HANDLER::OnWinHttpCompletionSendRequestOrWriteComplete(
|
|
HINTERNET hRequest,
|
|
DWORD,
|
|
__out BOOL * pfClientError,
|
|
__out BOOL * pfAnotherCompletionExpected
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IHttpRequest * pRequest = m_pW3Context->GetRequest();
|
|
|
|
//
|
|
// completion for sending the initial request or request entity to
|
|
// winhttp, get more request entity if available, else start receiving
|
|
// the response
|
|
//
|
|
if (m_BytesToReceive > 0)
|
|
{
|
|
if (m_pEntityBuffer == NULL)
|
|
{
|
|
m_pEntityBuffer = GetNewResponseBuffer(
|
|
ENTITY_BUFFER_SIZE);
|
|
if (m_pEntityBuffer == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
if (sm_pTraceLog != NULL)
|
|
{
|
|
WriteRefTraceLogEx(sm_pTraceLog,
|
|
m_cRefs,
|
|
this,
|
|
"Calling ReadEntityBody",
|
|
NULL,
|
|
NULL);
|
|
}
|
|
hr = pRequest->ReadEntityBody(
|
|
m_pEntityBuffer + 6,
|
|
min(m_BytesToReceive, BUFFER_SIZE),
|
|
TRUE, // fAsync
|
|
NULL, // pcbBytesReceived
|
|
NULL); // pfCompletionPending
|
|
if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF))
|
|
{
|
|
DBG_ASSERT(m_BytesToReceive == 0 ||
|
|
m_BytesToReceive == INFINITE);
|
|
|
|
//
|
|
// ERROR_HANDLE_EOF is not an error.
|
|
//
|
|
hr = S_OK;
|
|
|
|
if (m_BytesToReceive == INFINITE)
|
|
{
|
|
m_BytesToReceive = 0;
|
|
m_cchLastSend = 5;
|
|
|
|
//
|
|
// WinHttpWriteData can operate asynchronously.
|
|
//
|
|
// Take reference so that object does not go away as a result of
|
|
// async completion.
|
|
//
|
|
//ReferenceForwardingHandler();
|
|
if (!WinHttpWriteData(m_hRequest,
|
|
"0\r\n\r\n",
|
|
5,
|
|
NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
//DereferenceForwardingHandler();
|
|
goto Finished;
|
|
}
|
|
*pfAnotherCompletionExpected = TRUE;
|
|
|
|
goto Finished;
|
|
}
|
|
}
|
|
else if (FAILED(hr))
|
|
{
|
|
*pfClientError = TRUE;
|
|
goto Finished;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// ReadEntityBody will post a completion to IIS.
|
|
//
|
|
*pfAnotherCompletionExpected = TRUE;
|
|
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
m_RequestStatus = FORWARDER_RECEIVING_RESPONSE;
|
|
|
|
if (!WinHttpReceiveResponse(hRequest, NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Finished;
|
|
}
|
|
*pfAnotherCompletionExpected = TRUE;
|
|
|
|
Finished:
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
FORWARDING_HANDLER::OnWinHttpCompletionStatusHeadersAvailable(
|
|
HINTERNET hRequest,
|
|
__out BOOL * pfAnotherCompletionExpected
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
STACK_BUFFER(bufHeaderBuffer, 2048);
|
|
STACK_STRA(strHeaders, 2048);
|
|
DWORD dwHeaderSize = bufHeaderBuffer.QuerySize();
|
|
|
|
UNREFERENCED_PARAMETER(pfAnotherCompletionExpected);
|
|
|
|
//
|
|
// Headers are available, read the status line and headers and pass
|
|
// them on to the client
|
|
//
|
|
// WinHttpQueryHeaders operates synchronously,
|
|
// no need for taking reference.
|
|
//
|
|
dwHeaderSize = bufHeaderBuffer.QuerySize();
|
|
if (!WinHttpQueryHeaders(hRequest,
|
|
WINHTTP_QUERY_RAW_HEADERS_CRLF,
|
|
WINHTTP_HEADER_NAME_BY_INDEX,
|
|
bufHeaderBuffer.QueryPtr(),
|
|
&dwHeaderSize,
|
|
WINHTTP_NO_HEADER_INDEX))
|
|
{
|
|
if (!bufHeaderBuffer.Resize(dwHeaderSize))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// WinHttpQueryHeaders operates synchronously,
|
|
// no need for taking reference.
|
|
//
|
|
if (!WinHttpQueryHeaders(hRequest,
|
|
WINHTTP_QUERY_RAW_HEADERS_CRLF,
|
|
WINHTTP_HEADER_NAME_BY_INDEX,
|
|
bufHeaderBuffer.QueryPtr(),
|
|
&dwHeaderSize,
|
|
WINHTTP_NO_HEADER_INDEX))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr = strHeaders.CopyW(
|
|
reinterpret_cast<PWSTR>(bufHeaderBuffer.QueryPtr()))))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
// Issue: The reason we add trailing \r\n is to eliminate issues that have been observed
|
|
// in some configurations where status and headers would not have final \r\n nor \r\n\r\n
|
|
// (last header was null terminated).That caused crash within header parsing code that expected valid
|
|
// format. Parsing code was fized to return ERROR_INVALID_PARAMETER, but we still should make
|
|
// Example of a status+header string that was causing problems (note the missing \r\n at the end)
|
|
// HTTP/1.1 302 Moved Permanently\r\n....\r\nLocation:http://site\0
|
|
//
|
|
|
|
if (!strHeaders.IsEmpty() && strHeaders.QueryStr()[strHeaders.QueryCCH() - 1] != '\n')
|
|
{
|
|
hr = strHeaders.Append("\r\n");
|
|
if (FAILED(hr))
|
|
{
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr = SetStatusAndHeaders(
|
|
strHeaders.QueryStr(),
|
|
strHeaders.QueryCCH())))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
FreeResponseBuffers();
|
|
|
|
//
|
|
// If the request was websocket, and response was 101,
|
|
// trigger a flush, so that IIS's websocket module
|
|
// can get a chance to initialize and complete the handshake.
|
|
//
|
|
|
|
if (m_fWebSocketEnabled)
|
|
{
|
|
m_RequestStatus = FORWARDER_RECEIVED_WEBSOCKET_RESPONSE;
|
|
|
|
hr = m_pW3Context->GetResponse()->Flush(
|
|
TRUE,
|
|
TRUE,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
*pfAnotherCompletionExpected = FALSE;
|
|
}
|
|
else
|
|
{
|
|
*pfAnotherCompletionExpected = TRUE;
|
|
}
|
|
}
|
|
|
|
Finished:
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
FORWARDING_HANDLER::OnWinHttpCompletionStatusDataAvailable(
|
|
HINTERNET hRequest,
|
|
DWORD dwBytes,
|
|
_Out_ BOOL * pfAnotherCompletionExpected
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Response data is available from winhttp, read it
|
|
//
|
|
if (dwBytes == 0)
|
|
{
|
|
if (m_cContentLength != 0)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_INVALID_SERVER_RESPONSE);
|
|
goto Finished;
|
|
}
|
|
|
|
m_RequestStatus = FORWARDER_DONE;
|
|
|
|
goto Finished;
|
|
}
|
|
|
|
m_BytesToSend = dwBytes;
|
|
if (m_cContentLength != 0)
|
|
{
|
|
m_cContentLength -= dwBytes;
|
|
}
|
|
|
|
m_pEntityBuffer = GetNewResponseBuffer(
|
|
min(m_BytesToSend, BUFFER_SIZE));
|
|
if (m_pEntityBuffer == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// WinHttpReadData can operate asynchronously.
|
|
//
|
|
// Take reference so that object does not go away as a result of
|
|
// async completion.
|
|
//
|
|
//ReferenceForwardingHandler();
|
|
if (!WinHttpReadData(hRequest,
|
|
m_pEntityBuffer,
|
|
min(m_BytesToSend, BUFFER_SIZE),
|
|
NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
//DereferenceForwardingHandler();
|
|
goto Finished;
|
|
}
|
|
*pfAnotherCompletionExpected = TRUE;
|
|
|
|
Finished:
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
FORWARDING_HANDLER::OnWinHttpCompletionStatusReadComplete(
|
|
__in IHttpResponse * pResponse,
|
|
DWORD dwStatusInformationLength,
|
|
__out BOOL * pfAnotherCompletionExpected
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Response data has been read from winhttp, send it to the client
|
|
//
|
|
m_BytesToSend -= dwStatusInformationLength;
|
|
|
|
if (m_cMinBufferLimit >= BUFFER_SIZE / 2)
|
|
{
|
|
if (m_cContentLength != 0)
|
|
{
|
|
m_cContentLength -= dwStatusInformationLength;
|
|
}
|
|
|
|
//
|
|
// If we were not using WinHttpQueryDataAvailable and winhttp
|
|
// did not fill our buffer, we must have reached the end of the
|
|
// response
|
|
//
|
|
if (dwStatusInformationLength == 0 ||
|
|
m_BytesToSend != 0)
|
|
{
|
|
if (m_cContentLength != 0)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_INVALID_SERVER_RESPONSE);
|
|
goto Finished;
|
|
}
|
|
|
|
m_RequestStatus = FORWARDER_DONE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT(dwStatusInformationLength != 0);
|
|
}
|
|
|
|
if (dwStatusInformationLength == 0)
|
|
{
|
|
goto Finished;
|
|
}
|
|
else
|
|
{
|
|
m_cBytesBuffered += dwStatusInformationLength;
|
|
|
|
HTTP_DATA_CHUNK Chunk;
|
|
Chunk.DataChunkType = HttpDataChunkFromMemory;
|
|
Chunk.FromMemory.pBuffer = m_pEntityBuffer;
|
|
Chunk.FromMemory.BufferLength = dwStatusInformationLength;
|
|
if (FAILED(hr = pResponse->WriteEntityChunkByReference(&Chunk)))
|
|
{
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
if (m_cBytesBuffered >= m_cMinBufferLimit)
|
|
{
|
|
//
|
|
// Always post a completion to resume the WinHTTP data pump.
|
|
//
|
|
hr = pResponse->Flush(TRUE, // fAsync
|
|
TRUE, // fMoreData
|
|
NULL); // pcbSent
|
|
if (FAILED(hr))
|
|
{
|
|
goto Finished;
|
|
}
|
|
*pfAnotherCompletionExpected = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pfAnotherCompletionExpected = FALSE;
|
|
}
|
|
|
|
Finished:
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
FORWARDING_HANDLER::OnSendingRequest(
|
|
DWORD cbCompletion,
|
|
HRESULT hrCompletionStatus,
|
|
__out BOOL * pfClientError
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
//
|
|
// This is a completion for a read from http.sys, abort in case
|
|
// of failure, if we read anything write it out over WinHTTP,
|
|
// but we have already reached EOF, now read the response
|
|
//
|
|
if (hrCompletionStatus == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF))
|
|
{
|
|
DBG_ASSERT(m_BytesToReceive == 0 || m_BytesToReceive == INFINITE);
|
|
if (m_BytesToReceive == INFINITE)
|
|
{
|
|
m_BytesToReceive = 0;
|
|
m_cchLastSend = 5; // "0\r\n\r\n"
|
|
|
|
if (!WinHttpWriteData(m_hRequest,
|
|
"0\r\n\r\n",
|
|
5,
|
|
NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Failure;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_RequestStatus = FORWARDER_RECEIVING_RESPONSE;
|
|
|
|
if (!WinHttpReceiveResponse(m_hRequest, NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Failure;
|
|
}
|
|
}
|
|
}
|
|
else if (SUCCEEDED(hrCompletionStatus))
|
|
{
|
|
DWORD cbOffset;
|
|
|
|
if (m_BytesToReceive != INFINITE)
|
|
{
|
|
m_BytesToReceive -= cbCompletion;
|
|
cbOffset = 6;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// For chunk-encoded requests, need to re-chunk the entity body
|
|
// Add the CRLF just before and after the chunk data
|
|
//
|
|
m_pEntityBuffer[4] = '\r';
|
|
m_pEntityBuffer[5] = '\n';
|
|
|
|
m_pEntityBuffer[cbCompletion + 6] = '\r';
|
|
m_pEntityBuffer[cbCompletion + 7] = '\n';
|
|
|
|
if (cbCompletion < 0x10)
|
|
{
|
|
cbOffset = 3;
|
|
m_pEntityBuffer[3] = HEX_TO_ASCII(cbCompletion);
|
|
cbCompletion += 5;
|
|
}
|
|
else if (cbCompletion < 0x100)
|
|
{
|
|
cbOffset = 2;
|
|
m_pEntityBuffer[2] = HEX_TO_ASCII(cbCompletion >> 4);
|
|
m_pEntityBuffer[3] = HEX_TO_ASCII(cbCompletion & 0xf);
|
|
cbCompletion += 6;
|
|
}
|
|
else if (cbCompletion < 0x1000)
|
|
{
|
|
cbOffset = 1;
|
|
m_pEntityBuffer[1] = HEX_TO_ASCII(cbCompletion >> 8);
|
|
m_pEntityBuffer[2] = HEX_TO_ASCII((cbCompletion >> 4) & 0xf);
|
|
m_pEntityBuffer[3] = HEX_TO_ASCII(cbCompletion & 0xf);
|
|
cbCompletion += 7;
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT(cbCompletion < 0x10000);
|
|
|
|
cbOffset = 0;
|
|
m_pEntityBuffer[0] = HEX_TO_ASCII(cbCompletion >> 12);
|
|
m_pEntityBuffer[1] = HEX_TO_ASCII((cbCompletion >> 8) & 0xf);
|
|
m_pEntityBuffer[2] = HEX_TO_ASCII((cbCompletion >> 4) & 0xf);
|
|
m_pEntityBuffer[3] = HEX_TO_ASCII(cbCompletion & 0xf);
|
|
cbCompletion += 8;
|
|
}
|
|
}
|
|
m_cchLastSend = cbCompletion;
|
|
|
|
if (!WinHttpWriteData(m_hRequest,
|
|
m_pEntityBuffer + cbOffset,
|
|
cbCompletion,
|
|
NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Failure;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = hrCompletionStatus;
|
|
*pfClientError = TRUE;
|
|
goto Failure;
|
|
}
|
|
|
|
Failure:
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
FORWARDING_HANDLER::OnReceivingResponse(
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_cBytesBuffered >= m_cMinBufferLimit)
|
|
{
|
|
FreeResponseBuffers();
|
|
}
|
|
|
|
if (m_BytesToSend == 0)
|
|
{
|
|
//
|
|
// If response buffering is enabled, try to read large chunks
|
|
// at a time - also treat very small buffering limit as no
|
|
// buffering
|
|
//
|
|
m_BytesToSend = min(m_cMinBufferLimit, BUFFER_SIZE);
|
|
if (m_BytesToSend < BUFFER_SIZE / 2)
|
|
{
|
|
//
|
|
// Disable buffering.
|
|
//
|
|
m_BytesToSend = 0;
|
|
}
|
|
}
|
|
|
|
if (m_BytesToSend == 0)
|
|
{
|
|
//
|
|
// No buffering enabled.
|
|
//
|
|
if (!WinHttpQueryDataAvailable(m_hRequest, NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Failure;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Buffering enabled.
|
|
//
|
|
if (m_pEntityBuffer == NULL)
|
|
{
|
|
m_pEntityBuffer = GetNewResponseBuffer(min(m_BytesToSend, BUFFER_SIZE));
|
|
if (m_pEntityBuffer == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Failure;
|
|
}
|
|
}
|
|
|
|
if (!WinHttpReadData(m_hRequest,
|
|
m_pEntityBuffer,
|
|
min(m_BytesToSend, BUFFER_SIZE),
|
|
NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Failure;
|
|
}
|
|
}
|
|
|
|
Failure:
|
|
return hr;
|
|
}
|
|
|
|
BYTE *
|
|
FORWARDING_HANDLER::GetNewResponseBuffer(
|
|
DWORD dwBufferSize
|
|
)
|
|
{
|
|
DWORD dwNeededSize = (m_cEntityBuffers + 1) * sizeof(BYTE *);
|
|
if (dwNeededSize > m_buffEntityBuffers.QuerySize() &&
|
|
!m_buffEntityBuffers.Resize(
|
|
max(dwNeededSize, m_buffEntityBuffers.QuerySize() * 2)))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
BYTE *pBuffer = (BYTE *)HeapAlloc(GetProcessHeap(),
|
|
0, // dwFlags
|
|
dwBufferSize);
|
|
if (pBuffer == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
m_buffEntityBuffers.QueryPtr()[m_cEntityBuffers] = pBuffer;
|
|
m_cEntityBuffers++;
|
|
|
|
return pBuffer;
|
|
}
|
|
|
|
VOID
|
|
FORWARDING_HANDLER::FreeResponseBuffers()
|
|
{
|
|
BYTE **pBuffers = m_buffEntityBuffers.QueryPtr();
|
|
for (DWORD i = 0; i<m_cEntityBuffers; i++)
|
|
{
|
|
HeapFree(GetProcessHeap(),
|
|
0, // dwFlags
|
|
pBuffers[i]);
|
|
}
|
|
m_cEntityBuffers = 0;
|
|
m_pEntityBuffer = NULL;
|
|
m_cBytesBuffered = 0;
|
|
}
|
|
|
|
HRESULT
|
|
FORWARDING_HANDLER::SetStatusAndHeaders(
|
|
PCSTR pszHeaders,
|
|
DWORD
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
IHttpResponse * pResponse = m_pW3Context->GetResponse();
|
|
IHttpRequest * pRequest = m_pW3Context->GetRequest();
|
|
STACK_STRA(strHeaderName, 128);
|
|
STACK_STRA(strHeaderValue, 2048);
|
|
DWORD index = 0;
|
|
PSTR pchNewline;
|
|
PCSTR pchEndofHeaderValue;
|
|
BOOL fServerHeaderPresent = FALSE;
|
|
|
|
_ASSERT(pszHeaders != NULL);
|
|
|
|
//
|
|
// The first line is the status line
|
|
//
|
|
PSTR pchStatus = const_cast<PSTR>(strchr(pszHeaders, ' '));
|
|
if (pchStatus == NULL)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
|
}
|
|
while (*pchStatus == ' ')
|
|
{
|
|
pchStatus++;
|
|
}
|
|
USHORT uStatus = static_cast<USHORT>(atoi(pchStatus));
|
|
|
|
if (m_fWebSocketEnabled && uStatus != 101)
|
|
{
|
|
//
|
|
// Expected 101 response.
|
|
//
|
|
|
|
m_fWebSocketEnabled = FALSE;
|
|
}
|
|
|
|
pchStatus = strchr(pchStatus, ' ');
|
|
if (pchStatus == NULL)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
|
}
|
|
while (*pchStatus == ' ')
|
|
{
|
|
pchStatus++;
|
|
}
|
|
if (*pchStatus == '\r' || *pchStatus == '\n')
|
|
{
|
|
pchStatus--;
|
|
}
|
|
|
|
pchNewline = strchr(pchStatus, '\n');
|
|
if (pchNewline == NULL)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (uStatus != 200)
|
|
{
|
|
//
|
|
// Skip over any spaces before the '\n'
|
|
//
|
|
for (pchEndofHeaderValue = pchNewline - 1;
|
|
(pchEndofHeaderValue > pchStatus) &&
|
|
((*pchEndofHeaderValue == ' ') ||
|
|
(*pchEndofHeaderValue == '\r'));
|
|
pchEndofHeaderValue--)
|
|
{
|
|
}
|
|
|
|
//
|
|
// Copy the status description
|
|
//
|
|
if (FAILED(hr = strHeaderValue.Copy(
|
|
pchStatus,
|
|
(DWORD)(pchEndofHeaderValue - pchStatus) + 1)) ||
|
|
FAILED(hr = pResponse->SetStatus(uStatus,
|
|
strHeaderValue.QueryStr(),
|
|
0,
|
|
S_OK,
|
|
NULL,
|
|
TRUE)))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
for (index = static_cast<DWORD>(pchNewline - pszHeaders) + 1;
|
|
pszHeaders[index] != '\r' && pszHeaders[index] != '\n' && pszHeaders[index] != '\0';
|
|
index = static_cast<DWORD>(pchNewline - pszHeaders) + 1)
|
|
{
|
|
//
|
|
// Find the ':' in Header : Value\r\n
|
|
//
|
|
PCSTR pchColon = strchr(pszHeaders + index, ':');
|
|
|
|
//
|
|
// Find the '\n' in Header : Value\r\n
|
|
//
|
|
pchNewline = const_cast<PSTR>(strchr(pszHeaders + index, '\n'));
|
|
|
|
if (pchNewline == NULL)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Take care of header continuation
|
|
//
|
|
while (pchNewline[1] == ' ' ||
|
|
pchNewline[1] == '\t')
|
|
{
|
|
pchNewline = strchr(pchNewline + 1, '\n');
|
|
}
|
|
|
|
DBG_ASSERT(
|
|
(pchColon != NULL) && (pchColon < pchNewline));
|
|
if ((pchColon == NULL) || (pchColon >= pchNewline))
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
//
|
|
// Skip over any spaces before the ':'
|
|
//
|
|
PCSTR pchEndofHeaderName;
|
|
for (pchEndofHeaderName = pchColon - 1;
|
|
(pchEndofHeaderName >= pszHeaders + index) &&
|
|
(*pchEndofHeaderName == ' ');
|
|
pchEndofHeaderName--)
|
|
{
|
|
}
|
|
|
|
pchEndofHeaderName++;
|
|
|
|
//
|
|
// Copy the header name
|
|
//
|
|
if (FAILED(hr = strHeaderName.Copy(
|
|
pszHeaders + index,
|
|
(DWORD)(pchEndofHeaderName - pszHeaders) - index)))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Skip over the ':' and any trailing spaces
|
|
//
|
|
for (index = static_cast<DWORD>(pchColon - pszHeaders) + 1;
|
|
pszHeaders[index] == ' ';
|
|
index++)
|
|
{
|
|
}
|
|
|
|
//
|
|
// Skip over any spaces before the '\n'
|
|
//
|
|
for (pchEndofHeaderValue = pchNewline - 1;
|
|
(pchEndofHeaderValue >= pszHeaders + index) &&
|
|
((*pchEndofHeaderValue == ' ') ||
|
|
(*pchEndofHeaderValue == '\r'));
|
|
pchEndofHeaderValue--)
|
|
{
|
|
}
|
|
|
|
pchEndofHeaderValue++;
|
|
|
|
//
|
|
// Copy the header value
|
|
//
|
|
if (pchEndofHeaderValue == pszHeaders + index)
|
|
{
|
|
strHeaderValue.Reset();
|
|
}
|
|
else if (FAILED(hr = strHeaderValue.Copy(
|
|
pszHeaders + index,
|
|
(DWORD)(pchEndofHeaderValue - pszHeaders) - index)))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Do not pass the transfer-encoding:chunked, Connection, Date or
|
|
// Server headers along
|
|
//
|
|
DWORD headerIndex = sm_pResponseHeaderHash->GetIndex(strHeaderName.QueryStr());
|
|
if (headerIndex == UNKNOWN_INDEX)
|
|
{
|
|
hr = pResponse->SetHeader(strHeaderName.QueryStr(),
|
|
strHeaderValue.QueryStr(),
|
|
static_cast<USHORT>(strHeaderValue.QueryCCH()),
|
|
FALSE); // fReplace
|
|
}
|
|
else
|
|
{
|
|
switch (headerIndex)
|
|
{
|
|
case HttpHeaderTransferEncoding:
|
|
if (!strHeaderValue.Equals("chunked", TRUE))
|
|
{
|
|
break;
|
|
}
|
|
__fallthrough;
|
|
case HttpHeaderConnection:
|
|
case HttpHeaderDate:
|
|
continue;
|
|
|
|
case HttpHeaderServer:
|
|
fServerHeaderPresent = TRUE;
|
|
break;
|
|
|
|
case HttpHeaderContentLength:
|
|
if (pRequest->GetRawHttpRequest()->Verb != HttpVerbHEAD)
|
|
{
|
|
m_cContentLength = _atoi64(strHeaderValue.QueryStr());
|
|
}
|
|
break;
|
|
}
|
|
|
|
hr = pResponse->SetHeader(static_cast<HTTP_HEADER_ID>(headerIndex),
|
|
strHeaderValue.QueryStr(),
|
|
static_cast<USHORT>(strHeaderValue.QueryCCH()),
|
|
TRUE); // fReplace
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Explicitly remove the Server header if the back-end didn't set one.
|
|
//
|
|
|
|
if (!fServerHeaderPresent)
|
|
{
|
|
pResponse->DeleteHeader("Server");
|
|
}
|
|
|
|
if (m_fDoReverseRewriteHeaders)
|
|
{
|
|
hr = DoReverseRewrite(pResponse);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
m_fResponseHeadersReceivedAndSet = TRUE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
FORWARDING_HANDLER::DoReverseRewrite(
|
|
_In_ IHttpResponse *pResponse
|
|
)
|
|
{
|
|
DBG_ASSERT(pResponse == m_pW3Context->GetResponse());
|
|
BOOL fSecure = (m_pW3Context->GetRequest()->GetRawHttpRequest()->pSslInfo != NULL);
|
|
STRA strTemp;
|
|
PCSTR pszHeader;
|
|
PCSTR pszStartHost;
|
|
PCSTR pszEndHost;
|
|
HTTP_RESPONSE_HEADERS *pHeaders;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Content-Location and Location are easy, one known header in
|
|
// http[s]://host/url format
|
|
//
|
|
pszHeader = pResponse->GetHeader(HttpHeaderContentLocation);
|
|
if (pszHeader != NULL)
|
|
{
|
|
if (_strnicmp(pszHeader, "http://", 7) == 0)
|
|
{
|
|
pszStartHost = pszHeader + 7;
|
|
}
|
|
else if (_strnicmp(pszHeader, "https://", 8) == 0)
|
|
{
|
|
pszStartHost = pszHeader + 8;
|
|
}
|
|
else
|
|
{
|
|
goto Location;
|
|
}
|
|
|
|
pszEndHost = strchr(pszStartHost, '/');
|
|
|
|
if (FAILED(hr = strTemp.Copy(fSecure ? "https://" : "http://")) ||
|
|
FAILED(hr = strTemp.Append(m_pszOriginalHostHeader)))
|
|
{
|
|
return hr;
|
|
}
|
|
if (pszEndHost != NULL &&
|
|
FAILED(hr = strTemp.Append(pszEndHost)))
|
|
{
|
|
return hr;
|
|
}
|
|
if (FAILED(hr = pResponse->SetHeader(HttpHeaderContentLocation,
|
|
strTemp.QueryStr(),
|
|
static_cast<USHORT>(strTemp.QueryCCH()),
|
|
TRUE)))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
Location:
|
|
|
|
pszHeader = pResponse->GetHeader(HttpHeaderLocation);
|
|
if (pszHeader != NULL)
|
|
{
|
|
if (_strnicmp(pszHeader, "http://", 7) == 0)
|
|
{
|
|
pszStartHost = pszHeader + 7;
|
|
}
|
|
else if (_strnicmp(pszHeader, "https://", 8) == 0)
|
|
{
|
|
pszStartHost = pszHeader + 8;
|
|
}
|
|
else
|
|
{
|
|
goto SetCookie;
|
|
}
|
|
|
|
pszEndHost = strchr(pszStartHost, '/');
|
|
|
|
if (FAILED(hr = strTemp.Copy(fSecure ? "https://" : "http://")) ||
|
|
FAILED(hr = strTemp.Append(m_pszOriginalHostHeader)))
|
|
{
|
|
return hr;
|
|
}
|
|
if (pszEndHost != NULL &&
|
|
FAILED(hr = strTemp.Append(pszEndHost)))
|
|
{
|
|
return hr;
|
|
}
|
|
if (FAILED(hr = pResponse->SetHeader(HttpHeaderLocation,
|
|
strTemp.QueryStr(),
|
|
static_cast<USHORT>(strTemp.QueryCCH()),
|
|
TRUE)))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
SetCookie:
|
|
|
|
//
|
|
// Set-Cookie is different - possibly multiple unknown headers with
|
|
// syntax name=value ; ... ; Domain=.host ; ...
|
|
//
|
|
pHeaders = &pResponse->GetRawHttpResponse()->Headers;
|
|
for (DWORD i = 0; i<pHeaders->UnknownHeaderCount; i++)
|
|
{
|
|
if (_stricmp(pHeaders->pUnknownHeaders[i].pName, "Set-Cookie") != 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pszHeader = pHeaders->pUnknownHeaders[i].pRawValue;
|
|
pszStartHost = strchr(pszHeader, ';');
|
|
while (pszStartHost != NULL)
|
|
{
|
|
pszStartHost++;
|
|
while (IsSpace(*pszStartHost))
|
|
{
|
|
pszStartHost++;
|
|
}
|
|
|
|
if (_strnicmp(pszStartHost, "Domain", 6) != 0)
|
|
{
|
|
pszStartHost = strchr(pszStartHost, ';');
|
|
continue;
|
|
}
|
|
pszStartHost += 6;
|
|
|
|
while (IsSpace(*pszStartHost))
|
|
{
|
|
pszStartHost++;
|
|
}
|
|
if (*pszStartHost != '=')
|
|
{
|
|
break;
|
|
}
|
|
pszStartHost++;
|
|
while (IsSpace(*pszStartHost))
|
|
{
|
|
pszStartHost++;
|
|
}
|
|
if (*pszStartHost == '.')
|
|
{
|
|
pszStartHost++;
|
|
}
|
|
pszEndHost = pszStartHost;
|
|
while (!IsSpace(*pszEndHost) &&
|
|
*pszEndHost != ';' &&
|
|
*pszEndHost != '\0')
|
|
{
|
|
pszEndHost++;
|
|
}
|
|
|
|
if (FAILED(hr = strTemp.Copy(pszHeader, static_cast<DWORD>(pszStartHost - pszHeader))) ||
|
|
FAILED(hr = strTemp.Append(m_pszOriginalHostHeader)) ||
|
|
FAILED(hr = strTemp.Append(pszEndHost)))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
pszHeader = (PCSTR)m_pW3Context->AllocateRequestMemory(strTemp.QueryCCH() + 1);
|
|
if (pszHeader == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
StringCchCopyA(const_cast<PSTR>(pszHeader), strTemp.QueryCCH() + 1, strTemp.QueryStr());
|
|
pHeaders->pUnknownHeaders[i].pRawValue = pszHeader;
|
|
pHeaders->pUnknownHeaders[i].RawValueLength = static_cast<USHORT>(strTemp.QueryCCH());
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
VOID
|
|
FORWARDING_HANDLER::TerminateRequest(
|
|
bool fClientInitiated
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(fClientInitiated);
|
|
AcquireSRWLockExclusive(&m_RequestLock);
|
|
// Set tls as close winhttp handle will immediately trigger
|
|
// a winhttp callback on the same thread and we donot want to
|
|
// acquire the lock again
|
|
TlsSetValue(g_dwTlsIndex, this);
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == this);
|
|
|
|
if (m_hRequest != NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO,
|
|
"FORWARDING_HANDLER::TerminateRequest %d --%p\n", GetCurrentThreadId(), m_pW3Context);
|
|
#endif // DEBUG
|
|
m_fHandleClosedDueToClient = fClientInitiated;
|
|
WinHttpCloseHandle(m_hRequest);
|
|
}
|
|
|
|
//
|
|
// If the request is a websocket request, initiate cleanup.
|
|
//
|
|
if (m_pWebSocket != NULL)
|
|
{
|
|
m_pWebSocket->TerminateRequest();
|
|
}
|
|
|
|
TlsSetValue(g_dwTlsIndex, NULL);
|
|
ReleaseSRWLockExclusive(&m_RequestLock);
|
|
DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == NULL);
|
|
}
|
|
|