--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pkiutilities/ocsp/test/tocsphttpfilter/ocsphttpfilter.cpp Tue Jan 26 15:20:08 2010 +0200
@@ -0,0 +1,410 @@
+// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include "ocsphttpfilter.h"
+#include <e32cmn.h>
+#include <e32std.h>
+#include <e32property.h>
+#include <http/rhttptransaction.h>
+#include <http/rhttpheaders.h>
+#include <http/thttphdrval.h>
+#include <httpstringconstants.h>
+#include "testfilterparameters.h"
+
+// Filter name used to register the filter
+_LIT8(KFilterName,"tocsphttpfilter");
+
+// Filter log file read by the test code
+_LIT(KFilterLogFileName, "\\tocsphttpfilter.log");
+
+// HTTPMethod StartTimeInMicroseconds
+_LIT8(KFilterLogFormat1, "%S %Ld");
+// StopTimeInMicroseconds
+_LIT8(KFilterLogFormat2, " %Ld\n");
+
+// Canned responses
+_LIT8(KCannedResponseInteralError, "\x30\x03\x0a\x01\x02");
+_LIT8(KCannedResponseTryLater, "\x30\x03\x0a\x01\x03");
+_LIT8(KCannedResponseCorruptOCSPData, "\x30\x03\x0a\x01\xf0");
+
+// A non-existent ocsp responder. Redirect requests to this
+// responder to simulate a missing response
+_LIT8(KNonExistentServer, "http://42.042.042.042:0420/");
+
+// HTTP response header for content-type (canned corrupted)
+_LIT8(KOCSPContentTypeResponseCorrupted, "application/ocsp-reponse");
+
+// Byte offset (+1) of Responder ID field of OCSPResponse
+const TInt KOCSPResponderIDOffset = 42;
+
+const TInt KTimeMilliToMicro = 1000;
+
+COCSPHTTPFilter::COCSPHTTPFilter()
+ : iLogLineCompleted(ETrue)
+ {
+ }
+
+CEComFilter* COCSPHTTPFilter::InstallFilterL(TAny* aSession)
+ {
+ RHTTPSession* session = reinterpret_cast<RHTTPSession*>(aSession);
+ COCSPHTTPFilter* filter = new (ELeave) COCSPHTTPFilter();
+ CleanupStack::PushL(filter);
+ filter->ConstructL(*session);
+ CleanupStack::Pop(filter);
+ return filter;
+ }
+
+void COCSPHTTPFilter::ConstructL(const RHTTPSession& aSession)
+ {
+ iStringPool = aSession.StringPool();
+ iFilterName = iStringPool.OpenFStringL(KFilterName);
+ // Register the filter for submit events
+ aSession.FilterCollection().AddFilterL(*this, THTTPEvent::EAnyTransactionEvent, MHTTPFilter::EClientFilters, iFilterName);
+ User::LeaveIfError(iFs.Connect());
+ User::LeaveIfError(iLogFile.Replace(iFs, KFilterLogFileName, EFileShareAny|EFileWrite));
+ }
+
+COCSPHTTPFilter::~COCSPHTTPFilter()
+ {
+ iFilterName.Close();
+ iLogFile.Close();
+ iFs.Close();
+ delete iCustomDataSupplier;
+ }
+
+void COCSPHTTPFilter::MHFLoad(RHTTPSession, THTTPFilterHandle)
+ {
+ ++iLoadCount;
+ }
+
+void COCSPHTTPFilter::MHFUnload(RHTTPSession /*aSession*/, THTTPFilterHandle)
+ {
+ if (--iLoadCount)
+ return;
+
+ delete this;
+ }
+
+void COCSPHTTPFilter::MHFRunL(RHTTPTransaction aTransaction,
+ const THTTPEvent& aEvent)
+ {
+ // Read test parameters
+ TInt countDropResp, numDelayResp;
+ TInt countCorruptHTTPDataHeader, countCorruptHTTPDataBodySizeLarge, countCorruptHTTPDataBodySizeSmall;
+ TInt countCorruptOCSPData;
+ TInt countInternalErrorResp, countTryLaterResp;
+ TInt countSigValidateFailure;
+ ReadTestParameters(numDelayResp, countDropResp,
+ countCorruptHTTPDataHeader, countCorruptHTTPDataBodySizeLarge, countCorruptHTTPDataBodySizeSmall,
+ countCorruptOCSPData,
+ countInternalErrorResp, countTryLaterResp,
+ countSigValidateFailure);
+
+ switch (aEvent.iStatus)
+ {
+ case THTTPEvent::ESubmit:
+ // Start of the HTTP transaction
+ iDataSupplied = EFalse;
+ iLogLineCompleted = EFalse;
+ LogTransactionStartL(aTransaction);
+
+ if (countDropResp > 0)
+ {
+ // Drop the request
+ --countDropResp;
+ TUriParser8 uri;
+ uri.Parse(KNonExistentServer);
+ aTransaction.Request().SetURIL(uri);
+ }
+ break;
+ case THTTPEvent::EGotResponseHeaders:
+ {
+ RHTTPResponse response = aTransaction.Response();
+ RHTTPHeaders headers = response.GetHeaderCollection();
+ RStringPool stringPool = aTransaction.Session().StringPool();
+
+ // Modify http body size if we plan to modify the body (response data) later
+ if (countInternalErrorResp > 0 || countTryLaterResp > 0 || countCorruptOCSPData > 0) // Common code since size is same for all canned responses
+ {
+ // Create the data supplier
+ delete iCustomDataSupplier;
+ iCustomDataSupplier = NULL;
+ TPtrC8 ptr(KCannedResponseInteralError);
+ RStringF contentLengthString = stringPool.StringF(HTTP::EContentLength, RHTTPSession::GetTable());
+ if (countTryLaterResp > 0)
+ {
+ ptr.Set(KCannedResponseTryLater);
+ }
+ else if (countCorruptOCSPData > 0)
+ {
+ ptr.Set(KCannedResponseCorruptOCSPData);
+ }
+ iCustomDataSupplier = new (ELeave) TCustomDataSupplier(ptr, aTransaction);
+ THTTPHdrVal contentLengthVal;
+ TInt size = iCustomDataSupplier->OverallDataSize();
+ contentLengthVal.SetInt(size);
+ headers.RemoveFieldPart(contentLengthString, 0);
+ headers.SetFieldL(contentLengthString, contentLengthVal);
+ }
+ // Create and keep a data supplier if we plan to corrupt the data leading to a signature validation failure
+ if (countSigValidateFailure > 0)
+ {
+ // Create the data supplier
+ delete iCustomDataSupplier;
+ iCustomDataSupplier = NULL;
+ TPtrC8 ptr(KNullDesC8);
+ iCustomDataSupplier = new (ELeave) TCustomDataSupplier(ptr, aTransaction);
+ }
+ if (countCorruptHTTPDataHeader > 0)
+ {
+ // Corrupt the header
+ RStringF ocspResponse = stringPool.OpenFStringL(KOCSPContentTypeResponseCorrupted);
+ CleanupClosePushL(ocspResponse);
+ RStringF contentTypeString = stringPool.StringF(HTTP::EContentType, RHTTPSession::GetTable());
+ THTTPHdrVal contentTypeVal;
+ contentTypeVal.SetStrF(ocspResponse);
+ headers.RemoveFieldPart(contentTypeString, 0);
+ headers.SetFieldL(contentTypeString, contentTypeVal);
+ CleanupStack::PopAndDestroy(&ocspResponse);
+ --countCorruptHTTPDataHeader;
+ }
+ if (countCorruptHTTPDataBodySizeLarge > 0 || countCorruptHTTPDataBodySizeSmall > 0)
+ {
+ // Corrupt the body size recorded in the header
+ RStringF contentLengthString = stringPool.StringF(HTTP::EContentLength, RHTTPSession::GetTable());
+ THTTPHdrVal contentLengthVal;
+ TInt err = headers.GetField(contentLengthString, 0, contentLengthVal);
+ if ((err == KErrNone) && (contentLengthVal.Type() == THTTPHdrVal::KTIntVal))
+ {
+ TInt bodySize = contentLengthVal.Int();
+ if (countCorruptHTTPDataBodySizeLarge > 0)
+ {
+ --bodySize;
+ --countCorruptHTTPDataBodySizeLarge;
+ }
+ else
+ {
+ ++bodySize;
+ --countCorruptHTTPDataBodySizeSmall;
+ }
+ contentLengthVal.SetInt(bodySize);
+ headers.RemoveFieldPart(contentLengthString, 0);
+ headers.SetFieldL(contentLengthString, contentLengthVal);
+ }
+ }
+ }
+ break;
+ case THTTPEvent::EGotResponseBodyData:
+ if (iCustomDataSupplier && (countInternalErrorResp > 0 || countTryLaterResp > 0 || countCorruptOCSPData > 0 || countSigValidateFailure > 0))
+ {
+ aTransaction.Response().RemoveBody();
+ aTransaction.Response().SetBody(*iCustomDataSupplier);
+ // Make sure state machine goes on
+ aTransaction.Response().SetStatusCode(THTTPEvent::EResponseComplete);
+ }
+ break;
+ case THTTPEvent::ESucceeded:
+ case THTTPEvent::EFailed:
+ // Delay response (numDelayResp is in milliseconds)
+ if (numDelayResp > 0)
+ {
+ User::After(numDelayResp * KTimeMilliToMicro);
+ }
+ // Deliberate fall through case
+ case THTTPEvent::ECancel:
+ if (!iLogLineCompleted)
+ {
+ // End of the HTTP transaction
+ iLogLineCompleted = ETrue;
+ LogTransactionEndL(aTransaction);
+ }
+ // Since EGotResponseBodyData event can happen more than once per transaction ensure to decrement
+ // counters only once
+ if (!iDataSupplied)
+ {
+ if (countInternalErrorResp > 0)
+ {
+ --countInternalErrorResp;
+ }
+ if (countTryLaterResp > 0)
+ {
+ --countTryLaterResp;
+ }
+ if (countCorruptOCSPData > 0)
+ {
+ --countCorruptOCSPData;
+ }
+ if (countSigValidateFailure > 0)
+ {
+ --countSigValidateFailure;
+ }
+ iDataSupplied = ETrue;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Write the parameters back to keep them persistent between retries
+ WriteTestParameters(countDropResp,
+ countCorruptHTTPDataHeader, countCorruptHTTPDataBodySizeLarge, countCorruptHTTPDataBodySizeSmall,
+ countCorruptOCSPData,
+ countInternalErrorResp, countTryLaterResp,
+ countSigValidateFailure);
+ }
+
+TInt COCSPHTTPFilter::MHFRunError(TInt /*aError*/,
+ RHTTPTransaction aTransaction,
+ const THTTPEvent& /*aEvent*/)
+ {
+ // If anything left, we've run out of memory or something
+ // similarly catastrophic has gone wrong.
+ aTransaction.Fail();
+ return KErrNone;
+ }
+
+// Logs the transaction method used and the current (start) time
+void COCSPHTTPFilter::LogTransactionStartL(const RHTTPTransaction& aTransaction)
+ {
+ // Get the transaction method being used (GET/POST) and log it
+ RHTTPRequest request = aTransaction.Request();
+ RStringF method = request.Method();
+ TTime time;
+ time.HomeTime();
+ TInt64 intTime = time.Int64();
+ RBuf8 logText;
+ logText.CreateL(255);
+ CleanupClosePushL(logText);
+ logText.Format(KFilterLogFormat1, &method.DesC(), intTime);
+ User::LeaveIfError(iLogFile.Write(logText));
+ CleanupStack::PopAndDestroy(&logText);
+ }
+
+// Logs the current (end) time
+void COCSPHTTPFilter::LogTransactionEndL(const RHTTPTransaction& /*aTransaction*/)
+ {
+ TTime time;
+ time.HomeTime();
+ TInt64 intTime = time.Int64();
+ RBuf8 logText;
+ logText.CreateL(255);
+ CleanupClosePushL(logText);
+ logText.Format(KFilterLogFormat2, intTime);
+ User::LeaveIfError(iLogFile.Write(logText));
+ CleanupStack::PopAndDestroy(&logText);
+ }
+
+// Read test parameters using Publish & Subscribe method
+void COCSPHTTPFilter::ReadTestParameters(TInt& aNumDelayResp, TInt& aCountDropResp,
+ TInt& aCountCorruptHTTPDataHeader, TInt& aCountCorruptHTTPDataBodySizeLarge, TInt& aCountCorruptHTTPDataBodySizeSmall,
+ TInt& aCountCorruptOCSPData,
+ TInt& aCountInternalErrorResp, TInt& aCountTryLaterResp,
+ TInt& aCountSigValidateFailure)
+ {
+ // Set default values which will be used if P&S doesn't exist
+ aNumDelayResp = aCountDropResp =
+ aCountCorruptHTTPDataHeader = aCountCorruptHTTPDataBodySizeLarge = aCountCorruptHTTPDataBodySizeSmall =
+ aCountCorruptOCSPData =
+ aCountInternalErrorResp = aCountTryLaterResp =
+ aCountSigValidateFailure = 0;
+
+ TUid categoryUid = TUid::Uid(KFilterParametersCategoryUID);
+ RProperty::Get(categoryUid, KFilterParameterNumDelayResp, aNumDelayResp);
+ RProperty::Get(categoryUid, KFilterParameterCountDropResp, aCountDropResp);
+ RProperty::Get(categoryUid, KFilterParameterCountCorruptHTTPDataHeader, aCountCorruptHTTPDataHeader);
+ RProperty::Get(categoryUid, KFilterParameterCountCorruptHTTPDataBodySizeLarge, aCountCorruptHTTPDataBodySizeLarge);
+ RProperty::Get(categoryUid, KFilterParameterCountCorruptHTTPDataBodySizeSmall, aCountCorruptHTTPDataBodySizeSmall);
+ RProperty::Get(categoryUid, KFilterParameterCountCorruptOCSPData, aCountCorruptOCSPData);
+ RProperty::Get(categoryUid, KFilterParameterCountInternalErrorResp, aCountInternalErrorResp);
+ RProperty::Get(categoryUid, KFilterParameterCountTryLaterResp, aCountTryLaterResp);
+ RProperty::Get(categoryUid, KFilterParameterCountSigValidateFailure, aCountSigValidateFailure);
+ }
+
+// To maintain persistence between retry attempts store the updated counts back
+void COCSPHTTPFilter::WriteTestParameters(TInt aCountDropResp,
+ TInt aCountCorruptHTTPDataHeader, TInt aCountCorruptHTTPDataBodySizeLarge, TInt aCountCorruptHTTPDataBodySizeSmall,
+ TInt aCountCorruptOCSPData,
+ TInt aCountInternalErrorResp, TInt aCountTryLaterResp,
+ TInt aCountSigValidateFailure)
+ {
+ TUid categoryUid = TUid::Uid(KFilterParametersCategoryUID);
+ RProperty::Set(categoryUid, KFilterParameterCountDropResp, aCountDropResp);
+ RProperty::Set(categoryUid, KFilterParameterCountCorruptHTTPDataHeader, aCountCorruptHTTPDataHeader);
+ RProperty::Set(categoryUid, KFilterParameterCountCorruptHTTPDataBodySizeLarge, aCountCorruptHTTPDataBodySizeLarge);
+ RProperty::Set(categoryUid, KFilterParameterCountCorruptHTTPDataBodySizeSmall, aCountCorruptHTTPDataBodySizeSmall);
+ RProperty::Set(categoryUid, KFilterParameterCountCorruptOCSPData, aCountCorruptOCSPData);
+ RProperty::Set(categoryUid, KFilterParameterCountInternalErrorResp, aCountInternalErrorResp);
+ RProperty::Set(categoryUid, KFilterParameterCountTryLaterResp, aCountTryLaterResp);
+ RProperty::Set(categoryUid, KFilterParameterCountSigValidateFailure, aCountSigValidateFailure);
+ }
+
+TCustomDataSupplier::TCustomDataSupplier(TPtrC8& aData, RHTTPTransaction& aTransaction)
+ {
+ iData.Set(aData);
+ iTransaction = &aTransaction;
+ iDataSupplied = EFalse;
+ iOriginalSupplier = aTransaction.Response().Body();
+ }
+
+// Methods from MHTTPDataSupplier
+TBool TCustomDataSupplier::GetNextDataPart(TPtrC8& aDataPart)
+ {
+ // Check if we need to simulate a signature validation failure
+ if (iData == KNullDesC8)
+ {
+ // Get the original data
+ iOriginalSupplier->GetNextDataPart(aDataPart);
+ iCorruptData.Create(aDataPart);
+ // Corrupt it (byte is part of ResponderID field)
+ --iCorruptData[KOCSPResponderIDOffset];
+ // Send it along
+ aDataPart.Set(iCorruptData);
+ }
+ else
+ {
+ // Consume the original data and pass back canned response
+ iOriginalSupplier->GetNextDataPart(aDataPart);
+ if (!iDataSupplied)
+ {
+ aDataPart.Set(iData);
+ iDataSupplied = ETrue;
+ }
+ else
+ {
+ // no data
+ aDataPart.Set(KNullDesC8);
+ }
+ }
+ return ETrue;
+ }
+
+void TCustomDataSupplier::ReleaseData()
+ {
+ iCorruptData.Close();
+ // Call original method to ensure it does it's work to let the state machine go on
+ iOriginalSupplier->ReleaseData();
+ // We are done supplying canned response so restore original supplier
+ iTransaction->Response().SetBody(*iOriginalSupplier);
+ }
+
+TInt TCustomDataSupplier::OverallDataSize()
+ {
+ return iData.Length();
+ }
+
+TInt TCustomDataSupplier::Reset()
+ {
+ return KErrNotSupported;
+ }