|
1 // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include "ocsphttpfilter.h" |
|
17 #include <e32cmn.h> |
|
18 #include <e32std.h> |
|
19 #include <e32property.h> |
|
20 #include <http/rhttptransaction.h> |
|
21 #include <http/rhttpheaders.h> |
|
22 #include <http/thttphdrval.h> |
|
23 #include <httpstringconstants.h> |
|
24 #include "testfilterparameters.h" |
|
25 |
|
26 // Filter name used to register the filter |
|
27 _LIT8(KFilterName,"tocsphttpfilter"); |
|
28 |
|
29 // Filter log file read by the test code |
|
30 _LIT(KFilterLogFileName, "\\tocsphttpfilter.log"); |
|
31 |
|
32 // HTTPMethod StartTimeInMicroseconds |
|
33 _LIT8(KFilterLogFormat1, "%S %Ld"); |
|
34 // StopTimeInMicroseconds |
|
35 _LIT8(KFilterLogFormat2, " %Ld\n"); |
|
36 |
|
37 // Canned responses |
|
38 _LIT8(KCannedResponseInteralError, "\x30\x03\x0a\x01\x02"); |
|
39 _LIT8(KCannedResponseTryLater, "\x30\x03\x0a\x01\x03"); |
|
40 _LIT8(KCannedResponseCorruptOCSPData, "\x30\x03\x0a\x01\xf0"); |
|
41 |
|
42 // A non-existent ocsp responder. Redirect requests to this |
|
43 // responder to simulate a missing response |
|
44 _LIT8(KNonExistentServer, "http://42.042.042.042:0420/"); |
|
45 |
|
46 // HTTP response header for content-type (canned corrupted) |
|
47 _LIT8(KOCSPContentTypeResponseCorrupted, "application/ocsp-reponse"); |
|
48 |
|
49 // Byte offset (+1) of Responder ID field of OCSPResponse |
|
50 const TInt KOCSPResponderIDOffset = 42; |
|
51 |
|
52 const TInt KTimeMilliToMicro = 1000; |
|
53 |
|
54 COCSPHTTPFilter::COCSPHTTPFilter() |
|
55 : iLogLineCompleted(ETrue) |
|
56 { |
|
57 } |
|
58 |
|
59 CEComFilter* COCSPHTTPFilter::InstallFilterL(TAny* aSession) |
|
60 { |
|
61 RHTTPSession* session = reinterpret_cast<RHTTPSession*>(aSession); |
|
62 COCSPHTTPFilter* filter = new (ELeave) COCSPHTTPFilter(); |
|
63 CleanupStack::PushL(filter); |
|
64 filter->ConstructL(*session); |
|
65 CleanupStack::Pop(filter); |
|
66 return filter; |
|
67 } |
|
68 |
|
69 void COCSPHTTPFilter::ConstructL(const RHTTPSession& aSession) |
|
70 { |
|
71 iStringPool = aSession.StringPool(); |
|
72 iFilterName = iStringPool.OpenFStringL(KFilterName); |
|
73 // Register the filter for submit events |
|
74 aSession.FilterCollection().AddFilterL(*this, THTTPEvent::EAnyTransactionEvent, MHTTPFilter::EClientFilters, iFilterName); |
|
75 User::LeaveIfError(iFs.Connect()); |
|
76 User::LeaveIfError(iLogFile.Replace(iFs, KFilterLogFileName, EFileShareAny|EFileWrite)); |
|
77 } |
|
78 |
|
79 COCSPHTTPFilter::~COCSPHTTPFilter() |
|
80 { |
|
81 iFilterName.Close(); |
|
82 iLogFile.Close(); |
|
83 iFs.Close(); |
|
84 delete iCustomDataSupplier; |
|
85 } |
|
86 |
|
87 void COCSPHTTPFilter::MHFLoad(RHTTPSession, THTTPFilterHandle) |
|
88 { |
|
89 ++iLoadCount; |
|
90 } |
|
91 |
|
92 void COCSPHTTPFilter::MHFUnload(RHTTPSession /*aSession*/, THTTPFilterHandle) |
|
93 { |
|
94 if (--iLoadCount) |
|
95 return; |
|
96 |
|
97 delete this; |
|
98 } |
|
99 |
|
100 void COCSPHTTPFilter::MHFRunL(RHTTPTransaction aTransaction, |
|
101 const THTTPEvent& aEvent) |
|
102 { |
|
103 // Read test parameters |
|
104 TInt countDropResp, numDelayResp; |
|
105 TInt countCorruptHTTPDataHeader, countCorruptHTTPDataBodySizeLarge, countCorruptHTTPDataBodySizeSmall; |
|
106 TInt countCorruptOCSPData; |
|
107 TInt countInternalErrorResp, countTryLaterResp; |
|
108 TInt countSigValidateFailure; |
|
109 ReadTestParameters(numDelayResp, countDropResp, |
|
110 countCorruptHTTPDataHeader, countCorruptHTTPDataBodySizeLarge, countCorruptHTTPDataBodySizeSmall, |
|
111 countCorruptOCSPData, |
|
112 countInternalErrorResp, countTryLaterResp, |
|
113 countSigValidateFailure); |
|
114 |
|
115 switch (aEvent.iStatus) |
|
116 { |
|
117 case THTTPEvent::ESubmit: |
|
118 // Start of the HTTP transaction |
|
119 iDataSupplied = EFalse; |
|
120 iLogLineCompleted = EFalse; |
|
121 LogTransactionStartL(aTransaction); |
|
122 |
|
123 if (countDropResp > 0) |
|
124 { |
|
125 // Drop the request |
|
126 --countDropResp; |
|
127 TUriParser8 uri; |
|
128 uri.Parse(KNonExistentServer); |
|
129 aTransaction.Request().SetURIL(uri); |
|
130 } |
|
131 break; |
|
132 case THTTPEvent::EGotResponseHeaders: |
|
133 { |
|
134 RHTTPResponse response = aTransaction.Response(); |
|
135 RHTTPHeaders headers = response.GetHeaderCollection(); |
|
136 RStringPool stringPool = aTransaction.Session().StringPool(); |
|
137 |
|
138 // Modify http body size if we plan to modify the body (response data) later |
|
139 if (countInternalErrorResp > 0 || countTryLaterResp > 0 || countCorruptOCSPData > 0) // Common code since size is same for all canned responses |
|
140 { |
|
141 // Create the data supplier |
|
142 delete iCustomDataSupplier; |
|
143 iCustomDataSupplier = NULL; |
|
144 TPtrC8 ptr(KCannedResponseInteralError); |
|
145 RStringF contentLengthString = stringPool.StringF(HTTP::EContentLength, RHTTPSession::GetTable()); |
|
146 if (countTryLaterResp > 0) |
|
147 { |
|
148 ptr.Set(KCannedResponseTryLater); |
|
149 } |
|
150 else if (countCorruptOCSPData > 0) |
|
151 { |
|
152 ptr.Set(KCannedResponseCorruptOCSPData); |
|
153 } |
|
154 iCustomDataSupplier = new (ELeave) TCustomDataSupplier(ptr, aTransaction); |
|
155 THTTPHdrVal contentLengthVal; |
|
156 TInt size = iCustomDataSupplier->OverallDataSize(); |
|
157 contentLengthVal.SetInt(size); |
|
158 headers.RemoveFieldPart(contentLengthString, 0); |
|
159 headers.SetFieldL(contentLengthString, contentLengthVal); |
|
160 } |
|
161 // Create and keep a data supplier if we plan to corrupt the data leading to a signature validation failure |
|
162 if (countSigValidateFailure > 0) |
|
163 { |
|
164 // Create the data supplier |
|
165 delete iCustomDataSupplier; |
|
166 iCustomDataSupplier = NULL; |
|
167 TPtrC8 ptr(KNullDesC8); |
|
168 iCustomDataSupplier = new (ELeave) TCustomDataSupplier(ptr, aTransaction); |
|
169 } |
|
170 if (countCorruptHTTPDataHeader > 0) |
|
171 { |
|
172 // Corrupt the header |
|
173 RStringF ocspResponse = stringPool.OpenFStringL(KOCSPContentTypeResponseCorrupted); |
|
174 CleanupClosePushL(ocspResponse); |
|
175 RStringF contentTypeString = stringPool.StringF(HTTP::EContentType, RHTTPSession::GetTable()); |
|
176 THTTPHdrVal contentTypeVal; |
|
177 contentTypeVal.SetStrF(ocspResponse); |
|
178 headers.RemoveFieldPart(contentTypeString, 0); |
|
179 headers.SetFieldL(contentTypeString, contentTypeVal); |
|
180 CleanupStack::PopAndDestroy(&ocspResponse); |
|
181 --countCorruptHTTPDataHeader; |
|
182 } |
|
183 if (countCorruptHTTPDataBodySizeLarge > 0 || countCorruptHTTPDataBodySizeSmall > 0) |
|
184 { |
|
185 // Corrupt the body size recorded in the header |
|
186 RStringF contentLengthString = stringPool.StringF(HTTP::EContentLength, RHTTPSession::GetTable()); |
|
187 THTTPHdrVal contentLengthVal; |
|
188 TInt err = headers.GetField(contentLengthString, 0, contentLengthVal); |
|
189 if ((err == KErrNone) && (contentLengthVal.Type() == THTTPHdrVal::KTIntVal)) |
|
190 { |
|
191 TInt bodySize = contentLengthVal.Int(); |
|
192 if (countCorruptHTTPDataBodySizeLarge > 0) |
|
193 { |
|
194 --bodySize; |
|
195 --countCorruptHTTPDataBodySizeLarge; |
|
196 } |
|
197 else |
|
198 { |
|
199 ++bodySize; |
|
200 --countCorruptHTTPDataBodySizeSmall; |
|
201 } |
|
202 contentLengthVal.SetInt(bodySize); |
|
203 headers.RemoveFieldPart(contentLengthString, 0); |
|
204 headers.SetFieldL(contentLengthString, contentLengthVal); |
|
205 } |
|
206 } |
|
207 } |
|
208 break; |
|
209 case THTTPEvent::EGotResponseBodyData: |
|
210 if (iCustomDataSupplier && (countInternalErrorResp > 0 || countTryLaterResp > 0 || countCorruptOCSPData > 0 || countSigValidateFailure > 0)) |
|
211 { |
|
212 aTransaction.Response().RemoveBody(); |
|
213 aTransaction.Response().SetBody(*iCustomDataSupplier); |
|
214 // Make sure state machine goes on |
|
215 aTransaction.Response().SetStatusCode(THTTPEvent::EResponseComplete); |
|
216 } |
|
217 break; |
|
218 case THTTPEvent::ESucceeded: |
|
219 case THTTPEvent::EFailed: |
|
220 // Delay response (numDelayResp is in milliseconds) |
|
221 if (numDelayResp > 0) |
|
222 { |
|
223 User::After(numDelayResp * KTimeMilliToMicro); |
|
224 } |
|
225 // Deliberate fall through case |
|
226 case THTTPEvent::ECancel: |
|
227 if (!iLogLineCompleted) |
|
228 { |
|
229 // End of the HTTP transaction |
|
230 iLogLineCompleted = ETrue; |
|
231 LogTransactionEndL(aTransaction); |
|
232 } |
|
233 // Since EGotResponseBodyData event can happen more than once per transaction ensure to decrement |
|
234 // counters only once |
|
235 if (!iDataSupplied) |
|
236 { |
|
237 if (countInternalErrorResp > 0) |
|
238 { |
|
239 --countInternalErrorResp; |
|
240 } |
|
241 if (countTryLaterResp > 0) |
|
242 { |
|
243 --countTryLaterResp; |
|
244 } |
|
245 if (countCorruptOCSPData > 0) |
|
246 { |
|
247 --countCorruptOCSPData; |
|
248 } |
|
249 if (countSigValidateFailure > 0) |
|
250 { |
|
251 --countSigValidateFailure; |
|
252 } |
|
253 iDataSupplied = ETrue; |
|
254 } |
|
255 break; |
|
256 default: |
|
257 break; |
|
258 } |
|
259 |
|
260 // Write the parameters back to keep them persistent between retries |
|
261 WriteTestParameters(countDropResp, |
|
262 countCorruptHTTPDataHeader, countCorruptHTTPDataBodySizeLarge, countCorruptHTTPDataBodySizeSmall, |
|
263 countCorruptOCSPData, |
|
264 countInternalErrorResp, countTryLaterResp, |
|
265 countSigValidateFailure); |
|
266 } |
|
267 |
|
268 TInt COCSPHTTPFilter::MHFRunError(TInt /*aError*/, |
|
269 RHTTPTransaction aTransaction, |
|
270 const THTTPEvent& /*aEvent*/) |
|
271 { |
|
272 // If anything left, we've run out of memory or something |
|
273 // similarly catastrophic has gone wrong. |
|
274 aTransaction.Fail(); |
|
275 return KErrNone; |
|
276 } |
|
277 |
|
278 // Logs the transaction method used and the current (start) time |
|
279 void COCSPHTTPFilter::LogTransactionStartL(const RHTTPTransaction& aTransaction) |
|
280 { |
|
281 // Get the transaction method being used (GET/POST) and log it |
|
282 RHTTPRequest request = aTransaction.Request(); |
|
283 RStringF method = request.Method(); |
|
284 TTime time; |
|
285 time.HomeTime(); |
|
286 TInt64 intTime = time.Int64(); |
|
287 RBuf8 logText; |
|
288 logText.CreateL(255); |
|
289 CleanupClosePushL(logText); |
|
290 logText.Format(KFilterLogFormat1, &method.DesC(), intTime); |
|
291 User::LeaveIfError(iLogFile.Write(logText)); |
|
292 CleanupStack::PopAndDestroy(&logText); |
|
293 } |
|
294 |
|
295 // Logs the current (end) time |
|
296 void COCSPHTTPFilter::LogTransactionEndL(const RHTTPTransaction& /*aTransaction*/) |
|
297 { |
|
298 TTime time; |
|
299 time.HomeTime(); |
|
300 TInt64 intTime = time.Int64(); |
|
301 RBuf8 logText; |
|
302 logText.CreateL(255); |
|
303 CleanupClosePushL(logText); |
|
304 logText.Format(KFilterLogFormat2, intTime); |
|
305 User::LeaveIfError(iLogFile.Write(logText)); |
|
306 CleanupStack::PopAndDestroy(&logText); |
|
307 } |
|
308 |
|
309 // Read test parameters using Publish & Subscribe method |
|
310 void COCSPHTTPFilter::ReadTestParameters(TInt& aNumDelayResp, TInt& aCountDropResp, |
|
311 TInt& aCountCorruptHTTPDataHeader, TInt& aCountCorruptHTTPDataBodySizeLarge, TInt& aCountCorruptHTTPDataBodySizeSmall, |
|
312 TInt& aCountCorruptOCSPData, |
|
313 TInt& aCountInternalErrorResp, TInt& aCountTryLaterResp, |
|
314 TInt& aCountSigValidateFailure) |
|
315 { |
|
316 // Set default values which will be used if P&S doesn't exist |
|
317 aNumDelayResp = aCountDropResp = |
|
318 aCountCorruptHTTPDataHeader = aCountCorruptHTTPDataBodySizeLarge = aCountCorruptHTTPDataBodySizeSmall = |
|
319 aCountCorruptOCSPData = |
|
320 aCountInternalErrorResp = aCountTryLaterResp = |
|
321 aCountSigValidateFailure = 0; |
|
322 |
|
323 TUid categoryUid = TUid::Uid(KFilterParametersCategoryUID); |
|
324 RProperty::Get(categoryUid, KFilterParameterNumDelayResp, aNumDelayResp); |
|
325 RProperty::Get(categoryUid, KFilterParameterCountDropResp, aCountDropResp); |
|
326 RProperty::Get(categoryUid, KFilterParameterCountCorruptHTTPDataHeader, aCountCorruptHTTPDataHeader); |
|
327 RProperty::Get(categoryUid, KFilterParameterCountCorruptHTTPDataBodySizeLarge, aCountCorruptHTTPDataBodySizeLarge); |
|
328 RProperty::Get(categoryUid, KFilterParameterCountCorruptHTTPDataBodySizeSmall, aCountCorruptHTTPDataBodySizeSmall); |
|
329 RProperty::Get(categoryUid, KFilterParameterCountCorruptOCSPData, aCountCorruptOCSPData); |
|
330 RProperty::Get(categoryUid, KFilterParameterCountInternalErrorResp, aCountInternalErrorResp); |
|
331 RProperty::Get(categoryUid, KFilterParameterCountTryLaterResp, aCountTryLaterResp); |
|
332 RProperty::Get(categoryUid, KFilterParameterCountSigValidateFailure, aCountSigValidateFailure); |
|
333 } |
|
334 |
|
335 // To maintain persistence between retry attempts store the updated counts back |
|
336 void COCSPHTTPFilter::WriteTestParameters(TInt aCountDropResp, |
|
337 TInt aCountCorruptHTTPDataHeader, TInt aCountCorruptHTTPDataBodySizeLarge, TInt aCountCorruptHTTPDataBodySizeSmall, |
|
338 TInt aCountCorruptOCSPData, |
|
339 TInt aCountInternalErrorResp, TInt aCountTryLaterResp, |
|
340 TInt aCountSigValidateFailure) |
|
341 { |
|
342 TUid categoryUid = TUid::Uid(KFilterParametersCategoryUID); |
|
343 RProperty::Set(categoryUid, KFilterParameterCountDropResp, aCountDropResp); |
|
344 RProperty::Set(categoryUid, KFilterParameterCountCorruptHTTPDataHeader, aCountCorruptHTTPDataHeader); |
|
345 RProperty::Set(categoryUid, KFilterParameterCountCorruptHTTPDataBodySizeLarge, aCountCorruptHTTPDataBodySizeLarge); |
|
346 RProperty::Set(categoryUid, KFilterParameterCountCorruptHTTPDataBodySizeSmall, aCountCorruptHTTPDataBodySizeSmall); |
|
347 RProperty::Set(categoryUid, KFilterParameterCountCorruptOCSPData, aCountCorruptOCSPData); |
|
348 RProperty::Set(categoryUid, KFilterParameterCountInternalErrorResp, aCountInternalErrorResp); |
|
349 RProperty::Set(categoryUid, KFilterParameterCountTryLaterResp, aCountTryLaterResp); |
|
350 RProperty::Set(categoryUid, KFilterParameterCountSigValidateFailure, aCountSigValidateFailure); |
|
351 } |
|
352 |
|
353 TCustomDataSupplier::TCustomDataSupplier(TPtrC8& aData, RHTTPTransaction& aTransaction) |
|
354 { |
|
355 iData.Set(aData); |
|
356 iTransaction = &aTransaction; |
|
357 iDataSupplied = EFalse; |
|
358 iOriginalSupplier = aTransaction.Response().Body(); |
|
359 } |
|
360 |
|
361 // Methods from MHTTPDataSupplier |
|
362 TBool TCustomDataSupplier::GetNextDataPart(TPtrC8& aDataPart) |
|
363 { |
|
364 // Check if we need to simulate a signature validation failure |
|
365 if (iData == KNullDesC8) |
|
366 { |
|
367 // Get the original data |
|
368 iOriginalSupplier->GetNextDataPart(aDataPart); |
|
369 iCorruptData.Create(aDataPart); |
|
370 // Corrupt it (byte is part of ResponderID field) |
|
371 --iCorruptData[KOCSPResponderIDOffset]; |
|
372 // Send it along |
|
373 aDataPart.Set(iCorruptData); |
|
374 } |
|
375 else |
|
376 { |
|
377 // Consume the original data and pass back canned response |
|
378 iOriginalSupplier->GetNextDataPart(aDataPart); |
|
379 if (!iDataSupplied) |
|
380 { |
|
381 aDataPart.Set(iData); |
|
382 iDataSupplied = ETrue; |
|
383 } |
|
384 else |
|
385 { |
|
386 // no data |
|
387 aDataPart.Set(KNullDesC8); |
|
388 } |
|
389 } |
|
390 return ETrue; |
|
391 } |
|
392 |
|
393 void TCustomDataSupplier::ReleaseData() |
|
394 { |
|
395 iCorruptData.Close(); |
|
396 // Call original method to ensure it does it's work to let the state machine go on |
|
397 iOriginalSupplier->ReleaseData(); |
|
398 // We are done supplying canned response so restore original supplier |
|
399 iTransaction->Response().SetBody(*iOriginalSupplier); |
|
400 } |
|
401 |
|
402 TInt TCustomDataSupplier::OverallDataSize() |
|
403 { |
|
404 return iData.Length(); |
|
405 } |
|
406 |
|
407 TInt TCustomDataSupplier::Reset() |
|
408 { |
|
409 return KErrNotSupported; |
|
410 } |