|
1 /* |
|
2 * Copyright (c) 2005 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of the License "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Implementation of HttpCacheUtil |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 // INCLUDE FILES |
|
20 #include "HttpCacheUtil.h" |
|
21 #include <http/rhttpheaders.h> |
|
22 #include <http/RHTTPTransaction.h> |
|
23 #include <http.h> |
|
24 #include <stdlib.h> |
|
25 #include <string.h> |
|
26 #include <stringpool.h> |
|
27 #include <flogger.h> |
|
28 #include "TInternetdate.h" |
|
29 #include "HttpFilterCommonStringsExt.h" |
|
30 #include "HttpCacheEntry.h" |
|
31 |
|
32 // EXTERNAL DATA STRUCTURES |
|
33 |
|
34 // EXTERNAL FUNCTION PROTOTYPES |
|
35 |
|
36 // CONSTANTS |
|
37 _LIT8( KHttpNewLine, "\r\n" ); |
|
38 // if you change it here, change in HeadersToBufferL too |
|
39 _LIT8( KHttpFieldSeparator, ": " ); |
|
40 _LIT8( KHttpValueSep, "," ); |
|
41 const TInt KMaxHeaderStrLen = 1024; |
|
42 _LIT8( KHttpContentType, "Content-Type" ); |
|
43 |
|
44 #ifdef __CACHELOG__ |
|
45 _LIT( KDateString,"%D%M%Y%/0%1%/1%2%/2%3%/3 %-B%:0%J%:1%T%:2%S%.%*C4%:3%+B"); |
|
46 _LIT( KHttpCacheGeneralFileName, "cachehandler.txt" ); |
|
47 _LIT( KHttpCacheHashFileName, "hash.txt" ); |
|
48 const TInt KCurrentLogLevel = 0; |
|
49 #endif // __CACHELOG__ |
|
50 |
|
51 // MACROS |
|
52 |
|
53 // LOCAL CONSTANTS AND MACROS |
|
54 |
|
55 // MODULE DATA STRUCTURES |
|
56 |
|
57 // LOCAL FUNCTION PROTOTYPES |
|
58 |
|
59 // FORWARD DECLARATIONS |
|
60 |
|
61 // ============================= LOCAL FUNCTIONS =============================== |
|
62 |
|
63 // ============================ MEMBER FUNCTIONS =============================== |
|
64 |
|
65 // ============================ MEMBER FUNCTIONS =============================== |
|
66 |
|
67 // ----------------------------------------------------------------------------- |
|
68 // HttpCacheUtil::SetEntryOnTransL |
|
69 // |
|
70 // ----------------------------------------------------------------------------- |
|
71 // |
|
72 void HttpCacheUtil::SetEntryOnTransL( |
|
73 CHttpCacheEntry& aEntry, |
|
74 RHTTPTransaction& aTrans, |
|
75 RStringF& aCacheEntryStr ) |
|
76 { |
|
77 RHTTPTransactionPropertySet propSet = aTrans.PropertySet(); |
|
78 |
|
79 // set transaction property with the event handler callback functions' pointer |
|
80 THTTPHdrVal tokenVal = (TInt)(CHttpCacheEntry*)&aEntry; |
|
81 aTrans.PropertySet().RemoveProperty( aCacheEntryStr ); |
|
82 aTrans.PropertySet().SetPropertyL( aCacheEntryStr, tokenVal ); |
|
83 } |
|
84 |
|
85 // ----------------------------------------------------------------------------- |
|
86 // HttpCacheUtil::EntryOnTransL |
|
87 // |
|
88 // ----------------------------------------------------------------------------- |
|
89 // |
|
90 CHttpCacheEntry* HttpCacheUtil::EntryOnTransL( |
|
91 RHTTPTransaction& aTrans, |
|
92 RStringF& aCacheEntryStr ) |
|
93 { |
|
94 // |
|
95 CHttpCacheEntry* entry = NULL; |
|
96 // |
|
97 THTTPHdrVal entryPtr; |
|
98 RHTTPTransactionPropertySet propSet = aTrans.PropertySet(); |
|
99 // this is a transaction, already forwarded to download manager |
|
100 if( propSet.Property( aCacheEntryStr, entryPtr ) ) |
|
101 { |
|
102 entry = REINTERPRET_CAST( CHttpCacheEntry*, entryPtr.Int() ); |
|
103 } |
|
104 return entry; |
|
105 } |
|
106 |
|
107 // ----------------------------------------------------------------------------- |
|
108 // HttpCacheUtil::HeadersToBufferLC |
|
109 // |
|
110 // ----------------------------------------------------------------------------- |
|
111 // |
|
112 HBufC8* HttpCacheUtil::HeadersToBufferLC( |
|
113 RHTTPHeaders& aHttpHeaders, |
|
114 RStringPool& aStrP ) |
|
115 { |
|
116 // Get an iterator for the collection of response headers |
|
117 HBufC8* headerString = HBufC8::NewL( KMaxHeaderStrLen ); |
|
118 TPtr8 headerPtr( headerString->Des() ); |
|
119 TInt newLength( 0 ); |
|
120 THTTPHdrFieldIter it = aHttpHeaders.Fields(); |
|
121 |
|
122 while( it.AtEnd() == EFalse ) |
|
123 { |
|
124 TPtrC8 rawData; |
|
125 // Get name of next header field |
|
126 RStringTokenF fieldName = it(); |
|
127 RStringF fieldNameStr = aStrP.StringF( fieldName ); |
|
128 aHttpHeaders.GetRawField( fieldNameStr, rawData ); |
|
129 |
|
130 newLength+=( fieldNameStr.DesC().Length() + KHttpFieldSeparator().Length() + rawData.Length() + |
|
131 KHttpNewLine().Length() ); |
|
132 |
|
133 if( headerPtr.MaxLength() < newLength ) |
|
134 { |
|
135 // realloc |
|
136 HBufC8* temp = HBufC8::New( newLength + KMaxHeaderStrLen ); |
|
137 if( !temp ) |
|
138 { |
|
139 delete headerString; |
|
140 User::LeaveNoMemory(); |
|
141 } |
|
142 temp->Des().Copy( headerPtr ); |
|
143 delete headerString; |
|
144 headerString = temp; |
|
145 headerPtr.Set( headerString->Des() ); |
|
146 } |
|
147 // append |
|
148 headerPtr.Append( fieldNameStr.DesC() ); |
|
149 headerPtr.Append( KHttpFieldSeparator ); |
|
150 headerPtr.Append( rawData ); |
|
151 headerPtr.Append( KHttpNewLine ); |
|
152 ++it; |
|
153 } |
|
154 CleanupStack::PushL( headerString ); |
|
155 return headerString; |
|
156 } |
|
157 |
|
158 // ----------------------------------------------------------------------------- |
|
159 // HttpCacheUtil::BufferToHeadersL |
|
160 // |
|
161 // ----------------------------------------------------------------------------- |
|
162 // |
|
163 void HttpCacheUtil::BufferToHeadersL( |
|
164 const TDesC8& aHeaderStr, |
|
165 RHTTPHeaders& aHttpHeaders, |
|
166 RStringPool& aStrP ) |
|
167 { |
|
168 // take header str line by line |
|
169 TInt sepPos( 0 ); |
|
170 TInt lineEnd( 0 ); |
|
171 TPtrC8 headerStr( aHeaderStr ); |
|
172 |
|
173 while( lineEnd != KErrNotFound ) |
|
174 { |
|
175 lineEnd = headerStr.Find( KHttpNewLine ); |
|
176 sepPos = headerStr.Find( KHttpFieldSeparator ); |
|
177 |
|
178 if( lineEnd != KErrNotFound && sepPos != KErrNotFound && |
|
179 lineEnd > sepPos ) |
|
180 { |
|
181 // ETag: "9be043c1-175-793-41419a44" |
|
182 TPtrC8 line( headerStr.Left( lineEnd ) ); |
|
183 // ETag |
|
184 TPtrC8 key( line.Left( sepPos ) ); |
|
185 // "9be043c1-175-793-41419a44" |
|
186 TInt sepEnd( sepPos + KHttpFieldSeparator().Length() ); |
|
187 TPtrC8 value( line.Mid( sepEnd ) ); |
|
188 // add it to the http header |
|
189 RStringF nameStr = aStrP.OpenFStringL( key ); |
|
190 CleanupClosePushL( nameStr ); |
|
191 // remove if exists |
|
192 THTTPHdrVal headerValue; |
|
193 if( aHttpHeaders.GetField( nameStr, 0, headerValue ) == KErrNone ) |
|
194 { |
|
195 // |
|
196 aHttpHeaders.RemoveField( nameStr ); |
|
197 } |
|
198 TRAPD( err, aHttpHeaders.SetRawFieldL( nameStr, value, KHttpValueSep ) ); |
|
199 // leave OOM only |
|
200 if( err == KErrNoMemory ) |
|
201 { |
|
202 User::Leave( err ); |
|
203 } |
|
204 CleanupStack::PopAndDestroy(); // namestr |
|
205 |
|
206 // move string to the end of the line |
|
207 headerStr.Set( headerStr.Mid( lineEnd + KHttpNewLine().Length() ) ); |
|
208 } |
|
209 } |
|
210 } |
|
211 |
|
212 // ----------------------------------------------------------------------------- |
|
213 // HttpCacheUtil::BodyToBufferL |
|
214 // |
|
215 // ----------------------------------------------------------------------------- |
|
216 // |
|
217 HBufC8* HttpCacheUtil::BodyToBufferL( |
|
218 MHTTPDataSupplier& aBodySupplier ) |
|
219 { |
|
220 // get it from the transaction |
|
221 TPtrC8 ptr; |
|
222 aBodySupplier.GetNextDataPart( ptr ); |
|
223 // |
|
224 return ptr.AllocL(); |
|
225 } |
|
226 |
|
227 // ----------------------------------------------------------------------------- |
|
228 // HttpCacheUtil::MergeHeadersLC |
|
229 // |
|
230 // ----------------------------------------------------------------------------- |
|
231 // |
|
232 HBufC8* HttpCacheUtil::MergeHeadersLC( |
|
233 const TDesC8& aCachedHeaderStr, |
|
234 RHTTPHeaders& aResponseHeaders, |
|
235 RStringPool& aStrP ) |
|
236 { |
|
237 // FYI: it uses the response header to merge |
|
238 // take header str line by line |
|
239 TInt sepPos( 0 ); |
|
240 TInt lineEnd( 0 ); |
|
241 TPtrC8 headerStr( aCachedHeaderStr ); |
|
242 |
|
243 while( lineEnd != KErrNotFound ) |
|
244 { |
|
245 lineEnd = headerStr.Find( KHttpNewLine ); |
|
246 sepPos = headerStr.Find( KHttpFieldSeparator ); |
|
247 |
|
248 if( lineEnd != KErrNotFound && sepPos != KErrNotFound && |
|
249 lineEnd > sepPos ) |
|
250 { |
|
251 // ETag: "9be043c1-175-793-41419a44" |
|
252 TPtrC8 line( headerStr.Left( lineEnd ) ); |
|
253 // ETag |
|
254 TPtrC8 key( line.Left( sepPos ) ); |
|
255 // "9be043c1-175-793-41419a44" |
|
256 TInt sepEnd( sepPos + KHttpFieldSeparator().Length() ); |
|
257 TPtrC8 value( line.Mid( sepEnd ) ); |
|
258 // add it to the http header |
|
259 RStringF nameStr = aStrP.OpenFStringL( key ); |
|
260 CleanupClosePushL( nameStr ); |
|
261 // avoid corrupted content type from the stack in case of 304 |
|
262 if(key == KHttpContentType) |
|
263 { |
|
264 aResponseHeaders.RemoveField(nameStr); |
|
265 } |
|
266 THTTPHdrVal tempVal; |
|
267 // add this field unless it already exists |
|
268 if( aResponseHeaders.GetField( nameStr, 0, tempVal ) == KErrNotFound ) |
|
269 { |
|
270 TRAPD( err, aResponseHeaders.SetRawFieldL( nameStr, value, KHttpValueSep ) ); |
|
271 // leave OOM only |
|
272 if( err == KErrNoMemory ) |
|
273 { |
|
274 User::Leave( err ); |
|
275 } |
|
276 } |
|
277 CleanupStack::PopAndDestroy(); // namestr |
|
278 // move string to the end of the line |
|
279 headerStr.Set( headerStr.Mid( lineEnd + KHttpNewLine().Length() ) ); |
|
280 } |
|
281 } |
|
282 // we've got the merged headers, let's covert it to buffer |
|
283 return HeadersToBufferLC( aResponseHeaders, aStrP ); |
|
284 } |
|
285 |
|
286 // ----------------------------------------------------------------------------- |
|
287 // HttpCacheUtil::AddHeaderLC |
|
288 // |
|
289 // ----------------------------------------------------------------------------- |
|
290 // |
|
291 HBufC8* HttpCacheUtil::AddHeaderLC( |
|
292 const TDesC8& aName, |
|
293 const TDesC8& aValue, |
|
294 const TDesC8& aHeaders ) |
|
295 { |
|
296 HBufC8* newHeaders = NULL; |
|
297 // first check if the header exists so |
|
298 // then all we need to do is to update the value |
|
299 TInt pos( aHeaders.FindF( aName ) ); |
|
300 // |
|
301 newHeaders = HBufC8::NewLC( aHeaders.Length() + aName.Length() + KHttpFieldSeparator().Length() + |
|
302 aValue.Length() + KHttpNewLine().Length() ); |
|
303 // |
|
304 if( pos != KErrNotFound ) |
|
305 { |
|
306 // replace old header with this new one |
|
307 TPtr8 newHeadersPtr( newHeaders->Des() ); |
|
308 // copy headers |
|
309 newHeadersPtr.Copy( aHeaders ); |
|
310 // check what we need to replace |
|
311 TPtrC8 leftover( aHeaders.Mid( pos ) ); |
|
312 // headers are terminated by \r\n |
|
313 TInt endPos( leftover.Find( KHttpNewLine ) ); |
|
314 if( endPos != KErrNotFound ) |
|
315 { |
|
316 // value pos |
|
317 TInt valuePos( pos + aName.Length() + KHttpFieldSeparator().Length() ); |
|
318 newHeadersPtr.Replace( valuePos, pos + endPos - valuePos, aValue ); |
|
319 } |
|
320 } |
|
321 else |
|
322 { |
|
323 // add new header |
|
324 // new header = old header\r\n aName: aValue\r\n |
|
325 TPtr8 newHeadersPtr( newHeaders->Des() ); |
|
326 // old headers |
|
327 newHeadersPtr.Copy( aHeaders ); |
|
328 // Cache Control |
|
329 newHeadersPtr.Append( aName ); |
|
330 // : |
|
331 newHeadersPtr.Append( KHttpFieldSeparator ); |
|
332 // max age=180 |
|
333 newHeadersPtr.Append( aValue ); |
|
334 // \r\n |
|
335 newHeadersPtr.Append( KHttpNewLine ); |
|
336 } |
|
337 return newHeaders; |
|
338 } |
|
339 |
|
340 // ----------------------------------------------------------------------------- |
|
341 // HttpCacheUtil::HeaderFieldFromBufferLC |
|
342 // |
|
343 // ----------------------------------------------------------------------------- |
|
344 // |
|
345 HBufC8* HttpCacheUtil::HeaderFieldFromBufferLC( |
|
346 const TDesC8& aHeaders, |
|
347 const TDesC8& aKey ) |
|
348 { |
|
349 HBufC8* value = NULL; |
|
350 // |
|
351 TInt pos( aHeaders.Find( aKey ) ); |
|
352 if( pos != KErrNotFound ) |
|
353 { |
|
354 // line: Cache-Control: max-age=180 |
|
355 TPtrC8 linePtr = aHeaders.Mid( pos ); |
|
356 // line end \r\n |
|
357 TInt lineEnd( linePtr.Find( KHttpNewLine ) ); |
|
358 // separator : |
|
359 TInt sepPos( linePtr.Find( KHttpFieldSeparator ) ); |
|
360 // |
|
361 if( sepPos != KErrNotFound ) |
|
362 { |
|
363 // fix line end |
|
364 lineEnd = lineEnd == KErrNotFound ? linePtr.Length(): lineEnd; |
|
365 // max-age=180 |
|
366 TInt valueLen( lineEnd - ( sepPos + KHttpFieldSeparator().Length() ) ); |
|
367 value = HBufC8::NewLC( valueLen ); |
|
368 value->Des().Copy( linePtr.Mid( sepPos + KHttpFieldSeparator().Length(), valueLen ) ); |
|
369 } |
|
370 } |
|
371 return value; |
|
372 } |
|
373 |
|
374 // ----------------------------------------------------------------------------- |
|
375 // HttpCacheUtil::MethodFromStr |
|
376 // |
|
377 // ----------------------------------------------------------------------------- |
|
378 // |
|
379 |
|
380 TCacheLoadMethod HttpCacheUtil::MethodFromStr( |
|
381 RStringF aMethodStr, |
|
382 RStringPool aStrP ) |
|
383 { |
|
384 if( aMethodStr == aStrP.StringF( HTTP::EGET, RHTTPSession::GetTable() ) ) |
|
385 { |
|
386 return EMethodGet; |
|
387 } |
|
388 if( aMethodStr == aStrP.StringF(HTTP::ECONNECT, RHTTPSession::GetTable() ) ) |
|
389 { |
|
390 return EMethodConnect; |
|
391 } |
|
392 if( aMethodStr == aStrP.StringF(HTTP::EDELETE, RHTTPSession::GetTable() ) ) |
|
393 { |
|
394 return EMethodDelete; |
|
395 } |
|
396 if( aMethodStr == aStrP.StringF(HTTP::EHEAD, RHTTPSession::GetTable() ) ) |
|
397 { |
|
398 return EMethodHead; |
|
399 } |
|
400 if( aMethodStr == aStrP.StringF(HTTP::EOPTIONS, RHTTPSession::GetTable() ) ) |
|
401 { |
|
402 return EMethodOptions; |
|
403 } |
|
404 if( aMethodStr == aStrP.StringF(HTTP::EPUT, RHTTPSession::GetTable() ) ) |
|
405 { |
|
406 return EMethodPut; |
|
407 } |
|
408 if( aMethodStr == aStrP.StringF(HTTP::EPOST, RHTTPSession::GetTable() ) ) |
|
409 { |
|
410 return EMethodPost; |
|
411 } |
|
412 if( aMethodStr == aStrP.StringF(HTTP::ETRACE, RHTTPSession::GetTable() ) ) |
|
413 { |
|
414 return EMethodTrace; |
|
415 } |
|
416 return EMethodGet; |
|
417 } |
|
418 |
|
419 |
|
420 // ----------------------------------------------------------------------------- |
|
421 // HttpCacheUtil::ProtectedEntry |
|
422 // |
|
423 // Returns true if the entry is protected |
|
424 // |
|
425 // ----------------------------------------------------------------------------- |
|
426 // |
|
427 TBool HttpCacheUtil::ProtectedEntry( |
|
428 RHTTPHeaders& aHttpHeaders, |
|
429 RStringPool& aStrP ) |
|
430 { |
|
431 TBool protectedEntry( EFalse ); |
|
432 // |
|
433 THTTPHdrVal contType; |
|
434 RStringF fieldName = aStrP.StringF( HTTP::EContentType, RHTTPSession::GetTable() ); |
|
435 // |
|
436 if( aHttpHeaders.GetField( fieldName, 0, contType ) == KErrNone ) |
|
437 { |
|
438 if( contType.Type() == THTTPHdrVal::KStrFVal ) |
|
439 { |
|
440 RStringF fieldValStr = aStrP.StringF( contType.StrF() ); |
|
441 // css and javascrip are protected entries |
|
442 protectedEntry = fieldValStr.DesC().Find( _L8("text/css") ) != KErrNotFound || |
|
443 fieldValStr.DesC().Find( _L8("javascript") ) != KErrNotFound; |
|
444 } |
|
445 } |
|
446 return protectedEntry; |
|
447 } |
|
448 |
|
449 // ----------------------------------------------------------------------------- |
|
450 // HttpCacheUtil::CacheTimeIsFresh |
|
451 // |
|
452 // Returns true if the entry is fresh. |
|
453 // |
|
454 // ----------------------------------------------------------------------------- |
|
455 // |
|
456 TBool HttpCacheUtil::CacheTimeIsFresh( |
|
457 const RHTTPHeaders& aRequestHeaders, |
|
458 const RHTTPHeaders& aCachedHeaders, |
|
459 RStringPool aStrP ) |
|
460 { |
|
461 TInt status( KErrNone ); |
|
462 TBool isFresh( EFalse ); |
|
463 TInt64 freshness; |
|
464 TInt64 age; |
|
465 TInt64 maxAge; |
|
466 TInt64 minFresh; |
|
467 TInt64 maxStale; |
|
468 TTime cachedRequestTime; |
|
469 TTime cachedResponseTime; |
|
470 |
|
471 TTime date; |
|
472 TInt err; |
|
473 THTTPHdrVal dateValue; |
|
474 RStringF fieldName; |
|
475 |
|
476 // Get the date from the headers |
|
477 fieldName = aStrP.StringF( HTTP::EDate, RHTTPSession::GetTable() ); |
|
478 err = aCachedHeaders.GetField( fieldName, 0, dateValue ); |
|
479 if( err == KErrNotFound || dateValue.Type() != THTTPHdrVal::KDateVal ) |
|
480 { |
|
481 date = 0; |
|
482 } |
|
483 else |
|
484 { |
|
485 date = TTime( dateValue.DateTime() ); |
|
486 } |
|
487 cachedRequestTime = date; |
|
488 cachedResponseTime = date; |
|
489 |
|
490 // Get Max Age header. If maxAge = 0, then must revalidate. |
|
491 status = GetCacheControls( aCachedHeaders, &maxAge, NULL, NULL, NULL, NULL, NULL, aStrP ); |
|
492 if( status == KErrNone && maxAge == 0 ) |
|
493 { |
|
494 return EFalse; //Must revalidate |
|
495 } |
|
496 |
|
497 // Get the freshness and age of the cachedHeaders |
|
498 freshness = Freshness( aCachedHeaders, cachedResponseTime, aStrP ); |
|
499 age = Age( aCachedHeaders, cachedRequestTime, cachedResponseTime, aStrP ); |
|
500 #ifdef __CACHELOG__ |
|
501 TBuf<50> dateString; |
|
502 TTime fr( freshness ); |
|
503 |
|
504 fr.FormatL( dateString, KDateString ); |
|
505 HttpCacheUtil::WriteLog( 0, _L( "fresness" ) ); |
|
506 HttpCacheUtil::WriteLog( 0, dateString ); |
|
507 |
|
508 TTime ca( age ); |
|
509 ca.FormatL( dateString, KDateString ); |
|
510 HttpCacheUtil::WriteLog( 0, _L( "age" ) ); |
|
511 HttpCacheUtil::WriteLog( 0, dateString ); |
|
512 #endif // __CACHELOG__ |
|
513 |
|
514 // Get useful cache-control directives from the requestHeaders |
|
515 status = GetCacheControls( aRequestHeaders, &maxAge, &minFresh, &maxStale, NULL, NULL, NULL, aStrP ); |
|
516 if( status == KErrNone ) |
|
517 { |
|
518 // Above values for maxAge, minFresh and maxStale are in seconds |
|
519 // Convert to micro seconds before using it. |
|
520 |
|
521 // If maxStale is present it means that it is willing to |
|
522 // accept a response maxStale seconds after it expires. To |
|
523 // allow this freshness is extended by maxStale seconds. |
|
524 if( maxStale != -1 ) |
|
525 { |
|
526 if( maxStale == MAKE_TINT64( KMaxTInt, KMaxTInt ) ) |
|
527 { |
|
528 freshness = MAKE_TINT64( KMaxTInt, KMaxTInt ); |
|
529 } |
|
530 else |
|
531 { |
|
532 freshness += maxStale * 1000 * 1000; |
|
533 } |
|
534 } |
|
535 // If minFresh is present reject it if it would expire |
|
536 // within minFresh seconds. |
|
537 if( minFresh != -1 ) |
|
538 { |
|
539 if( ( age + minFresh * 1000 * 1000 ) > freshness ) |
|
540 { |
|
541 return EFalse; |
|
542 } |
|
543 } |
|
544 // If the age > request's maxAge, reject it |
|
545 if( maxAge != -1 ) |
|
546 { |
|
547 if( age > maxAge * 1000 * 1000 ) |
|
548 { |
|
549 return EFalse; |
|
550 } |
|
551 } |
|
552 // If age is less than freshness its fresh |
|
553 // If age == 0 and freshness == 0 - revalidation is needed |
|
554 if( freshness && age <= freshness ) |
|
555 { |
|
556 isFresh = ETrue; |
|
557 } |
|
558 } |
|
559 return isFresh; |
|
560 } |
|
561 |
|
562 // ----------------------------------------------------------------------------- |
|
563 // HttpCacheUtil::AddValidationHeaders |
|
564 // |
|
565 // Adds appropriate headers to make the given "request" conditional. |
|
566 // |
|
567 // ----------------------------------------------------------------------------- |
|
568 // |
|
569 void HttpCacheUtil::AddValidationHeaders( |
|
570 const RHTTPHeaders aCachedHeaders, |
|
571 RHTTPHeaders& aRequestHeaders, |
|
572 RStringPool& aStrP ) |
|
573 { |
|
574 THTTPHdrVal lastMod; |
|
575 THTTPHdrVal eTag; |
|
576 RStringF fieldName; |
|
577 TInt err; |
|
578 |
|
579 // Get the entry's last-modified header |
|
580 fieldName = aStrP.StringF( HTTP::ELastModified, RHTTPSession::GetTable() ); |
|
581 err = aCachedHeaders.GetField( fieldName, 0, lastMod ); |
|
582 |
|
583 // If last-Modified is present add an If-Modified-Since header |
|
584 if( err != KErrNotFound ) |
|
585 { |
|
586 fieldName = aStrP.StringF( HTTP::EIfModifiedSince, RHTTPSession::GetTable() ); |
|
587 TRAP_IGNORE( aRequestHeaders.SetFieldL( fieldName, lastMod ) ); |
|
588 } |
|
589 |
|
590 // Get the entry's ETag header |
|
591 fieldName = aStrP.StringF( HTTP::EETag, RHTTPSession::GetTable() ); |
|
592 err = aCachedHeaders.GetField( fieldName, 0, eTag ); |
|
593 |
|
594 // If etag is present add an etag header |
|
595 if( err != KErrNotFound ) |
|
596 { |
|
597 fieldName = aStrP.StringF( HTTP::EIfNoneMatch, RHTTPSession::GetTable() ); |
|
598 TRAP_IGNORE( aRequestHeaders.SetFieldL( fieldName, eTag ) ); |
|
599 } |
|
600 } |
|
601 |
|
602 // ----------------------------------------------------------------------------- |
|
603 // HttpCacheUtil::OperatorCacheContent |
|
604 // This function checks if aUrl is part of the operator domain. for examp: if aUrl->http://www.opdomain.com/pub/index.html |
|
605 // and operator domain is http://www.opdomain.com/pub then it returns ETrue. |
|
606 // ----------------------------------------------------------------------------- |
|
607 // |
|
608 TBool HttpCacheUtil::OperatorCacheContent( |
|
609 const TDesC8& aOpDomain, |
|
610 const TDesC8& aUrl ) |
|
611 { |
|
612 TUriParser8 opDomain; |
|
613 TUriParser8 url; |
|
614 TBool found( EFalse ); |
|
615 |
|
616 // check if both URIs are correct. |
|
617 if( opDomain.Parse( aOpDomain ) == KErrNone && url.Parse( aUrl ) == KErrNone ) |
|
618 { |
|
619 // host value must be the same. for examp: www.operator.co is different from www.operator.com |
|
620 // note: check if compare is case sensitive! |
|
621 // note2: if http:// is missing from operator domain then TUriParser8 takes the host part as path. |
|
622 if( opDomain.Compare( url, EUriHost ) == 0 ) |
|
623 { |
|
624 // further checking on the path value. domain path must be a substring of the given url. |
|
625 // for examp: op path: /news |
|
626 // url path: /news/local_news.html |
|
627 const TDesC8& opPath = opDomain.Extract( EUriPath ); |
|
628 if( opPath.Length() ) |
|
629 { |
|
630 const TDesC8& urlPath = url.Extract( EUriPath ); |
|
631 // we don't take the content if the url path is not defined (while domain path is) or the |
|
632 // domain path is not a substring of url path. actually url path must start with domain path |
|
633 // op path: /news url path: /media/news/local_news.html is not valid. |
|
634 found = ( urlPath.Length() && urlPath.FindF( opPath ) == 0 ); |
|
635 } |
|
636 else |
|
637 { |
|
638 // no opPath means that we take every content on this host. |
|
639 found = ETrue; |
|
640 } |
|
641 } |
|
642 } |
|
643 return found; |
|
644 } |
|
645 |
|
646 |
|
647 // ----------------------------------------------------------------------------- |
|
648 // HttpCacheUtil::VSSCacheContent |
|
649 // |
|
650 // ----------------------------------------------------------------------------- |
|
651 // |
|
652 TBool HttpCacheUtil::VSSCacheContent( const TDesC8& aUrl, const HBufC8* aWhiteList ) |
|
653 { |
|
654 TBool found( EFalse ); |
|
655 if( aWhiteList ) |
|
656 { |
|
657 TUint8* bufPtr = (TUint8* ) aWhiteList->Ptr(); |
|
658 TUint8* startBufPtr = bufPtr; |
|
659 TUint len = aWhiteList->Length(); |
|
660 TInt i, startUri, uriLen; |
|
661 TPtrC8 uri ( aUrl ); |
|
662 for(i=0; i < len; i++) |
|
663 { |
|
664 startUri = i; |
|
665 for( ;( ( *bufPtr != ';' ) && ( i<len ) ) ; i++, bufPtr++ ) ; |
|
666 uriLen = i - startUri; |
|
667 if( i == len ) |
|
668 { |
|
669 uriLen += 2; //For getting total length |
|
670 } |
|
671 TPtrC8 uriDomain( startBufPtr, uriLen); |
|
672 if ( OperatorCacheContent( uriDomain, uri ) ) |
|
673 { |
|
674 found = ETrue; |
|
675 break; |
|
676 } |
|
677 startBufPtr = ++bufPtr; |
|
678 i++; |
|
679 } //end for() |
|
680 } //end for() |
|
681 return found; |
|
682 } |
|
683 |
|
684 // ----------------------------------------------------------------------------- |
|
685 // HttpCacheUtil::PragmaNoCache |
|
686 // |
|
687 // ----------------------------------------------------------------------------- |
|
688 // |
|
689 TBool HttpCacheUtil::PragmaNoCache( |
|
690 RHTTPTransaction& aTrans ) |
|
691 { |
|
692 // |
|
693 RHTTPHeaders requestHeaders = aTrans.Request().GetHeaderCollection(); |
|
694 RStringPool strP = aTrans.Session().StringPool(); |
|
695 RStringF fieldName = strP.StringF( HTTP::ENoCache, RHTTPSession::GetTable() ); |
|
696 |
|
697 // check no-cache |
|
698 THTTPHdrVal headerVal; |
|
699 TInt noCacheField( requestHeaders.GetField( fieldName, 0, headerVal ) ); |
|
700 // check no-store |
|
701 fieldName = strP.StringF( HTTP::ENoStore, RHTTPSession::GetTable() ); |
|
702 TInt noStoreField( requestHeaders.GetField( fieldName, 0, headerVal ) ); |
|
703 // |
|
704 return( noCacheField == KErrNone || noStoreField == KErrNone ); |
|
705 } |
|
706 |
|
707 // ----------------------------------------------------------------------------- |
|
708 // HttpCacheUtil::GetHeaderFileName |
|
709 // |
|
710 // ----------------------------------------------------------------------------- |
|
711 // |
|
712 void HttpCacheUtil::GetHeaderFileName( |
|
713 const TDesC& aBodyFileName, |
|
714 TDes& aHeaderFileName ) |
|
715 { |
|
716 // body file name = foo |
|
717 // header file name = foo.h |
|
718 aHeaderFileName.Copy( aBodyFileName ); |
|
719 // take the filename and append the new extension |
|
720 aHeaderFileName.Append( KHttpCacheHeaderExt() ); |
|
721 } |
|
722 |
|
723 // ----------------------------------------------------------------------------- |
|
724 // HttpCacheUtil::AdjustExpirationTime |
|
725 // |
|
726 // ----------------------------------------------------------------------------- |
|
727 // |
|
728 void HttpCacheUtil::AdjustExpirationTimeL( |
|
729 RHTTPHeaders& aResponseHeaders, |
|
730 RStringPool& aStrP ) |
|
731 { |
|
732 const TStringTable& stringTable = RHTTPSession::GetTable(); |
|
733 THTTPHdrVal hdrVal; |
|
734 |
|
735 if( aResponseHeaders.GetField( aStrP.StringF( HTTP::EExpires, stringTable ), 0, hdrVal ) == KErrNone ) |
|
736 { |
|
737 HttpCacheUtil::WriteLog( 0, _L( "adjust expiration time from" ) ); |
|
738 |
|
739 TTime expDate( hdrVal.DateTime() ); |
|
740 #ifdef __CACHELOG__ |
|
741 TBuf<50> dateString; |
|
742 TTime expTime( hdrVal.DateTime() ); |
|
743 |
|
744 expTime.FormatL( dateString, KDateString ); |
|
745 HttpCacheUtil::WriteLog( 0, dateString ); |
|
746 #endif // __CACHELOG__ |
|
747 // double it |
|
748 TTimeIntervalMinutes minutes; |
|
749 TTimeIntervalHours hours; |
|
750 TTime now; |
|
751 now.UniversalTime(); |
|
752 |
|
753 if( expDate.MinutesFrom( now, minutes ) == KErrNone ) |
|
754 { |
|
755 #ifdef __CACHELOG__ |
|
756 // |
|
757 now.FormatL( dateString, KDateString ); |
|
758 HttpCacheUtil::WriteLog( 0, _L( "current time" ) ); |
|
759 HttpCacheUtil::WriteLog( 0, dateString ); |
|
760 // |
|
761 now.FormatL( dateString, KDateString ); |
|
762 HttpCacheUtil::WriteLog( 0, _L( "expires in (minutes)" ), minutes.Int() ); |
|
763 #endif // __CACHELOG__ |
|
764 // |
|
765 expDate+=minutes; |
|
766 } |
|
767 // minutes owerflow? take hours instead |
|
768 else if( expDate.HoursFrom( now, hours ) == KErrNone ) |
|
769 { |
|
770 expDate+=hours; |
|
771 } |
|
772 else |
|
773 { |
|
774 // last resort |
|
775 TTimeIntervalDays days( expDate.DaysFrom( now ) ); |
|
776 expDate+=days; |
|
777 } |
|
778 // set new date on the response header |
|
779 aResponseHeaders.RemoveField( aStrP.StringF( HTTP::EExpires, stringTable ) ); |
|
780 // add it |
|
781 hdrVal.SetDateTime( expDate.DateTime() ); |
|
782 #ifdef __CACHELOG__ |
|
783 HttpCacheUtil::WriteLog( 0, _L( "to" ) ); |
|
784 // |
|
785 TTime newExptime( hdrVal.DateTime() ); |
|
786 newExptime.FormatL( dateString, KDateString ); |
|
787 HttpCacheUtil::WriteLog( 0, dateString ); |
|
788 |
|
789 TTimeIntervalMinutes min; |
|
790 |
|
791 newExptime.MinutesFrom( now, min ); |
|
792 |
|
793 HttpCacheUtil::WriteLog( 0, _L( "now it expires in (minutes)" ), min.Int() ); |
|
794 #endif // __CACHELOG__ |
|
795 aResponseHeaders.SetFieldL( aStrP.StringF( HTTP::EExpires, stringTable ), hdrVal ); |
|
796 } |
|
797 } |
|
798 |
|
799 // ----------------------------------------------------------------------------- |
|
800 // HttpCacheUtil::CacheNeedsUpdateL |
|
801 // |
|
802 // ----------------------------------------------------------------------------- |
|
803 // |
|
804 TBool HttpCacheUtil::CacheNeedsUpdateL( |
|
805 RHTTPHeaders& aResponseHeaders, |
|
806 const TDesC8& aCachedHeadersStr, |
|
807 RStringPool& aStrP ) |
|
808 { |
|
809 HBufC8* valueStr; |
|
810 TTime cachedDate( 0 ); |
|
811 TTime responseDate( 0 ); |
|
812 TTime cachedLastModified( 0 ); |
|
813 TTime responseLastModified( 0 ); |
|
814 RStringF cachedETag; |
|
815 RStringF responseETag; |
|
816 RStringF fieldName; |
|
817 TInternetDate value; |
|
818 THTTPHdrVal hdrValue; |
|
819 TBool eTag( EFalse ); |
|
820 TBool eLastModified( EFalse ); |
|
821 TBool eDate( EFalse ); |
|
822 TInt status; |
|
823 TBool update( ETrue ); |
|
824 |
|
825 // date |
|
826 fieldName = aStrP.StringF( HTTP::EDate, RHTTPSession::GetTable() ); |
|
827 // new |
|
828 status = aResponseHeaders.GetField( fieldName, 0, hdrValue ); |
|
829 if( status == KErrNone ) |
|
830 { |
|
831 responseDate = TTime( hdrValue.DateTime() ); |
|
832 // orig |
|
833 valueStr = HttpCacheUtil::HeaderFieldFromBufferLC( aCachedHeadersStr, fieldName.DesC() ); |
|
834 if( valueStr ) |
|
835 { |
|
836 eDate = ETrue; |
|
837 value.SetDateL( valueStr->Des() ); |
|
838 cachedDate = TTime( value.DateTime() ); |
|
839 CleanupStack::PopAndDestroy(); // valueStr |
|
840 } |
|
841 } |
|
842 |
|
843 |
|
844 // last modified |
|
845 fieldName = aStrP.StringF( HTTP::ELastModified, RHTTPSession::GetTable() ); |
|
846 // new |
|
847 status = aResponseHeaders.GetField( fieldName, 0, hdrValue ); |
|
848 if( status == KErrNone ) |
|
849 { |
|
850 responseLastModified = TTime( hdrValue.DateTime() ); |
|
851 // orig |
|
852 valueStr = HttpCacheUtil::HeaderFieldFromBufferLC( aCachedHeadersStr, fieldName.DesC() ); |
|
853 if( valueStr ) |
|
854 { |
|
855 eLastModified = ETrue; |
|
856 value.SetDateL( valueStr->Des() ); |
|
857 cachedLastModified = TTime( value.DateTime() ); |
|
858 CleanupStack::PopAndDestroy(); // valueStr |
|
859 } |
|
860 } |
|
861 |
|
862 // Etag |
|
863 fieldName = aStrP.StringF( HTTP::EETag, RHTTPSession::GetTable() ); |
|
864 // new |
|
865 status = aResponseHeaders.GetField( fieldName, 0, hdrValue ); |
|
866 if( status == KErrNone ) |
|
867 { |
|
868 responseETag = hdrValue.StrF(); |
|
869 // orig |
|
870 valueStr = HttpCacheUtil::HeaderFieldFromBufferLC( aCachedHeadersStr, fieldName.DesC() ); |
|
871 if( valueStr ) |
|
872 { |
|
873 TRAP( status, cachedETag = aStrP.OpenFStringL( valueStr->Des() ) ); |
|
874 CleanupStack::PopAndDestroy(); // valueStr |
|
875 eTag = ( status == KErrNone ); |
|
876 // |
|
877 if( eTag ) |
|
878 { |
|
879 CleanupClosePushL( cachedETag ); |
|
880 } |
|
881 } |
|
882 } |
|
883 |
|
884 // If the entry's date is greater than the responses's date, |
|
885 // ignore the response. |
|
886 if( eDate && cachedDate > responseDate ) |
|
887 { |
|
888 update = EFalse; |
|
889 } |
|
890 // If the entry's ETag does not equal the responses's ETag, |
|
891 // replace the cached entry with the response. |
|
892 else if( eTag && cachedETag != responseETag ) |
|
893 { |
|
894 update = ETrue; |
|
895 } |
|
896 // If the entry's lastMod is greater than the responses's lastMod, |
|
897 // ignore the response. |
|
898 else if( eLastModified && cachedLastModified > responseLastModified ) |
|
899 { |
|
900 update = EFalse; |
|
901 } |
|
902 // If the entry's lastMod is equal to the responses's lastMod, |
|
903 // update the cached headers with the new response headers. |
|
904 else if( eLastModified && cachedLastModified == responseLastModified ) |
|
905 { |
|
906 update = ETrue; |
|
907 } |
|
908 // If the entry's lastMod is less than the responses's lastMod, |
|
909 // replace the cached entry with the response. |
|
910 else if( eLastModified && cachedLastModified < responseLastModified ) |
|
911 { |
|
912 update = ETrue; |
|
913 } |
|
914 else |
|
915 { |
|
916 // If we get here the headers need to be replaced |
|
917 update = ETrue; |
|
918 } |
|
919 // |
|
920 if( eTag ) |
|
921 { |
|
922 CleanupStack::PopAndDestroy(); // cachedETag |
|
923 } |
|
924 return update; |
|
925 } |
|
926 |
|
927 // ----------------------------------------------------------------------------- |
|
928 // HttpCacheUtil::IsCacheable |
|
929 // |
|
930 // ----------------------------------------------------------------------------- |
|
931 // |
|
932 TBool HttpCacheUtil::IsCacheable( |
|
933 RHTTPTransaction& aTrans, |
|
934 TUint aMaxSize, |
|
935 TBool& aProtectedEntry ) |
|
936 { |
|
937 TBool isCacheable( ETrue ); |
|
938 TBool hasExplicitCache( EFalse ); |
|
939 TInt64 maxAge; |
|
940 TBool noCache; |
|
941 TBool noStore; |
|
942 TTime now; |
|
943 TTime expires; |
|
944 RStringF fieldName; |
|
945 THTTPHdrVal expiresVal; |
|
946 TInt err; |
|
947 RStringPool strP = aTrans.Session().StringPool(); |
|
948 RHTTPHeaders respHeaders = aTrans.Response().GetHeaderCollection(); |
|
949 |
|
950 // not protected by default |
|
951 aProtectedEntry = EFalse; |
|
952 // 1. do not cache sesnitive content (DRM) |
|
953 // 2. do not cache content bigger than the cache |
|
954 // 3. check if the content is protected |
|
955 // 4. check normal cache directives |
|
956 |
|
957 // init the field name |
|
958 THTTPHdrVal contType; |
|
959 fieldName = strP.StringF( HTTP::EContentType, RHTTPSession::GetTable() ); |
|
960 |
|
961 // check if this is a noncacheable content type |
|
962 if( respHeaders.GetField( fieldName, 0, contType ) == KErrNone && |
|
963 contType.StrF() == strP.StringF( HttpFilterCommonStringsExt::EApplicationVndOmaDrm, |
|
964 HttpFilterCommonStringsExt::GetTable() ) ) |
|
965 { |
|
966 HttpCacheUtil::WriteLog( 0, _L( "sensitive content. do not cache" ) ); |
|
967 // drm == nocache |
|
968 isCacheable = EFalse; |
|
969 } |
|
970 else |
|
971 { |
|
972 // check if the content is bigger than the cache |
|
973 THTTPHdrVal contLen; |
|
974 fieldName = strP.StringF( HTTP::EContentLength, RHTTPSession::GetTable() ); |
|
975 |
|
976 if( respHeaders.GetField( fieldName, 0, contLen ) != KErrNotFound && |
|
977 ( contLen.Type() == THTTPHdrVal::KTIntVal && contLen.Int() > aMaxSize ) ) |
|
978 { |
|
979 HttpCacheUtil::WriteLog( 0, _L( "oversized content. do not cache" ) ); |
|
980 // oversized content |
|
981 return EFalse; |
|
982 } |
|
983 // check if this is a proteced entry |
|
984 aProtectedEntry = ProtectedEntry( respHeaders, strP ); |
|
985 // check various cache controls |
|
986 if( GetCacheControls( respHeaders, &maxAge, NULL, NULL, NULL, |
|
987 &noCache, &noStore, strP ) == KErrNone ) |
|
988 { |
|
989 // There are several header "conditions" that make a resource |
|
990 // non-cachable. Reject if they are present. |
|
991 // |
|
992 // If no-cache or no-store directives exist -> don't cache. |
|
993 if( noCache || noStore ) |
|
994 { |
|
995 HttpCacheUtil::WriteLog( 0, _L( "no cache/no store header. do not cache" ) ); |
|
996 // no protection on this entry |
|
997 aProtectedEntry = EFalse; |
|
998 return EFalse; |
|
999 } |
|
1000 // Get the current time |
|
1001 now.UniversalTime(); |
|
1002 |
|
1003 // Get the expires from the respHeaders |
|
1004 fieldName = strP.StringF( HTTP::EExpires, RHTTPSession::GetTable() ); |
|
1005 err = respHeaders.GetField( fieldName, 0, expiresVal ); |
|
1006 if( err == KErrNone ) |
|
1007 { |
|
1008 expires = TTime( expiresVal.DateTime() ); |
|
1009 } |
|
1010 else |
|
1011 { |
|
1012 expires = now; |
|
1013 } |
|
1014 |
|
1015 // if past-expire date do not cache. According to RFC2616, section 13.2.4/14.9.3, |
|
1016 // if maxage is present, then ignore expires |
|
1017 if (!maxAge && now > expires) |
|
1018 { |
|
1019 return EFalse; |
|
1020 } |
|
1021 |
|
1022 if( err == KErrNone || maxAge > 0 ) |
|
1023 { |
|
1024 hasExplicitCache = ETrue; |
|
1025 } |
|
1026 // Reject if the http status code doesn't equal 200, 304, 301, 410. |
|
1027 // Note: We accept status codes, 307, 302 if some cache |
|
1028 // control directives exist. |
|
1029 switch( aTrans.Response().StatusCode() ) |
|
1030 { |
|
1031 case HTTPStatus::EOk: |
|
1032 case HTTPStatus::ENotModified: |
|
1033 case HTTPStatus::EMovedPermanently: |
|
1034 case HTTPStatus::EGone: |
|
1035 { |
|
1036 break; |
|
1037 } |
|
1038 case HTTPStatus::EFound: |
|
1039 case HTTPStatus::ETemporaryRedirect: |
|
1040 { |
|
1041 if( !hasExplicitCache ) |
|
1042 { |
|
1043 isCacheable = EFalse; |
|
1044 } |
|
1045 break; |
|
1046 } |
|
1047 default: |
|
1048 { |
|
1049 isCacheable = EFalse; |
|
1050 break; |
|
1051 } |
|
1052 } |
|
1053 |
|
1054 // Check method specific conditions. |
|
1055 switch( HttpCacheUtil::MethodFromStr( aTrans.Request().Method(), strP ) ) |
|
1056 { |
|
1057 case EMethodGet: |
|
1058 { |
|
1059 // Reject if the url contains a "query" part unless there |
|
1060 // are caching directives. |
|
1061 TBool isQuery; |
|
1062 isQuery = aTrans.Request().URI().IsPresent( EUriQuery ); |
|
1063 |
|
1064 if( isQuery && !hasExplicitCache ) |
|
1065 { |
|
1066 isCacheable = EFalse; |
|
1067 } |
|
1068 break; |
|
1069 } |
|
1070 case EMethodPost: |
|
1071 { |
|
1072 // Reject unless there are caching directives |
|
1073 if( !hasExplicitCache ) |
|
1074 { |
|
1075 isCacheable = EFalse; |
|
1076 } |
|
1077 break; |
|
1078 } |
|
1079 case EMethodHead: |
|
1080 { |
|
1081 // Don't cache Head responses...we don't need to, and the |
|
1082 // cache implementation can't currently handle it anyway. |
|
1083 // So just fall through to the default case. |
|
1084 // Reject all other methods |
|
1085 } |
|
1086 default: |
|
1087 { |
|
1088 isCacheable = EFalse; |
|
1089 break; |
|
1090 } |
|
1091 } |
|
1092 } |
|
1093 } |
|
1094 return isCacheable; |
|
1095 } |
|
1096 |
|
1097 // ----------------------------------------------------------------------------- |
|
1098 // HttpCacheUtil::WriteLog |
|
1099 // |
|
1100 // ----------------------------------------------------------------------------- |
|
1101 // |
|
1102 void HttpCacheUtil::WriteLog( |
|
1103 TInt aLogLevel, |
|
1104 TPtrC aBuf, |
|
1105 TInt aAny ) |
|
1106 { |
|
1107 #ifdef __CACHELOG__ |
|
1108 TBool log( aLogLevel <= KCurrentLogLevel ); |
|
1109 TPtrC fileName( KHttpCacheGeneralFileName ); |
|
1110 |
|
1111 if( aLogLevel == 1 ) |
|
1112 { |
|
1113 // hash |
|
1114 fileName.Set( KHttpCacheHashFileName ); |
|
1115 log = ETrue; |
|
1116 } |
|
1117 if( log ) |
|
1118 { |
|
1119 if( aAny != 0xffff ) |
|
1120 { |
|
1121 RFileLogger::WriteFormat(_L("Browser"), fileName, EFileLoggingModeAppend, |
|
1122 _L("%S %d"), &aBuf, aAny ); |
|
1123 } |
|
1124 else |
|
1125 { |
|
1126 RFileLogger::WriteFormat(_L("Browser"), fileName, EFileLoggingModeAppend, |
|
1127 _L("%S"), &aBuf ); |
|
1128 } |
|
1129 } |
|
1130 #else // __CACHELOG__ |
|
1131 (void)aLogLevel; |
|
1132 (void)aBuf; |
|
1133 (void)aAny; |
|
1134 #endif // __CACHELOG__ |
|
1135 } |
|
1136 |
|
1137 // ----------------------------------------------------------------------------- |
|
1138 // HttpCacheUtil::WriteUrlToLog |
|
1139 // |
|
1140 // Get the freshness of the "entry". |
|
1141 // |
|
1142 // ----------------------------------------------------------------------------- |
|
1143 // |
|
1144 void HttpCacheUtil::WriteUrlToLog( |
|
1145 TInt aLogLevel, |
|
1146 const TDesC8& aUrl, |
|
1147 TInt aAny ) |
|
1148 { |
|
1149 #ifdef __CACHELOG__ |
|
1150 HBufC* tmp = HBufC::New( aUrl.Length() ); |
|
1151 if( tmp ) |
|
1152 { |
|
1153 TPtr tmpPtr( tmp->Des() ); |
|
1154 tmpPtr.Copy( aUrl ); |
|
1155 HttpCacheUtil::WriteLog( aLogLevel, tmpPtr, aAny ); |
|
1156 delete tmp; |
|
1157 } |
|
1158 #else // __CACHELOG__ |
|
1159 (void)aLogLevel; |
|
1160 (void)aUrl; |
|
1161 (void)aAny; |
|
1162 #endif // __CACHELOG__ |
|
1163 } |
|
1164 |
|
1165 // ----------------------------------------------------------------------------- |
|
1166 // HttpCacheUtil::WriteUrlToLog |
|
1167 // |
|
1168 // Get the freshness of the "entry". |
|
1169 // |
|
1170 // ----------------------------------------------------------------------------- |
|
1171 // |
|
1172 void HttpCacheUtil::WriteUrlToLog( |
|
1173 TInt aLogLevel, |
|
1174 const TDesC& aTxt, |
|
1175 const TDesC8& aUrl, |
|
1176 TInt aAny ) |
|
1177 { |
|
1178 #ifdef __CACHELOG__ |
|
1179 TBool log( aLogLevel <= KCurrentLogLevel ); |
|
1180 TPtrC fileName( KHttpCacheGeneralFileName ); |
|
1181 |
|
1182 if( aLogLevel == 1 ) |
|
1183 { |
|
1184 // hash |
|
1185 fileName.Set( KHttpCacheHashFileName ); |
|
1186 log = ETrue; |
|
1187 } |
|
1188 if( log ) |
|
1189 { // |
|
1190 HBufC* tmp = HBufC::New( aUrl.Length() ); |
|
1191 if( tmp ) |
|
1192 { |
|
1193 TPtr tmpPtr( tmp->Des() ); |
|
1194 tmpPtr.Copy( aUrl ); |
|
1195 if( aAny != 0xffff ) |
|
1196 { |
|
1197 RFileLogger::WriteFormat( _L("Browser"), fileName, EFileLoggingModeAppend, |
|
1198 _L("%S %S %d"), &aTxt, &tmpPtr, aAny ); |
|
1199 } |
|
1200 else |
|
1201 { |
|
1202 RFileLogger::WriteFormat(_L("Browser"), fileName, EFileLoggingModeAppend, |
|
1203 _L("%S %S"), &aTxt, &tmpPtr ); |
|
1204 } |
|
1205 delete tmp; |
|
1206 } |
|
1207 } |
|
1208 #else // __CACHELOG__ |
|
1209 (void)aLogLevel; |
|
1210 (void)aTxt; |
|
1211 (void)aUrl; |
|
1212 (void)aAny; |
|
1213 #endif // __CACHELOG__ |
|
1214 } |
|
1215 |
|
1216 // ----------------------------------------------------------------------------- |
|
1217 // HttpCacheUtil::GenerateNameLC |
|
1218 // Given a URL, generates fully qualified Symbian path for storing HTTP response. |
|
1219 // The format is <cache base dir> + <subdirectory> + <file name>. |
|
1220 // Caller must free the returned HBufC* when done. |
|
1221 // ----------------------------------------------------------------------------- |
|
1222 HBufC* HttpCacheUtil::GenerateNameLC( |
|
1223 const TDesC8& aUrl, const TDesC& aBaseDir) |
|
1224 { |
|
1225 |
|
1226 TUint32 crc (0); |
|
1227 |
|
1228 //use the entire URL for CRC calculation: maximizes source entropy/avoids collisions |
|
1229 Mem::Crc32(crc, aUrl.Ptr(), aUrl.Size()); |
|
1230 TUint32 nibble (crc & (KCacheSubdirCount-1)); // extract least significant 4 bits (nibble) for subdirectory |
|
1231 |
|
1232 HBufC* fileName = HBufC::NewLC( KMaxPath ); // e.g E\078AFEFE |
|
1233 _LIT(KFormat,"%S%x%c%08x"); // Note the %08x : a 32-bit value can represented as 0xFFFFFFFF |
|
1234 fileName->Des().Format(KFormat, &aBaseDir, nibble, KPathDelimiter, crc); |
|
1235 return fileName; |
|
1236 |
|
1237 } |
|
1238 |
|
1239 |
|
1240 // ----------------------------------------------------------------------------- |
|
1241 // HttpCacheUtil::Freshness |
|
1242 // |
|
1243 // Get the freshness of the "entry". |
|
1244 // |
|
1245 // ----------------------------------------------------------------------------- |
|
1246 // |
|
1247 TInt64 HttpCacheUtil::Freshness( |
|
1248 const RHTTPHeaders& aHeaders, |
|
1249 TTime aResponseTime, |
|
1250 RStringPool aStrP ) |
|
1251 { |
|
1252 TInt status( KErrNone ); |
|
1253 TInt64 maxAge; |
|
1254 TTime expires; |
|
1255 TTime lastModified( 0 ); |
|
1256 TTime date; |
|
1257 RStringF fieldName; |
|
1258 THTTPHdrVal hdrValue; |
|
1259 TInt err; |
|
1260 TInt64 freshness( 0 ); |
|
1261 |
|
1262 // Get the date from the headers |
|
1263 fieldName = aStrP.StringF( HTTP::EDate, RHTTPSession::GetTable() ); |
|
1264 err = aHeaders.GetField( fieldName, 0, hdrValue ); |
|
1265 if( err == KErrNotFound || hdrValue.Type() != THTTPHdrVal::KDateVal ) |
|
1266 { |
|
1267 date = aResponseTime; |
|
1268 } |
|
1269 else |
|
1270 { |
|
1271 date = TTime( hdrValue.DateTime() ); |
|
1272 } |
|
1273 |
|
1274 // Get useful cache-control directives |
|
1275 status = GetCacheControls( aHeaders, &maxAge, NULL, NULL, NULL, NULL, NULL, aStrP ); |
|
1276 if( status == KErrNone ) |
|
1277 { |
|
1278 // max-age is in delta-seconds. Convert it to micro seconds. |
|
1279 // All our calculations are in micro-seconds |
|
1280 // If maxAge is present, use it |
|
1281 if( maxAge != -1 ) |
|
1282 { |
|
1283 freshness = maxAge * 1000 * 1000; |
|
1284 |
|
1285 return freshness; |
|
1286 } |
|
1287 |
|
1288 // Get the expires from the headers |
|
1289 fieldName = aStrP.StringF( HTTP::EExpires, RHTTPSession::GetTable() ); |
|
1290 err = aHeaders.GetField( fieldName, 0, hdrValue ); |
|
1291 if( err == KErrNotFound || hdrValue.Type() != THTTPHdrVal::KDateVal ) |
|
1292 { |
|
1293 expires = 0; |
|
1294 } |
|
1295 else |
|
1296 { |
|
1297 expires = TTime( hdrValue.DateTime() ); |
|
1298 } |
|
1299 |
|
1300 // Otherwise, if the expires is present use it |
|
1301 if( err != KErrNotFound ) |
|
1302 { |
|
1303 freshness = expires.Int64() - date.Int64(); |
|
1304 return freshness; |
|
1305 } |
|
1306 |
|
1307 // Get the last modified from the headers |
|
1308 fieldName = aStrP.StringF( HTTP::ELastModified, RHTTPSession::GetTable() ); |
|
1309 err = aHeaders.GetField( fieldName, 0, hdrValue ); |
|
1310 if( err == KErrNotFound || hdrValue.Type() != THTTPHdrVal::KDateVal ) |
|
1311 { |
|
1312 hdrValue = 0; |
|
1313 } |
|
1314 else |
|
1315 { |
|
1316 lastModified = TTime( hdrValue.DateTime() ); |
|
1317 } |
|
1318 |
|
1319 // Otherwise, if last-modified is present use it |
|
1320 if( err != KErrNotFound ) |
|
1321 { |
|
1322 if( aResponseTime > lastModified ) |
|
1323 { |
|
1324 freshness = aResponseTime.Int64() - lastModified.Int64(); |
|
1325 freshness += ((TInt64)( freshness * 0.10 ) ); |
|
1326 } |
|
1327 return freshness; |
|
1328 } |
|
1329 // If it get here then we have no way to determine if it is |
|
1330 // fresh. So we set freshness to zero, which will most likely |
|
1331 // cause the resource to be validated. |
|
1332 freshness = 0; |
|
1333 } |
|
1334 return freshness; |
|
1335 } |
|
1336 |
|
1337 // ----------------------------------------------------------------------------- |
|
1338 // HttpCacheUtil::Age |
|
1339 // |
|
1340 // Get the age of the "entry". |
|
1341 // |
|
1342 // ----------------------------------------------------------------------------- |
|
1343 // |
|
1344 TInt64 HttpCacheUtil::Age( |
|
1345 const RHTTPHeaders& aHeaders, |
|
1346 TTime aRequestTime, |
|
1347 TTime aResponseTime, |
|
1348 RStringPool aStrP ) |
|
1349 { |
|
1350 TTime now; |
|
1351 TDateTime date; |
|
1352 TTime time; |
|
1353 |
|
1354 // Int64 timeinsec; |
|
1355 TInt64 correctedRecvAge; |
|
1356 TInt64 apparentAge; |
|
1357 TInt64 responseDelay; |
|
1358 TInt64 correctedInitialAge; |
|
1359 TInt64 residentTime; |
|
1360 TInt64 currentAge; |
|
1361 TInt64 age; |
|
1362 |
|
1363 |
|
1364 RStringF fieldName; |
|
1365 THTTPHdrVal dateValue; |
|
1366 TInt err; |
|
1367 |
|
1368 // Get the current time. All internet dates are GMT |
|
1369 now.UniversalTime(); |
|
1370 #ifdef __CACHELOG__ |
|
1371 TBuf<50> dateString; |
|
1372 // |
|
1373 now.FormatL( dateString, KDateString ); |
|
1374 HttpCacheUtil::WriteLog( 0, _L( "current time" ) ); |
|
1375 HttpCacheUtil::WriteLog( 0, dateString ); |
|
1376 #endif // __CACHELOG__ |
|
1377 |
|
1378 // The aRequestTime is same as that of headers time. |
|
1379 |
|
1380 time = aRequestTime; |
|
1381 |
|
1382 // Do a sanity check |
|
1383 if( aRequestTime > now ) |
|
1384 { |
|
1385 aRequestTime = now; |
|
1386 } |
|
1387 if( aResponseTime > now ) |
|
1388 { |
|
1389 aResponseTime = now; |
|
1390 } |
|
1391 if( aRequestTime > aResponseTime ) |
|
1392 { |
|
1393 aRequestTime = aResponseTime; |
|
1394 } |
|
1395 if( time > aResponseTime ) |
|
1396 { |
|
1397 time = aResponseTime; |
|
1398 } |
|
1399 |
|
1400 // Get the age from the headers. If age is missing it equals |
|
1401 // 0, which is a safe value to use below. |
|
1402 fieldName = aStrP.StringF( HTTP::EAge, RHTTPSession::GetTable() ); |
|
1403 err = aHeaders.GetField( fieldName, 0, dateValue ); |
|
1404 if( err == KErrNotFound ) |
|
1405 { |
|
1406 age = 0; |
|
1407 } |
|
1408 else |
|
1409 { |
|
1410 // Age should be in delta seconds |
|
1411 if( dateValue.Type() == THTTPHdrVal::KTIntVal ) |
|
1412 { |
|
1413 age = dateValue; |
|
1414 } |
|
1415 else |
|
1416 { |
|
1417 age = 0; |
|
1418 } |
|
1419 } |
|
1420 |
|
1421 // Get the "current" age |
|
1422 apparentAge = Max( TInt64( 0 ), aResponseTime.Int64() - time.Int64() ); |
|
1423 // The TTime::Int64() gives the time in micro seconds. And the age field is in |
|
1424 // delta seconds. |
|
1425 correctedRecvAge = Max( apparentAge, age * 1000 * 1000 ); |
|
1426 responseDelay = aResponseTime.Int64() - aRequestTime.Int64(); |
|
1427 correctedInitialAge = correctedRecvAge + responseDelay; |
|
1428 residentTime = now.Int64() - aResponseTime.Int64(); |
|
1429 |
|
1430 currentAge = correctedInitialAge + residentTime; |
|
1431 |
|
1432 //the current age in micro seconds. |
|
1433 return currentAge; |
|
1434 } |
|
1435 |
|
1436 // ----------------------------------------------------------------------------- |
|
1437 // HttpCacheUtil::GetCacheControls |
|
1438 // |
|
1439 // Description: Returns various cache controls. |
|
1440 // |
|
1441 // ----------------------------------------------------------------------------- |
|
1442 // |
|
1443 TInt HttpCacheUtil::GetCacheControls( |
|
1444 const RHTTPHeaders& aHeaders, |
|
1445 TInt64* aMaxAge, |
|
1446 TInt64* aMinFresh, |
|
1447 TInt64* aMaxStale, |
|
1448 TBool* aMustRevalidate, |
|
1449 TBool* aNoCache, |
|
1450 TBool* aNoStore, |
|
1451 RStringPool aStrP ) |
|
1452 { |
|
1453 TInt status( KErrNone ); |
|
1454 TInt i; |
|
1455 TInt cacheCount( 0 ); |
|
1456 RStringF directive; |
|
1457 TInt64 value; |
|
1458 char* extraValues = NULL; |
|
1459 RStringF fieldName; |
|
1460 |
|
1461 |
|
1462 if( aMaxAge ) |
|
1463 { |
|
1464 *aMaxAge = -1; |
|
1465 } |
|
1466 if( aMinFresh ) |
|
1467 { |
|
1468 *aMinFresh = -1; |
|
1469 } |
|
1470 if( aMaxStale ) |
|
1471 { |
|
1472 *aMaxStale = -1; |
|
1473 } |
|
1474 if( aMustRevalidate ) |
|
1475 { |
|
1476 *aMustRevalidate = EFalse; |
|
1477 } |
|
1478 if( aNoCache ) |
|
1479 { |
|
1480 *aNoCache = EFalse; |
|
1481 } |
|
1482 if( aNoStore ) |
|
1483 { |
|
1484 *aNoStore = EFalse; |
|
1485 } |
|
1486 |
|
1487 // init the field name |
|
1488 fieldName = aStrP.StringF( HTTP::ECacheControl, RHTTPSession::GetTable() ); |
|
1489 TRAP( status, cacheCount = aHeaders.FieldPartsL( fieldName ) ); |
|
1490 if( status == KErrNone ) |
|
1491 { |
|
1492 for( i = 0; i < cacheCount; i++ ) |
|
1493 { |
|
1494 status = GetCacheControlDirective( aHeaders, i, directive, &value, &extraValues, aStrP ); |
|
1495 if( status == KErrNone ) |
|
1496 { |
|
1497 if( directive == aStrP.StringF( HTTP::EMaxAge, RHTTPSession::GetTable() ) ) |
|
1498 { |
|
1499 if( aMaxAge ) |
|
1500 { |
|
1501 *aMaxAge = value; |
|
1502 } |
|
1503 } |
|
1504 else if( directive == aStrP.StringF( HTTP::EMinFresh, RHTTPSession::GetTable() ) ) |
|
1505 { |
|
1506 if( aMinFresh ) |
|
1507 { |
|
1508 *aMinFresh = value; |
|
1509 } |
|
1510 } |
|
1511 else if( directive == aStrP.StringF( HTTP::EMaxStale, RHTTPSession::GetTable() ) ) |
|
1512 { |
|
1513 if( aMaxStale ) |
|
1514 { |
|
1515 *aMaxStale = value; |
|
1516 } |
|
1517 } |
|
1518 else if( directive == aStrP.StringF( HTTP::EMustRevalidate, RHTTPSession::GetTable() ) ) |
|
1519 { |
|
1520 if( aMustRevalidate ) |
|
1521 { |
|
1522 *aMustRevalidate = ETrue; |
|
1523 } |
|
1524 } |
|
1525 else if( directive == aStrP.StringF( HTTP::ENoCache, RHTTPSession::GetTable() ) ) |
|
1526 { |
|
1527 if( aNoCache ) |
|
1528 { |
|
1529 *aNoCache = ETrue; |
|
1530 } |
|
1531 } |
|
1532 else if( directive == aStrP.StringF( HTTP::ENoStore, RHTTPSession::GetTable() ) ) |
|
1533 { |
|
1534 if( aNoStore ) |
|
1535 { |
|
1536 *aNoStore = ETrue; |
|
1537 } |
|
1538 } |
|
1539 directive.Close(); |
|
1540 // It ignores extraValues, just free it |
|
1541 User::Free( extraValues ); |
|
1542 extraValues = NULL; |
|
1543 } |
|
1544 else |
|
1545 { |
|
1546 break; |
|
1547 } |
|
1548 } |
|
1549 } |
|
1550 return status; |
|
1551 } |
|
1552 |
|
1553 // ----------------------------------------------------------------------------- |
|
1554 // HttpCacheUtil::GetCacheControlDirective |
|
1555 // |
|
1556 // ----------------------------------------------------------------------------- |
|
1557 // |
|
1558 TInt HttpCacheUtil::GetCacheControlDirective( |
|
1559 const RHTTPHeaders& aHeaders, |
|
1560 TInt aIndex, |
|
1561 RStringF& aDirective, |
|
1562 TInt64* aDirectiveValue, |
|
1563 char** aExtraValue, |
|
1564 RStringPool aStrP ) |
|
1565 { |
|
1566 RStringF fieldName; |
|
1567 THTTPHdrVal hdrVal; |
|
1568 TInt err; |
|
1569 |
|
1570 // Get the cache-control from the headers |
|
1571 fieldName = aStrP.StringF( HTTP::ECacheControl, RHTTPSession::GetTable() ); |
|
1572 aHeaders.GetField( fieldName, aIndex, hdrVal ); |
|
1573 |
|
1574 if( hdrVal.Type() == THTTPHdrVal::KStrVal || hdrVal.Type() == THTTPHdrVal::KStrFVal ) |
|
1575 { |
|
1576 RStringF cacheDir = hdrVal.StrF(); |
|
1577 |
|
1578 TInt endPos; |
|
1579 _LIT8(KFind, "="); |
|
1580 |
|
1581 endPos = cacheDir.DesC().Find( KFind ); |
|
1582 if( endPos != -1 ) |
|
1583 { |
|
1584 TRAP( err, aDirective = aStrP.OpenFStringL( cacheDir.DesC().Left( endPos ) ) ); |
|
1585 if( err != KErrNone ) |
|
1586 { |
|
1587 return err; |
|
1588 } |
|
1589 TPtrC8 value = cacheDir.DesC().Right( cacheDir.DesC().Length() - endPos - 1 ); |
|
1590 err = ExtractCacheControlDirectiveValue( aStrP, aDirective, value, aDirectiveValue, aExtraValue ); |
|
1591 if( err != KErrNone ) |
|
1592 { |
|
1593 aDirective.Close(); |
|
1594 return err; |
|
1595 } |
|
1596 } |
|
1597 else |
|
1598 { |
|
1599 aDirective = cacheDir.Copy(); |
|
1600 // Directives which MUST have values; |
|
1601 if( ( aDirective == aStrP.StringF( HTTP::EMaxAge, RHTTPSession::GetTable() ) ) || |
|
1602 ( aDirective == aStrP.StringF( HTTP::EMinFresh, RHTTPSession::GetTable() ) ) || |
|
1603 ( aDirective == aStrP.StringF( HTTP::ESMaxAge, RHTTPSession::GetTable() ) ) ) |
|
1604 { |
|
1605 aDirective.Close(); |
|
1606 return KErrGeneral; |
|
1607 } |
|
1608 } |
|
1609 } |
|
1610 return KErrNone; |
|
1611 } |
|
1612 |
|
1613 // ----------------------------------------------------------------------------- |
|
1614 // HttpCacheUtil::ExtractCacheControlDirectiveValue |
|
1615 // |
|
1616 // ----------------------------------------------------------------------------- |
|
1617 // |
|
1618 TInt HttpCacheUtil::ExtractCacheControlDirectiveValue( |
|
1619 RStringPool aStrP, |
|
1620 RStringF& aDirective, |
|
1621 TDesC8& aValue, |
|
1622 TInt64* aDirectiveValue, |
|
1623 char** aExtraValue ) |
|
1624 { |
|
1625 TInt status( KErrNone ); |
|
1626 TInt temp; |
|
1627 char* errorIfNull; |
|
1628 |
|
1629 *aDirectiveValue = -1; |
|
1630 *aExtraValue = NULL; |
|
1631 char* valuestr = (char*)User::Alloc( aValue.Length() + 1 ); |
|
1632 if( !valuestr ) |
|
1633 { |
|
1634 return KErrNoMemory; |
|
1635 } |
|
1636 memcpy( valuestr, aValue.Ptr(), aValue.Length() ); |
|
1637 valuestr[ aValue.Length() ] = '\0'; |
|
1638 |
|
1639 |
|
1640 if( ( aDirective == aStrP.StringF( HTTP::EMaxAge, RHTTPSession::GetTable() ) ) || |
|
1641 ( aDirective == aStrP.StringF( HTTP::EMinFresh, RHTTPSession::GetTable() ) ) || |
|
1642 ( aDirective == aStrP.StringF( HTTP::ESMaxAge, RHTTPSession::GetTable() ) ) ) |
|
1643 { |
|
1644 // Cases with directiveValues |
|
1645 temp = strtoul( valuestr, &errorIfNull, 10 ); |
|
1646 if( !errorIfNull ) |
|
1647 { |
|
1648 status = KErrGeneral; |
|
1649 } |
|
1650 else |
|
1651 { |
|
1652 *aDirectiveValue = temp; |
|
1653 } |
|
1654 User::Free( valuestr ); |
|
1655 return status; |
|
1656 } |
|
1657 |
|
1658 if( ( aDirective == aStrP.StringF( HTTP::EMaxStale, RHTTPSession::GetTable() ) ) ) |
|
1659 { |
|
1660 // Cases with optional directiveValues |
|
1661 temp = strtoul( valuestr, &errorIfNull, 10 ); |
|
1662 if( errorIfNull ) |
|
1663 { |
|
1664 *aDirectiveValue = temp; |
|
1665 } |
|
1666 User::Free( valuestr ); |
|
1667 return status; |
|
1668 } |
|
1669 |
|
1670 if( ( aDirective == aStrP.StringF( HTTP::ENoCache, RHTTPSession::GetTable() ) ) || |
|
1671 ( aDirective == aStrP.StringF( HTTP::EPrivate, RHTTPSession::GetTable() ) ) ) |
|
1672 { |
|
1673 *aExtraValue = valuestr; |
|
1674 return status; |
|
1675 } |
|
1676 User::Free( valuestr ); |
|
1677 return status; |
|
1678 } |
|
1679 |
|
1680 // End of File |