bool CHttpClientReqSocket::ProcessHttpPacket(const BYTE* pucData, UINT uSize)
{
	if (GetHttpState() == HttpStateRecvExpected || GetHttpState() == HttpStateRecvHeaders)
	{
		// search for EOH
		LPBYTE pBody = NULL;
		int iSizeBody = 0;
		ProcessHttpHeaderPacket((const char*)pucData, uSize, pBody, iSizeBody);

		if (pBody) // EOH found, packet may contain partial body
		{
			if (thePrefs.GetDebugClientTCPLevel() > 0){
				Debug(_T("Received HTTP\n"));
				DebugHttpHeaders(m_astrHttpHeaders);
			}

			// PC-TODO: Should be done right in 'ProcessHttpHeaderPacket'
			int iSizeHeader = 2;
			for (int i = 0; i < m_astrHttpHeaders.GetCount(); i++)
				iSizeHeader += m_astrHttpHeaders[i].GetLength() + 2;
			theStats.AddDownDataOverheadFileRequest(iSizeHeader);

			if (iSizeBody < 0)
				throw CString(_T("Internal HTTP header/body parsing error"));

			if (m_astrHttpHeaders[0].GetLength() >= 4 && memcmp((LPCSTR)m_astrHttpHeaders[0], "HTTP", 4) == 0)
			{
				if (!ProcessHttpResponse())
					return false;

				SetHttpState(HttpStateRecvBody);
				if (iSizeBody > 0){
					// packet contained HTTP headers and (partial) body
					ProcessHttpResponseBody(pBody, iSizeBody);
				}
				else{
					// packet contained HTTP headers but no body (packet terminates with EOH)
					// body will be processed because of HTTP state 'HttpStateRecvBody' with next recv
					;
				}
			}
			else if (m_astrHttpHeaders[0].GetLength() >= 3 && memcmp((LPCSTR)m_astrHttpHeaders[0], "GET", 3) == 0)
			{
				if (!ProcessHttpRequest())
					return false;
				if (iSizeBody != 0){
					ASSERT(0); // no body for GET requests allowed yet
					return false;
				}
			}
			else
				throw CString(_T("Invalid HTTP header received"));
		}
		else
		{
			TRACE("+++ Received partial HTTP header packet\n");
		}
	}
	else if (GetHttpState() == HttpStateRecvBody)
	{
		ProcessHttpResponseBody(pucData, uSize);
	}
	else{
		theStats.AddDownDataOverheadFileRequest(uSize);
		throw CString(_T("Invalid HTTP socket state"));
	}

	return true;
}
예제 #2
0
UINT CUpDownClient::ProcessPeerCacheUpHttpRequest(const CStringAArray& astrHeaders)
{
	ASSERT( m_ePeerCacheUpState == PCUS_WAIT_CACHE_REPLY );

	if (astrHeaders.GetCount() == 0)
		return HTTP_STATUS_BAD_REQUEST;

	const CStringA& rstrHdr = astrHeaders.GetAt(0);
	char szUrl[1024];
	UINT uHttpMajVer, uHttpMinVer;
	if (sscanf(rstrHdr, "GET %1023s HTTP/%u.%u", szUrl, &uHttpMajVer, &uHttpMinVer) != 3){
		DebugHttpHeaders(astrHeaders);
		return HTTP_STATUS_BAD_REQUEST;
	}
	if (uHttpMajVer != 1 || (uHttpMinVer != 0 && uHttpMinVer != 1)){
		DebugHttpHeaders(astrHeaders);
		return HTTP_STATUS_BAD_REQUEST;
	}

	char szFileHash[33];
	if (sscanf(szUrl, "/.ed2khash=%32s", szFileHash) != 1){
		DebugHttpHeaders(astrHeaders);
		return HTTP_STATUS_BAD_REQUEST;
	}
	uchar aucUploadFileID[16];
	if (!strmd4(szFileHash, aucUploadFileID)){
		DebugHttpHeaders(astrHeaders);
		return HTTP_STATUS_BAD_REQUEST;
	}

	CKnownFile* pUploadFile = theApp.sharedfiles->GetFileByID(aucUploadFileID);
	if (pUploadFile == NULL){
		DebugHttpHeaders(astrHeaders);
		return HTTP_STATUS_NOT_FOUND;
	}

	bool bValidRange = false;
	uint64 ui64RangeStart = 0;
	uint64 ui64RangeEnd = 0;
	DWORD dwPushID = 0;
	for (int i = 1; i < astrHeaders.GetCount(); i++)
	{
		const CStringA& rstrHdr = astrHeaders.GetAt(i);
		if (strnicmp(rstrHdr, "Range:", 6) == 0)
		{
			int iParams;
			if (   (iParams = sscanf((LPCSTR)rstrHdr+6," bytes = %I64u - %I64u", &ui64RangeStart, &ui64RangeEnd)) != 2
				&& (iParams = sscanf((LPCSTR)rstrHdr+6," bytes = %I64u -", &ui64RangeStart)) != 1){
				DebugHttpHeaders(astrHeaders);
				TRACE("*** Unexpected HTTP %hs\n", rstrHdr);
				return HTTP_STATUS_BAD_REQUEST;
			}
			if (iParams == 1)
				ui64RangeEnd = pUploadFile->GetFileSize() - (uint64)1;

			if (ui64RangeEnd < ui64RangeStart){
				DebugHttpHeaders(astrHeaders);
				TRACE("*** Unexpected HTTP %hs\n", rstrHdr);
				return HTTP_STATUS_INV_RANGE;
			}

			bValidRange = true;
		}
		else if (strnicmp(rstrHdr, "X-ED2K-PushId:", 14) == 0)
		{
			if (sscanf((LPCSTR)rstrHdr+14, "%u", &dwPushID) != 1){
				DebugHttpHeaders(astrHeaders);
				TRACE("*** Unexpected HTTP %hs\n", rstrHdr);
				return HTTP_STATUS_BAD_REQUEST;
			}
		}
	}

	if (!bValidRange){
		DebugHttpHeaders(astrHeaders);
		return HTTP_STATUS_LENGTH_REQUIRED;
	}

	m_uPeerCacheUploadPushId = dwPushID;

	//PC-TODO: Where does this flag need to be cleared again? 
	// When client is removed from uploading list?
	// When client is allowed to send more block requests?
	
	// everything is setup for uploading with PeerCache.
	SetPeerCacheUpState(PCUS_UPLOADING);

	Requested_Block_Struct* reqblock = new Requested_Block_Struct;
	reqblock->StartOffset = ui64RangeStart;
	reqblock->EndOffset = ui64RangeEnd + 1;
	md4cpy(reqblock->FileID, aucUploadFileID);
	reqblock->transferred = 0;
	AddReqBlock(reqblock);

	return HTTP_STATUS_OK;
}
예제 #3
0
bool CUrlClient::ProcessHttpDownResponse(const CStringAArray& astrHeaders)
{
	if (reqfile == NULL)
		throw CString(_T("Failed to process received HTTP data block - No 'reqfile' attached"));
	if (astrHeaders.GetCount() == 0)
		throw CString(_T("Unexpected HTTP response - No headers available"));

	const CStringA& rstrHdr = astrHeaders.GetAt(0);
	UINT uHttpMajVer, uHttpMinVer, uHttpStatusCode;
	if (sscanf(rstrHdr, "HTTP/%u.%u %u", &uHttpMajVer, &uHttpMinVer, &uHttpStatusCode) != 3){
		CString strError;
		strError.Format(_T("Unexpected HTTP response: \"%hs\""), rstrHdr);
		throw strError;
	}
	if (uHttpMajVer != 1 || (uHttpMinVer != 0 && uHttpMinVer != 1)){
		CString strError;
		strError.Format(_T("Unexpected HTTP version: \"%hs\""), rstrHdr);
		throw strError;
	}
	bool bExpectData = uHttpStatusCode == HTTP_STATUS_OK || uHttpStatusCode == HTTP_STATUS_PARTIAL_CONTENT;
	bool bRedirection = uHttpStatusCode == HTTP_STATUS_MOVED || uHttpStatusCode == HTTP_STATUS_REDIRECT;
	if (!bExpectData && !bRedirection){
		CString strError;
		strError.Format(_T("Unexpected HTTP status code \"%u\""), uHttpStatusCode);
		throw strError;
	}

	bool bNewLocation = false;
	bool bValidContentRange = false;
	for (int i = 1; i < astrHeaders.GetCount(); i++)
	{
		const CStringA& rstrHdr = astrHeaders.GetAt(i);
		if (bExpectData && strnicmp(rstrHdr, "Content-Length:", 15) == 0)
		{
			UINT uContentLength = atoi((LPCSTR)rstrHdr + 15);
			if (uContentLength != m_uReqEnd - m_uReqStart + 1){
				if (uContentLength != reqfile->GetFileSize()){ // tolerate this case only
					CString strError;
					strError.Format(_T("Unexpected HTTP header field \"%hs\""), rstrHdr);
					throw strError;
				}
				TRACE("+++ Unexpected HTTP header field \"%s\"\n", rstrHdr);
			}
		}
		else if (bExpectData && strnicmp(rstrHdr, "Content-Range:", 14) == 0)
		{
			DWORD dwStart = 0, dwEnd = 0, dwLen = 0;
			if (sscanf((LPCSTR)rstrHdr + 14," bytes %u - %u / %u", &dwStart, &dwEnd, &dwLen) != 3){
				CString strError;
				strError.Format(_T("Unexpected HTTP header field \"%hs\""), rstrHdr);
				throw strError;
			}

			if (dwStart != m_uReqStart || dwEnd != m_uReqEnd || dwLen != reqfile->GetFileSize()){
				CString strError;
				strError.Format(_T("Unexpected HTTP header field \"%hs\""), rstrHdr);
				throw strError;
			}
			bValidContentRange = true;
		}
		else if (strnicmp(rstrHdr, "Server:", 7) == 0)
		{
			if (m_strClientSoftware.IsEmpty())
				m_strClientSoftware = rstrHdr.Mid(7).Trim();
		}
		else if (bRedirection && strnicmp(rstrHdr, "Location:", 9) == 0)
		{
			CString strLocation(rstrHdr.Mid(9).Trim());
			if (!SetUrl(strLocation)){
				CString strError;
				strError.Format(_T("Failed to process HTTP redirection URL \"%s\""), strLocation);
				throw strError;
			}
			bNewLocation = true;
		}
	}

	if (bNewLocation)
	{
		m_iRedirected++;
		if (m_iRedirected >= 3)
			throw CString(_T("Max. HTTP redirection count exceeded"));

		// the tricky part
		socket->Safe_Delete();		// mark our parent object for getting deleted!
		if (!TryToConnect(true))	// replace our parent object with a new one
			throw CString(_T("Failed to connect to redirected URL"));
		return false;				// tell our old parent object (which was marked as to get deleted 
									// and which is no longer attached to us) to disconnect.
	}

	if (!bValidContentRange){
		if (thePrefs.GetDebugClientTCPLevel() <= 0)
			DebugHttpHeaders(astrHeaders);
		CString strError;
		strError.Format(_T("Unexpected HTTP response - No valid HTTP content range found"));
		throw strError;
	}

	SetDownloadState(DS_DOWNLOADING);
	return true;
}
예제 #4
0
bool CUpDownClient::ProcessPeerCacheDownHttpResponse(const CStringAArray& astrHeaders)
{
	ASSERT( GetDownloadState() == DS_DOWNLOADING );
	ASSERT( m_ePeerCacheDownState == PCDS_WAIT_CACHE_REPLY );

	if (reqfile == NULL)
		throw CString(_T("Failed to process HTTP response - No 'reqfile' attached"));
	if (GetDownloadState() != DS_DOWNLOADING)
		throw CString(_T("Failed to process HTTP response - Invalid client download state"));
	if (astrHeaders.GetCount() == 0)
		throw CString(_T("Unexpected HTTP response - No headers available"));

	const CStringA& rstrHdr = astrHeaders.GetAt(0);
	UINT uHttpMajVer, uHttpMinVer, uHttpStatusCode;
	if (sscanf(rstrHdr, "HTTP/%u.%u %u", &uHttpMajVer, &uHttpMinVer, &uHttpStatusCode) != 3){
		CString strError;
		strError.Format(_T("Unexpected HTTP response: \"%hs\""), rstrHdr);
		throw strError;
	}
	if (uHttpMajVer != 1 || (uHttpMinVer != 0 && uHttpMinVer != 1)){
		CString strError;
		strError.Format(_T("Unexpected HTTP version: \"%hs\""), rstrHdr);
		throw strError;
	}
	bool bExpectData = uHttpStatusCode == HTTP_STATUS_OK || uHttpStatusCode == HTTP_STATUS_PARTIAL_CONTENT;
	if (!bExpectData){
		CString strError;
		strError.Format(_T("Unexpected HTTP status code \"%u\""), uHttpStatusCode);
		throw strError;
	}

	uint64 uContentLength = 0;
	bool bCacheHit = false;
	bool bValidContentRange = false;
	for (int i = 1; i < astrHeaders.GetCount(); i++)
	{
		const CStringA& rstrHdr = astrHeaders.GetAt(i);
		if (bExpectData && strnicmp(rstrHdr, "Content-Length:", 15) == 0)
		{
			uContentLength = _atoi64((LPCSTR)rstrHdr + 15);
			if (uContentLength > m_uReqEnd - m_uReqStart + 1){
				CString strError;
				strError.Format(_T("Unexpected HTTP header field \"%hs\""), rstrHdr);
				throw strError;
			}
		}
		else if (bExpectData && strnicmp(rstrHdr, "Content-Range:", 14) == 0)
		{
			uint64 ui64Start = 0, ui64End = 0, ui64Len = 0;
			if (sscanf((LPCSTR)rstrHdr + 14," bytes %I64u - %I64u / %I64u", &ui64Start, &ui64End, &ui64Len) != 3){
				CString strError;
				strError.Format(_T("Unexpected HTTP header field \"%hs\""), rstrHdr);
				throw strError;
			}

			if (ui64Start > ui64End 
				|| ui64Len != reqfile->GetFileSize()
				|| ui64Start < m_uReqStart || ui64Start > m_uReqEnd 
				|| ui64End < m_uReqStart || ui64End > m_uReqEnd){
				CString strError;
				strError.Format(_T("Unexpected HTTP header field \"%hs\""), rstrHdr);
				throw strError;
			}
			bValidContentRange = true;
			m_nUrlStartPos = ui64Start;
		}
		else if (strnicmp(rstrHdr, "Server:", 7) == 0)
		{
			if (m_strClientSoftware.IsEmpty())
				m_strClientSoftware = rstrHdr.Mid(7).Trim();
		}
		else if (bExpectData && strnicmp(rstrHdr, "X-Cache: MISS", 13) == 0)
		{
			bCacheHit = false;
		}
		else if (bExpectData && strnicmp(rstrHdr, "X-Cache: HIT", 12) == 0)
		{
			bCacheHit = true;
		}
	}

	// we either get a 'Content-Range' or (for very small files) just a 'Content-Length' for the entire file
	if (!bValidContentRange && uContentLength == reqfile->GetFileSize())
	{
		bValidContentRange = true;
		m_nUrlStartPos = 0;
	}

	if (!bValidContentRange){
		if (thePrefs.GetDebugClientTCPLevel() <= 0)
			DebugHttpHeaders(astrHeaders);
		CString strError;
		strError.Format(_T("Unexpected HTTP response - No valid HTTP content range found"));
		throw strError;
	}

	if (m_pPCDownSocket)
	{
		CSafeMemFile dataAck(128);
		dataAck.WriteUInt8( bCacheHit ? 1 : 0 );
		if (thePrefs.GetDebugClientTCPLevel() > 0){
			DebugSend("OP__PeerCacheAck", this, reqfile->GetFileHash());
			Debug(_T("  %s\n"), bCacheHit ? _T("CacheHit") : _T("CacheMiss"));
		}

		Packet* pEd2kPacket = new Packet(&dataAck, OP_EMULEPROT, OP_PEERCACHE_ACK);
		theStats.AddUpDataOverheadFileRequest(pEd2kPacket->size);
		socket->SendPacket(pEd2kPacket);
	}

//	SetDownloadState(DS_DOWNLOADING);

	//PC-TODO: Where does this flag need to be cleared again? 
	// When client is allowed to send more block requests?
	// Also, we have to support both type of downloads within in the same connection.
	SetPeerCacheDownState(PCDS_DOWNLOADING);
	m_bPeerCacheDownHit = bCacheHit;

	return true;
}