// HTTPDownload.cpp: implementation of the CHTTPDownload class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Download.h"
#include "HTTPDownload.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CHTTPDownload::CHTTPDownload()
{
for(int i = 0; i < 4; i++){
m_bTerminate[i] = FALSE;
}
m_bSupportResume = FALSE;
m_bResume = FALSE;
}
CHTTPDownload::~CHTTPDownload()
{
m_lsTask.RemoveAll();
}
BOOL CHTTPDownload::StartTask(CString remoteurl, CString localfile)
{
if(remoteurl.IsEmpty())
return FALSE;
if(!ParseURL(remoteurl)){
remoteurl = _T("http://") + remoteurl;
if(!ParseURL(remoteurl)){
TRACE("Requested URL is invalid!\n");
return FALSE;
}
}
m_strSavePath = localfile;
m_strSavePath.TrimLeft();
m_strSavePath.TrimRight();
if(m_strSavePath.IsEmpty() )
return FALSE;
m_strTempSavePath = m_strSavePath;
m_strTempSavePath += ".down";
FILE* fp = NULL;
if((fp = fopen(m_strTempSavePath, "r")) == NULL){
m_state.range[0] = -1;
m_bResume = FALSE;
}
else{
m_bResume = TRUE;
char* str = new char[1024];
memset(str, 0, 1024);
fgets(str, 1024, fp);
m_state.url.Empty();
m_state.url += str;
m_state.url = m_state.url.Left(m_state.url.GetLength() - 2);
memset(str, 0, 1024);
fgets(str, 1024, fp);
m_state.localfile.Empty();
m_state.localfile += str;
m_state.localfile = m_state.localfile.Left(m_state.localfile.GetLength() - 2);
delete [] str;
fread(&m_state.length, sizeof(LONG), 1, fp);
fread(&m_state.time, sizeof(CTime), 1, fp);
fread(m_state.range, sizeof(LONG), 8, fp);
fclose(fp);
}
if(SendRequest() != SENDREQUEST_SUCCESS){
TRACE("Remote Web Server is not reachable,or somthing else error occured!\n");
return FALSE;
}
if(m_state.range[0] == -1){
m_state.localfile = localfile;
m_state.url = remoteurl;
m_state.time = m_TimeLastModified;
m_state.length = m_dwFileSize;
for(int i = 0; i < 4; i++){
m_state.range[i * 2] = i * (m_dwFileSize / 4);
m_state.range[i * 2 + 1] = (i + 1) * (m_dwFileSize / 4) - 1;
}
m_state.range[7] = m_dwFileSize - 1;
}
else{
if(m_state.url != remoteurl){
AfxMessageBox("Maybe the download file is not you want,you can try to save as another file!");
return FALSE;
}
if(m_state.time < m_TimeLastModified || m_state.range[7] != (LONG)(m_dwFileSize - 1)){
m_state.time = m_TimeLastModified;
m_state.length = m_dwFileSize;
for(int i = 0; i < 4; i++){
m_state.range[i * 2] = i * (m_dwFileSize / 4);
m_state.range[i * 2 + 1] = (i + 1) * (m_dwFileSize / 4);
}
m_state.range[7] = m_dwFileSize - 1;
}
}
return TRUE;
}
BOOL CHTTPDownload::ParseURL(CString str)
{
str.TrimLeft();
if(str.IsEmpty())
return FALSE;
CString strURL = str;
// 清除数据
m_strServer = _T("");
m_strObject = _T("");
m_nPort = 0;
int nPos = strURL.Find("://");
if( nPos == -1 )
return FALSE;
// 进一步验证是否为http://
CString strTemp = strURL.Left( nPos+lstrlen("://") );
strTemp.MakeLower();
if( strTemp.Compare("http://") != 0 )
return FALSE;
strURL = strURL.Mid( strTemp.GetLength() );
nPos = strURL.Find('/');
if ( nPos == -1 )
return FALSE;
m_strObject = strURL.Mid(nPos);
strTemp = strURL.Left(nPos);
///////////////////////////////////////////////////////////////
/// 注意:并没有考虑URL中有用户名和口令的情形和最后有#的情形
/// 例如:http://abc@def:www.yahoo.com:81/index.html#link1
///
//////////////////////////////////////////////////////////////
// 查找是否有端口号
nPos = strTemp.Find(":");
if( nPos == -1 )
{
m_strServer = strTemp;
m_nPort = DEFAULT_HTTP_PORT;
}
else
{
m_strServer = strTemp.Left( nPos );
strTemp = strTemp.Mid( nPos+1 );
m_nPort = (USHORT)_ttoi((LPCTSTR)strTemp);
}
return TRUE;
}
UINT CHTTPDownload::SendRequest(BOOL bHead)
{
CString strVerb;
if( bHead )
strVerb = _T("HEAD ");
else
strVerb = _T("GET ");
CString strSend,strHeader,strRange;
int iStatus = 0,nRet;
char szReadBuf[1025];
DWORD dwContentLength,dwStatusCode;
while (TRUE)
{
if(m_pSocket.m_hSocket != NULL)
m_pSocket.Close();
m_pSocket.Create();
m_pSocket.Connect(m_strServer, m_nPort);
strSend = strVerb + m_strObject + " HTTP/1.1\r\n";
strSend += "Host: " + m_strServer + "\r\n";
strSend += "Accept: */*\r\n";
strSend += "Pragma: no-cache\r\n";
strSend += "Cache-Control: no-cache\r\n";
if( !m_strReferer.IsEmpty() )
strSend += "Referer: " + m_strReferer + "\r\n";
strSend += "Connection: close\r\n";
strRange = "Range: bytes=100-\r\n";
strSend += strRange;
//必须要加一个空行,否则Http服务器将不会应答
strSend += "\r\n";
int ret = m_pSocket.Send(strSend.GetBuffer(0), strSend.GetLength());
strSend.ReleaseBuffer();
strHeader.Empty();
while( TRUE )
{
ZeroMemory(szReadBuf,1025);
ret = m_pSocket.Receive(szReadBuf, 1025);
if( szReadBuf[0] == '\0' ) // We have encountered "\r\n\r\n"
break;
strHeader += szReadBuf;
if( iStatus == 0)
strHeader += "\r\n";
}
nRet = GetInfo(strHeader,dwContentLength,
dwStatusCode,m_TimeLastModified);
switch ( nRet )
{
case HTTP_FAIL:
return SENDREQUEST_FAIL;
break;
case HTTP_ERROR:
return SENDREQUEST_ERROR;
break;
case HTTP_REDIRECT:
continue;
break;
case HTTP_OK:
m_dwDownloadSize = dwContentLength + 100;
// 应该判断一下服务器是否支持断点续传
if( strRange.IsEmpty() )
m_dwFileSize = dwContentLength + 100; // 整个文件的长度
else
{
if ( dwStatusCode == 206 ) //支持断点续传
{
m_bSupportResume = TRUE;
m_dwFileSize = dwContentLength + 100;
}
else //不支持断点续传
{
m_bSupportResume = FALSE;
m_dwFileSize = dwContentLength + 100;
}
}
return SENDREQUEST_SUCCESS;
break;
default:
return SENDREQUEST_FAIL;
break;
}
}
m_pSocket.Close();
return SENDREQUEST_SUCCESS;
}
UINT CHTTPDownload::GetInfo(LPCTSTR lpszHeader, DWORD &dwContentLength,
DWORD &dwStatusCode, CTime &TimeLastModified)
{
dwContentLength = 0;
dwStatusCode = 0;
TimeLastModified= CTime::GetCurrentTime();
CString strHeader = lpszHeader;
strHeader.MakeLower();
//拆分出HTTP应答的头信息的第一行
int nPos = strHeader.Find("\r\n");
if (nPos == -1)
return HTTP_FAIL;
CString strFirstLine = strHeader.Left(nPos);
// 获得返回码: Status Code
strFirstLine.TrimLeft();
strFirstLine.TrimRight();
nPos = strFirstLine.Find(' ');
if( nPos == -1 )
return HTTP_FAIL;
strFirstLine = strFirstLine.Mid(nPos+1);
nPos = strFirstLine.Find(' ');
if( nPos == -1 )
return HTTP_FAIL;
strFirstLine = strFirstLine.Left(nPos);
dwStatusCode = (DWORD)_ttoi((LPCTSTR)strFirstLine);
// 检查返回码
if( dwStatusCode >= 300 && dwStatusCode < 400 ) //首先检测一下服务器的应答是否为重定向
{
nPos = strHeader.Find("location:");
if (nPos == -1)
return HTTP_FAIL;
CString strRedirectFileName = strHeader.Mid(nPos + strlen("location:"));
nPos = strRedirectFileName.Find("\r\n");
if (nPos == -1)
return HTTP_FAIL;
strRedirectFileName = strRedirectFileName.Left(nPos);
strRedirectFileName.TrimLeft();
strRedirectFileName.TrimRight();
// 设置Referer
m_strReferer = m_strDownloadUrl;
// 判断是否重定向到其他的服务器
nPos = strRedirectFileName.Find("http://");
if( nPos != -1 )
{
m_strDownloadUrl = strRedirectFileName;
// 检验要下载的URL是否有效
if ( !ParseURL(m_strDownloadUrl))
return HTTP_FAIL;
return HTTP_REDI