mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2497 lines
60 KiB
2497 lines
60 KiB
/**********************************************************************/
|
|
/** Microsoft Windows NT **/
|
|
/** Copyright(c) Microsoft Corp., 1994 **/
|
|
/**********************************************************************/
|
|
|
|
/*
|
|
httpreq.cxx
|
|
|
|
This module contains the http request class implementation
|
|
|
|
|
|
FILE HISTORY:
|
|
Johnl 24-Aug-1994 Created
|
|
MuraliK 16-May-1995 Modified LogInformation structure
|
|
after adding additional fields.
|
|
MuraliK 22-Jan-1996 Cache & use UNC impersonation.
|
|
*/
|
|
|
|
|
|
#include "w3p.hxx"
|
|
#include <inetinfo.h>
|
|
|
|
#pragma warning( disable:4355 ) // 'this' used in base member initialization
|
|
|
|
//
|
|
// Private constants.
|
|
//
|
|
|
|
#define MAX_HEADER_LENGTH 255
|
|
|
|
//
|
|
// Private globals.
|
|
//
|
|
|
|
CRITICAL_SECTION HTTP_REQUEST::_csBuffList;
|
|
LIST_ENTRY HTTP_REQUEST::_BuffListHead;
|
|
BOOL HTTP_REQUEST::_fGlobalInit = FALSE;
|
|
|
|
//
|
|
// This structure contains the field name to action mapping of the fields
|
|
// we recognize
|
|
//
|
|
|
|
struct _HTTP_RFC_FIELDS
|
|
{
|
|
TCHAR * pchFieldName;
|
|
HTTP_REQUEST::PMFN_ONGRAMMAR pmfnOnField;
|
|
int cName;
|
|
}
|
|
//
|
|
// List of common HTTP headers.
|
|
// This must be synchronized with the HM_ID enum type in parmlist.hxx
|
|
//
|
|
OnFieldName[] =
|
|
{
|
|
//
|
|
// Fields that we don't process are commented out.
|
|
//
|
|
|
|
{"method", &HTTP_REQUEST::OnVerb},
|
|
{"url", &HTTP_REQUEST::OnURL},
|
|
{"version", &HTTP_REQUEST::OnVersion},
|
|
|
|
{"Accept:", &HTTP_REQUEST::OnAccept},
|
|
// {"Accept-Encoding:", &HTTP_REQUEST::OnAcceptEncoding},
|
|
{"Accept-Language:", &HTTP_REQUEST::OnAcceptLanguage},
|
|
{"Authorization:", &HTTP_REQ_BASE::OnAuthorization},
|
|
{"Connection:", &HTTP_REQUEST::OnConnection},
|
|
{"Content-Length:", &HTTP_REQ_BASE::OnContentLength},
|
|
{"Content-Type:", &HTTP_REQUEST::OnContentType},
|
|
// {"Date:", &HTTP_REQUEST::OnDate},
|
|
// {"Forwarded:", &HTTP_REQUEST::OnForwarded},
|
|
// {"From:", &HTTP_REQUEST::OnFrom},
|
|
{"If-Modified-Since:", &HTTP_REQ_BASE::OnIfModifiedSince},
|
|
{"Unless-Modified-Since:", &HTTP_REQ_BASE::OnUnlessModifiedSince},
|
|
// {"Mandatory:", &HTTP_REQUEST::OnMandatory},
|
|
// {"Message-ID:", &HTTP_REQUEST::OnMessageID},
|
|
// {"MIME-Version:" &HTTP_REQUEST::OnMimeVersion},
|
|
// {"Pragma:", &HTTP_REQUEST::OnPragma},
|
|
{"Proxy-Authorization:", &HTTP_REQUEST::OnProxyAuthorization},
|
|
// {"Referer:", &HTTP_REQUEST::OnReferer},
|
|
// {"User-Agent:", &HTTP_REQUEST::OnUserAgent},
|
|
{"Host:", &HTTP_REQ_BASE::OnHost},
|
|
{"Range:", &HTTP_REQ_BASE::OnRange},
|
|
NULL, NULL,
|
|
};
|
|
|
|
|
|
// array set to TRUE for linear white space characters
|
|
|
|
int _HTTP_LINEAR_SPACE[]={
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
} ;
|
|
|
|
|
|
TCHAR* FastMapFieldName(
|
|
HM_ID iN
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Map a field index in OnFieldName to the field name
|
|
|
|
Arguments:
|
|
|
|
iN - index in OnFieldName
|
|
|
|
Returns:
|
|
|
|
Ptr to field name
|
|
|
|
--*/
|
|
{
|
|
return OnFieldName[(int)iN].pchFieldName;
|
|
}
|
|
|
|
|
|
int g_OnFieldNameMapper[_HTTP_HEADER_SIGNIFICANT_CHARS*_HTTP_HEADER_SIGNIFICANT_CHARS];
|
|
|
|
|
|
VOID
|
|
InitFastFindFieldMapper(
|
|
VOID )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the fast find HTTP header mapper
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
for ( int x = 0 ; x < sizeof(g_OnFieldNameMapper)/sizeof(int) ; ++x )
|
|
g_OnFieldNameMapper[x] = -1;
|
|
|
|
struct _HTTP_RFC_FIELDS *pH = OnFieldName;
|
|
int i = 0;
|
|
while ( pH->pchFieldName )
|
|
{
|
|
pH->cName = strlen( pH->pchFieldName );
|
|
int iN = _HTTP_HEADER_HASH(pH->pchFieldName, pH->cName);
|
|
|
|
// If this asserts, someone must have added an entry in OnFieldName wich collides
|
|
// with another entry. To prevent collision, check that the 1st char and the next
|
|
// to last char pairs are unique. If not, then you will have to modify the algorithm
|
|
// to use another pair or a different method ( linked list in OnFieldName ? )
|
|
// or revert to change references from FastFindField() to FindField()
|
|
|
|
TCP_ASSERT( g_OnFieldNameMapper[iN] == -1 );
|
|
g_OnFieldNameMapper[iN] = i;
|
|
++pH;
|
|
++i;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
FastFindField(
|
|
CHAR * pszHeader,
|
|
int cN,
|
|
int *piF )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get index of Header in OnFieldName array
|
|
|
|
Arguments:
|
|
|
|
pszHeader - name of header field
|
|
cN - length of header name
|
|
piF - pointer to updated index inside OnFieldName
|
|
|
|
Return Value:
|
|
|
|
TRUE if header name found, else FALSE
|
|
|
|
--*/
|
|
{
|
|
if ( cN > 1 )
|
|
{
|
|
int iF = g_OnFieldNameMapper[ _HTTP_HEADER_HASH(pszHeader, cN) ];
|
|
if ( iF != -1 && cN == OnFieldName[iF].cName )
|
|
{
|
|
LPSTR pszFN = OnFieldName[iF].pchFieldName;
|
|
|
|
LPSTR pszMax = pszHeader + cN;
|
|
while ( pszHeader < pszMax )
|
|
{
|
|
if ( _HTTP_HEADER_CHAR_I_NOTEQUAL( *pszHeader++,
|
|
*pszFN++ ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
*piF = iF;
|
|
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// This table contains the verbs we recognize
|
|
//
|
|
|
|
struct _HTTP_VERBS
|
|
{
|
|
TCHAR * pchVerb; // Verb name
|
|
UINT cchVerb; // Count of characters in verb
|
|
HTTP_VERB httpVerb;
|
|
HTTP_REQUEST::PMFN_DOVERB pmfnVerb; // Pointer to member function
|
|
}
|
|
DoVerb[] =
|
|
{
|
|
"GET", 3, HTV_GET, &HTTP_REQUEST::DoGet,
|
|
"HEAD", 4, HTV_HEAD, &HTTP_REQUEST::DoHead,
|
|
NULL, 0, HTV_UNKNOWN, NULL
|
|
};
|
|
|
|
|
|
//
|
|
// Private prototypes.
|
|
//
|
|
|
|
BOOL
|
|
BuildCGIHeaderList( STR * pstr,
|
|
PARAM_LIST * pHeaderList
|
|
);
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
//
|
|
// Private functions.
|
|
//
|
|
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HTTP_REQUEST::HTTP_REQUEST
|
|
|
|
SYNOPSIS: Http request object constructor
|
|
|
|
ENTRY: pClientConn - Client connection the request is being made on
|
|
|
|
NOTES: Constructor can't fail
|
|
|
|
HISTORY:
|
|
Johnl 24-Aug-1994 Created
|
|
|
|
********************************************************************/
|
|
|
|
HTTP_REQUEST::HTTP_REQUEST(
|
|
CLIENT_CONN * pClientConn,
|
|
PVOID pvInitialBuff,
|
|
DWORD cbInitialBuff
|
|
)
|
|
: HTTP_REQ_BASE( pClientConn,
|
|
pvInitialBuff,
|
|
cbInitialBuff ),
|
|
_pGetFile ( NULL )
|
|
{
|
|
}
|
|
|
|
HTTP_REQUEST::~HTTP_REQUEST( VOID )
|
|
{
|
|
if ( QueryFileHandle() != INVALID_HANDLE_VALUE )
|
|
{
|
|
TCP_REQUIRE(::TsCloseHandle( g_pTsvcInfo->GetTsvcCache(),
|
|
_pGetFile ));
|
|
_pGetFile = NULL;
|
|
}
|
|
|
|
DBG_REQUIRE( m_hVrootImpersonation == NULL);
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HTTP_REQUEST::Reset
|
|
|
|
SYNOPSIS: Resets the request object getting it ready for the next
|
|
client request
|
|
|
|
RETURNS: TRUE if successful, FALSE if an error occurred (call
|
|
GetLastError())
|
|
|
|
HISTORY:
|
|
Johnl 04-Sep-1994 Created
|
|
|
|
********************************************************************/
|
|
|
|
BOOL HTTP_REQUEST::Reset( VOID )
|
|
{
|
|
//
|
|
// Must reset the base object first
|
|
//
|
|
|
|
TCP_REQUIRE( HTTP_REQ_BASE::Reset() );
|
|
|
|
_fAcceptsAll = FALSE;
|
|
_fProbablyGatewayRequest = FALSE;
|
|
_fNPHScript = FALSE;
|
|
_GatewayType = GATEWAY_UNKNOWN;
|
|
_dwRootMask = 0;
|
|
|
|
//TCP_REQUIRE( _strBase.Copy ( (TCHAR *) NULL ));
|
|
TCP_REQUIRE( _strGatewayImage.Copy ( (TCHAR *) NULL ));
|
|
TCP_REQUIRE( _strContentType.Copy ( (TCHAR *) NULL ));
|
|
TCP_REQUIRE( _strAcceptLang.Copy ( (TCHAR *) NULL ));
|
|
TCP_REQUIRE( _strReturnMimeType.Copy( (TCHAR *) NULL ));
|
|
|
|
if ( QueryFileHandle() != INVALID_HANDLE_VALUE )
|
|
{
|
|
TCP_REQUIRE(::TsCloseHandle( g_pTsvcInfo->GetTsvcCache(),
|
|
_pGetFile ));
|
|
_pGetFile = NULL;
|
|
}
|
|
|
|
// probably we should decr ref counts for handle
|
|
m_hVrootImpersonation = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HTTP_REQUEST::DoWork
|
|
|
|
SYNOPSIS: Calls the appropriate work item based on our state
|
|
|
|
ENTRY: pfFinished - Gets set to TRUE if the client request has
|
|
been completed
|
|
|
|
RETURNS: TRUE if successful, FALSE if an error occurred (call
|
|
GetLastError())
|
|
|
|
NOTES: If a failure occurs because of bad info from the client (bad
|
|
URL, syntax error etc) then the code that found the problem
|
|
should call SendErrorResponse( error ), set the state to
|
|
HTR_DONE and return TRUE (when send completes, HTR_DONE will
|
|
cleanup).
|
|
|
|
If an error occurs in the server (out of memory etc) then
|
|
LastError should be set and FALSE should be returned. A server
|
|
error will be sent then the client will be disconnected.
|
|
|
|
HISTORY:
|
|
Johnl 26-Aug-1994 Created
|
|
|
|
********************************************************************/
|
|
|
|
BOOL HTTP_REQUEST::DoWork(
|
|
BOOL * pfFinished
|
|
)
|
|
{
|
|
BOOL fRet;
|
|
BOOL fHandled;
|
|
BOOL fDone;
|
|
BOOL fCompleteRequest;
|
|
DWORD dwOffset;
|
|
DWORD dwSizeToSend;
|
|
BOOL fEntireFile;
|
|
BOOL fIsNxRange;
|
|
BOOL fIsLastRange;
|
|
|
|
IF_DEBUG( CONNECTION )
|
|
{
|
|
TCP_PRINT(( DBG_CONTEXT,
|
|
"[http_request::DoWork] Object %lx. State = %d, Bytes Written = %d\n",
|
|
QueryClientConn(),
|
|
QueryState(),
|
|
QueryBytesWritten() ));
|
|
|
|
}
|
|
|
|
switch ( QueryState() )
|
|
{
|
|
|
|
case HTR_GATEWAY_ASYNC_IO:
|
|
|
|
fRet = ProcessAsyncGatewayIO();
|
|
break;
|
|
|
|
|
|
case HTR_READING_CLIENT_REQUEST:
|
|
|
|
_cbBytesReceived += QueryBytesWritten();
|
|
|
|
fRet = OnFillClientReq( &fCompleteRequest,
|
|
pfFinished );
|
|
|
|
if ( !fRet )
|
|
break;
|
|
|
|
if ( *pfFinished )
|
|
break;
|
|
|
|
if ( fCompleteRequest && QueryState() == HTR_DOVERB )
|
|
{
|
|
goto ProcessClientRequest;
|
|
}
|
|
break;
|
|
|
|
case HTR_CERT_RENEGOTIATE:
|
|
_cbBytesReceived += QueryBytesWritten();
|
|
|
|
fRet = HandleCertRenegotiation( pfFinished,
|
|
QueryBytesWritten() );
|
|
|
|
if ( !fRet || *pfFinished || QueryState() != HTR_DOVERB )
|
|
{
|
|
break;
|
|
}
|
|
goto ProcessClientRequest;
|
|
|
|
case HTR_READING_GATEWAY_DATA:
|
|
|
|
_cbBytesReceived += QueryBytesWritten();
|
|
|
|
fRet = ReadGatewayData( &fDone );
|
|
|
|
if ( fRet && fDone )
|
|
goto ProcessClientRequest;
|
|
|
|
break;
|
|
|
|
case HTR_DOVERB:
|
|
|
|
ProcessClientRequest:
|
|
|
|
//
|
|
// Check to see if encryption is required before we do any processing
|
|
//
|
|
|
|
if ( (_dwRootMask & VROOT_MASK_SSL) && !IsSecurePort() )
|
|
{
|
|
SetState( HTR_DONE, HT_FORBIDDEN, ERROR_ACCESS_DENIED );
|
|
|
|
Disconnect( HT_FORBIDDEN, IDS_SSL_REQUIRED );
|
|
fRet = TRUE;
|
|
break;
|
|
}
|
|
|
|
if ( IsProbablyGatewayRequest() )
|
|
{
|
|
if ( !(fRet = ProcessGateway( &fHandled, pfFinished )))
|
|
break;
|
|
|
|
if ( fHandled || *pfFinished )
|
|
break;
|
|
|
|
//
|
|
// Ooops, doesn't appear to be a gateway afterall. Restore
|
|
// the original URL and do the verb like we normally would
|
|
//
|
|
|
|
if ( !(fRet = RestoreURL()) )
|
|
break;
|
|
}
|
|
|
|
fRet = (this->*_pmfnVerb)();
|
|
break;
|
|
|
|
case HTR_CGI:
|
|
fRet = TRUE;
|
|
break;
|
|
|
|
|
|
case HTR_RANGE:
|
|
dwOffset = _dwRgNxOffset;
|
|
dwSizeToSend = _dwRgNxSizeToSend;
|
|
fIsNxRange = ScanRange( &_dwRgNxOffset, &_dwRgNxSizeToSend, &fEntireFile, &fIsLastRange );
|
|
fRet = SendRange( 0, dwOffset, dwSizeToSend, !fIsNxRange );
|
|
break;
|
|
|
|
case HTR_DONE:
|
|
fRet = TRUE;
|
|
*pfFinished = TRUE;
|
|
|
|
//
|
|
// Don't keep system resources open across uses of this object
|
|
//
|
|
|
|
if ( QueryFileHandle() != INVALID_HANDLE_VALUE )
|
|
{
|
|
TCP_REQUIRE(::TsCloseHandle( g_pTsvcInfo->GetTsvcCache(),
|
|
_pGetFile ));
|
|
_pGetFile = NULL;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
TCP_ASSERT( FALSE );
|
|
fRet = FALSE;
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
break;
|
|
}
|
|
|
|
IF_DEBUG( CONNECTION )
|
|
{
|
|
TCP_PRINT(( DBG_CONTEXT,
|
|
"[http_request::DoWork] Leaving, Object %lx. State = %d\n",
|
|
QueryClientConn(),
|
|
QueryState() ));
|
|
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HTTP_REQUEST::Parse
|
|
|
|
SYNOPSIS: Gathers all of the interesting information in the client
|
|
request
|
|
|
|
ENTRY: pchRequest - raw Latin-1 request received from the
|
|
client, zero terminated
|
|
pfFinished - Set to TRUE if no further processing is needed
|
|
|
|
RETURNS: APIERR if an error occurred parsing the header
|
|
|
|
HISTORY:
|
|
Johnl 24-Aug-1994 Created
|
|
|
|
********************************************************************/
|
|
|
|
BOOL
|
|
HTTP_REQUEST::Parse(
|
|
const TCHAR * pchRequest,
|
|
BOOL * pfFinished
|
|
)
|
|
{
|
|
PMFN_ONGRAMMAR pmfn;
|
|
CHAR * pchHeaders;
|
|
CHAR * pch;
|
|
CHAR * pszHeader;
|
|
CHAR * pszValue;
|
|
BOOL fRet;
|
|
int cReq;
|
|
int cHead;
|
|
LPSTR pszArg2;
|
|
LPSTR pszArg3;
|
|
LPSTR pszName;
|
|
LPSTR pszNext;
|
|
int iField;
|
|
int ch;
|
|
CHAR achField[MAX_HEADER_LENGTH];
|
|
|
|
//
|
|
// Put all of the values into the header list
|
|
//
|
|
|
|
while ( isspace( *(char*)pchRequest ) )
|
|
{
|
|
++pchRequest;
|
|
}
|
|
|
|
cHead = strlen( (char*)pchRequest );
|
|
pch = (char*)memchr( (char*)pchRequest, '\n', cHead );
|
|
|
|
if ( pch == NULL )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
cReq = pch - (char*)pchRequest;
|
|
|
|
// we are only considering spaces as separator,
|
|
// not any space. this may be a problem with some clients
|
|
|
|
if ( pszArg2 = (LPSTR)memchr( (char*)pchRequest, ' ', cReq ) )
|
|
{
|
|
*pszArg2++ = '\0';
|
|
while ( _HTTP_IS_LINEAR_SPACE( *pszArg2 ) )
|
|
++pszArg2;
|
|
cReq = pch - pszArg2;
|
|
|
|
_HeaderList.GetFastMap()->Store( HM_MET, (char*)pchRequest );
|
|
if ( pszArg3 = (LPSTR)memchr( pszArg2, ' ', cReq ) )
|
|
{
|
|
*pszArg3++ = '\0';
|
|
while ( _HTTP_IS_LINEAR_SPACE( *pszArg3 ) )
|
|
++pszArg3;
|
|
|
|
_HeaderList.GetFastMap()->Store( HM_URL, pszArg2 );
|
|
|
|
// Note that we are not removing spaces between the version
|
|
// and the line delimiter
|
|
|
|
if ( pch > pszArg3 && pch[-1] == '\r' )
|
|
{
|
|
pch[-1] = '\0';
|
|
_HeaderList.GetFastMap()->Store( HM_VER, pszArg3 );
|
|
}
|
|
else
|
|
{
|
|
pch[0] = '\0';
|
|
_HeaderList.GetFastMap()->Store( HM_VER, pszArg3 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// only 2 parameters
|
|
|
|
if ( pch > pszArg2 && pch[-1] == '\r' )
|
|
{
|
|
pch[-1] = '\0';
|
|
}
|
|
else
|
|
{
|
|
pch[0] = '\0';
|
|
}
|
|
|
|
_HeaderList.GetFastMap()->Store( HM_URL, pszArg2 );
|
|
_HeaderList.GetFastMap()->Store( HM_VER, "" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// handle case where fields are not separated with space
|
|
|
|
*pch = '\0';
|
|
for ( pszArg2 = (LPSTR)pchRequest ;
|
|
*pszArg2 && !_HTTP_IS_LINEAR_SPACE(*pszArg2) ; )
|
|
{
|
|
++pszArg2;
|
|
}
|
|
ch = *pszArg2;
|
|
*pszArg2 = '\0';
|
|
_HeaderList.GetFastMap()->Store( HM_MET, (char*)pchRequest );
|
|
|
|
// check if more than one field
|
|
|
|
if ( ch )
|
|
{
|
|
for ( ++pszArg2;
|
|
*pszArg2 && _HTTP_IS_LINEAR_SPACE( *pszArg2 ) ; )
|
|
{
|
|
++pszArg2;
|
|
}
|
|
for ( pszArg3 = pszArg2;
|
|
*pszArg3 && !isspace(*pszArg3) ; )
|
|
{
|
|
++pszArg3;
|
|
}
|
|
ch = *pszArg3;
|
|
*pszArg3 = '\0';
|
|
_HeaderList.GetFastMap()->Store( HM_URL, (char*)pszArg2 );
|
|
|
|
// check if more than 2 fields
|
|
|
|
if ( ch )
|
|
{
|
|
for ( ++pszArg3;
|
|
*pszArg3 && _HTTP_IS_LINEAR_SPACE( *pszArg3 ) ; )
|
|
{
|
|
++pszArg3;
|
|
}
|
|
for ( pszNext = pszArg3;
|
|
*pszNext && !isspace(*pszNext) ; )
|
|
{
|
|
++pszNext;
|
|
}
|
|
*pszNext = '\0';
|
|
}
|
|
_HeaderList.GetFastMap()->Store( HM_VER, (char*)pszArg3 );
|
|
}
|
|
else
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
pszHeader = pch+1;
|
|
|
|
//
|
|
// Now scan for all headers
|
|
//
|
|
|
|
cReq = (char*)pchRequest + cHead - pszHeader;
|
|
pch = pszHeader + cReq;
|
|
|
|
while ( pszNext = (LPSTR)memchr( pszHeader, '\n', cReq ) )
|
|
{
|
|
if ( pszValue = (LPSTR)memchr( pszHeader,
|
|
':',
|
|
pszNext - pszHeader ) )
|
|
{
|
|
UINT chN = *(PBYTE)++pszValue;
|
|
*pszValue = '\0';
|
|
int cName = pszValue - pszHeader;
|
|
if ( _HTTP_IS_LINEAR_SPACE( chN ) )
|
|
{
|
|
while ( _HTTP_IS_LINEAR_SPACE( *(PBYTE)++pszValue ) )
|
|
;
|
|
}
|
|
|
|
IF_DEBUG( PARSING )
|
|
{
|
|
TCP_PRINT((DBG_CONTEXT,
|
|
"\t%s = %s\n",
|
|
pszHeader,
|
|
pszValue ));
|
|
}
|
|
|
|
if ( pszNext > pszValue && pszNext[-1] == '\r' )
|
|
{
|
|
pszNext[-1] = '\0';
|
|
}
|
|
else
|
|
{
|
|
pszNext[0] = '\0';
|
|
}
|
|
|
|
if ( FastFindField( pszHeader, cName, &iField ) )
|
|
{
|
|
pszHeader[ cName ] = (CHAR)chN;
|
|
_HeaderList.GetFastMap()->CheckConcatAndStore(
|
|
(HM_ID)iField, pszValue );
|
|
}
|
|
else
|
|
{
|
|
if ( _HTTP_IS_LINEAR_SPACE( chN ) )
|
|
{
|
|
_HeaderList.AddEntryUsingConcat( pszHeader, pszValue );
|
|
}
|
|
else
|
|
{
|
|
// copy name to temp string
|
|
if ( cName < sizeof(achField) )
|
|
{
|
|
memcpy( achField, pszHeader, cName + 1 );
|
|
pszHeader[ cName ] = (CHAR)chN;
|
|
_HeaderList.AddEntryUsingConcat( achField, pszValue );
|
|
}
|
|
else
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( *pszHeader == '\r' || *pszHeader == '\n' )
|
|
{
|
|
pszHeader = pszNext + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pszHeader = pszNext + 1;
|
|
cReq = pch - pszHeader;
|
|
}
|
|
|
|
if ( !g_fAllowKeepAlives )
|
|
{
|
|
_HeaderList.GetFastMap()->Cancel( HM_CON );
|
|
}
|
|
|
|
if ( fAnyFilters )
|
|
{
|
|
//
|
|
// Notify any filters interested in the request and headers before
|
|
// we do any processing
|
|
//
|
|
|
|
if ( !_Filter.NotifyPreProcHeaderFilters( &_Filter,
|
|
&_HeaderList,
|
|
pfFinished ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( *pfFinished )
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Now scan for any RFC822 field names that we recognize
|
|
//
|
|
|
|
for ( int x = 0, mx = _HeaderList.GetFastMap()->MaxIndex() ;
|
|
x < mx ;
|
|
++x )
|
|
{
|
|
LPSTR pszV;
|
|
if ( pszV = _HeaderList.GetFastMap()->QueryValue( (HM_ID)x ) )
|
|
{
|
|
if ( ! (this->*OnFieldName[x].pmfnOnField )( pszV ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
SetState( HTR_DOVERB );
|
|
|
|
return ProcessURL( pfFinished );
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HTTP_REQUEST::OnVerb
|
|
|
|
SYNOPSIS: Parses the verb from an HTTP request
|
|
|
|
ENTRY: pszValue - Pointer to zero terminated string
|
|
|
|
|
|
RETURNS: TRUE if successful, FALSE if an error occurred
|
|
|
|
HISTORY:
|
|
Johnl 24-Aug-1994 Created
|
|
|
|
********************************************************************/
|
|
|
|
BOOL HTTP_REQUEST::OnVerb( CHAR * pszValue )
|
|
{
|
|
UINT i = 0;
|
|
|
|
if ( !_strMethod.Copy( pszValue ) )
|
|
return FALSE;
|
|
|
|
//
|
|
// Look for the verbs we recognize
|
|
//
|
|
|
|
while ( DoVerb[i].pchVerb )
|
|
{
|
|
if ( ::_tcsncmp( pszValue,
|
|
DoVerb[i].pchVerb,
|
|
DoVerb[i].cchVerb ) == 0)
|
|
{
|
|
_verb = DoVerb[i].httpVerb;
|
|
|
|
switch ( _verb )
|
|
{
|
|
case HTV_GET:
|
|
INCREMENT_COUNTER( TotalGets );
|
|
break;
|
|
|
|
case HTV_HEAD:
|
|
INCREMENT_COUNTER( TotalHeads );
|
|
break;
|
|
|
|
default:
|
|
TCP_ASSERT( FALSE );
|
|
break;
|
|
}
|
|
|
|
_pmfnVerb = DoVerb[i].pmfnVerb;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
//
|
|
// The verb may be a verb a gateway knows how to deal with so
|
|
// all hope isn't lost
|
|
//
|
|
|
|
if ( !strcmp( pszValue, "POST" ))
|
|
{
|
|
INCREMENT_COUNTER( TotalPosts );
|
|
}
|
|
else
|
|
{
|
|
INCREMENT_COUNTER( TotalOthers );
|
|
}
|
|
|
|
_pmfnVerb = &HTTP_REQUEST::DoUnknown;
|
|
_verb = HTV_UNKNOWN;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HTTP_REQUEST::OnURL
|
|
|
|
SYNOPSIS: Parses the URL from an HTTP request
|
|
|
|
ENTRY: pszValue - URL on http request line
|
|
|
|
|
|
RETURNS: TRUE if successful, FALSE if an error occurred
|
|
|
|
NOTES: The URL and Path info are unescaped in this method.
|
|
Parameters coming after the "?" are *not* unescaped as they
|
|
contain encoded '&' or other meaningful items.
|
|
|
|
HISTORY:
|
|
Johnl 24-Aug-1994 Created
|
|
|
|
********************************************************************/
|
|
|
|
BOOL HTTP_REQUEST::OnURL( CHAR * pszValue )
|
|
{
|
|
TCHAR * pchParams;
|
|
TCHAR * pchStart;
|
|
TCHAR * pch;
|
|
TCHAR chParams;
|
|
BOOL fValid;
|
|
LPSTR pszSlash;
|
|
LPSTR pszURL;
|
|
|
|
|
|
if ( *pszValue != '/' )
|
|
{
|
|
//
|
|
// assume HTTP URL, skip protocol & host name by
|
|
// searching for 1st '/' following "//"
|
|
//
|
|
// We handle this information as a "Host:" header.
|
|
// It will be overwritten by the real header if it is
|
|
// present.
|
|
//
|
|
// We do not check for a match in this case.
|
|
//
|
|
|
|
if ( (pszSlash = strchr( pszValue, '/' )) && pszSlash[1] == '/' )
|
|
{
|
|
pszSlash += 2;
|
|
if ( pszURL = strchr( pszSlash, '/' ) )
|
|
{
|
|
//
|
|
// prepare for call to OnHost() by delimiting Host name
|
|
//
|
|
|
|
*pszURL = '\0';
|
|
|
|
if ( !OnHost( pszSlash ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*pszURL = '/';
|
|
|
|
//
|
|
// update pointer to URL to point to the 1st slash
|
|
// following host name
|
|
//
|
|
|
|
pszValue = pszURL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// if no single slash following host name
|
|
// consider the URL to be empty.
|
|
//
|
|
|
|
pszValue = pszSlash + strlen( pszSlash );
|
|
}
|
|
}
|
|
|
|
//
|
|
// if no double slash, this is not a fully qualified URL
|
|
// and we leave it alone.
|
|
//
|
|
}
|
|
|
|
if ( !_strRawURL.Copy( pszValue ) )
|
|
return FALSE;
|
|
|
|
pchStart = pszValue;
|
|
|
|
//
|
|
// Check for a question mark which indicates this URL contains some
|
|
// parameters and break the two apart if found
|
|
//
|
|
|
|
if ( pchParams = ::_tcschr( pchStart, TEXT('?') ) )
|
|
{
|
|
chParams = *pchParams;
|
|
*pchParams = TEXT('\0');
|
|
|
|
_fAnyParams = TRUE;
|
|
|
|
if ( !_strURL.Copy( pchStart ) ||
|
|
!_strURL.Unescape() ||
|
|
!_strURLParams.Copy( pchParams + 1 ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_fAnyParams = FALSE;
|
|
|
|
if ( !_strURL.Copy( _strRawURL ) ||
|
|
!_strURL.Unescape() )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
TCP_REQUIRE( _strURLParams.Copy( (TCHAR *) NULL ));
|
|
}
|
|
|
|
//
|
|
// Canonicalize the URL and make sure it's valid
|
|
//
|
|
|
|
if ( !CanonURL( &_strURL, &fValid ) )
|
|
return FALSE;
|
|
|
|
if ( !fValid )
|
|
{
|
|
IF_DEBUG( PARSING )
|
|
{
|
|
TCP_PRINT((DBG_CONTEXT,
|
|
"[OnURL] CanonURL reported invalid URL\n"));
|
|
}
|
|
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( pchParams )
|
|
*pchParams = chParams;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
HTTP_REQUEST::ProcessURL(
|
|
BOOL * pfFinished
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Finally converts the URL to a physical path, checking for extension mappings
|
|
|
|
Arguments:
|
|
|
|
pfHandled - Set to TRUE if no further processing is needed.
|
|
|
|
Returns:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
TCHAR * pch;
|
|
TCHAR chParams;
|
|
BOOL fValid;
|
|
BOOL fImageInURL;
|
|
|
|
//
|
|
// Lookup the URL to get its access mask
|
|
//
|
|
|
|
if ( !LookupVirtualRoot( &_strPhysicalPath,
|
|
_strURL.QueryStr(),
|
|
NULL,
|
|
NULL,
|
|
TRUE,
|
|
&_dwRootMask,
|
|
pfFinished ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( *pfFinished )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If the read bit is set, the execute bit is not set, we recognize
|
|
// the verb and there are no parameters, then don't bother looking
|
|
// at the execute mask
|
|
//
|
|
|
|
if ( (_dwRootMask & VROOT_MASK_READ) &&
|
|
!(_dwRootMask & VROOT_MASK_EXECUTE) &&
|
|
_verb != HTV_UNKNOWN &&
|
|
!_fAnyParams )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If this is on a virtual root with execute permissions ||
|
|
// this might be an ismap request
|
|
// {
|
|
// Check for a possible .exe, .com, or .dll gateway (CGI or BGI)
|
|
// }
|
|
//
|
|
|
|
if ( _dwRootMask & VROOT_MASK_EXECUTE || _fAnyParams )
|
|
{
|
|
TCHAR * pchStart = _strURL.QueryStr();
|
|
TCHAR * pchtmp = pchStart;
|
|
TCHAR * pchSlash = NULL;
|
|
INT cchToEnd;
|
|
DWORD cchExt;
|
|
|
|
while ( *pchtmp )
|
|
{
|
|
pchtmp = strchr( pchtmp + 1, '.' );
|
|
|
|
if ( !pchtmp )
|
|
break;
|
|
|
|
//
|
|
// Is this file extension mapped to a script? _GatewayType is
|
|
// set to unknown if a mapping isn't found
|
|
//
|
|
|
|
if ( !LookupExtMap( pchtmp,
|
|
&_strGatewayImage,
|
|
&_GatewayType,
|
|
&cchExt,
|
|
&fImageInURL ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( _GatewayType != GATEWAY_UNKNOWN )
|
|
{
|
|
if ( _GatewayType == GATEWAY_NONE )
|
|
{
|
|
_fAnyParams = FALSE;
|
|
return TRUE;
|
|
}
|
|
//
|
|
// If this is a regular CGI script, check for an "nph-"
|
|
//
|
|
|
|
if ( _GatewayType == GATEWAY_CGI )
|
|
{
|
|
//
|
|
// Walk backwards till we find the '/' that begins
|
|
// this segment
|
|
//
|
|
|
|
pch = pchtmp;
|
|
|
|
while ( pch >= pchStart && *pch != '/' )
|
|
{
|
|
pch--;
|
|
}
|
|
|
|
if ( !_strnicmp( (*pch == '/' ? pch+1 : pch),
|
|
"nph-",
|
|
4 ))
|
|
{
|
|
_fNPHScript = TRUE;
|
|
}
|
|
}
|
|
|
|
cchToEnd = ((pchtmp+cchExt) - pchStart);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( _GatewayType == GATEWAY_CGI || _GatewayType == GATEWAY_BGI )
|
|
{
|
|
//
|
|
// Save the path info and remove it from the URL. If this is a
|
|
// script by association and there isn't any path info, then
|
|
// copy the base URL as the path info (reflects what the URL
|
|
// would like without an association (i.e., "/foo/bar.idc?a=b"
|
|
// is really "/scripts/httpodbc.dll/foo/bar.idc?a=b"))
|
|
//
|
|
// If the binary image is actually in the URL, then always copy
|
|
// the path info.
|
|
//
|
|
|
|
if ( !_strPathInfo.Copy( ( fImageInURL || *(pchStart + cchToEnd) ?
|
|
pchStart + cchToEnd :
|
|
pchStart )))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*(_strURL.QueryStr() + cchToEnd) = TEXT('\0');
|
|
|
|
_fProbablyGatewayRequest = TRUE;
|
|
|
|
IF_DEBUG( PARSING )
|
|
{
|
|
TCP_PRINT((DBG_CONTEXT,
|
|
"[OnURL] Possible script \"%s\" with path info \"%s\", parms \"%s\"\n",
|
|
_strURL.QueryStr(),
|
|
_strPathInfo.QueryStr(),
|
|
_strURLParams.QueryStr()));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Is this is an ISINDEX request (i.e., get /foo/bar.htm?find+these+words)
|
|
//
|
|
|
|
if ( _GatewayType != GATEWAY_MAP &&
|
|
!_fProbablyGatewayRequest &&
|
|
fCheckForWAISDB &&
|
|
QueryVerb() == HTV_GET &&
|
|
*_strURLParams.QueryStr())
|
|
{
|
|
//
|
|
// If this is a GET, there are Query params in the URL and there
|
|
// is a WAIS database with the same name, then we will try to
|
|
// spawn the WAIS lookup program
|
|
//
|
|
|
|
_fProbablyGatewayRequest = TRUE;
|
|
_GatewayType = GATEWAY_WAIS;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HTTP_REQUEST::OnVersion
|
|
|
|
SYNOPSIS: Parses the version from an HTTP request
|
|
|
|
ENTRY: pszValue - Pointer to zero terminated string
|
|
|
|
|
|
RETURNS: TRUE if successful, FALSE if an error occurred
|
|
|
|
HISTORY:
|
|
Johnl 24-Aug-1994 Created
|
|
|
|
********************************************************************/
|
|
|
|
BOOL HTTP_REQUEST::OnVersion( CHAR * pszValue )
|
|
{
|
|
//
|
|
// Did the client specify a version string? If not, assume 0.9
|
|
//
|
|
|
|
if ( ::_tcsncmp( TEXT("HTTP/"),
|
|
pszValue,
|
|
5 ) == 0 )
|
|
{
|
|
//
|
|
// Move past "HTTP/"
|
|
//
|
|
|
|
pszValue += 5;
|
|
|
|
_VersionMajor = (BYTE) atoi( pszValue );
|
|
|
|
pszValue = strchr( pszValue, '.' );
|
|
|
|
if ( pszValue )
|
|
{
|
|
pszValue++;
|
|
_VersionMinor = (BYTE) atoi( pszValue );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_VersionMajor = 0;
|
|
_VersionMinor = 9;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HTTP_REQUEST::FindField
|
|
|
|
SYNOPSIS: Parses the field name and retrieve the index
|
|
in OnFieldName
|
|
|
|
ENTRY: pszHeader - Pointer to header to find
|
|
pif - updated with index in OnFieldName if found
|
|
|
|
RETURNS: TRUE if name found
|
|
|
|
HISTORY:
|
|
Johnl 24-Aug-1994 Created
|
|
|
|
********************************************************************/
|
|
|
|
BOOL HTTP_REQUEST::FindField( CHAR * pszHeader, int *piF )
|
|
{
|
|
UINT i = 0;
|
|
|
|
//
|
|
// Search for the fields we recognize
|
|
//
|
|
|
|
while ( OnFieldName[i].pchFieldName )
|
|
{
|
|
//
|
|
// NOTE: The HTTP spec indicates we are supposed to do case
|
|
// sensitive compares, however some apps (Mosaic, Netscape)
|
|
// pass the wrong case (Content-length as opposed to Content-Length)
|
|
// so do a case insensitive compare on everything but the first
|
|
// letter
|
|
//
|
|
|
|
if ( *pszHeader == *OnFieldName[i].pchFieldName &&
|
|
!::_tcsicmp( pszHeader + 1,
|
|
OnFieldName[i].pchFieldName + 1 ))
|
|
|
|
{
|
|
*piF = i;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HTTP_REQUEST::OnAccept
|
|
|
|
SYNOPSIS: Adds the MIME type to our accept list
|
|
|
|
ENTRY: CHAR * pszValue
|
|
|
|
|
|
RETURNS: TRUE if successful, FALSE otherwise
|
|
|
|
NOTES: Acce pt fields can look like:
|
|
|
|
Accept: text/html
|
|
Accept: image/gif; audio/wav
|
|
Accept: image/jpeg, q=.8, mxb=10000, mxt=5.0; image/gif
|
|
|
|
q - Quality (between zero and one)
|
|
mxb - Maximum bytes acceptable
|
|
mxs - Maximum seconds acceptable
|
|
|
|
We currently ignore the parameters
|
|
|
|
HISTORY:
|
|
Johnl 21-Sep-1994 Created
|
|
|
|
********************************************************************/
|
|
|
|
BOOL HTTP_REQUEST::OnAccept( CHAR * pszValue )
|
|
{
|
|
//
|
|
// Keep an eye out for "*/*". If it's sent then we
|
|
// don't have to search the list for acceptable client
|
|
// types later on. Note it won't catch the case if the "*" occurs
|
|
// after the first item in the list.
|
|
//
|
|
|
|
if ( *pszValue == '*'
|
|
|| strstr( pszValue, TEXT("*/*") ) )
|
|
{
|
|
_fAcceptsAll = TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HTTP_REQUEST::DoesClientAccept
|
|
|
|
SYNOPSIS: Searches the client accept list for the specified
|
|
MIME type
|
|
|
|
ENTRY: str - MIME type to search for
|
|
|
|
RETURNS: TRUE if found, FALSE if not found
|
|
|
|
HISTORY:
|
|
Johnl 22-Sep-1994 Created
|
|
|
|
********************************************************************/
|
|
|
|
BOOL HTTP_REQUEST::DoesClientAccept( PCSTR pstr )
|
|
{
|
|
TCHAR * pchSlash;
|
|
TCHAR * pchType;
|
|
INT cchToSlash;
|
|
|
|
//
|
|
// If the client indicated "*/*" in their accept list, then
|
|
// we don't need to check
|
|
//
|
|
|
|
if ( IsAcceptAllSet() )
|
|
return TRUE;
|
|
|
|
LPSTR pszAcc = _HeaderList.GetFastMap()->QueryStrValue(HM_ACC);
|
|
INET_PARSER Parser( pszAcc );
|
|
|
|
//
|
|
// If no accept headers were passed, then assume client
|
|
// accepts "text/plain" and "text/html"
|
|
//
|
|
|
|
if ( *pszAcc == '\0' )
|
|
{
|
|
return !::_tcsicmp( pstr,
|
|
TEXT("text/html")) ||
|
|
!::_tcsicmp( pstr,
|
|
TEXT("text/plain"));
|
|
}
|
|
|
|
//
|
|
// Find out where the slash is so we can do a prefix compare
|
|
//
|
|
|
|
pchSlash = _tcschr( pstr, TEXT('/') );
|
|
|
|
if ( !pchSlash )
|
|
{
|
|
TCP_PRINT((DBG_CONTEXT,
|
|
"[DoesClientAccept] Bad accept type - \"%s\"",
|
|
pstr ));
|
|
return FALSE;
|
|
}
|
|
|
|
cchToSlash = pchSlash - pstr;
|
|
|
|
//
|
|
// Scan through the list for entries that match up to the slash
|
|
//
|
|
|
|
Parser.SetListMode( TRUE );
|
|
|
|
pchType = Parser.QueryToken();
|
|
|
|
while ( *pchType )
|
|
{
|
|
if ( !::_tcscmp( TEXT("*/*"), pchType ) ||
|
|
!::_tcscmp( TEXT("*"), pchType ))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if ( !_tcsnicmp( pstr,
|
|
pchType,
|
|
cchToSlash ))
|
|
{
|
|
//
|
|
// We matched to the slash. Is the second part a '*'
|
|
// or a real match?
|
|
//
|
|
|
|
if ( *(pchType + cchToSlash + 1) == TEXT('*') ||
|
|
!_tcsicmp( pstr + cchToSlash + 1,
|
|
pchType + cchToSlash + 1 ))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
pchType = Parser.NextItem();
|
|
}
|
|
|
|
IF_DEBUG( PARSING )
|
|
{
|
|
TCP_PRINT((DBG_CONTEXT,
|
|
"[DoesClientAccept] Client doesn't accept %s\n",
|
|
pstr ));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HTTP_REQUEST::OnContentType
|
|
|
|
SYNOPSIS: Saves the content type
|
|
|
|
ENTRY: pszValue - Pointer to zero terminated string
|
|
|
|
RETURNS: TRUE if successful, FALSE on error
|
|
|
|
NOTES: Client's will generally specify this only for gateway data
|
|
|
|
HISTORY:
|
|
Johnl 10-Oct-1994 Created
|
|
|
|
********************************************************************/
|
|
|
|
BOOL HTTP_REQUEST::OnContentType( CHAR * pszValue )
|
|
{
|
|
return _strContentType.Copy( pszValue );
|
|
}
|
|
|
|
BOOL
|
|
HTTP_REQUEST::OnConnection(
|
|
CHAR * pszValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks to see if this connection is a keep-alive connection
|
|
|
|
Arguments:
|
|
|
|
pszValue - Pointer to zero terminated string
|
|
|
|
--*/
|
|
{
|
|
INET_PARSER Parser( pszValue );
|
|
|
|
Parser.SetListMode( TRUE );
|
|
|
|
while ( *Parser.QueryToken() )
|
|
{
|
|
if ( !_stricmp( "Keep-Alive", Parser.QueryToken() ))
|
|
{
|
|
SetKeepConn( TRUE );
|
|
return TRUE;
|
|
}
|
|
|
|
Parser.NextItem();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
HTTP_REQUEST::OnAcceptLanguage(
|
|
CHAR * pszValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Snags the value for the Accept-Language header
|
|
|
|
Arguments:
|
|
|
|
pszValue - Pointer to zero terminated string
|
|
|
|
--*/
|
|
{
|
|
return _strAcceptLang.Copy( pszValue );
|
|
}
|
|
|
|
//
|
|
// Verb worker methods
|
|
//
|
|
|
|
BOOL HTTP_REQUEST::DoUnknown ( VOID )
|
|
{
|
|
TCP_PRINT((DBG_CONTEXT,
|
|
"OnDoUnknown - Unknown method - %s\n",
|
|
_strMethod.QueryStr()));
|
|
|
|
SetState( HTR_DONE, HT_NOT_SUPPORTED, ERROR_NOT_SUPPORTED );
|
|
Disconnect( HT_NOT_SUPPORTED );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
HTTP_REQUEST::LookupVirtualRoot(
|
|
OUT STR * pstrPath,
|
|
IN const CHAR * pszURL,
|
|
OUT DWORD * pcchDirRoot,
|
|
OUT DWORD * pcchVRoot,
|
|
IN BOOL fUpdateURL,
|
|
OUT DWORD * pdwMask,
|
|
OUT BOOL * pfFinished
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks up the virtual root to find the physical drive mapping. If an
|
|
Accept-Language header was sent by the client, we look for a virtual
|
|
root prefixed by the language tag
|
|
|
|
Arguments:
|
|
|
|
pstrPath - Receives physical drive path
|
|
pszURL - URL to look for
|
|
pcchDirRoot - Number of characters in the found physical path
|
|
pcchVRoot - Number of characters in the found virtual root
|
|
fUpdateURL - Indicates if the logged URL for this request should be
|
|
updated to the language modified found URL
|
|
pdwMask - Access mask for the specified URL
|
|
pfFinished - Set to TRUE if a filter indicated the request should end
|
|
|
|
--*/
|
|
{
|
|
DWORD cbPath;
|
|
BOOL fRet;
|
|
|
|
if ( !pstrPath->Resize( MAX_PATH+sizeof(TCHAR) ))
|
|
return FALSE;
|
|
|
|
if ( _strAcceptLang.IsEmpty() )
|
|
{
|
|
GetDefault:
|
|
//
|
|
// Generally there won't be any language tags
|
|
//
|
|
|
|
cbPath = pstrPath->QuerySize();
|
|
|
|
fRet = TsLookupVirtualRoot( g_pTsvcInfo->GetTsvcCache(),
|
|
pszURL,
|
|
pstrPath->QueryStr(),
|
|
&cbPath,
|
|
pdwMask,
|
|
pcchDirRoot,
|
|
pcchVRoot,
|
|
&m_hVrootImpersonation,
|
|
QueryClientConn()->QueryLocalAddr() );
|
|
}
|
|
else
|
|
{
|
|
INET_PARSER Parser( _strAcceptLang.QueryStr() );
|
|
STR strLangURL;
|
|
CHAR * pchLang;
|
|
DWORD cchVRoot;
|
|
DWORD cchLang;
|
|
|
|
Parser.SetListMode( TRUE );
|
|
|
|
if ( !pcchVRoot )
|
|
pcchVRoot = &cchVRoot;
|
|
|
|
//
|
|
// The client browser has expressed a language preference. Prefix
|
|
// the language tag to the URL and see if we get a match
|
|
//
|
|
|
|
while ( *(pchLang = Parser.QueryToken()) )
|
|
{
|
|
//
|
|
// First try with just the two byte country code
|
|
//
|
|
|
|
if ( !strLangURL.Copy( "/" ) ||
|
|
!strLangURL.Append( pchLang ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
cchLang = strLangURL.QueryCCH();
|
|
|
|
//
|
|
// Only take the first two characters of the language tag,
|
|
// we do not use the country/locale code currently
|
|
//
|
|
|
|
if ( cchLang > 3 )
|
|
{
|
|
strLangURL.QueryStr()[3] = '\0';
|
|
cchLang = 3;
|
|
}
|
|
|
|
if ( !strLangURL.Append( pszURL ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
cbPath = pstrPath->QuerySize();
|
|
|
|
fRet = TsLookupVirtualRoot( g_pTsvcInfo->GetTsvcCache(),
|
|
strLangURL.QueryStr(),
|
|
pstrPath->QueryStr(),
|
|
&cbPath,
|
|
pdwMask,
|
|
pcchDirRoot,
|
|
pcchVRoot,
|
|
&m_hVrootImpersonation,
|
|
QueryClientConn()->QueryLocalAddr() );
|
|
|
|
//
|
|
// Ignore errors as it is probably a "Not found" error
|
|
//
|
|
|
|
if ( !fRet )
|
|
{
|
|
//
|
|
// Advance to the next language tag
|
|
//
|
|
|
|
Parser.NextItem();
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Did the root match our language tag? We matched if the
|
|
// matching virtual root contains the language tag. That is,
|
|
//
|
|
// We match "/gr/", "/gr/common/". We do not match "/".
|
|
//
|
|
|
|
if ( *pcchVRoot >= cchLang )
|
|
{
|
|
//
|
|
// Should we update the URL for logging purposes?
|
|
//
|
|
|
|
if ( fUpdateURL )
|
|
{
|
|
fRet = (_strURL.Copy( strLangURL ) &&
|
|
_strLanguage.Copy( pchLang ));
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Advance to the next language tag
|
|
//
|
|
|
|
Parser.NextItem();
|
|
}
|
|
|
|
//
|
|
// If we got here, we didn't find a virtual root that matched, so
|
|
// just return the default
|
|
//
|
|
|
|
goto GetDefault;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if ( fRet && fAnyFilters )
|
|
{
|
|
BOOL fTmp;
|
|
|
|
//
|
|
// If the caller is going to ignore the Finished request flag, supply
|
|
// a value ourselves
|
|
//
|
|
|
|
if ( !pfFinished )
|
|
{
|
|
pfFinished = &fTmp;
|
|
}
|
|
|
|
fRet = HTTP_FILTER::NotifyUrlMap( &_Filter,
|
|
pszURL,
|
|
pstrPath->QueryStr(),
|
|
pstrPath->QuerySize(),
|
|
pfFinished );
|
|
}
|
|
|
|
return fRet;
|
|
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HTTP_REQUEST::ReprocessURL
|
|
|
|
SYNOPSIS: Called when a map file or gateway has redirected us
|
|
to a different URL. An async completion will be posted
|
|
if TRUE is returned.
|
|
|
|
ENTRY: pchURL - URL we've been redirected to
|
|
htverb - New verb to use (or unknown to leave as is)
|
|
|
|
RETURNS: TRUE if successful, FALSE otherwise
|
|
|
|
HISTORY:
|
|
Johnl 04-Oct-1994 Created
|
|
|
|
********************************************************************/
|
|
|
|
BOOL HTTP_REQUEST::ReprocessURL( TCHAR * pchURL,
|
|
enum HTTP_VERB htverb )
|
|
{
|
|
BOOL fFinished = FALSE;
|
|
|
|
//
|
|
// Reset the gateway type
|
|
//
|
|
|
|
_GatewayType = GATEWAY_UNKNOWN;
|
|
_fProbablyGatewayRequest = FALSE;
|
|
|
|
switch ( htverb )
|
|
{
|
|
case HTV_GET:
|
|
_verb = HTV_GET;
|
|
_pmfnVerb = DoGet;
|
|
break;
|
|
|
|
case HTV_HEAD:
|
|
_verb = HTV_HEAD;
|
|
_pmfnVerb = DoHead;
|
|
break;
|
|
|
|
case HTV_UNKNOWN:
|
|
break;
|
|
|
|
default:
|
|
TCP_ASSERT( !"[ReprocessURL] Unknown verb type" );
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
SetState( HTR_DOVERB );
|
|
|
|
if ( !OnURL( pchURL ) ||
|
|
!ProcessURL( &fFinished ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !fFinished )
|
|
{
|
|
if ( !DoWork( &fFinished ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If no further processing is needed, set our state to done and post
|
|
// an async completion. We do this as the caller expects an async
|
|
// completion to clean things up
|
|
//
|
|
|
|
if ( fFinished )
|
|
{
|
|
TCP_ASSERT( QueryLogHttpResponse() != HT_DONT_LOG );
|
|
|
|
SetState( HTR_DONE, QueryLogHttpResponse(), QueryLogWinError() );
|
|
return PostCompletionStatus( 0 );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: HTTP_REQUEST::GetInfo
|
|
|
|
SYNOPSIS: Pulls out various bits of information from this request.
|
|
|
|
ENTRY: pszValName - Value to retrieve
|
|
pstr - Receives information in a string format
|
|
pfFound - Option, Set to TRUE if a value was found, FALSE
|
|
otherwise
|
|
|
|
NOTES:
|
|
|
|
HISTORY:
|
|
Johnl 25-Sep-1994 Created
|
|
|
|
********************************************************************/
|
|
|
|
BOOL
|
|
HTTP_REQUEST::GetInfo(
|
|
const TCHAR * pszValName,
|
|
STR * pstr,
|
|
BOOL * pfFound
|
|
)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
DWORD cb, i = 0;
|
|
CHAR achHeader[MAX_HEADER_LENGTH];
|
|
CHAR * pch;
|
|
|
|
if ( !pszValName )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( pfFound )
|
|
{
|
|
*pfFound = TRUE;
|
|
}
|
|
|
|
//
|
|
// terminate the string
|
|
//
|
|
|
|
TCP_REQUIRE( pstr->Copy( (TCHAR *) NULL ) );
|
|
|
|
switch ( *pszValName) {
|
|
|
|
case 'A':
|
|
|
|
//
|
|
// This is a special server specific value used by the CGI code
|
|
// to retrieve all of the HTTP headers the client sent
|
|
//
|
|
|
|
if ( !strcmp( "ALL_HTTP", pszValName ))
|
|
return BuildCGIHeaderList( pstr, &_HeaderList );
|
|
|
|
if ( !strcmp( "AUTH_TYPE", pszValName ))
|
|
return pstr->Copy( _strAuthType );
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
if ( !strcmp( "CONTENT_LENGTH", pszValName ))
|
|
{
|
|
if ( !pstr->Resize( 40 * sizeof(TCHAR)))
|
|
return FALSE;
|
|
|
|
_ultoa( _cbContentLength,
|
|
pstr->QueryStr(),
|
|
10 );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if ( !strcmp( "CONTENT_TYPE", pszValName ))
|
|
return pstr->Copy( _strContentType );
|
|
|
|
break;
|
|
|
|
|
|
case 'G':
|
|
if ( !strcmp( "GATEWAY_INTERFACE", pszValName ))
|
|
return pstr->Copy( "CGI/1.1" );
|
|
|
|
break;
|
|
|
|
case 'H':
|
|
|
|
//
|
|
// If the value begins with "HTTP_" then it's probably in our field
|
|
// list
|
|
//
|
|
|
|
if ( !_strnicmp( "HTTP_", pszValName, 5 ))
|
|
{
|
|
INT cchVal = strlen( pszValName );
|
|
|
|
if ( cchVal >= sizeof( achHeader ) )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Copy the client specified header name to a temp buffer
|
|
// so we can replace all "_" with "-"s so it will match the
|
|
// header names in
|
|
// our list, we also need to append a colon.
|
|
//
|
|
|
|
strcpy( achHeader, pszValName + 5 );
|
|
pch = achHeader;
|
|
|
|
while ( pch = strchr( achHeader, '_' ))
|
|
*pch = '-';
|
|
|
|
strcat( achHeader, ":" );
|
|
|
|
pch = _HeaderList.FindValue( achHeader );
|
|
|
|
if ( pch )
|
|
{
|
|
return pstr->Copy( pch );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 'L':
|
|
//
|
|
// Note this returns the server IP address since we don't necessarily
|
|
// know the DNS name of this server
|
|
//
|
|
|
|
if ( !strcmp( "LOCAL_ADDR", pszValName ) )
|
|
return pstr->Copy( QueryClientConn()->QueryLocalAddr() );
|
|
|
|
if ( !strcmp( "LOGON_USER", pszValName ))
|
|
return pstr->Copy( _strUserName );
|
|
|
|
break;
|
|
|
|
case 'P':
|
|
|
|
if ( !strcmp( "PATH_INFO", pszValName ))
|
|
return pstr->Copy( _strPathInfo );
|
|
|
|
if ( !strcmp( "PATH_TRANSLATED", pszValName ))
|
|
{
|
|
//
|
|
// Note that _strPathInfo has already been escaped
|
|
//
|
|
|
|
return LookupVirtualRoot( pstr,
|
|
_strPathInfo.QueryStr() );
|
|
}
|
|
|
|
break;
|
|
|
|
case 'Q':
|
|
|
|
if ( !strcmp( "QUERY_STRING", pszValName ))
|
|
return pstr->Copy( _strURLParams );
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
if ( !strcmp( "REQUEST_METHOD", pszValName ))
|
|
return pstr->Copy( _strMethod );
|
|
|
|
//
|
|
// Note that REMOTE_HOST does not do the Reverse lookup
|
|
//
|
|
|
|
|
|
if ( !strcmp( "REMOTE_ADDR", pszValName ) ||
|
|
!strcmp( "REMOTE_HOST", pszValName ) )
|
|
{
|
|
return pstr->Copy( QueryClientConn()->QueryRemoteAddr() );
|
|
}
|
|
|
|
if ( !strcmp( "REMOTE_USER", pszValName ))
|
|
return pstr->Copy( _strUnmappedUserName );
|
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
if ( !strcmp( "SERVER_NAME", pszValName ) )
|
|
return pstr->Copy( QueryHostAddr() );
|
|
|
|
if ( !strcmp( "SCRIPT_NAME", pszValName )) {
|
|
return pstr->Copy( _strURL );
|
|
}
|
|
|
|
if ( !strcmp( "SERVER_PROTOCOL", pszValName ))
|
|
{
|
|
if ( !(fRet = pstr->Resize( 20 * sizeof(TCHAR))) )
|
|
return FALSE;
|
|
|
|
wsprintf( pstr->QueryStr(),
|
|
TEXT("HTTP/%d.%d"),
|
|
_VersionMajor,
|
|
_VersionMinor );
|
|
return TRUE;
|
|
}
|
|
|
|
if ( !strcmp( "SERVER_PORT", pszValName ))
|
|
{
|
|
if ( !(fRet = pstr->Resize( 10 * sizeof(TCHAR))) )
|
|
return FALSE;
|
|
|
|
wsprintf( pstr->QueryStr(),
|
|
TEXT("%d"),
|
|
(INT) QueryClientConn()->QueryPort() );
|
|
return TRUE;
|
|
}
|
|
|
|
if ( !strcmp( "SERVER_PORT_SECURE", pszValName ))
|
|
{
|
|
if ( !(fRet = pstr->Resize( 10 * sizeof(TCHAR))) )
|
|
return FALSE;
|
|
|
|
return pstr->Copy( IsSecurePort() ? "1" : "0" );
|
|
}
|
|
|
|
|
|
//
|
|
// Copy the correct server software based on the platform type
|
|
//
|
|
|
|
if ( !strcmp( "SERVER_SOFTWARE", pszValName )) {
|
|
|
|
switch (W3PlatformType) {
|
|
case PtNtWorkstation:
|
|
return pstr->Copy( TEXT(MSW3_VERSION_STR_NTW) );
|
|
case PtWindows95:
|
|
case PtWindows9x:
|
|
return pstr->Copy( TEXT(MSW3_VERSION_STR_W95) );
|
|
default:
|
|
ASSERT(W3PlatformType == PtNtServer);
|
|
return pstr->Copy( TEXT(MSW3_VERSION_STR_IIS) );
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 'U':
|
|
|
|
if ( !strcmp( "UNMAPPED_REMOTE_USER", pszValName ))
|
|
return pstr->Copy( _strUnmappedUserName );
|
|
if ( !strcmp( "URL", pszValName ))
|
|
{
|
|
return pstr->Copy( _strURL );
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Any other value we assume to be a real environment variable
|
|
//
|
|
|
|
for ( i = 0 ; i < 2; i++ )
|
|
{
|
|
//
|
|
// If not big enough, count is returned that includes
|
|
// terminator, otherwise the number of bytes copied minus
|
|
// the terminator is returned
|
|
//
|
|
|
|
cb = GetEnvironmentVariable( pszValName,
|
|
(char *) pstr->QueryPtr(),
|
|
pstr->QuerySize() / sizeof(TCHAR) );
|
|
|
|
if ( !cb )
|
|
break;
|
|
|
|
if ( cb > (pstr->QueryCB() + sizeof(TCHAR) ))
|
|
{
|
|
if ( !pstr->Resize( cb ) )
|
|
return FALSE;
|
|
}
|
|
else
|
|
return TRUE;
|
|
} // for
|
|
break;
|
|
|
|
} // switch
|
|
|
|
//
|
|
// The value wasn't found
|
|
//
|
|
|
|
if ( pfFound )
|
|
{
|
|
*pfFound = FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
BuildCGIHeaderList( STR * pstr,
|
|
PARAM_LIST * pHeaderList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds a list of all client passed headers in the form of
|
|
|
|
//
|
|
// Builds a list of all client HTTP headers in the form of:
|
|
//
|
|
// HTTP_<up-case header>: <field>\n
|
|
// HTTP_<up-case header>: <field>\n
|
|
// ...
|
|
//
|
|
|
|
Arguments:
|
|
|
|
pstr - Receives full list
|
|
pHeaderList - List of headers
|
|
|
|
--*/
|
|
{
|
|
VOID * pvCookie = NULL;
|
|
CHAR * pszField;
|
|
CHAR * pszValue;
|
|
CHAR * pch;
|
|
CHAR ach[MAX_HEADER_LENGTH + 5 + 1];
|
|
DWORD cbHeader;
|
|
|
|
memcpy( ach, "HTTP_", 5 );
|
|
|
|
while ( pvCookie = pHeaderList->NextPair( pvCookie,
|
|
&pszField,
|
|
&pszValue ))
|
|
{
|
|
cbHeader = strlen( pszField );
|
|
|
|
if ( cbHeader >= sizeof( ach ))
|
|
continue;
|
|
|
|
//
|
|
// Ignore "method", "url" and "version"
|
|
//
|
|
|
|
if ( pszField[cbHeader - 1] != ':' )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Convert the destination to upper and replace all '-' with '_'
|
|
//
|
|
|
|
pch = ach + 5;
|
|
while ( *pszField )
|
|
{
|
|
*pch = toupper( *pszField );
|
|
|
|
if ( *pch == '-' )
|
|
*pch = '_';
|
|
|
|
pch++;
|
|
pszField++;
|
|
}
|
|
|
|
*pch = '\0';
|
|
|
|
if ( !pstr->Append( ach ) ||
|
|
!pstr->Append( pszValue ) ||
|
|
!pstr->Append( "\n" ))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD
|
|
HTTP_REQUEST::Initialize(
|
|
VOID
|
|
)
|
|
{
|
|
InitializeCriticalSection( &_csBuffList );
|
|
InitializeListHead( &_BuffListHead );
|
|
|
|
InitFastFindFieldMapper();
|
|
|
|
_fGlobalInit = TRUE;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
VOID
|
|
HTTP_REQUEST::Terminate(
|
|
VOID
|
|
)
|
|
{
|
|
HTTP_REQUEST * pReq;
|
|
|
|
if ( !_fGlobalInit )
|
|
return;
|
|
|
|
EnterCriticalSection( &_csBuffList );
|
|
|
|
while ( !IsListEmpty( &_BuffListHead ))
|
|
{
|
|
pReq = CONTAINING_RECORD( _BuffListHead.Flink,
|
|
HTTP_REQUEST,
|
|
_BuffListEntry );
|
|
|
|
RemoveEntryList( &pReq->_BuffListEntry );
|
|
|
|
delete pReq;
|
|
}
|
|
|
|
LeaveCriticalSection( &_csBuffList );
|
|
DeleteCriticalSection( &_csBuffList );
|
|
}
|
|
|
|
HTTP_REQUEST *
|
|
HTTP_REQUEST::Alloc(
|
|
CLIENT_CONN * pConn,
|
|
PVOID pvInitialBuff,
|
|
DWORD cbInitialBuff
|
|
)
|
|
{
|
|
HTTP_REQUEST * pReq;
|
|
|
|
EnterCriticalSection( &_csBuffList );
|
|
|
|
if ( !IsListEmpty( &_BuffListHead ))
|
|
{
|
|
pReq = CONTAINING_RECORD( _BuffListHead.Flink,
|
|
HTTP_REQUEST,
|
|
_BuffListEntry );
|
|
|
|
RemoveEntryList( &pReq->_BuffListEntry );
|
|
|
|
LeaveCriticalSection( &_csBuffList );
|
|
|
|
pReq->InitializeSession( pConn,
|
|
pvInitialBuff,
|
|
cbInitialBuff );
|
|
|
|
return pReq;
|
|
}
|
|
|
|
LeaveCriticalSection( &_csBuffList );
|
|
|
|
|
|
pReq = new HTTP_REQUEST( pConn,
|
|
pvInitialBuff,
|
|
cbInitialBuff );
|
|
|
|
if ( pReq &&
|
|
pReq->IsValid() )
|
|
{
|
|
return pReq;
|
|
}
|
|
|
|
delete pReq;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
HTTP_REQUEST::Free(
|
|
HTTP_REQUEST * pReq
|
|
)
|
|
{
|
|
pReq->SessionTerminated();
|
|
|
|
EnterCriticalSection( &_csBuffList );
|
|
|
|
InsertHeadList( &_BuffListHead,
|
|
&pReq->_BuffListEntry );
|
|
|
|
LeaveCriticalSection( &_csBuffList );
|
|
}
|
|
|
|
|
|
BOOL
|
|
HTTP_REQUEST::RequestRenegotiate(
|
|
LPBOOL pfAccepted
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method is invoked to request a SSL cert renegotiation
|
|
|
|
Arguments:
|
|
|
|
pfAccepted - updated with TRUE if renegotiation accepted
|
|
|
|
Returns:
|
|
|
|
TRUE if no error, otherwise FALSE
|
|
|
|
--*/
|
|
{
|
|
if ( _dwRenegotiated ||
|
|
!IsSecurePort() ||
|
|
!(_dwRootMask&VROOT_MASK_NEGO_CERT) )
|
|
{
|
|
*pfAccepted = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Ask the filter to handle renegotiation
|
|
//
|
|
|
|
if ( !_Filter.NotifyRequestRenegotiate( &_Filter,
|
|
pfAccepted,
|
|
_dwRootMask&VROOT_MASK_MAP_CERT )
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( *pfAccepted )
|
|
{
|
|
SetState( HTR_CERT_RENEGOTIATE );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
HTTP_REQUEST::DoneRenegotiate(
|
|
BOOL fSuccess
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method is invoked on SSL cert renegotiation completion
|
|
|
|
Arguments:
|
|
|
|
fSuccess - TRUE if renegotiation successfully retrieve a certificate
|
|
|
|
Returns:
|
|
|
|
TRUE if no error, otherwise FALSE
|
|
|
|
--*/
|
|
{
|
|
_dwRenegotiated = fSuccess ? CERT_NEGO_SUCCESS : CERT_NEGO_FAILURE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
HTTP_REQUEST::SessionTerminated(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This method does the necessary cleanup for the connected session
|
|
for this request object.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
switch ( QueryState()) {
|
|
|
|
case HTR_GATEWAY_ASYNC_IO:
|
|
|
|
// does the necessary actions to cleanup outstanding IO operation
|
|
(VOID ) ProcessAsyncGatewayIO();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
} // switch()
|
|
|
|
//
|
|
// do the cleanup for base object
|
|
//
|
|
HTTP_REQ_BASE::SessionTerminated();
|
|
|
|
return;
|
|
|
|
} // HTTP_REQUEST::SessionTerminated()
|
|
|
|
|
|
|