windows-nt/Source/XPSP1/NT/admin/pchealth/upload/server/isapi/httpcontext.cpp
2020-09-26 16:20:57 +08:00

707 lines
19 KiB
C++

/******************************************************************************
Copyright (c) 2000 Microsoft Corporation
Module Name:
HttpContext.cpp
Abstract:
This file contains the implementation of the MPCHttpContext class,
which handles the interface with IIS.
Revision History:
Davide Massarenti (Dmassare) 04/20/99
created
******************************************************************************/
#include "stdafx.h"
#define BUFFER_SIZE_TMP (64)
static const char szStatus [] = "200 OK";
static const char szNewLine[] = "\r\n";
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//
// Static functions.
//
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
static void SupportAddHeader( /*[in/out]*/ MPC::string& szHeaders ,
/*[in] */ const char* szHeaderName ,
/*[in] */ const char* szHeaderValue )
{
__ULT_FUNC_ENTRY("SupportAddHeader");
szHeaders.append( szHeaderName );
szHeaders.append( ": " );
szHeaders.append( szHeaderValue );
szHeaders.append( szNewLine );
}
static void SupportAddHeader( /*[in/out]*/ MPC::string& szHeaders ,
/*[in] */ const char* szHeaderName ,
/*[in] */ DWORD dwHeaderValue )
{
__ULT_FUNC_ENTRY("SupportAddHeader");
char rgBuf[BUFFER_SIZE_TMP];
sprintf( rgBuf, "%lu", dwHeaderValue );
SupportAddHeader( szHeaders, szHeaderName, rgBuf );
}
static void SupportEndHeaders( /*[in/out]*/ MPC::string& szHeaders )
{
__ULT_FUNC_ENTRY("SupportEndHeaders");
szHeaders.append( szNewLine );
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//
// Construction/Destruction
//
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
MPCHttpContext::MPCHttpContext() : m_hsInput (g_Heap),
m_hsOutput(g_Heap)
{
__ULT_FUNC_ENTRY("MPCHttpContext::MPCHttpContext");
m_pECB = NULL;
m_mpcsServer = NULL;
m_dwSkippedInput = 0;
m_fRequestProcessed = FALSE;
m_fKeepConnection = TRUE;
m_fAsync = FALSE;
m_FSMstate = FSM_REGISTER;
m_IOstate = IO_IDLE;
#ifdef DEBUG
m_Debug_NO_RESPONSE_TO_OPEN = false;
m_Debug_NO_RESPONSE_TO_WRITE = false;
m_Debug_RESPONSE_TO_OPEN = false;
m_Debug_RESPONSE_TO_OPEN_response = 0;
m_Debug_RESPONSE_TO_OPEN_position = -1;
m_Debug_RESPONSE_TO_OPEN_protocol = UPLOAD_LIBRARY_PROTOCOL_VERSION_SRV;
m_Debug_RESPONSE_TO_WRITE = false;
m_Debug_RESPONSE_TO_WRITE_response = 0;
m_Debug_RESPONSE_TO_WRITE_position = -1;
m_Debug_RESPONSE_TO_WRITE_protocol = UPLOAD_LIBRARY_PROTOCOL_VERSION_SRV;
m_Debug_RANDOM_POINTER_ERROR = false;
m_Debug_RANDOM_POINTER_ERROR_pos_low = 0;
m_Debug_RANDOM_POINTER_ERROR_pos_high = -1;
m_Debug_FIXED_POINTER_ERROR = false;
m_Debug_FIXED_POINTER_ERROR_pos = 0;
#endif
}
MPCHttpContext::~MPCHttpContext()
{
__ULT_FUNC_ENTRY("MPCHttpContext::~MPCHttpContext");
if(m_mpcsServer) delete m_mpcsServer;
if(m_fAsync && m_pECB)
{
//
// Close session.
//
m_pECB->ServerSupportFunction( m_pECB->ConnID ,
HSE_REQ_DONE_WITH_SESSION ,
NULL ,
NULL ,
NULL );
}
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//
// Callbacks
//
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
VOID WINAPI MPCHttpContext::IOCompletion( /*[in]*/ EXTENSION_CONTROL_BLOCK* pECB ,
/*[in]*/ PVOID pContext ,
/*[in]*/ DWORD cbIO ,
/*[in]*/ DWORD dwError )
{
__ULT_FUNC_ENTRY("MPCHttpContext::IOCompletion");
MPCHttpContext* ptr = NULL;
try
{
ptr = reinterpret_cast<MPCHttpContext*>(pContext);
ptr->m_pECB = pECB;
if(dwError != ERROR_SUCCESS)
{
delete ptr; ptr = NULL;
}
else
{
switch( ptr->m_IOstate )
{
case IO_IDLE:
break;
case IO_READING:
//
// If the request has already been processed, simply count the number of bytes received.
//
if(ptr->m_fRequestProcessed)
{
ptr->m_dwSkippedInput += cbIO;
}
else
{
ptr->m_hsInput.write( ptr->m_rgBuffer, cbIO );
}
//
// If this is the last request (cbIO==0) or the number of bytes skipped is equal to the number of missing bytes,
// proceed to the next phase.
//
if(cbIO == 0 || ptr->m_dwSkippedInput == ptr->m_hsInput.GetAvailableForWrite())
{
ptr->m_IOstate = IO_IDLE;
}
else
{
if(ptr->Fsm_Process() == HSE_STATUS_ERROR)
{
delete ptr; ptr = NULL;
}
else
{
ptr->AsyncRead();
}
}
break;
case IO_WRITING:
if(cbIO == 0 || ptr->m_hsOutput.IsEOR())
{
ptr->m_IOstate = IO_IDLE;
}
else
{
ptr->AsyncWrite();
}
break;
}
if(ptr->m_IOstate == IO_IDLE)
{
ptr->AdvanceFSM();
}
}
}
catch(...)
{
__ULT_TRACE_ERROR( UPLOADLIBID, "Upload Server raised an exception. Gracefully exiting..." );
(void)g_NTEvents.LogEvent( EVENTLOG_ERROR_TYPE, PCHUL_ERR_EXCEPTION,
L"" , // %1 = SERVER
L"IOCompletion", // %2 = CLIENT
NULL );
if(ptr)
{
delete ptr; ptr = NULL;
}
}
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//
// Protected Methods.
//
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
DWORD MPCHttpContext::AsyncRead()
{
__ULT_FUNC_ENTRY("MPCHttpContext::AsyncRead");
DWORD dwRes = HSE_STATUS_SUCCESS;
DWORD dwSize = m_hsInput.GetAvailableForWrite();
//
// If not all the data has been read, ask for async I/O operation.
//
if(dwSize)
{
DWORD dwTmp = HSE_IO_ASYNC;
m_fAsync = TRUE;
m_IOstate = IO_READING;
dwSize = min( dwSize, sizeof(m_rgBuffer) );
if(!m_pECB->ServerSupportFunction( m_pECB->ConnID ,
HSE_REQ_ASYNC_READ_CLIENT,
m_rgBuffer ,
&dwSize ,
&dwTmp ))
{
dwRes = HSE_STATUS_ERROR; __ULT_FUNC_LEAVE;
}
else
{
dwRes = HSE_STATUS_PENDING; __ULT_FUNC_LEAVE;
}
}
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(dwRes);
}
DWORD MPCHttpContext::AsyncWrite()
{
__ULT_FUNC_ENTRY("MPCHttpContext::AsyncWrite");
DWORD dwRes = HSE_STATUS_SUCCESS;
DWORD dwSize = m_hsOutput.GetAvailableForRead();
//
// If not all the data has been read, ask for async I/O operation.
//
if(dwSize)
{
m_fAsync = TRUE;
m_IOstate = IO_WRITING;
dwSize = min( dwSize, sizeof(m_rgBuffer) );
if(FAILED(m_hsOutput.read( m_rgBuffer, dwSize )))
{
dwRes = HSE_STATUS_ERROR; __ULT_FUNC_LEAVE;
}
if(m_pECB->WriteClient( m_pECB->ConnID, m_rgBuffer, &dwSize, HSE_IO_ASYNC ) == FALSE)
{
dwRes = HSE_STATUS_ERROR; __ULT_FUNC_LEAVE;
}
dwRes = HSE_STATUS_PENDING; __ULT_FUNC_LEAVE;
}
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(dwRes);
}
DWORD MPCHttpContext::AdvanceFSM()
{
__ULT_FUNC_ENTRY("MPCHttpContext::AdvanceFSM");
DWORD dwRes = HSE_STATUS_SUCCESS;
bool fClean = false;
while(dwRes == HSE_STATUS_SUCCESS)
{
switch(m_FSMstate)
{
case FSM_REGISTER: m_FSMstate = FSM_INPUT ; dwRes = Fsm_Register (); break; // Register IO callback.
case FSM_INPUT : m_FSMstate = FSM_PROCESS; dwRes = Fsm_ReceiveInput(); break; // Read all the input.
case FSM_PROCESS : m_FSMstate = FSM_OUTPUT ; dwRes = Fsm_Process (); break; // Process request.
case FSM_OUTPUT : m_FSMstate = FSM_DELETE ; dwRes = Fsm_SendOutput (); break; // Send output.
case FSM_DELETE : fClean = true; __ULT_FUNC_LEAVE; // Delete the request object.
}
}
if(dwRes != HSE_STATUS_SUCCESS &&
dwRes != HSE_STATUS_PENDING )
{
fClean = true;
}
__ULT_FUNC_CLEANUP;
if(fClean) delete this;
__ULT_FUNC_EXIT(dwRes);
}
DWORD MPCHttpContext::Fsm_Register()
{
__ULT_FUNC_ENTRY("MPCHttpContext::Fsm_Register");
DWORD dwRes = HSE_STATUS_SUCCESS;
if(!m_pECB->ServerSupportFunction( m_pECB->ConnID ,
HSE_REQ_IO_COMPLETION,
IOCompletion ,
NULL ,
(LPDWORD)this ))
{
dwRes = HSE_STATUS_ERROR; __ULT_FUNC_LEAVE;
}
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(dwRes);
}
DWORD MPCHttpContext::Fsm_ReceiveInput()
{
__ULT_FUNC_ENTRY("MPCHttpContext::Fsm_ReceiveInput");
DWORD dwRes = HSE_STATUS_SUCCESS;
//
// Alloc a buffer large enough to hold the request data.
//
if(FAILED(m_hsInput.SetSize( m_pECB->cbTotalBytes ))) { dwRes = HSE_STATUS_ERROR; __ULT_FUNC_LEAVE; }
if(FAILED(m_hsInput.write ( m_pECB->lpbData, m_pECB->cbAvailable ))) { dwRes = HSE_STATUS_ERROR; __ULT_FUNC_LEAVE; }
dwRes = AsyncRead();
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(dwRes);
}
DWORD MPCHttpContext::Fsm_Process()
{
__ULT_FUNC_ENTRY("MPCHttpContext::Fsm_Process");
HRESULT hr;
DWORD dwRes = HSE_STATUS_SUCCESS;
if(m_fRequestProcessed == TRUE) __ULT_FUNC_LEAVE;
m_hsInput .Rewind();
m_hsOutput.Reset ();
if(FAILED(hr = m_mpcsServer->Process( m_fKeepConnection )))
{
if(hr == E_PENDING)
{
dwRes = HSE_STATUS_PENDING; __ULT_FUNC_LEAVE;
}
m_fRequestProcessed = TRUE;
dwRes = HSE_STATUS_ERROR; __ULT_FUNC_LEAVE;
}
m_fRequestProcessed = TRUE;
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(dwRes);
}
DWORD MPCHttpContext::Fsm_SendOutput()
{
__ULT_FUNC_ENTRY("MPCHttpContext::Fsm_SendOutput");
HSE_SEND_HEADER_EX_INFO headerInfo;
MPC::string szHeaders;
DWORD dwRes;
//
// Built headers.
//
SupportAddHeader( szHeaders, "Content-Length", m_hsOutput.GetSize() );
SupportAddHeader( szHeaders, "Content-Type" , "application/uploadlibrary" );
SupportEndHeaders( szHeaders );
//
// Populate HSE_SEND_HEADER_EX_INFO struct.
//
headerInfo.pszStatus = szStatus;
headerInfo.cchStatus = strlen( szStatus );
headerInfo.pszHeader = szHeaders.c_str();
headerInfo.cchHeader = szHeaders.length();
headerInfo.fKeepConn = TRUE;
//
// Send response.
//
if(!m_pECB->ServerSupportFunction( m_pECB->ConnID ,
HSE_REQ_SEND_RESPONSE_HEADER_EX ,
&headerInfo ,
NULL ,
NULL ))
{
dwRes = HSE_STATUS_ERROR; __ULT_FUNC_LEAVE;
}
//
// Send data, if present.
//
dwRes = AsyncWrite();
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(dwRes);
}
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//
// Methods.
//
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
DWORD MPCHttpContext::Init( /*[in]*/ LPEXTENSION_CONTROL_BLOCK pECB )
{
__ULT_FUNC_ENTRY("MPCHttpContext::Init");
DWORD dwRes;
MPC::wstring szURL;
MPC::wstring szUser;
m_pECB = pECB;
if(FAILED(GetServerVariable( "URL", szURL )))
{
szURL = L"DEFAULT";
}
if(FAILED(GetServerVariable( "REMOTE_USER", szUser )))
{
szUser = L"";
}
#ifdef DEBUG
if(pECB->lpszQueryString)
{
static MPC::string constNO_RESPONSE_TO_OPEN ( "NO_RESPONSE_TO_OPEN" );
static MPC::string constNO_RESPONSE_TO_WRITE( "NO_RESPONSE_TO_WRITE" );
static MPC::string constRESPONSE_TO_OPEN ( "RESPONSE_TO_OPEN" );
static MPC::string constRESPONSE_TO_WRITE ( "RESPONSE_TO_WRITE" );
static MPC::string constRANDOM_POINTER_ERROR( "RANDOM_POINTER_ERROR" );
static MPC::string constFIXED_POINTER_ERROR ( "FIXED_POINTER_ERROR" );
std::vector<MPC::string> vec;
std::vector<MPC::string> vec2;
std::vector<MPC::string> vec3;
MPC::NocaseCompare cmp;
int i;
MPC::SplitAtDelimiter( vec, pECB->lpszQueryString, "&" );
for(i=0; i<vec.size(); i++)
{
MPC::SplitAtDelimiter( vec2, vec[i].c_str(), "=" );
switch( vec2.size() )
{
default:
case 2 : MPC::SplitAtDelimiter( vec3, vec2[1].c_str(), "," );
case 1 : break;
case 0 : continue;
}
MPC::string& name = vec2[0];
if(cmp( name, constNO_RESPONSE_TO_OPEN ))
{
m_Debug_NO_RESPONSE_TO_OPEN = true;
}
else if(cmp( name, constNO_RESPONSE_TO_WRITE ))
{
m_Debug_NO_RESPONSE_TO_WRITE = true;
}
else if(cmp( name, constRESPONSE_TO_OPEN ))
{
switch( vec3.size() )
{
case 3 : m_Debug_RESPONSE_TO_OPEN_protocol = atol( vec3[2].c_str() );
case 2 : m_Debug_RESPONSE_TO_OPEN_position = atol( vec3[1].c_str() );
case 1 : m_Debug_RESPONSE_TO_OPEN_response = atol( vec3[0].c_str() );
m_Debug_RESPONSE_TO_OPEN = true;
}
}
else if(cmp( name, constRESPONSE_TO_WRITE ))
{
switch( vec3.size() )
{
case 3 : m_Debug_RESPONSE_TO_WRITE_protocol = atol( vec3[2].c_str() );
case 2 : m_Debug_RESPONSE_TO_WRITE_position = atol( vec3[1].c_str() );
case 1 : m_Debug_RESPONSE_TO_WRITE_response = atol( vec3[0].c_str() );
m_Debug_RESPONSE_TO_WRITE = true;
}
}
else if(cmp( name, constRANDOM_POINTER_ERROR ))
{
switch( vec3.size() )
{
case 2: m_Debug_RANDOM_POINTER_ERROR_pos_high = atol( vec3[1].c_str() );
m_Debug_RANDOM_POINTER_ERROR_pos_low = atol( vec3[0].c_str() );
m_Debug_RANDOM_POINTER_ERROR = true;
}
}
else if(cmp( name, constFIXED_POINTER_ERROR ))
{
switch( vec3.size() )
{
case 1 : m_Debug_FIXED_POINTER_ERROR_pos = atol( vec3[0].c_str() );
m_Debug_FIXED_POINTER_ERROR = true;
}
}
}
}
#endif
m_mpcsServer = new MPCServer( this, szURL.c_str(), szUser.c_str() );
if(m_mpcsServer == NULL)
{
dwRes = HSE_STATUS_ERROR; __ULT_FUNC_LEAVE;
}
dwRes = AdvanceFSM();
__ULT_FUNC_CLEANUP;
__ULT_FUNC_EXIT(dwRes);
}
HRESULT MPCHttpContext::GetServerVariable( /*[in]*/ LPCSTR szVar, /*[out]*/ MPC::wstring& szValue )
{
__ULT_FUNC_ENTRY("MPCHttpContext::GetServerVariable");
USES_CONVERSION;
HRESULT hr;
DWORD dwRes;
LPSTR szData = NULL;
DWORD dwSize = 0;
m_pECB->GetServerVariable( m_pECB->ConnID, (LPSTR)szVar, NULL, &dwSize );
dwRes = ::GetLastError();
if(dwRes != ERROR_INSUFFICIENT_BUFFER)
{
__MPC_SET_WIN32_ERROR_AND_EXIT(hr, dwRes);
}
__MPC_EXIT_IF_ALLOC_FAILS(hr, szData, new CHAR[dwSize+1]);
__MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, m_pECB->GetServerVariable( m_pECB->ConnID, (LPSTR)szVar, szData, &dwSize ));
szValue = A2W( szData );
hr = S_OK;
__ULT_FUNC_CLEANUP;
if(szData) delete [] szData;
__ULT_FUNC_EXIT(hr);
}
HRESULT MPCHttpContext::GetRequestSize( /*[out]*/ DWORD& dwCount )
{
__ULT_FUNC_ENTRY("MPCHttpContext::GetRequestSize");
HRESULT hr;
dwCount = m_hsInput.GetSize();
hr = S_OK;
__ULT_FUNC_EXIT(hr);
}
HRESULT MPCHttpContext::CheckDataAvailable( /*[in] */ DWORD dwCount ,
/*[out]*/ bool& fAvailable )
{
__ULT_FUNC_ENTRY("MPCHttpContext::CheckDataAvailable");
HRESULT hr;
fAvailable = (m_hsInput.GetAvailableForRead() >= dwCount);
hr = S_OK;
__ULT_FUNC_EXIT(hr);
}
HRESULT MPCHttpContext::Read( /*[in]*/ void* pBuffer ,
/*[in]*/ DWORD dwCount )
{
__ULT_FUNC_ENTRY("MPCHttpContext::Read");
HRESULT hr = m_hsInput.read( pBuffer, dwCount );
__ULT_FUNC_EXIT(hr);
}
HRESULT MPCHttpContext::Write( /*[in]*/ const void* pBuffer ,
/*[in]*/ DWORD dwCount )
{
__ULT_FUNC_ENTRY("MPCHttpContext::Write");
HRESULT hr = m_hsOutput.write( pBuffer, dwCount );
__ULT_FUNC_EXIT(hr);
}