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; 
}