📄 httpclient.cpp
字号:
/*
Module : HttpClient.cpp
Purpose: Implementation for the CHttpClient class
Created: PJN / 22-04-1999
History: None
Copyright (c) 1999 - 2005 by PJ Naughter. (Web: www.naughter.com, Email: pjna@naughter.com)
All rights reserved.
Copyright / Usage Details:
You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise)
when your product is released in binary form. You are allowed to modify the source code in any way you want
except you cannot modify the copyright details at the top of each module. If you want to distribute source
code with your application, then you are only allowed to distribute versions released by the author. This is
to maintain a single distribution point for the source code.
*/
//////////////// Includes ////////////////////////////////////////////
#include "stdafx.h"
#ifndef __AFXPRIV_H__
#pragma message("To avoid this message please put afxpriv.h in your PCH (normally stdafx.h)")
#include <afxpriv.h>
#endif
#include "W3Mfc.h"
#include "HttpResponseHeader.h"
#include "Base64.h"
#include "HttpClient.h"
#include "Win32Handle.h"
//////////////// Macros / Defines ////////////////////////////////////
#ifndef RT_HTML
#define RT_HTML MAKEINTRESOURCE(23)
#endif
#ifndef SEC_E_OK
#define SEC_E_OK ((SECURITY_STATUS)0x0000)
#endif
#ifndef SEC_I_CONTINUE_NEEDED
#define SEC_I_CONTINUE_NEEDED ((SECURITY_STATUS)0x1012)
#endif
#ifndef INVALID_FILE_ATTRIBUTES
#define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
#endif
#ifndef HSE_REQ_CLOSE_CONNECTION
#define HSE_REQ_CLOSE_CONNECTION (HSE_REQ_END_RESERVED+17)
#endif
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//The local variable which handle the function pointers
_W3MFC_DATA _W3MFCData;
//////////////// Implementation //////////////////////////////////////
_W3MFC_DATA::_W3MFC_DATA()
{
//Initialize the function pointers to sane defaults
m_lpfnTransmitFile = NULL;
m_lpfnTransmitPackets = NULL;
//Only use the TransmitFile and TransmitPacket API's if we are running on 2000/XP/.NET Server
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
BOOL bNT = (GetVersionEx(&osvi) && osvi.dwPlatformId == VER_PLATFORM_WIN32_NT);
if (bNT && (osvi.dwMajorVersion > 4))
{
m_hMSWsock = LoadLibrary(_T("MSWSOCK.DLL"));
if (m_hMSWsock)
{
m_lpfnTransmitFile = (LPTRANSMITFILE) GetProcAddress(m_hMSWsock, "TransmitFile");
m_lpfnTransmitPackets = (LPTRANSMITPACKETS) GetProcAddress(m_hMSWsock, "TransmitPackets");
}
}
}
_W3MFC_DATA::~_W3MFC_DATA()
{
if (m_hMSWsock)
{
FreeLibrary(m_hMSWsock);
m_hMSWsock = NULL;
}
}
#ifndef W3MFC_NO_ISAPI_SUPPORT
BOOL _W3MFC_DATA::TransmitFile(CHttpSocket& socket, CHttpResponseHeader& responseHdr, HSE_TF_INFO* pInfo)
{
//Assume the worst
BOOL bSuccess = FALSE;
if (pInfo->pfnHseIO)
{
SetLastError(ERROR_INVALID_PARAMETER); //we do not support ascynchronous notifications
return FALSE;
}
if (pInfo->Offset)
{
SetLastError(ERROR_INVALID_PARAMETER); //we do not support partials sens thro TransmitFile because that requires overlapped IO
return FALSE;
}
if (pInfo->dwFlags & HSE_IO_SEND_HEADERS)
{
if (m_lpfnTransmitPackets && m_lpfnTransmitFile)
{
TRANSMIT_PACKETS_ELEMENT tpe[2];
tpe[0].pBuffer = (void*) pInfo->pszStatusCode;
tpe[0].dwElFlags = TP_ELEMENT_MEMORY;
tpe[0].cLength = strlen(pInfo->pszStatusCode);
tpe[1].pBuffer = responseHdr.GetData(tpe[1].cLength);
tpe[1].dwElFlags = TP_ELEMENT_MEMORY;
//Call the TransmitPackets function
bSuccess = m_lpfnTransmitPackets(socket, tpe, 2, 0, NULL, TF_USE_KERNEL_APC);
//And the TransmitFile function
if (bSuccess)
bSuccess = m_lpfnTransmitFile(socket, pInfo->hFile, pInfo->BytesToWrite, 0, NULL, NULL, TF_USE_KERNEL_APC);
}
else
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
}
else
{
if (m_lpfnTransmitFile)
{
TRANSMIT_FILE_BUFFERS tfb;
tfb.Head = pInfo->pHead;
tfb.HeadLength = pInfo->HeadLength;
tfb.Tail = pInfo->pTail;
tfb.TailLength = pInfo->TailLength;
//Call the TransmitFile function
bSuccess = m_lpfnTransmitFile(socket, pInfo->hFile, pInfo->BytesToWrite, 0, NULL, &tfb, TF_USE_KERNEL_APC);
}
else
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
}
return bSuccess;
}
#endif
BOOL _W3MFC_DATA::TransmitFile(CHttpSocket& socket, CHttpResponseHeader& responseHdr, HANDLE hFile, DWORD dwSize)
{
ASSERT(m_lpfnTransmitFile);
//Setup the TFB ready for the call to the "TransmitFile" function
TRANSMIT_FILE_BUFFERS tfb;
tfb.Head = responseHdr.GetData(tfb.HeadLength);
tfb.Tail = NULL;
tfb.TailLength = 0;
//Call the TransmitFile function
BOOL bSuccess = m_lpfnTransmitFile(socket, hFile, dwSize, 0, NULL, &tfb, TF_USE_KERNEL_APC);
//Tidy up the heap memory we have used
delete [] tfb.Head;
return bSuccess;
}
BOOL _W3MFC_DATA::TransmitBuffer(CHttpSocket& socket, CHttpResponseHeader& responseHdr, BYTE* byData, DWORD dwSize)
{
ASSERT(m_lpfnTransmitPackets);
//Setup the TFB ready for the call to the "TransmitPackets" function
TRANSMIT_PACKETS_ELEMENT tpe[2];
tpe[0].pBuffer = responseHdr.GetData(tpe[0].cLength);
tpe[0].dwElFlags = TP_ELEMENT_MEMORY;
tpe[1].pBuffer = byData;
tpe[1].cLength = dwSize;
tpe[0].dwElFlags = TP_ELEMENT_MEMORY;
//Call the TransmitPackets function
return m_lpfnTransmitPackets(socket, tpe, 2, 0, NULL, TF_USE_KERNEL_APC);
}
IMPLEMENT_DYNCREATE(CHttpClient, CThreadPoolClient)
CHttpClient::CHttpClient()
{
m_pServer = NULL;
#ifdef W3MFC_SSL_SUPPORT
m_pSSLContext = NULL;
#endif
#ifndef W3MFC_NO_ISAPI_SUPPORT
m_dwDataSentViaWriteClient = 0;
m_nHttpStatusCodeSent = 0;
#endif
}
CHttpClient::~CHttpClient()
{
}
BOOL CHttpClient::AllowThisConnection()
{
return TRUE;
}
#ifdef W3MFC_SSL_SUPPORT
BOOL CHttpClient::InitializeSSLConnection()
{
SSL* pSSL = SSL_new(m_pSSLContext->operator SSL_CTX*());
if (pSSL == NULL)
{
//Report the error
CString sError;
sError.Format(_T("CHttpClient::InitializeSSLConnection, Failed to create create SSL connection object"));
ASSERT(m_pServer);
m_pServer->OnError(sError);
m_pServer->LogSSLErrors();
return FALSE;
}
else
m_SSL.Attach(pSSL);
//Associate the raw socket with the SSL connection object
if (SSL_set_fd(m_SSL, m_Socket) != 1)
{
//Report the error
CString sError;
sError.Format(_T("CHttpClient::InitializeSSLConnection, Failed to create create SSL connection object"));
ASSERT(m_pServer);
m_pServer->OnError(sError);
m_pServer->LogSSLErrors();
return FALSE;
}
return TRUE;
}
BOOL CHttpClient::DoSSLNegotiation()
{
ASSERT(m_pServer);
CHttpServerSettings* pSettings = m_pServer->GetSettings();
ASSERT(pSettings);
BOOL bNegotiationComplete = FALSE;
while (!bNegotiationComplete)
{
int nSSLAccept = SSL_accept(m_SSL);
if (nSSLAccept != 1)
{
BOOL bRetry = FALSE;
int nSSL_get_error = SSL_get_error(m_SSL, nSSLAccept);
if (nSSL_get_error == SSL_ERROR_WANT_READ)
{
if (m_Socket.IsReadible(pSettings->m_dwSSLNegotiationTimeout))
bRetry = TRUE;
}
if (!bRetry)
{
//Report the error
CString sError;
sError.Format(_T("CHttpClient::DoSSLNegotiation, Failed to perform SSL handshake, SSL_accept:%d SSL_get_error:%d"), nSSLAccept, nSSL_get_error);
m_pServer->OnError(sError);
m_pServer->LogSSLErrors();
return FALSE;
}
}
else
bNegotiationComplete = TRUE;
}
return TRUE;
}
#endif
BOOL CHttpClient::Run(const CThreadPoolRequest& request)
{
//Validate our parameters
ASSERT(request.m_pData);
CHttpThreadPoolRequest* pHttpRequest = (CHttpThreadPoolRequest*) request.m_pData;
//Hive away the parameters in member variables
SOCKET clientSocket = pHttpRequest->m_ClientSocket.Detach();
m_Socket.Attach(clientSocket);
CopyMemory(&m_Request.m_ClientAddress, &pHttpRequest->m_ClientAddress, sizeof(sockaddr_in));
#ifdef W3MFC_SSL_SUPPORT
m_pSSLContext = pHttpRequest->m_pSSLContext;
#endif
//Call the helper function which does all of the work
HandleClient();
#ifdef W3MFC_SSL_SUPPORT
//Close the SSL connection
m_SSL.Close();
#endif
//Close down the connection
m_Socket.Close();
//Tidy up our heap memory after ourselves
delete pHttpRequest;
//Reset the request data
m_Request = CHttpRequest();
return TRUE;
}
int CHttpClient::ExitInstance()
{
//Tidy up per thread SSL structures if we are using SSL
#ifdef W3MFC_SSL_SUPPORT
ERR_remove_state(0);
#endif
return CThreadPoolClient::ExitInstance();
}
void CHttpClient::HandleClient()
{
//Validate our parameters
ASSERT(m_pServer);
CHttpServerSettings* pSettings = m_pServer->GetSettings();
ASSERT(pSettings);
//Do the reverse DNS lookup if configured to do so
m_Request.m_sRemoteHost.Empty();
if (pSettings->m_bDNSLookup)
{
HOSTENT* pHostEnt = gethostbyaddr((const char*) &m_Request.m_ClientAddress.sin_addr, sizeof(IN_ADDR), AF_INET);
if (pHostEnt)
m_Request.m_sRemoteHost = pHostEnt->h_name;
}
//Should we allow this client to connect
if (!AllowThisConnection())
{
ReturnErrorMessage(400); //Bad Request
return;
}
//Create the SSL connection if required
#ifdef W3MFC_SSL_SUPPORT
if (pSettings->m_SSLProtocol != CHttpServerSettings::SSL_NONE)
{
if (!InitializeSSLConnection())
return;
//Do the SSL negotiation
if (!DoSSLNegotiation())
return;
}
#endif
//Use a Win32 event notification on the socket
CEvent dataEvent;
int nError = WSAEventSelect(m_Socket, dataEvent, FD_READ | FD_CLOSE);
if (nError == SOCKET_ERROR)
{
DWORD dwError = ::GetLastError();
//Report the error
CString sError;
sError.Format(_T("CHttpClient::HandleClient, Failed in call to WSAEventSelect, GetLastError:%d"), dwError);
m_pServer->OnError(sError);
return;
}
//Also create a waitable timer if we can
CWaitableTimer dataTimer;
if (dataTimer.Create(TRUE))
dataTimer.SetOnceOffRelative(pSettings->m_dwIdleClientTimeout);
BOOL bMoreRequests = FALSE;
#ifndef W3MFC_NO_SSPI_SUPPORT
BOOL bImpersonatedUsingSSPI = FALSE;
#endif
do
{
m_bResponseKeepAlive = FALSE;
#ifdef W3MFC_SSL_SUPPORT
//Read the client request
BOOL bReadResponse = FALSE;
if (dataTimer)
bReadResponse = m_Socket.ReadResponse(m_Request, pSettings->m_dwIdleClientTimeout, 4096, m_SSL, *this, dataTimer, m_StopEvent, dataEvent);
else
bReadResponse = m_Socket.ReadResponse(m_Request, pSettings->m_dwIdleClientTimeout, 4096, m_SSL, *this);
#else
BOOL bReadResponse = FALSE;
if (dataTimer)
bReadResponse = m_Socket.ReadResponse(m_Request, pSettings->m_dwIdleClientTimeout, 4096, *this, dataTimer, m_StopEvent, dataEvent);
else
bReadResponse = m_Socket.ReadResponse(m_Request, pSettings->m_dwIdleClientTimeout, 4096, *this);
#endif
if (bReadResponse)
{
//Parse the client request
if (ParseRequest())
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -