www.pudn.com > NetPaw.rar > HttpSocket.cpp
#include "StdAfx.h"
#include "netpawdoc.h"
#include "mainfrm.h"
#include "downloadfile.h"
#include ".\httpsocket.h"
// this is our working constructor
CHttpSocket::CHttpSocket(CDownloadFile *pDlFile)
: m_pDownldFile(pDlFile)
, m_sHostName(_T(""))
, m_sFileObj(_T(""))
, m_nPort(80) // default http port
, m_nDataToRead(0)
, m_nDataToWrite(0)
, m_nSendFailed(0)
, m_bConnected(FALSE)
, m_bProcessHeader(FALSE)
, m_nFileOffset(0)
, m_nRequestTo(0)
{
m_pbSendBuf = NULL;
m_pbRecvBuf = NULL;
}
CHttpSocket::~CHttpSocket(void)
{
AtlTrace("Socket: %08X will be deleted.\n", this);
// lock the socket
CSingleLock sl(&m_csSockAccess);
sl.Lock();
// clear segments list
SEGMENT_S *pstSeg;
list::iterator itor;
for(itor = m_lstSegments.begin(); itor != m_lstSegments.end(); itor++)
{
pstSeg = *itor;
delete pstSeg;
}
m_lstSegments.clear();
// delete buffer
if( m_pbRecvBuf )
{
delete m_pbRecvBuf;
m_pbRecvBuf = NULL;
}
if( m_pbSendBuf )
{
delete m_pbSendBuf;
m_pbSendBuf = NULL;
}
}
void CHttpSocket::OnConnect(BOOL bSuccess, CEventCallback * /*pc*/)
{
if( bSuccess )
{
m_bConnected = TRUE;
m_pDownldFile->NotifyConnected(this);
// send request to host
SendHttpRequest();
}
}
void CHttpSocket::OnReceive(BOOL bSuccess, LPWSABUF pBuf, DWORD dwBuffers, DWORD dwBytesRecvd, DWORD dwFlags, CEventCallback * /*pc*/)
{
// done ansynchronize receiving, you may process received data here
if( bSuccess && (dwBytesRecvd > 0) )
{
AtlTrace("Socket %08X Recvd: %u Bytes\n", this, dwBytesRecvd);
// 处理接收到的数据
if( !ProcessRcvData(pBuf->buf, dwBytesRecvd) )
{
return;
}
}
// maybe receiving buffer is not allocated yet
if( !m_pbRecvBuf )
{
m_pbRecvBuf = new BYTE[MAX_BUFF_SIZE];
if( !m_pbRecvBuf )
{
AtlTrace("严重系统错误,无法分配内存\n");
return;
}
ZeroMemory(m_pbRecvBuf, MAX_BUFF_SIZE);
}
// now post another receive request
dwFlags = 0;
if( !Receive(m_pbRecvBuf, MAX_BUFF_SIZE, &dwBytesRecvd, &dwFlags) )
{
int nErrSocket = WSAGetLastError();
AtlTrace( "Socket %08X 接收错误,错误号:%d\n", this, nErrSocket );
// no connection, reconnect
if( nErrSocket == WSAECONNABORTED )
{
if( Connect() ) return;
}
// other failures
m_pDownldFile->NotifyStatus(STATUS_RECEIVEFAILED);
}
}
void CHttpSocket::OnReceiveFrom(BOOL /*bSuccess*/, LPWSABUF pBuf, DWORD /*dwBuffers*/, DWORD dwBytesRecvd, DWORD dwFlags, LPSOCKADDR pAddrFrom, INT nFromLen, CEventCallback * /*pc*/)
{
}
void CHttpSocket::OnSend(BOOL bSuccess, LPWSABUF pBuf, DWORD dwBuffers, DWORD dwBytesSent, CEventCallback * /*pc*/)
{
DWORD dwBytesToSend;
// done ansynchronic sending, you may process post-sent data here
if( bSuccess )
{
m_nDataToWrite -= dwBytesSent;
AtlTrace("Socket %08X Sent: %u Bytes\n", this, dwBytesSent);
}
else
{
++m_nSendFailed;
}
// all the data is sent, prepare to receive response
if( m_nDataToWrite <= 0 )
{
m_bProcessHeader = TRUE;
OnReceive(TRUE, NULL, 0, 0, 0, NULL);
return;
}
// maybe sending buffer is not allocated yet
if( !m_pbSendBuf )
{
m_pbSendBuf = new BYTE[MAX_BUFF_SIZE];
if( !m_pbSendBuf )
{
AtlTrace("严重系统错误,无法分配内存\n");
return;
}
ZeroMemory(m_pbSendBuf, MAX_BUFF_SIZE);
}
// read data from header and send it
m_HttpRequest.GetData((LPTSTR)m_pbSendBuf);
// determine the sending data length
if(m_nDataToWrite > MAX_BUFF_SIZE)
{
dwBytesToSend = MAX_BUFF_SIZE;
}
else
{
dwBytesToSend = (DWORD)m_nDataToWrite;
}
// now post another sending request
if( !Send(m_pbSendBuf, dwBytesToSend, &dwBytesSent) )
{
m_nSendFailed++;
AtlTrace( "Socket %08X 发送错误,错误号:%d\n", this, WSAGetLastError() );
m_pDownldFile->NotifyStatus(STATUS_SENDFAILED);
}
}
BOOL CHttpSocket::SendHttpRequest()
{
if( !m_bConnected )
{
return FALSE;
}
// lock socket
SEGMENT_S *pstSeg;
m_csSockAccess.Lock();
if( m_lstSegments.empty() )
{
m_csSockAccess.Unlock();
return FALSE;
}
// get downloading range
pstSeg = m_lstSegments.back();
m_nFileOffset = pstSeg->nDataFrom + pstSeg->nBytesDone;
m_nRequestTo = pstSeg->nDataTo;
m_nDataToRead = m_nRequestTo - m_nFileOffset + 1;
// fill the request header
m_HttpRequest.FormatRequestHeader(m_sHostName, m_sFileObj, m_nFileOffset, m_nRequestTo, m_pDownldFile->m_sReferer);
m_nDataToWrite = m_HttpRequest.GetHeaderSize();
// unlock socket
m_csSockAccess.Unlock();
// for debug use
AtlTrace("Socket: %08X send http request: %I64d-%I64d.\n", this, m_nFileOffset, m_nRequestTo);
// post a sending request
if( m_nDataToWrite > 0 )
{
OnSend(TRUE, NULL, 0, 0, NULL);
}
else
{
// invalid request, just close the socket
m_pDownldFile->CloseSocket(this);
}
return TRUE;
}
BOOL CHttpSocket::ProcessRcvData(char* pBuffer, DWORD dwBytesRecvd)
{
// strip off the response header
long nOffset = 0, nDataLen;
if( m_bProcessHeader )
{
m_bProcessHeader = FALSE;
// parse response header
if( !ProcResponseHdr(pBuffer, dwBytesRecvd) )
{
return FALSE;
}
// should cut off the header size when saving file
nOffset = m_HttpResponseHdr.m_nHeaderSize;
}
// write the data to the file
nDataLen = dwBytesRecvd - nOffset;
m_pDownldFile->NotifyRcvData(this, pBuffer+nOffset, nDataLen);
// this socket finished its task
if( m_nDataToRead <= 0 || m_pDownldFile->IsStopDownld() )
{
m_pDownldFile->NotifyFinished(this);
return FALSE;
}
return TRUE; // continue receive
}
BOOL CHttpSocket::ProcStatusCode(int nCode)
{
BOOL bResult = FALSE;
AtlTrace("Response status code: %d\n", nCode);
switch(nCode)
{
case 200:
case 206:
bResult = TRUE; // OK
break;
case 300:
// Multiple Choices
break;
case 302:
{
// Moved Temporarily, need redirection
CString sNewUrl("");
m_HttpResponseHdr.GetField("Location", sNewUrl.GetBuffer(MAX_PATH), MAX_PATH);
sNewUrl.ReleaseBuffer();
if( !Redirect(sNewUrl) )
{
if( m_pDownldFile->GetSnifferSock() == (SOCKET)this )
{
m_pDownldFile->NotifyStatus(STATUS_CONNECTFAIL);
m_pDownldFile->DelSniffSocket();
}
else
{
// delete or close? it's a question...
// how to tell CDownloadFile a socket is stopped ?
// ? m_pDownldFile->m_nStoppedSockets++;
m_pDownldFile->CloseSocket(this);
}
}
}
break;
default:
if( m_pDownldFile->GetSnifferSock() == (SOCKET)this )
{
m_pDownldFile->NotifyStatus(STATUS_CONNECTFAIL);
m_pDownldFile->DelSniffSocket();
}
else
{
// delete or close? it's a question...
m_pDownldFile->CloseSocket(this);
}
break;
}
return bResult;
}
// create a new segment in segment list and set its data
void CHttpSocket::NewSegment(SEGMENT_S* pstSeg)
{
SEGMENT_S *pstNewSeg = new SEGMENT_S;
if( pstNewSeg )
{
CopyMemory( pstNewSeg, pstSeg, sizeof(SEGMENT_S) );
m_csSockAccess.Lock();
m_lstSegments.push_back(pstNewSeg);
m_csSockAccess.Unlock();
}
}
BOOL CHttpSocket::ParseUrl(LPCTSTR szUrl)
{
CString sHostName, sFileObj;
DWORD dwServiceType;
INTERNET_PORT nPort;
// parse the URL
if( !AfxParseURL(szUrl, dwServiceType, sHostName, sFileObj, nPort) )
{
return FALSE;
}
if( dwServiceType != AFX_INET_SERVICE_HTTP )
{
return FALSE;
}
// save the parameter at first
m_sHostName = sHostName;
m_sFileObj = sFileObj;
m_nPort = nPort;
return TRUE;
}
// connect or reconnect the server
BOOL CHttpSocket::Connect(void)
{
BOOL bResult = FALSE;
// create new socket, which will close the old socket automatically
if( !Create() )
{
goto ENDP;
}
// connect the server
if( !CIocpSocket::Connect( m_sHostName, m_nPort ) )
{
goto ENDP;
}
// successful
bResult = TRUE;
ENDP:
return bResult;
}
BOOL CHttpSocket::Redirect(LPCTSTR szNewUrl)
{
// notify redirection
m_pDownldFile->NotifyStatus( STATUS_REDIRECTION );
// parse the url
if( !ParseUrl(szNewUrl) )
{
return FALSE;
}
// compare if the file name here is the same as that sniffersocket gets
CString sFileName = m_sFileObj;
// get rid of remote folder
int nIndex = sFileName.ReverseFind('/');
if( nIndex != -1 )
{
sFileName = sFileName.Mid(nIndex + 1);
}
if( sFileName.IsEmpty() || sFileName.CompareNoCase( m_pDownldFile->GetFileName() ) != 0 )
{
return FALSE;
}
// start connection
if( !Connect() )
{
return FALSE;
}
return TRUE;
}
BOOL CHttpSocket::ProcResponseHdr(char* pBuffer, DWORD dwBytesRecvd)
{
LONGLONG nLength;
// get the header from the packet
m_HttpResponseHdr.ParseHeader(pBuffer, dwBytesRecvd);
// 200 OK, 206 Partial download
if( !ProcStatusCode( m_HttpResponseHdr.GetStatusCode() ) )
{
return FALSE;
}
// get file data length
nLength = m_HttpResponseHdr.GetDataLength();
AtlTrace( "Response data len: %I64d\n", nLength );
if( nLength == -1 )
{
AtlTrace("Error response data length\n");
return FALSE;
}
// notify owner the file length is known
if( m_pDownldFile->GetSnifferSock() == (SOCKET)this )
{
// set data to read
m_nDataToRead = nLength;
m_pDownldFile->NotifyFileLen(nLength);
return FALSE;
}
return TRUE;
}