Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1477 lines
34 KiB

/**********************************************************************/
/** Microsoft Windows NT **/
/** Copyright(c) Microsoft Corp., 1994 **/
/**********************************************************************/
/*
proxy.cxx
This module contains the http proxy code
FILE HISTORY:
Johnl 25-Mar-1995 Created
*/
#include "w3p.hxx"
extern "C" {
#include <inetaccs.h>
#include <cacheapi.h>
#include <urlutil.h>
}
//
// Private constants.
//
//
// This is the size of buffer used for reads from the remote server
//
#define PROXY_BUFFER_SIZE 8192
//
// Private globals.
//
CRITICAL_SECTION HTTP_PROXY_REQUEST::_csBuffList;
LIST_ENTRY HTTP_PROXY_REQUEST::_BuffListHead;
BOOL HTTP_PROXY_REQUEST::_fGlobalInit = FALSE;
BOOL fCacheManagerInit = FALSE;
//
// This structure contains the field name to action mapping of the fields
// we recognize
//
struct _HTTP_RFC_FIELDS
{
TCHAR * pchFieldName;
HTTP_PROXY_REQUEST::PMFN_ONPROXYGRAMMAR pmfnOnField;
}
OnProxyFieldName[] =
{
//
// Fields that we don't process are commented out. If they are found
// in a request header, then they are added to _strUnknown
//
"method", &HTTP_PROXY_REQUEST::OnVerb,
"url", &HTTP_PROXY_REQUEST::OnURL,
"version", &HTTP_PROXY_REQUEST::OnVersion,
// "Accept:", &HTTP_PROXY_REQUEST::OnAccept,
// "Accept-Encoding:", &HTTP_PROXY_REQUEST::OnAcceptEncoding,
// "Accept-Language:", &HTTP_PROXY_REQUEST::OnAcceptLanguage,
"Authorization:", &HTTP_PROXY_REQUEST::OnAuthorization,
// "Base:", &HTTP_PROXY_REQUEST::OnBaseURL,
"Connection:", &HTTP_PROXY_REQUEST::OnConnection,
"Content-Length:", &HTTP_REQ_BASE::OnContentLength,
// "Content-Type:", &HTTP_PROXY_REQUEST::OnContentType,
// "Date:", &HTTP_PROXY_REQUEST::OnDate,
// "Forwarded:", &HTTP_PROXY_REQUEST::OnForwarded,
// "From:", &HTTP_PROXY_REQUEST::OnFrom,
"If-Modified-Since:", &HTTP_REQ_BASE::OnIfModifiedSince,
// "Mandatory:", &HTTP_PROXY_REQUEST::OnMandatory,
// "Message-ID:", &HTTP_PROXY_REQUEST::OnMessageID,
// "MIME-Version:", &HTTP_PROXY_REQUEST::OnMimeVersion,
"Pragma:", &HTTP_PROXY_REQUEST::OnPragma,
"Proxy-Authorization:", &HTTP_REQ_BASE::OnAuthorization,
// "Referer:", &HTTP_PROXY_REQUEST::OnReferer,
// "User-Agent:", &HTTP_PROXY_REQUEST::OnUserAgent,
NULL, NULL,
};
//
// Private prototypes.
//
//
// Public functions.
//
//
// Private functions.
//
/*******************************************************************
NAME: HTTP_PROXY_REQUEST::HTTP_PROXY_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_PROXY_REQUEST::HTTP_PROXY_REQUEST(
CLIENT_CONN * pClientConn,
PVOID pvInitialBuff,
DWORD cbInitialBuff
)
: HTTP_REQ_BASE ( pClientConn,
pvInitialBuff,
cbInitialBuff ),
_hInternet ( NULL ),
_hCacheFile ( NULL ),
_fURLCheckedOut( FALSE )
{
memset( &_InternetContext,
0,
sizeof( _InternetContext ) );
*_awchCacheFileName = L'\0';
TCP_REQUIRE( Reset() );
}
HTTP_PROXY_REQUEST::~HTTP_PROXY_REQUEST( VOID )
{
TCP_REQUIRE( CloseInternetData( &_InternetContext ) );
}
/*******************************************************************
NAME: HTTP_PROXY_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_PROXY_REQUEST::Reset( VOID )
{
//
// Note we must close the session handle before we close the
// internet handle
//
TCP_REQUIRE( CloseInternetData( &_InternetContext ) );
memset( &_InternetContext,
0,
sizeof( _InternetContext ) );
if ( _hInternet )
{
TCP_REQUIRE( InternetCloseHandle( _hInternet ));
_hInternet = NULL;
}
if ( _hCacheFile )
{
TCP_REQUIRE( CloseHandle( _hCacheFile ));
_hCacheFile = NULL;
}
//
// If we didn't successfully cache the file, then delete it
//
if ( !_fCache && *_awchCacheFileName )
{
if ( !DeleteFileW( _awchCacheFileName ))
{
TCP_PRINT(( DBG_CONTEXT,
"[Reset] Failed to delete %S, error %d\n",
_awchCacheFileName,
GetLastError() ));
}
}
//
// Check the URL back in
//
if ( _fURLCheckedOut )
{
if ( !UnlockUrlFile( _strURL.QueryStr() ) )
{
TCP_PRINT(( DBG_CONTEXT,
"[Reset] UnlockUrlFile returned %d\n",
GetLastError() ));
}
_fURLCheckedOut = FALSE;
}
_fReceivedHeader = FALSE;
_fFirstSend = TRUE;
_fCache = TRUE;
_cbServer = 0;
_cbServerResponseTotal = 0xffffffff;
_cbServerResponseSent = 0;
*_awchCacheFileName = L'\0';
_strRealURL.Copy( (CHAR *) NULL );
TCP_REQUIRE( HTTP_REQ_BASE::Reset() );
return TRUE;
}
/*******************************************************************
NAME: HTTP_PROXY_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_PROXY_REQUEST::DoWork(
BOOL * pfFinished
)
{
BOOL fRet;
BOOL fDone;
BOOL fCompleteRequest;
DWORD cbRead;
DWORD IoFlags;
DWORD BytesSent;
BOOL fHandled;
*pfFinished = FALSE;
switch ( QueryState() )
{
case HTR_READING_CLIENT_REQUEST:
_cbBytesReceived += QueryBytesWritten();
fRet = OnFillClientReq( &fCompleteRequest,
pfFinished );
if ( !fRet || *pfFinished )
{
break;
}
if ( fCompleteRequest && QueryState() == HTR_DOVERB )
{
goto ProcessClientRequest;
}
break;
case HTR_READING_GATEWAY_DATA:
_cbBytesReceived += QueryBytesWritten();
fRet = ReadGatewayData( &fDone );
if ( fRet && fDone )
goto ProcessClientRequest;
break;
case HTR_DOVERB:
ProcessClientRequest:
//
// If this looks like a cacheable request, try and get it from the
// local file cache. Note we do not impersonate for the cache
// retrieval.
//
if ( _fCache )
{
if ( !RetrieveFromCache( _strURL.QueryStr(),
&fHandled ))
{
return FALSE;
}
if ( fHandled )
{
return TRUE;
}
}
TCP_REQUIRE( ImpersonateUser() );
//
// Open the internet handle if we haven't already
//
if ( !_hInternet )
{
_hInternet = InternetOpen( MSW3_VERSION_STR,
0,
NULL,
0,
0 );
if ( !_hInternet )
{
TCP_PRINT(( DBG_CONTEXT,
"InternetOpen failed, error %d\n",
GetLastError() ));
RevertUser();
return FALSE;
}
}
if ( !OpenInternetData( _hInternet,
(CHAR *) QueryClientRequest(),
_cbClientRequest,
QueryGatewayData(),
_cbGatewayData,
&_InternetContext,
FALSE ))
{
RevertUser();
Disconnect( HT_BAD_GATEWAY,
GetLastError() );
return TRUE;
}
RevertUser();
if ( !_bufServer.Resize( PROXY_BUFFER_SIZE ))
return FALSE;
SetState( HTR_PROXY_READING_RESPONSE );
//
// Fall through
//
case HTR_PROXY_READING_RESPONSE:
if ( !ReadInternetData( &_InternetContext,
_bufServer.QueryPtr(),
_bufServer.QuerySize(),
&cbRead ))
{
Disconnect( HT_BAD_GATEWAY,
GetLastError() );
return TRUE;
}
if ( !cbRead )
{
if ( _fCache )
{
EndCacheWrite( _strURL.QueryStr() );
}
SetState( HTR_DONE );
}
else
{
//
// Open the cache file on the first write, append on subsequent
// writes
//
if ( _fCache )
{
CHAR * pch = NULL;
if ( _fFirstSend )
{
//
// Create the cache file if we've successfully retrieved
// the file. We scan for "HTTP/x.x 2xx"
//
pch = SkipWhite( SkipNonWhite( (CHAR *) _bufServer.QueryPtr() ));
if ( *pch == '2' )
{
TCP_REQUIRE( ImpersonateUser() );
if ( !BeginCacheWrite( _strURL.QueryStr() ))
{
RevertUser();
return FALSE;
}
RevertUser();
}
else
{
_fCache = FALSE;
}
#if 0
Leave the http status in for the time being for compatibility with
the gateway server. Fix after beta1.
//
// Don't put the status line in the cached file
//
// BUGBUG - This puts the "Date:" field in
//
pch = strchr( (CHAR *) _bufServer.QueryPtr(), '\n' );
if ( pch )
pch++;
#else
pch = NULL; // don't use pch offset for headers
#endif
}
if ( _fCache )
{
if ( !pch )
{
if ( !WriteCacheData( _bufServer.QueryPtr(),
cbRead ) )
{
return FALSE;
}
}
else
{
//
// Offset the write to after the status line
//
if ( !WriteCacheData( pch,
cbRead - (pch -
(CHAR *) _bufServer.QueryPtr())))
{
return FALSE;
}
}
}
}
}
IoFlags = IO_FLAG_ASYNC | (cbRead ? 0 : IO_FLAG_LAST_SEND )
| (_fFirstSend ? IO_FLAG_HEADER_INFO :
0 );
if ( !cbRead )
{
TCP_PRINT(( DBG_CONTEXT,
"Done reading data, disconnecting\n"));
//
// BUGBUG - We haven't notified the filters
//
*pfFinished = TRUE;
return TRUE;
}
else
{
TCP_PRINT(( DBG_CONTEXT,
"Read %d bytes of data\n",
cbRead ));
}
if ( _fFirstSend )
{
_fFirstSend = FALSE;
}
if ( !WriteFile( _bufServer.QueryPtr(),
cbRead,
&BytesSent,
IoFlags ))
{
TCP_PRINT(( DBG_CONTEXT,
"[DoProxyProtocol] Error %d from WriteFile\n",
GetLastError() ));
return FALSE;
}
return TRUE;
case HTR_DONE:
fRet = WriteLogRecord();
//
// Call filter to post last notification
//
*pfFinished = TRUE;
break;
default:
TCP_ASSERT( FALSE );
fRet = FALSE;
SetLastError( ERROR_INVALID_PARAMETER );
break;
}
return fRet;
}
/*******************************************************************
NAME: HTTP_PROXY_REQUEST::Parse
SYNOPSIS: Gathers all of the interesting information in the client
request
ENTRY: pchRequest - raw Latin-1 request received from the
client
RETURNS: APIERR if an error occurred parsing the header
HISTORY:
Johnl 24-Aug-1994 Created
********************************************************************/
BOOL HTTP_PROXY_REQUEST::Parse( const TCHAR * pchRequest,
BOOL * pfFinished )
{
PMFN_ONPROXYGRAMMAR pmfn;
CHAR * pchHeaders;
CHAR * pch;
CHAR * pszHeader;
CHAR * pszValue;
BOOL fRet;
VOID * pvCookie = NULL;
//
// Break out the initial request line, if we got here, there should
// always be a terminating line feed
//
pch = strchr( pchRequest, '\n' );
TCP_ASSERT( pch );
if ( !pch )
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
*pch = '\0';
pchHeaders = pch+1;
INET_PARSER Request( (CHAR *) pchRequest );
//
// Put all of the values into the header list
//
fRet = _HeaderList.AddEntry( "method", Request.QueryToken() ) &&
_HeaderList.AddEntry( "url", Request.NextToken() ) &&
_HeaderList.AddEntry( "version", Request.NextToken() ) &&
_HeaderList.ParseHeaderList( pchHeaders );
if ( !fRet )
{
return FALSE;
}
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
//
while ( pvCookie = _HeaderList.NextPair( pvCookie,
&pszHeader,
&pszValue ))
{
BOOL fRet;
IF_DEBUG( PARSING )
{
TCP_PRINT((DBG_CONTEXT,
"\t%s = %s\n",
pszHeader,
pszValue ));
}
if ( FindField( pszHeader, &pmfn ) )
{
if ( pmfn )
fRet = (this->*pmfn)( pszValue );
if ( !fRet )
return FALSE;
}
}
SetState( HTR_DOVERB );
return TRUE;
}
/*******************************************************************
NAME: HTTP_PROXY_REQUEST::OnVerb
SYNOPSIS: Parses the "verb URL version" portion of an HTTP request
ENTRY: pszVerb - Pointer to method for this request
RETURNS: TRUE if successful, FALSE if an error occurred
HISTORY:
Johnl 30-Mar-1995 Created
********************************************************************/
BOOL HTTP_PROXY_REQUEST::OnVerb( CHAR * pszVerb )
{
UINT i = 0;
if ( !_strMethod.Copy( pszVerb ) )
return FALSE;
//
// Only cache GET requests
//
if ( _stricmp( _strMethod.QueryStr(), "GET" ))
_fCache = FALSE;
return TRUE;
}
/*******************************************************************
NAME: HTTP_PROXY_REQUEST::OnURL
SYNOPSIS: Parses the URL from an HTTP request
ENTRY: pszURL - Pointer to URL
RETURNS: TRUE if successful, FALSE if an error occurred
HISTORY:
Johnl 24-Aug-1994 Created
********************************************************************/
BOOL HTTP_PROXY_REQUEST::OnURL( CHAR * pszValue )
{
TCHAR * pchParams;
TCHAR * pchStart;
TCHAR chParams;
//
// pchStart should now point at the protocol
//
pchStart = pszValue;
//
// Check for a question mark which indicates this URL contains some
// parameters and break the two apart if found (for logging purposes)
//
if ( pchParams = strchr( pchStart, TEXT('?') ) )
{
chParams = *pchParams;
*pchParams = TEXT('\0');
//
// Don't cache parameterized requests
//
_fCache = FALSE;
if ( !_strURL.Copy( pchStart ) ||
!_strURLParams.Copy( pchParams + 1 ))
{
return FALSE;
}
}
else
{
if ( !_strURL.Copy( pchStart ))
{
return FALSE;
}
}
if ( pchParams )
*pchParams = chParams;
return TRUE;
}
/*******************************************************************
NAME: HTTP_PROXY_REQUEST::OnVerb
SYNOPSIS: Parses the "verb URL version" portion of an HTTP request
ENTRY: pszVerb - Pointer to method for this request
RETURNS: TRUE if successful, FALSE if an error occurred
HISTORY:
Johnl 30-Mar-1995 Created
********************************************************************/
BOOL HTTP_PROXY_REQUEST::OnVersion( CHAR * pszVersion )
{
//
// Not used
//
return TRUE;
}
/*******************************************************************
NAME: HTTP_PROXY_REQUEST::FindField
SYNOPSIS: Parses the field name and determines the appropriate worker
method
ENTRY: ppch - Current parse location
ppmfn - member function pointer to call, set to NULL if the
field wasn't found.
RETURNS: TRUE if successful
HISTORY:
Johnl 24-Aug-1994 Created
********************************************************************/
BOOL HTTP_PROXY_REQUEST::FindField( CHAR * pszHeader,
PMFN_ONPROXYGRAMMAR * ppmfn )
{
UINT i = 0;
//
// Search for the fields we recognize
//
while ( OnProxyFieldName[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 == *OnProxyFieldName[i].pchFieldName &&
!::_tcsicmp( pszHeader + 1,
OnProxyFieldName[i].pchFieldName + 1 ))
{
*ppmfn = OnProxyFieldName[i].pmfnOnField;
return TRUE;
}
i++;
}
*ppmfn = (PMFN_ONPROXYGRAMMAR) NULL;
return TRUE;
}
BOOL
HTTP_PROXY_REQUEST::OnConnection(
CHAR * pszValue
)
/*++
Routine Description:
Looks to see if this connection is a keep-alive connection
Arguments:
pszValue - Value of connection header
Notes:
The HTTP spec specifies "Connect:" headers are never passsed along
by proxy servers
--*/
{
CHAR * pch;
CHAR * pchEnd;
DWORD cbToCopy;
INET_PARSER Parser( pszValue );
Parser.SetListMode( TRUE );
while ( Parser.QueryToken() )
{
if ( !_stricmp( "keep-alive", Parser.QueryToken() ))
{
SetKeepConn( TRUE );
goto Exit;
}
Parser.NextItem();
}
Exit:
//
// Delete this whole line as proxies shouldn't pass the connection through
//
*pszValue = '\0';
return TRUE;
}
BOOL
HTTP_PROXY_REQUEST::OnPragma(
CHAR * pszValue
)
/*++
Routine Description:
Looks to see if this is a no-cache pragma
Arguments:
pszValue - Value of pragma header
--*/
{
INET_PARSER Parser( pszValue );
while ( *Parser.QueryToken() )
{
if ( !_stricmp( "no-cache", Parser.QueryToken() ))
{
_fCache = FALSE;
break;
}
Parser.NextItem();
}
return TRUE;
}
BOOL
HTTP_PROXY_REQUEST::OnAuthorization(
CHAR * pszValue
)
/*++
Routine Description:
Requests that contain authorization information are never cached,
we do no other processing except set the cache flag.
Arguments:
Value of authorization header
--*/
{
_fCache = FALSE;
return TRUE;
}
BOOL
HTTP_PROXY_REQUEST::ProcessProxyHeaders(
BOOL * pfCompleteHeader,
DWORD * pcbEntityData
)
/*++
Routine Description:
Checks to see if the header from the remote server is complete
and munges the headers as necessary for the proxy
Arguments:
pfCompleteHeader - Set to TRUE if the header is complete
pcbEntityData - The number of bytes following the HTTP headers
that consist of the actual object data
--*/
{
BYTE * pbData;
if ( !CheckForTermination( pfCompleteHeader,
&_bufServer,
_cbServer,
&pbData,
pcbEntityData,
1024 ))
{
return FALSE;
}
if ( !*pfCompleteHeader )
return TRUE;
//
// Look for Content-Length
//
INET_PARSER Parser( (CHAR *) _bufServer.QueryPtr() );
while ( *Parser.QueryToken() )
{
if ( !_strnicmp( Parser.QueryToken(),
"Content-Length",
14 ))
{
Parser.SkipTo( ':' );
Parser += 1;
Parser.EatWhite();
_cbServerResponseTotal = atoi( Parser.QueryPos() );
}
Parser.NextLine();
}
return TRUE;
}
BOOL
HTTP_PROXY_REQUEST::RetrieveFromCache(
CHAR * pszURL,
BOOL * pfHandled
)
/*++
Routine Description:
Attempts to retrieve the specified URL from the URL cache. If it's
found, then this method sends the headers and file.
Arguments:
pszURL - URL to attempt to retrieve
pfHandled - Set to TRUE if no further processing is needed
--*/
{
DWORD err;
BOOL fIsExpired;
DWORD BytesRead;
DWORD cbHeaders;
LARGE_INTEGER liSize;
*pfHandled = FALSE;
if ( !fCacheManagerInit )
{
//
// Initialize the cache manager the first time it's needed
//
err = UrlCacheInit();
if ( err )
{
TCP_PRINT(( DBG_CONTEXT,
"[UrlCacheInit] failed, error %d, not caching\n",
GetLastError() ));
_fCache = FALSE;
return TRUE;
}
else
{
fCacheManagerInit = TRUE;
}
}
//
// See if the file is in the cache
//
err = RetrieveUrlFile( pszURL,
_awchCacheFileName,
&fIsExpired,
NULL );
if ( err )
{
//
// If the file wasn't found in the cache, just proceed but indicate
// we didn't handle the situation
//
if ( err == ERROR_FILE_NOT_FOUND )
{
return TRUE;
}
TCP_PRINT(( DBG_CONTEXT,
"[RetrieveFromCache] RetrieveUrlFile for %s failed, error %d, not caching\n",
pszURL,
GetLastError() ));
SetLastError( err );
return FALSE;
}
_fURLCheckedOut = TRUE;
//
// CODEWORK: We should send an If-Modified-Since header to determine
// if the data we have might still be valid
//
if ( fIsExpired )
{
return TRUE;
}
//
// Attempt to open the file so we can send the headers and the file
//
_hCacheFile = CreateFileW( _awchCacheFileName,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL |
FILE_FLAG_SEQUENTIAL_SCAN,
NULL );
if ( _hCacheFile == INVALID_HANDLE_VALUE )
{
TCP_PRINT(( DBG_CONTEXT,
"[RetrieveFromCache] CreateFileW for %S failed, error %d, not caching\n",
_awchCacheFileName,
GetLastError() ));
_hCacheFile = NULL;
_fCache = FALSE;
return TRUE;
}
//
// The headers prefix the file so we just need to fix up our own
// headers then blast out the file
//
// BUGBUG - Sending the headers as part of the file breaks filters
// on proxies (which may or may not be interesting).
//
//
// Get the number of bytes to send
//
liSize.LowPart = GetFileSize( _hCacheFile,
(PULONG) &liSize.HighPart );
if ( liSize.LowPart == 0xffffffff && GetLastError() != NO_ERROR )
{
return FALSE;
}
if ( liSize.HighPart )
{
*pfHandled = TRUE;
Disconnect( HT_NOT_SUPPORTED );
return TRUE;
}
if ( !BuildBaseResponseHeader( QueryRespBuf(),
NULL,
FALSE )) // No expires for proxy reqs.
{
return FALSE;
}
//
// Transmit the file and get out
//
SetState( HTR_DONE );
*pfHandled = TRUE;
if ( !TransmitFile( _hCacheFile,
liSize.LowPart,
IO_FLAG_ASYNC | IO_FLAG_HEADER_INFO
#if 0
BUGBUG - Don't include any headers for beta1 for compatibility with the
catapult cache.
,
QueryRespBufPtr(),
QueryRespBufCB()
#endif
))
{
return FALSE;
}
return TRUE;
}
BOOL
HTTP_PROXY_REQUEST::BeginCacheWrite(
CHAR * pszURL
)
/*++
Routine Description:
Attempts to retrieve the specified URL from the URL cache. If it's
found, then this method sends the headers and file.
Arguments:
pszURL - URL to attempt to retrieve
--*/
{
DWORD err;
//
// Get the file name to use for this URL
//
err = CreateUrlFile( pszURL,
0,
_awchCacheFileName );
if ( err )
{
TCP_PRINT(( DBG_CONTEXT,
"[BeginCacheWrite] CreateUrlFile for %s failed, error %d, not caching\n",
pszURL,
GetLastError() ));
_fCache = FALSE;
return TRUE;
}
//
// Create the file name the cache manager gave back to us
//
_hCacheFile = CreateFileW( _awchCacheFileName,
GENERIC_WRITE,
0, // no sharing
NULL,
TRUNCATE_EXISTING,
FILE_ATTRIBUTE_NORMAL |
FILE_FLAG_SEQUENTIAL_SCAN,
NULL );
if ( _hCacheFile == INVALID_HANDLE_VALUE )
{
TCP_PRINT(( DBG_CONTEXT,
"[BeginCacheWrite] CreateFileW for %S failed, error %d, not caching\n",
_awchCacheFileName,
GetLastError() ));
_fCache = FALSE;
_hCacheFile = NULL;
_awchCacheFileName[0] = L'\0';
return TRUE;
}
return TRUE;
}
BOOL
HTTP_PROXY_REQUEST::WriteCacheData(
VOID * pvData,
DWORD cbData
)
/*++
Routine Description:
Adds data to the cache write in progress
Arguments:
pvData - Data read from remote server to cache
cbData - Number of bytes of data
--*/
{
DWORD BytesWritten;
//
// BUGBUG - We need to strip headers that are not appropriate for the
// file ("Date:" for example, would be good to add "Forwarded:" or
// whatever the proxy indicator is
//
if ( !::WriteFile( _hCacheFile,
pvData,
cbData,
&BytesWritten,
NULL ))
{
TCP_PRINT(( DBG_CONTEXT,
"[WriteCacheWrite] WriteFile for %S failed, error %d, not caching\n",
_awchCacheFileName,
GetLastError() ));
_fCache = FALSE;
return TRUE;
}
return TRUE;
}
BOOL
HTTP_PROXY_REQUEST::EndCacheWrite(
CHAR * pszURL
)
/*++
Routine Description:
Closes the file we are in the process of caching and attempts to add
it to the file cache manager
Arguments:
pszURL - Name of URL to save the current cache file under
--*/
{
DWORD err;
DWORD cbExpires;
CHAR achExpires[64];
LONGLONG ExpiresTime;
//
// Close the file
//
if ( _hCacheFile )
{
TCP_REQUIRE( CloseHandle( _hCacheFile ));
_hCacheFile = NULL;
}
//
// Get the expires header if there was one
//
cbExpires = sizeof(achExpires);
if ( strncmp( _strURL.QueryStr(), "http:", 5 ) ||
!HttpQueryInfo( _InternetContext.hRequest,
HTTP_QUERY_EXPIRES,
achExpires,
&cbExpires ) ||
!StringTimeToFileTime( achExpires,
(LARGE_INTEGER*) &ExpiresTime ))
{
//
// Default to no expires time
//
ExpiresTime = 0;
}
err = CacheUrlFile( pszURL,
_awchCacheFileName,
ExpiresTime,
NULL );
if ( err )
{
TCP_PRINT(( DBG_CONTEXT,
"[EndCacheWrite] CacheUrlFile for %S failed, error %d, not caching\n",
_awchCacheFileName,
err ));
_fCache = FALSE;
return TRUE;
}
return TRUE;
}
DWORD
HTTP_PROXY_REQUEST::Initialize(
VOID
)
{
InitializeCriticalSection( &_csBuffList );
InitializeListHead( &_BuffListHead );
_fGlobalInit = TRUE;
return NO_ERROR;
}
VOID
HTTP_PROXY_REQUEST::Terminate(
VOID
)
{
HTTP_PROXY_REQUEST * pReq;
if ( !_fGlobalInit )
return;
EnterCriticalSection( &_csBuffList );
while ( !IsListEmpty( &_BuffListHead ))
{
pReq = CONTAINING_RECORD( _BuffListHead.Flink,
HTTP_PROXY_REQUEST,
_BuffListEntry );
RemoveEntryList( &pReq->_BuffListEntry );
delete pReq;
}
LeaveCriticalSection( &_csBuffList );
DeleteCriticalSection( &_csBuffList );
//
// Cleanup the cache manager if we're going away
//
if ( fCacheManagerInit )
{
UrlCacheCleanup();
}
}
HTTP_PROXY_REQUEST *
HTTP_PROXY_REQUEST::Alloc(
CLIENT_CONN * pConn,
PVOID pvInitialBuff,
DWORD cbInitialBuff
)
{
HTTP_PROXY_REQUEST * pReq;
EnterCriticalSection( &_csBuffList );
if ( !IsListEmpty( &_BuffListHead ))
{
pReq = CONTAINING_RECORD( _BuffListHead.Flink,
HTTP_PROXY_REQUEST,
_BuffListEntry );
RemoveEntryList( &pReq->_BuffListEntry );
LeaveCriticalSection( &_csBuffList );
pReq->InitializeSession( pConn,
pvInitialBuff,
cbInitialBuff );
return pReq;
}
LeaveCriticalSection( &_csBuffList );
pReq = new HTTP_PROXY_REQUEST( pConn,
pvInitialBuff,
cbInitialBuff );
if ( pReq &&
pReq->IsValid() )
{
return pReq;
}
delete pReq;
return NULL;
}
VOID
HTTP_PROXY_REQUEST::Free(
HTTP_PROXY_REQUEST * pReq
)
{
pReq->Reset();
pReq->SessionTerminated();
EnterCriticalSection( &_csBuffList );
InsertHeadList( &_BuffListHead,
&pReq->_BuffListEntry );
LeaveCriticalSection( &_csBuffList );
}