void CUpDownClient::ProcessPeerCacheUpHttpResponse(const CStringAArray& astrHeaders) { ASSERT( m_ePeerCacheUpState == PCUS_WAIT_CACHE_REPLY ); 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; } CString strError; strError.Format(_T("Unexpected HTTP status code \"%u\""), uHttpStatusCode); throw strError; }
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; }
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; }
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; }