📄 reqsock.cpp
字号:
// ReqSock.cpp : implementation of the CRequestSocket class
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
#include "stdafx.h"
#include "HttpSvr.h"
#include "HttpDoc.h"
#include "Http.h"
#include "ReqSock.h"
#include "Request.h"
#include "resource.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNCREATE(CRequestSocket, CAsyncSocket)
#define CRLF "\x0d\x0a"
SECURITY_ATTRIBUTES g_sa = {sizeof(SECURITY_ATTRIBUTES),NULL,TRUE};
CRequestSocket::CRequestSocket( void )
{
}
CRequestSocket::CRequestSocket( CHttpSvrDoc* pDoc )
{
#ifdef IMPL_CGI
m_pThread = NULL;
m_pCancel = NULL;
#endif // IMPL_CGI
m_bKilled = FALSE;
m_nRefs = 1;
m_reqStatus = REQ_REQUEST;
m_buf.SetSize( 1024 );
m_cbOut = 0;
m_hFile = INVALID_HANDLE_VALUE;
m_pRequest = NULL;
m_pDoc = pDoc;
}
CRequestSocket::~CRequestSocket( void )
{
// JIC....
#ifdef IMPL_CGI
if ( m_pCancel )
{
if ( m_pThread )
{
DWORD dwCode;
// signal a cancel if still running....
if ( ::GetExitCodeThread( m_pThread->m_hThread, &dwCode )
&& dwCode == STILL_ACTIVE )
{
// signal a cancel....
m_pCancel->SetEvent();
// wait for the thread to die....
WaitForSingleObject( m_pThread->m_hThread, INFINITE );
}
// kill the object...
delete m_pThread;
}
delete m_pCancel;
}
#endif // IMPL_CGI
if ( m_hFile )
CloseHandle( m_hFile );
if ( m_pRequest )
{
// release our hold on the request object....
m_pRequest->m_bDone = TRUE;
m_pRequest->Release();
}
}
void CRequestSocket::OnReceive(int /*nErrorCode*/)
{
if ( m_pRequest == NULL )
{
// new request....
m_pRequest = new CRequest;
m_bKeepOpen = m_bWantKeepOpen = FALSE;
}
if ( m_pRequest )
{
// get the bytes....
int nBytes = Receive( m_buf.GetData(), (int)m_buf.GetSize() );
if ( nBytes != SOCKET_ERROR )
{
int ndx = 0;
switch ( m_reqStatus )
{
case REQ_REQUEST:
case REQ_HEADER:
while( GetLine( m_buf, nBytes, ndx ) == TRUE )
{
if ( !m_strLine.IsEmpty() )
ProcessLine();
else
{
m_reqStatus = REQ_BODY;
break;
}
}
// HTTP 1.0 type request contains only a single line....
if ( m_reqStatus == REQ_SIMPLE)
{
m_reqStatus = REQ_DONE;
break;
}
// break if we're not looking for the body....
if ( m_reqStatus != REQ_BODY )
break;
// stop if no body sent....
if ( !BodySent() )
{
m_reqStatus = REQ_DONE;
break;
}
// else fall through....
case REQ_BODY:
AddToBody( nBytes, ndx );
break;
}
if ( m_reqStatus == REQ_DONE )
{
m_buf.SetSize(0);
if ( !StartResponse() )
{
BOOL bOk = AsyncSelect( FD_WRITE | FD_CLOSE );
ASSERT(bOk);
}
}
}
else
nBytes = GetLastError();
}
else
{
// couldn't allocate request object....
ShutDown( both );
m_bKilled = TRUE;
Release();
}
}
void CRequestSocket::OnSend(int /*nErrorCode*/)
{
int nBytes = Send( m_buf.GetData(), m_cbOut );
if ( nBytes == SOCKET_ERROR )
{
if ( GetLastError() != WSAEWOULDBLOCK )
{
ShutDown( both );
m_bKilled = TRUE;
Release();
}
else
{
BOOL bOk = AsyncSelect( FD_WRITE | FD_CLOSE );
ASSERT(bOk);
}
}
else if ( nBytes < m_cbOut )
{
// still got some left....
m_buf.RemoveAt( 0, nBytes );
m_cbOut -= nBytes;
// adjust the bytes-sent value for the request....
m_pRequest->m_cbSent += nBytes;
// set up for next write....
BOOL bOk = AsyncSelect( FD_WRITE | FD_CLOSE );
ASSERT(bOk);
}
else
{
// adjust the bytes-sent value for the request....
m_pRequest->m_cbSent += nBytes;
// if we're not done with the file....
if ( m_hFile != INVALID_HANDLE_VALUE )
{
DWORD dwRead = 0;
// read next chunk....
if(ReadFile( m_hFile, m_buf.GetData(),
(DWORD)m_buf.GetSize(), &dwRead, NULL ) && dwRead > 0)
{
m_cbOut = dwRead;
}
else
{
// no more data to send....
CloseHandle( m_hFile );
m_hFile = INVALID_HANDLE_VALUE;
}
}
// see if we need to keep going....
if ( m_hFile != INVALID_HANDLE_VALUE )
{
BOOL bOk = AsyncSelect( FD_WRITE | FD_CLOSE );
ASSERT(bOk);
}
else
{
// eh, we're outta here....
ShutDown( both );
m_bKilled = TRUE;
Release();
}
}
}
void CRequestSocket::OnClose(int /*nErrorCode*/)
{
m_bKilled = TRUE;
Release();
}
BOOL CRequestSocket::GetLine( const CByteArray& bytes, int nBytes, int& ndx )
{
// MIME-style line-contuniation is not supported
BOOL bLine = FALSE;
while ( bLine == FALSE && ndx < nBytes )
{
char ch = (char)(bytes.GetAt( ndx ));
switch( ch )
{
case '\r': // ignore
break;
case '\n': // end-of-line
bLine = TRUE;
break;
default: // other....
m_strLine += ch;
break;
}
++ndx;
}
return bLine;
}
void CRequestSocket::ProcessLine( void )
{
int ndx;
switch( m_reqStatus )
{
case REQ_REQUEST:
ndx = m_strLine.Find( ' ' );
if ( ndx != -1 )
{
m_pRequest->m_strMethod = m_strLine.Left( ndx );
m_strLine = m_strLine.Mid( ndx+1 );
m_strLine.TrimLeft();
ndx = m_strLine.Find( ' ' );
if ( ndx == -1 )
{
m_pRequest->m_strURL = m_strLine;
m_pRequest->m_strURL.TrimRight();
m_reqStatus = REQ_SIMPLE;
}
else
{
m_pRequest->m_strURL = m_strLine.Left( ndx );
m_pRequest->m_strVersion = m_strLine.Mid( ndx+1 );
m_pRequest->m_strVersion.TrimLeft();
}
// check for execution arguments....
ndx = m_pRequest->m_strURL.Find( '?' );
if ( ndx != -1 )
{
// yup; save the args....
m_pRequest->m_strArgs = m_pRequest->m_strURL.Mid( ndx+1 );
// strip from file name....
m_pRequest->m_strURL = m_pRequest->m_strURL.Left( ndx );
m_pRequest->m_dwExecute = CRequest::APP_EXECUTE;
}
// change any "%xx"s to the appropriate char....
m_pRequest->m_strURL = Decode( m_pRequest->m_strURL );
}
if (m_reqStatus != REQ_SIMPLE)
m_reqStatus = REQ_HEADER;
break;
case REQ_HEADER:
ndx = m_strLine.Find( ':' );
if ( ndx != -1 )
{
CString strName = m_strLine.Left( ndx );
CString strValue = m_strLine.Mid( ndx+1 );
strName.MakeLower();
strValue.TrimLeft();
m_pRequest->m_mapHeaders.SetAt( strName, strValue );
}
break;
};
m_strLine.Empty();
}
BOOL CRequestSocket::BodySent( void )
{
BOOL bSent = FALSE;
CString strValue = m_pRequest->GetHeaderValue( "Content-Length" );
if ( !strValue.IsEmpty() )
{
m_pRequest->m_cbBody = atoi( strValue );
bSent = TRUE;
}
return bSent;
}
void CRequestSocket::AddToBody( int nBytes, int ndx )
{
// save the buffer size....
int nOldSize = (int)m_buf.GetSize();
// get rid of old stuff; append to body data....
m_buf.RemoveAt( 0, ndx );
m_buf.SetSize( nBytes - ndx );
m_pRequest->m_baBody.Append( m_buf );
// restore the buffer size....
m_buf.SetSize( nOldSize );
// see if we're done....
if ( m_pRequest->m_baBody.GetSize() >= m_pRequest->m_cbBody )
{
m_pRequest->m_baBody.SetSize( m_pRequest->m_cbBody );
m_reqStatus = REQ_DONE;
}
}
BOOL CRequestSocket::StartResponse( void )
{
BOOL bWait = FALSE;
CString strFile;
UINT uPort;
// save the host address....
GetPeerName( m_pRequest->m_strHost, uPort );
// switch on the method....
if ( m_pRequest->m_cbBody == 0 &&
m_pRequest->m_strMethod.CompareNoCase( "GET" ) == 0 )
{
FindTarget( strFile );
if( m_pRequest->m_uStatus == 0 )
{
if ( m_pRequest->m_dwExecute )
bWait=StartSvrApp();
else
{
if ( StuffHeading() )
StartTargetStuff();
}
}
}
else if ( m_pRequest->m_cbBody == 0 && m_reqStatus == REQ_DONE &&
m_pRequest->m_strMethod.CompareNoCase( "HEAD" ) == 0 )
{
FindTarget( strFile );
if( m_pRequest->m_uStatus == 0 )
{
if ( m_pRequest->m_dwExecute )
bWait=StartSvrApp();
else
{
StuffHeading();
// we don't send the file for HEAD reqs....
if ( m_hFile != INVALID_HANDLE_VALUE)
{
CloseHandle( m_hFile );
m_hFile = INVALID_HANDLE_VALUE;
}
}
}
}
else if ( m_pRequest->m_cbBody > 0 && m_reqStatus == REQ_DONE &&
m_pRequest->m_strMethod.CompareNoCase( "POST" ) == 0 )
{
// assume an executable....
m_pRequest->m_dwExecute = CRequest::APP_EXECUTE;
FindTarget( strFile );
if ( m_pRequest->m_uStatus == 0 )
{
bWait=StartSvrApp();
} else {
StuffError( IDS_STATUS_NOTIMPL );
}
}
else
StuffError( IDS_STATUS_NOTIMPL );
// notify the active view of the hit....
m_pDoc->DocHit( m_pRequest );
return bWait;
}
BOOL CRequestSocket::FindTarget( CString& strFile )
{
BOOL bFound = FALSE;
strFile = m_pRequest->m_strURL;
// change from URL to local file system path....
if ( URLtoPath( strFile ) )
{
CString strExtra; // extra path info
m_pRequest->m_dwAttr = GetFileAttributes( strFile );
if ( m_pRequest->m_dwAttr != INVALID_FILE_ATTRIBUTES )
bFound = TRUE;
else
{
// rip off the last portion....
strExtra = StripLast( strFile );
while( !strFile.IsEmpty() )
{
// anything there?
m_pRequest->m_dwAttr = GetFileAttributes( strFile );
if ( m_pRequest->m_dwAttr != -1 )
{
// found something; better not be a folder....
if( (m_pRequest->m_dwAttr&FILE_ATTRIBUTE_DIRECTORY) == 0 )
bFound = TRUE;
break;
}
// rip off the next portion....
strExtra = StripLast( strFile ) + strExtra;
}
}
if ( bFound )
{
// strip any trailing SEPCHAR....
if ( strFile.GetAt( strFile.GetLength()-1) == SEPCHAR )
m_pRequest->m_strFullPath = strFile.Left( strFile.GetLength()-1 );
else
m_pRequest->m_strFullPath = strFile;
// see if we need to set the extra path info....
if ( !strExtra.IsEmpty() )
{
m_pRequest->m_strPathInfo = strExtra;
if ( URLtoPath( strExtra ) )
m_pRequest->m_strPathTranslated = strExtra;
}
// if it's a folder, see if we can redirect to
// on of the default docs or apps....
if ( m_pRequest->m_dwAttr & FILE_ATTRIBUTE_DIRECTORY )
{
// check for existence of a default doc or app....
if ( !CheckDefault( IDS_DEFAULTDOC, FALSE ) )
CheckDefault( IDS_DEFAULTAPP, TRUE );
}
else if ( m_pRequest->m_dwExecute && !IsSvrApp() )
{
StuffError( IDS_STATUS_BADREQUEST );
}
}
else
StuffError( IDS_STATUS_NOTFOUND );
}
else
StuffError( IDS_STATUS_BADREQUEST );
return bFound;
}
BOOL CRequestSocket::URLtoPath( CString& strFile )
{
BOOL bLegal = FALSE;
CString& strRoot = m_pDoc->m_strRoot;
// start with the root, append the abs path....
CString strTemp = strRoot + strFile;
// now canonicalize it....
DWORD dwSize = GetFullPathName( strTemp, MAX_PATH, strFile.GetBuffer(MAX_PATH+1), NULL );
strFile.ReleaseBuffer();
// get the full path okay?
if ( dwSize != 0 && dwSize <= MAX_PATH )
{
int cchRoot = strRoot.GetLength();
int cchFile = strFile.GetLength();
// must be the same or longer than the root....
if ( cchRoot == cchFile )
{
// must be exactly the same....
if ( strRoot.Compare( strFile ) == 0 )
bLegal = TRUE;
}
else if ( cchRoot < cchFile )
{
// must have the root as the base....
if ( strRoot.Compare( strFile.Left(cchRoot) ) == 0
&& strFile.GetAt( cchRoot ) == SEPCHAR )
bLegal = TRUE;
}
}
return bLegal;
}
BOOL CRequestSocket::PathToURL( CString& strFile )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -