1212//
1313
1414#import " SPDYCacheStoragePolicy.h"
15+ #include < time.h>
16+ #include < xlocale.h>
1517
1618typedef struct _HTTPTimeFormatInfo {
1719 const char *readFormat;
3133{
3234 NSDate *date = nil ;
3335 if (string) {
34- struct tm parsedTime;
3536 const char *utf8String = [string UTF8String ];
3637
3738 for (int format = 0 ; (size_t )format < (sizeof (kTimeFormatInfos ) / sizeof (kTimeFormatInfos [0 ])); format++) {
3839 HTTPTimeFormatInfo info = kTimeFormatInfos [format];
39- if (info.readFormat != NULL && strptime (utf8String, info.readFormat , &parsedTime)) {
40+ struct tm parsedTime = { 0 };
41+ if (info.readFormat != NULL && strptime_l (utf8String, info.readFormat , &parsedTime, NULL )) {
4042 NSTimeInterval ti = (info.usesHasTimezoneInfo ? mktime (&parsedTime) : timegm (&parsedTime));
4143 date = [NSDate dateWithTimeIntervalSince1970: ti];
4244 if (date) {
4951 return date;
5052}
5153
54+ NSString *GetKey (const char **ppStr) {
55+ const char *p = *ppStr;
56+
57+ // Advance to next delimiter
58+ while (*p != ' \0 ' && *p != ' =' && *p != ' ,' ) {
59+ p++;
60+ }
61+
62+ // No progress? Error.
63+ if (p == *ppStr) {
64+ return nil ;
65+ }
66+
67+ NSString *str = [[NSString alloc ] initWithBytes: *ppStr length: (p - *ppStr) encoding: NSUTF8StringEncoding];
68+ *ppStr = p;
69+ return [str stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet ]];
70+ }
71+
72+ NSString *GetValue (const char **ppStr) {
73+ // **ppStr, at input, should be null for EOS, "," for single token, or "=" for dual token.
74+ const char *p = *ppStr;
75+
76+ // Single token with no value, EOS
77+ if (*p == ' \0 ' ) {
78+ return @" " ;
79+ }
80+
81+ // Single token with no value, more after
82+ if (*p == ' ,' ) {
83+ (*ppStr)++;
84+ return @" " ;
85+ }
86+
87+ // Must be token with value, error if not
88+ if (*p != ' =' ) {
89+ return nil ;
90+ }
91+
92+ // skip '='
93+ p++;
94+ (*ppStr)++;
95+
96+ // Value is either a quoted string or a token
97+ NSString *str;
98+ if (*p == ' "' ) {
99+ p++; // skip opening quote
100+
101+ // Advance to delimiter
102+ while (*p != ' \0 ' && *p != ' "' ) {
103+ p++;
104+ }
105+
106+ // EOS before closing quote? Error.
107+ if (*p == ' \0 ' ) {
108+ return nil ;
109+ }
110+
111+ p++; // skip closing quote
112+
113+ // Don't trim whitespace from within quoted string
114+ str = [[NSString alloc ] initWithBytes: (*ppStr + 1 ) length: (p - *ppStr - 2 ) encoding: NSUTF8StringEncoding];
115+ } else {
116+ // Advance to delimiter
117+ while (*p != ' \0 ' && *p != ' ,' ) {
118+ p++;
119+ }
120+
121+ // No progress? Error.
122+ if (p == *ppStr) {
123+ return nil ;
124+ }
125+
126+ str = [[[NSString alloc ] initWithBytes: *ppStr length: (p - *ppStr) encoding: NSUTF8StringEncoding] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet ]];
127+ }
128+
129+ // Skip trailing whitespace and trailing token delimiter
130+ while (*p != ' \0 ' && (*p == ' ' || *p == ' ,' )) {
131+ p++;
132+ }
133+
134+ *ppStr = p;
135+ return str;
136+ }
137+
52138NSDictionary *HTTPCacheControlParameters (NSString *cacheControl)
53139{
54140 if (cacheControl.length == 0 ) {
55141 return nil ;
56142 }
57143
58- NSArray *components = [cacheControl componentsSeparatedByString: @" ," ];
59- NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithCapacity: components.count];
60- for (NSString *component in components) {
61- NSArray *pair = [component componentsSeparatedByString: @" =" ];
62- NSString *key = [pair[0 ] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet ]];
63- NSString *value = pair.count == 2 ? [pair[1 ] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet ]] : @" " ;
144+ const char *pStr = [cacheControl cStringUsingEncoding: NSUTF8StringEncoding];
145+
146+ NSMutableDictionary *parameters = [[NSMutableDictionary alloc ] init ];
147+
148+ while (YES ) {
149+ NSString *key = GetKey (&pStr);
150+ if (key.length == 0 ) {
151+ break ;
152+ }
153+ NSString *value = GetValue (&pStr);
154+ if (value == nil ) {
155+ break ;
156+ }
64157 parameters[key] = value;
65158 }
66159 return parameters;
@@ -125,14 +218,13 @@ extern NSURLCacheStoragePolicy SPDYCacheStoragePolicy(NSURLRequest *request, NSH
125218 }
126219
127220 // If we still think it might be cacheable, look at the "Cache-Control" header in
128- // the request. Also rule out requests with Authorization in them.
221+ // the request.
129222
130223 if (cacheable) {
131224 NSString *requestHeader;
132225
133226 requestHeader = [[request valueForHTTPHeaderField: @" cache-control" ] lowercaseString ];
134- if ((requestHeader != nil && [requestHeader rangeOfString: @" no-store" ].location != NSNotFound ) ||
135- [request valueForHTTPHeaderField: @" authorization" ].length > 0 ) {
227+ if (requestHeader != nil && [requestHeader rangeOfString: @" no-store" ].location != NSNotFound ) {
136228 cacheable = NO ;
137229 }
138230 }
@@ -207,14 +299,18 @@ extern SPDYCachedResponseState SPDYCacheLoadingPolicy(NSURLRequest *request, NSC
207299 }
208300 }
209301
210- // Note: there's a lot more validation we should do, to be a well-behaving user agent.
302+ // Note: there's a lot more validation we should do, to be a well-behaving user agent. RFC7234
303+ // should be consulted.
211304 // We don't support Pragma header.
212305 // We don't support Expires header.
213306 // We don't support Vary header.
214307 // We don't support ETag response header or If-None-Match request header.
215308 // We don't support Last-Modified response header or If-Modified-Since request header.
216309 // We don't look at more of the Cache-Control parameters, including ones that specify a field name.
310+ // We need to generate the Age header in the cached response.
311+ // We need to invalidate the cached item if PUT,POST,DELETE request gets a successful response.
312+ // - including the item in Location header.
217313 // ...
218314
219315 return SPDYCachedResponseStateValid;
220- }
316+ }
0 commit comments