/* ============================================================================ Name : HttpEventHandler.cpp Author : Anton Antonov Version : 1.0 Copyright : Copyright (C) 2008 Rhomobile. All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Description : CHttpEventHandler implementation ============================================================================ */ #include "HttpEventHandler.h" #include #include #include #include #include #include "HttpConstants.h" #include "HttpFileManager.h" #include "tcmalloc/rhomem.h" #include // Supplied as the name of the test program _LIT(KHttpClientTestName, "RhoHttpClient"); CHttpEventHandler::CHttpEventHandler() : iResBodyBufferPtr(0,0), iResBodyBuffer(NULL) { iVerbose = EFalse; iUsingFile = EFalse; iTest = NULL; } CHttpEventHandler::~CHttpEventHandler() { iFileServ.Close(); if ( iTest ) { iTest->End(); iTest->Close(); delete iTest; } if ( iHttpFileManager ) delete iHttpFileManager; } CHttpEventHandler* CHttpEventHandler::NewLC() { CHttpEventHandler* self = new (ELeave)CHttpEventHandler(); CleanupStack::PushL(self); self->ConstructL(); return self; } CHttpEventHandler* CHttpEventHandler::NewL() { CHttpEventHandler* self=CHttpEventHandler::NewLC(); CleanupStack::Pop(); // self; return self; } void CHttpEventHandler::ConstructL() { User::LeaveIfError(iFileServ.Connect()); iHttpFileManager = CHttpFileManager::NewL(); } void CHttpEventHandler::SetVerbose(TBool aVerbose) { iVerbose = aVerbose; iTest = new RTest(KHttpClientTestName); iTest->Start(KNullDesC); } TInt CHttpEventHandler::MHFRunError(TInt aError, RHTTPTransaction /*aTransaction*/, const THTTPEvent& /*aEvent*/) { if (iVerbose) iTest->Console()->Printf(_L("MHFRunError fired with error code %d\n"), aError); return KErrNone; } void CHttpEventHandler::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent) { switch (aEvent.iStatus) { case THTTPEvent::EGotResponseHeaders: { // HTTP response headers have been received. We can determine now if there is // going to be a response body to save. RHTTPResponse resp = aTransaction.Response(); TInt status = resp.StatusCode(); if (iVerbose) { RStringF statusStr = resp.StatusText(); TBuf<32> statusStr16; statusStr16.Copy(statusStr.DesC()); iTest->Console()->Printf(_L("Status: %d (%S)\n"), status, &statusStr16); // Dump the headers if we're being verbose DumpRespHeadersL(aTransaction); } // Determine if the body will be saved to disk iSavingResponseBody = EFalse; if (resp.HasBody() && (status >= 200) && (status < 300) && (status != 204)) { if (iVerbose) { TInt dataSize = resp.Body()->OverallDataSize(); if (dataSize >= 0) iTest->Console()->Printf(_L("Response body size is %d\n"), dataSize); else iTest->Console()->Printf(_L("Response body size is unknown\n")); } iSavingResponseBody = ETrue; } else { if ( resp.HasBody() ){ iResBodyBuffer = HBufC8::NewMaxL(1); iResBodyBufferPtr.Set(iResBodyBuffer->Des()); iResBodyBufferPtr[0] = 0; } if (iVerbose) iTest->Console()->Printf(_L("Response status is bad\n")); } if ((status >= 200) && (status < 300) && (status != 204)) { ParseCookieL(aTransaction); } if (iSavingResponseBody) // If we're saving, then open a file handle for the new file { if ( iUsingFile ) { iHttpFileManager->GetNewFile(iRespBodyFilePath, iRespBodyFileName, EFalse); // Check it exists and open a file handle TInt valid = iFileServ.IsValidName(iRespBodyFilePath); if (!valid) { if (iVerbose) iTest->Console()->Printf(_L("The specified filename is not valid!.\n")); iSavingResponseBody = EFalse; } else { TInt err = iRespBodyFile.Create(iFileServ, iRespBodyFilePath, EFileWrite|EFileShareExclusive); if (err) { iSavingResponseBody = EFalse; User::Leave(err); } } } else { TInt dataSize = resp.Body()->OverallDataSize(); if ( iResBodyBuffer ) delete iResBodyBuffer; iResBodyBuffer = NULL; if ( dataSize > 50 * 1024) //skip large chunks of data { iSavingResponseBody = false; //try to stop current connection if (iVerbose) iTest->Console()->Printf(_L("Transaction Failed\n")); aTransaction.Close(); CActiveScheduler::Stop(); } else { iResBodyBuffer = HBufC8::NewMaxL(dataSize); iResBodyBufferPtr.Set(iResBodyBuffer->Des()); } iCurPos = 0; } } } break; case THTTPEvent::EGotResponseBodyData: { // Get the body data supplier iRespBody = aTransaction.Response().Body(); // Some (more) body data has been received (in the HTTP response) if (iVerbose) DumpRespBody(aTransaction); // Append to the output file if we're saving responses if (iSavingResponseBody) { TPtrC8 bodyData; TBool lastChunk = iRespBody->GetNextDataPart(bodyData); if ( iUsingFile ) { iRespBodyFile.Write(bodyData); if (lastChunk) { iRespBodyFile.Flush(); iRespBodyFile.Rename(iRespBodyFileName); iRespBodyFile.Close(); } } else { Mem::Copy((void*)(iResBodyBuffer->Ptr()+iCurPos), (void*)bodyData.Ptr(), bodyData.Size()); iCurPos += bodyData.Size(); } } // Done with that bit of body data iRespBody->ReleaseData(); } break; case THTTPEvent::EResponseComplete: { // The transaction's response is complete if (iVerbose) iTest->Console()->Printf(_L("\nTransaction Complete\n")); } break; case THTTPEvent::ESucceeded: { if (iVerbose) iTest->Console()->Printf(_L("Transaction Successful\n")); aTransaction.Close(); CActiveScheduler::Stop(); } break; case THTTPEvent::EFailed: { if (iVerbose) iTest->Console()->Printf(_L("Transaction Failed\n")); aTransaction.Close(); CActiveScheduler::Stop(); } break; case THTTPEvent::ERedirectedPermanently: { if (iVerbose) iTest->Console()->Printf(_L("Permanent Redirection\n")); } break; case THTTPEvent::ERedirectedTemporarily: { if (iVerbose) iTest->Console()->Printf(_L("Temporary Redirection\n")); } break; default: { if (iVerbose) iTest->Console()->Printf(_L("\n"), aEvent.iStatus); // close off the transaction if it's an error if (aEvent.iStatus < 0) { aTransaction.Close(); CActiveScheduler::Stop(); } } break; } } void CHttpEventHandler::DumpRespHeadersL(RHTTPTransaction& aTrans) { RHTTPResponse resp = aTrans.Response(); RStringPool strP = aTrans.Session().StringPool(); RHTTPHeaders hdr = resp.GetHeaderCollection(); THTTPHdrFieldIter it = hdr.Fields(); TBuf fieldName16; TBuf fieldVal16; while (it.AtEnd() == EFalse) { RStringTokenF fieldName = it(); RStringF fieldNameStr = strP.StringF(fieldName); THTTPHdrVal fieldVal; if (hdr.GetField(fieldNameStr,0,fieldVal) == KErrNone) { const TDesC8& fieldNameDesC = fieldNameStr.DesC(); fieldName16.Copy(fieldNameDesC.Left(CHttpConstants::KMaxHeaderNameLen)); switch (fieldVal.Type()) { case THTTPHdrVal::KTIntVal: iTest->Console()->Printf(_L("%S: %d\n"), &fieldName16, fieldVal.Int()); break; case THTTPHdrVal::KStrFVal: { RStringF fieldValStr = strP.StringF(fieldVal.StrF()); const TDesC8& fieldValDesC = fieldValStr.DesC(); fieldVal16.Copy(fieldValDesC.Left(CHttpConstants::KMaxHeaderValueLen)); iTest->Console()->Printf(_L("%S: %S\n"), &fieldName16, &fieldVal16); } break; case THTTPHdrVal::KStrVal: { RString fieldValStr = strP.String(fieldVal.Str()); const TDesC8& fieldValDesC = fieldValStr.DesC(); fieldVal16.Copy(fieldValDesC.Left(CHttpConstants::KMaxHeaderValueLen)); iTest->Console()->Printf(_L("%S: %S\n"), &fieldName16, &fieldVal16); } break; case THTTPHdrVal::KDateVal: { TDateTime date = fieldVal.DateTime(); TBuf<40> dateTimeString; TTime t(date); t.FormatL(dateTimeString,CHttpConstants::KDateFormat); iTest->Console()->Printf(_L("%S: %S\n"), &fieldName16, &dateTimeString); } break; default: iTest->Console()->Printf(_L("%S: \n"), &fieldName16); break; } // Display realm for WWW-Authenticate header RStringF wwwAuth = strP.StringF(HTTP::EWWWAuthenticate,RHTTPSession::GetTable()); if (fieldNameStr == wwwAuth) { // check the auth scheme is 'basic' RStringF basic = strP.StringF(HTTP::EBasic,RHTTPSession::GetTable()); RStringF realm = strP.StringF(HTTP::ERealm,RHTTPSession::GetTable()); THTTPHdrVal realmVal; if ((fieldVal.StrF() == basic) && (!hdr.GetParam(wwwAuth, realm, realmVal))) { RStringF realmValStr = strP.StringF(realmVal.StrF()); fieldVal16.Copy(realmValStr.DesC()); iTest->Console()->Printf(_L("Realm is: %S\n"), &fieldVal16); } } } ++it; } } void CHttpEventHandler::DumpRespBody(RHTTPTransaction& aTrans) { MHTTPDataSupplier* body = aTrans.Response().Body(); TPtrC8 dataChunk; TBool isLast = body->GetNextDataPart(dataChunk); DumpIt(dataChunk); if (isLast) iTest->Console()->Printf(_L("Got last data chunk.\n")); } CConsoleBase* CHttpEventHandler::Console() { return iTest->Console(); } void CHttpEventHandler::DumpIt(const TDesC8& aData) //Do a formatted dump of binary data { // Iterate the supplied block of data in blocks of cols=80 bytes const TInt cols=16; TInt pos = 0; TBuf logLine; TBuf anEntry; const TInt dataLength = aData.Length(); while (pos < dataLength) { //start-line hexadecimal( a 4 digit number) anEntry.Format(TRefByValue_L("%04x : "), pos); logLine.Append(anEntry); // Hex output TInt offset; for (offset = 0; offset < cols; ++offset) { if (pos + offset < aData.Length()) { TInt nextByte = aData[pos + offset]; anEntry.Format(TRefByValue_L("%02x "), nextByte); logLine.Append(anEntry); } else { //fill the remaining spaces with blanks untill the cols-th Hex number anEntry.Format(TRefByValue_L(" ")); logLine.Append(anEntry); } } anEntry.Format(TRefByValue_L(": ")); logLine.Append(anEntry); // Char output for (offset = 0; offset < cols; ++offset) { if (pos + offset < aData.Length()) { TInt nextByte = aData[pos + offset]; if ((nextByte >= ' ') && (nextByte <= '~')) { anEntry.Format(TRefByValue_L("%c"), nextByte); logLine.Append(anEntry); } else { anEntry.Format(TRefByValue_L(".")); logLine.Append(anEntry); } } else { anEntry.Format(TRefByValue_L(" ")); logLine.Append(anEntry); } } iTest->Console()->Printf(TRefByValue_L("%S\n"), &logLine); logLine.Zero(); // Advance to next byte segment (1 seg= cols) pos += cols; } } char* CHttpEventHandler::GetResponse() { char* str = NULL; if ( iResBodyBuffer && iResBodyBufferPtr.Length() > 0 ) { TInt size = iResBodyBufferPtr.Length(); str = (char*)malloc(size + 1); Mem::Copy(str, iResBodyBuffer->Ptr(), size); str[size] = '\0'; }else if ( iResBodyBuffer ) str = (char*)calloc(1,1); if ( iResBodyBuffer ) delete iResBodyBuffer; iResBodyBuffer = NULL; return str; } void CHttpEventHandler::ParseCookieL(RHTTPTransaction& aTrans) { RHTTPResponse response = aTrans.Response(); RHTTPResponse resp = aTrans.Response(); RStringPool pool = aTrans.Session().StringPool(); RHTTPHeaders headers = resp.GetHeaderCollection(); RStringF fieldName = pool.StringF(HTTP::ESetCookie,RHTTPSession::GetTable()); _LIT(KSeparator,";"); _LIT(KPathName,";path="); _LIT(KEqual,"="); THTTPHdrVal val; if (headers.GetField(fieldName, 0, val) != KErrNotFound) { RStringF cookieValueName = pool.StringF(HTTP::ECookieValue,RHTTPSession::GetTable()); RStringF cookieNameName = pool.StringF(HTTP::ECookieName,RHTTPSession::GetTable()); RStringF cookiePathName = pool.StringF(HTTP::EPath,RHTTPSession::GetTable()); if (val.StrF() == pool.StringF(HTTP::ECookie, RHTTPSession::GetTable())) { THTTPHdrVal cookieValue; THTTPHdrVal cookieName; THTTPHdrVal cookiePath; TInt parts = headers.FieldPartsL(fieldName); Mem::Fill((void*)iCookies.Ptr(), 1024, 0); // Get all the cookies. for (TInt i = 0; i < parts; i++) { headers.GetParam(fieldName, cookieValueName, cookieValue, i); headers.GetParam(fieldName, cookieNameName, cookieName, i); headers.GetParam(fieldName, cookiePathName, cookiePath, i); if ( GetHdrVal( cookieName, pool) ) iCookies.Append(KEqual); if ( GetHdrVal( cookieValue, pool) ) { iCookies.Append(KPathName); GetHdrVal( cookiePath, pool); iCookies.Append(KSeparator); } } } } } TBool CHttpEventHandler::GetHdrVal( THTTPHdrVal& hdrVal, RStringPool& pool) { TBool retval = ETrue; TPtrC8 auth_token((const TUint8*)"auth_token"); switch (hdrVal.Type()) { case THTTPHdrVal::KStrFVal: { RStringF fieldNameStr = pool.StringF(hdrVal.StrF()); const TDesC8& fieldNameDesC = fieldNameStr.DesC(); if (iVerbose) { TBuf value; value.Copy(fieldNameDesC.Left(CHttpConstants::KMaxHeaderValueLen)); iTest->Console()->Printf(_L("%S:\n"), &value); } if ( fieldNameDesC.Length() > 0 && fieldNameDesC.Compare(auth_token) ) iCookies.Append(fieldNameDesC); else retval = EFalse; } break; case THTTPHdrVal::KStrVal: { RString fieldNameStr = pool.String(hdrVal.Str()); const TDesC8& fieldNameDesC = fieldNameStr.DesC(); if (iVerbose) { TBuf value; value.Copy(fieldNameDesC.Left(CHttpConstants::KMaxHeaderValueLen)); iTest->Console()->Printf(_L("%S:\n"), &value); } if ( fieldNameDesC.Length() > 0 && fieldNameDesC.Compare(auth_token) ) iCookies.Append(fieldNameDesC); else retval = EFalse; } break; } return retval; } char* CHttpEventHandler::GetCookie() { if (iVerbose) { if ( iCookies.Length() > 0) iTest->Console()->Printf(_L("Have cookie")); else iTest->Console()->Printf(_L("No cookie")); } if ( iCookies.Length() > 0 ) return (char *)iCookies.Ptr(); else return NULL; } void CHttpEventHandler::ClearCookie() { Mem::Fill((void*)iCookies.Ptr(), CHttpConstants::KMaxUrlSize, 0); iCookies.Zero(); if (iVerbose) iTest->Console()->Printf(_L("CHttpEventHandler::ClearCookie")); }