Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(118)

Side by Side Diff: sky/engine/platform/network/HTTPParsers.cpp

Issue 1239633002: Remove //sky/engine/platform/network (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: rebase Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
3 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
4 * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
5 * Copyright (C) 2009 Google Inc. All rights reserved.
6 * Copyright (C) 2011 Apple Inc. All Rights Reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
18 * its contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "sky/engine/platform/network/HTTPParsers.h"
34
35 #include "sky/engine/wtf/DateMath.h"
36 #include "sky/engine/wtf/MathExtras.h"
37 #include "sky/engine/wtf/text/CString.h"
38 #include "sky/engine/wtf/text/StringBuilder.h"
39 #include "sky/engine/wtf/text/WTFString.h"
40 #include "sky/engine/wtf/unicode/CharacterNames.h"
41
42 using namespace WTF;
43
44 namespace blink {
45
46 // true if there is more to parse, after incrementing pos past whitespace.
47 // Note: Might return pos == str.length()
48 static inline bool skipWhiteSpace(const String& str, unsigned& pos, bool fromHtt pEquivMeta)
49 {
50 unsigned len = str.length();
51
52 if (fromHttpEquivMeta) {
53 while (pos < len && str[pos] <= ' ')
54 ++pos;
55 } else {
56 while (pos < len && (str[pos] == '\t' || str[pos] == ' '))
57 ++pos;
58 }
59
60 return pos < len;
61 }
62
63 // Returns true if the function can match the whole token (case insensitive)
64 // incrementing pos on match, otherwise leaving pos unchanged.
65 // Note: Might return pos == str.length()
66 static inline bool skipToken(const String& str, unsigned& pos, const char* token )
67 {
68 unsigned len = str.length();
69 unsigned current = pos;
70
71 while (current < len && *token) {
72 if (toASCIILower(str[current]) != *token++)
73 return false;
74 ++current;
75 }
76
77 if (*token)
78 return false;
79
80 pos = current;
81 return true;
82 }
83
84 // True if the expected equals sign is seen and there is more to follow.
85 static inline bool skipEquals(const String& str, unsigned &pos)
86 {
87 return skipWhiteSpace(str, pos, false) && str[pos++] == '=' && skipWhiteSpac e(str, pos, false);
88 }
89
90 // True if a value present, incrementing pos to next space or semicolon, if any.
91 // Note: might return pos == str.length().
92 static inline bool skipValue(const String& str, unsigned& pos)
93 {
94 unsigned start = pos;
95 unsigned len = str.length();
96 while (pos < len) {
97 if (str[pos] == ' ' || str[pos] == '\t' || str[pos] == ';')
98 break;
99 ++pos;
100 }
101 return pos != start;
102 }
103
104 bool isValidHTTPHeaderValue(const String& name)
105 {
106 // FIXME: This should really match name against
107 // field-value in section 4.2 of RFC 2616.
108
109 return name.containsOnlyLatin1() && !name.contains('\r') && !name.contains(' \n');
110 }
111
112 // See RFC 2616, Section 2.2.
113 bool isValidHTTPToken(const String& characters)
114 {
115 if (characters.isEmpty())
116 return false;
117 for (unsigned i = 0; i < characters.length(); ++i) {
118 UChar c = characters[i];
119 if (c <= 0x20 || c >= 0x7F
120 || c == '(' || c == ')' || c == '<' || c == '>' || c == '@'
121 || c == ',' || c == ';' || c == ':' || c == '\\' || c == '"'
122 || c == '/' || c == '[' || c == ']' || c == '?' || c == '='
123 || c == '{' || c == '}')
124 return false;
125 }
126 return true;
127 }
128
129 static const size_t maxInputSampleSize = 128;
130 static String trimInputSample(const char* p, size_t length)
131 {
132 if (length > maxInputSampleSize)
133 return String(p, maxInputSampleSize) + horizontalEllipsis;
134 return String(p, length);
135 }
136
137 ContentDispositionType contentDispositionType(const String& contentDisposition)
138 {
139 if (contentDisposition.isEmpty())
140 return ContentDispositionNone;
141
142 Vector<String> parameters;
143 contentDisposition.split(';', parameters);
144
145 if (parameters.isEmpty())
146 return ContentDispositionNone;
147
148 String dispositionType = parameters[0];
149 dispositionType.stripWhiteSpace();
150
151 if (equalIgnoringCase(dispositionType, "inline"))
152 return ContentDispositionInline;
153
154 // Some broken sites just send bogus headers like
155 //
156 // Content-Disposition: ; filename="file"
157 // Content-Disposition: filename="file"
158 // Content-Disposition: name="file"
159 //
160 // without a disposition token... screen those out.
161 if (!isValidHTTPToken(dispositionType))
162 return ContentDispositionNone;
163
164 // We have a content-disposition of "attachment" or unknown.
165 // RFC 2183, section 2.8 says that an unknown disposition
166 // value should be treated as "attachment"
167 return ContentDispositionAttachment;
168 }
169
170 double parseDate(const String& value)
171 {
172 return parseDateFromNullTerminatedCharacters(value.utf8().data());
173 }
174
175 // FIXME: This function doesn't comply with RFC 6266.
176 // For example, this function doesn't handle the interaction between " and ;
177 // that arises from quoted-string, nor does this function properly unquote
178 // attribute values. Further this function appears to process parameter names
179 // in a case-sensitive manner. (There are likely other bugs as well.)
180 String filenameFromHTTPContentDisposition(const String& value)
181 {
182 Vector<String> keyValuePairs;
183 value.split(';', keyValuePairs);
184
185 unsigned length = keyValuePairs.size();
186 for (unsigned i = 0; i < length; i++) {
187 size_t valueStartPos = keyValuePairs[i].find('=');
188 if (valueStartPos == kNotFound)
189 continue;
190
191 String key = keyValuePairs[i].left(valueStartPos).stripWhiteSpace();
192
193 if (key.isEmpty() || key != "filename")
194 continue;
195
196 String value = keyValuePairs[i].substring(valueStartPos + 1).stripWhiteS pace();
197
198 // Remove quotes if there are any
199 if (value[0] == '\"')
200 value = value.substring(1, value.length() - 2);
201
202 return value;
203 }
204
205 return String();
206 }
207
208 AtomicString extractMIMETypeFromMediaType(const AtomicString& mediaType)
209 {
210 StringBuilder mimeType;
211 unsigned length = mediaType.length();
212 mimeType.reserveCapacity(length);
213 for (unsigned i = 0; i < length; i++) {
214 UChar c = mediaType[i];
215
216 if (c == ';')
217 break;
218
219 // While RFC 2616 does not allow it, other browsers allow multiple value s in the HTTP media
220 // type header field, Content-Type. In such cases, the media type string passed here may contain
221 // the multiple values separated by commas. For now, this code ignores t ext after the first comma,
222 // which prevents it from simply failing to parse such types altogether. Later for better
223 // compatibility we could consider using the first or last valid MIME ty pe instead.
224 // See https://bugs.webkit.org/show_bug.cgi?id=25352 for more discussion .
225 if (c == ',')
226 break;
227
228 // FIXME: The following is not correct. RFC 2616 allows linear white spa ce before and
229 // after the MIME type, but not within the MIME type itself. And linear white space
230 // includes only a few specific ASCII characters; a small subset of isSp aceOrNewline.
231 // See https://bugs.webkit.org/show_bug.cgi?id=8644 for a bug tracking p art of this.
232 if (isSpaceOrNewline(c))
233 continue;
234
235 mimeType.append(c);
236 }
237
238 if (mimeType.length() == length)
239 return mediaType;
240 return mimeType.toAtomicString();
241 }
242
243 String extractCharsetFromMediaType(const String& mediaType)
244 {
245 unsigned pos, len;
246 findCharsetInMediaType(mediaType, pos, len);
247 return mediaType.substring(pos, len);
248 }
249
250 void findCharsetInMediaType(const String& mediaType, unsigned& charsetPos, unsig ned& charsetLen, unsigned start)
251 {
252 charsetPos = start;
253 charsetLen = 0;
254
255 size_t pos = start;
256 unsigned length = mediaType.length();
257
258 while (pos < length) {
259 pos = mediaType.find("charset", pos, false);
260 if (pos == kNotFound || !pos) {
261 charsetLen = 0;
262 return;
263 }
264
265 // is what we found a beginning of a word?
266 if (mediaType[pos-1] > ' ' && mediaType[pos-1] != ';') {
267 pos += 7;
268 continue;
269 }
270
271 pos += 7;
272
273 // skip whitespace
274 while (pos != length && mediaType[pos] <= ' ')
275 ++pos;
276
277 if (mediaType[pos++] != '=') // this "charset" substring wasn't a parame ter name, but there may be others
278 continue;
279
280 while (pos != length && (mediaType[pos] <= ' ' || mediaType[pos] == '"' || mediaType[pos] == '\''))
281 ++pos;
282
283 // we don't handle spaces within quoted parameter values, because charse t names cannot have any
284 unsigned endpos = pos;
285 while (pos != length && mediaType[endpos] > ' ' && mediaType[endpos] != '"' && mediaType[endpos] != '\'' && mediaType[endpos] != ';')
286 ++endpos;
287
288 charsetPos = pos;
289 charsetLen = endpos - pos;
290 return;
291 }
292 }
293
294 ReflectedXSSDisposition parseXSSProtectionHeader(const String& header, String& f ailureReason, unsigned& failurePosition, String& reportURL)
295 {
296 DEFINE_STATIC_LOCAL(String, failureReasonInvalidToggle, ("expected 0 or 1")) ;
297 DEFINE_STATIC_LOCAL(String, failureReasonInvalidSeparator, ("expected semico lon"));
298 DEFINE_STATIC_LOCAL(String, failureReasonInvalidEquals, ("expected equals si gn"));
299 DEFINE_STATIC_LOCAL(String, failureReasonInvalidMode, ("invalid mode directi ve"));
300 DEFINE_STATIC_LOCAL(String, failureReasonInvalidReport, ("invalid report dir ective"));
301 DEFINE_STATIC_LOCAL(String, failureReasonDuplicateMode, ("duplicate mode dir ective"));
302 DEFINE_STATIC_LOCAL(String, failureReasonDuplicateReport, ("duplicate report directive"));
303 DEFINE_STATIC_LOCAL(String, failureReasonInvalidDirective, ("unrecognized di rective"));
304
305 unsigned pos = 0;
306
307 if (!skipWhiteSpace(header, pos, false))
308 return ReflectedXSSUnset;
309
310 if (header[pos] == '0')
311 return AllowReflectedXSS;
312
313 if (header[pos++] != '1') {
314 failureReason = failureReasonInvalidToggle;
315 return ReflectedXSSInvalid;
316 }
317
318 ReflectedXSSDisposition result = FilterReflectedXSS;
319 bool modeDirectiveSeen = false;
320 bool reportDirectiveSeen = false;
321
322 while (1) {
323 // At end of previous directive: consume whitespace, semicolon, and whit espace.
324 if (!skipWhiteSpace(header, pos, false))
325 return result;
326
327 if (header[pos++] != ';') {
328 failureReason = failureReasonInvalidSeparator;
329 failurePosition = pos;
330 return ReflectedXSSInvalid;
331 }
332
333 if (!skipWhiteSpace(header, pos, false))
334 return result;
335
336 // At start of next directive.
337 if (skipToken(header, pos, "mode")) {
338 if (modeDirectiveSeen) {
339 failureReason = failureReasonDuplicateMode;
340 failurePosition = pos;
341 return ReflectedXSSInvalid;
342 }
343 modeDirectiveSeen = true;
344 if (!skipEquals(header, pos)) {
345 failureReason = failureReasonInvalidEquals;
346 failurePosition = pos;
347 return ReflectedXSSInvalid;
348 }
349 if (!skipToken(header, pos, "block")) {
350 failureReason = failureReasonInvalidMode;
351 failurePosition = pos;
352 return ReflectedXSSInvalid;
353 }
354 result = BlockReflectedXSS;
355 } else if (skipToken(header, pos, "report")) {
356 if (reportDirectiveSeen) {
357 failureReason = failureReasonDuplicateReport;
358 failurePosition = pos;
359 return ReflectedXSSInvalid;
360 }
361 reportDirectiveSeen = true;
362 if (!skipEquals(header, pos)) {
363 failureReason = failureReasonInvalidEquals;
364 failurePosition = pos;
365 return ReflectedXSSInvalid;
366 }
367 size_t startPos = pos;
368 if (!skipValue(header, pos)) {
369 failureReason = failureReasonInvalidReport;
370 failurePosition = pos;
371 return ReflectedXSSInvalid;
372 }
373 reportURL = header.substring(startPos, pos - startPos);
374 failurePosition = startPos; // If later semantic check deems unaccep table.
375 } else {
376 failureReason = failureReasonInvalidDirective;
377 failurePosition = pos;
378 return ReflectedXSSInvalid;
379 }
380 }
381 }
382
383 ContentTypeOptionsDisposition parseContentTypeOptionsHeader(const String& header )
384 {
385 if (header.stripWhiteSpace().lower() == "nosniff")
386 return ContentTypeOptionsNosniff;
387 return ContentTypeOptionsNone;
388 }
389
390 String extractReasonPhraseFromHTTPStatusLine(const String& statusLine)
391 {
392 size_t spacePos = statusLine.find(' ');
393 // Remove status code from the status line.
394 spacePos = statusLine.find(' ', spacePos + 1);
395 return statusLine.substring(spacePos + 1);
396 }
397
398 bool parseRange(const String& range, long long& rangeOffset, long long& rangeEnd , long long& rangeSuffixLength)
399 {
400 // The format of "Range" header is defined in RFC 2616 Section 14.35.1.
401 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1
402 // We don't support multiple range requests.
403
404 rangeOffset = rangeEnd = rangeSuffixLength = -1;
405
406 // The "bytes" unit identifier should be present.
407 static const char bytesStart[] = "bytes=";
408 if (!range.startsWith(bytesStart, false))
409 return false;
410 String byteRange = range.substring(sizeof(bytesStart) - 1);
411
412 // The '-' character needs to be present.
413 int index = byteRange.find('-');
414 if (index == -1)
415 return false;
416
417 // If the '-' character is at the beginning, the suffix length, which specif ies the last N bytes, is provided.
418 // Example:
419 // -500
420 if (!index) {
421 String suffixLengthString = byteRange.substring(index + 1).stripWhiteSpa ce();
422 bool ok;
423 long long value = suffixLengthString.toInt64Strict(&ok);
424 if (ok)
425 rangeSuffixLength = value;
426 return true;
427 }
428
429 // Otherwise, the first-byte-position and the last-byte-position are provied .
430 // Examples:
431 // 0-499
432 // 500-
433 String firstBytePosStr = byteRange.left(index).stripWhiteSpace();
434 bool ok;
435 long long firstBytePos = firstBytePosStr.toInt64Strict(&ok);
436 if (!ok)
437 return false;
438
439 String lastBytePosStr = byteRange.substring(index + 1).stripWhiteSpace();
440 long long lastBytePos = -1;
441 if (!lastBytePosStr.isEmpty()) {
442 lastBytePos = lastBytePosStr.toInt64Strict(&ok);
443 if (!ok)
444 return false;
445 }
446
447 if (firstBytePos < 0 || !(lastBytePos == -1 || lastBytePos >= firstBytePos))
448 return false;
449
450 rangeOffset = firstBytePos;
451 rangeEnd = lastBytePos;
452 return true;
453 }
454
455 // HTTP/1.1 - RFC 2616
456 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1
457 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
458 size_t parseHTTPRequestLine(const char* data, size_t length, String& failureReas on, String& method, String& url, HTTPVersion& httpVersion)
459 {
460 method = String();
461 url = String();
462 httpVersion = Unknown;
463
464 const char* space1 = 0;
465 const char* space2 = 0;
466 const char* p;
467 size_t consumedLength;
468
469 for (p = data, consumedLength = 0; consumedLength < length; p++, consumedLen gth++) {
470 if (*p == ' ') {
471 if (!space1)
472 space1 = p;
473 else if (!space2)
474 space2 = p;
475 } else if (*p == '\n') {
476 break;
477 }
478 }
479
480 // Haven't finished header line.
481 if (consumedLength == length) {
482 failureReason = "Incomplete Request Line";
483 return 0;
484 }
485
486 // RequestLine does not contain 3 parts.
487 if (!space1 || !space2) {
488 failureReason = "Request Line does not appear to contain: <Method> <Url> <HTTPVersion>.";
489 return 0;
490 }
491
492 // The line must end with "\r\n".
493 const char* end = p + 1;
494 if (*(end - 2) != '\r') {
495 failureReason = "Request line does not end with CRLF";
496 return 0;
497 }
498
499 // Request Method.
500 method = String(data, space1 - data); // For length subtract 1 for space, bu t add 1 for data being the first character.
501
502 // Request URI.
503 url = String(space1 + 1, space2 - space1 - 1); // For length subtract 1 for space.
504
505 // HTTP Version.
506 String httpVersionString(space2 + 1, end - space2 - 3); // For length subtra ct 1 for space, and 2 for "\r\n".
507 if (httpVersionString.length() != 8 || !httpVersionString.startsWith("HTTP/1 ."))
508 httpVersion = Unknown;
509 else if (httpVersionString[7] == '0')
510 httpVersion = HTTP_1_0;
511 else if (httpVersionString[7] == '1')
512 httpVersion = HTTP_1_1;
513 else
514 httpVersion = Unknown;
515
516 return end - data;
517 }
518
519 static bool parseHTTPHeaderName(const char* s, size_t start, size_t size, String & failureReason, size_t* position, AtomicString* name)
520 {
521 size_t nameBegin = start;
522 for (size_t i = start; i < size; ++i) {
523 switch (s[i]) {
524 case '\r':
525 failureReason = "Unexpected CR in name at " + trimInputSample(&s[nam eBegin], i - nameBegin);
526 return false;
527 case '\n':
528 failureReason = "Unexpected LF in name at " + trimInputSample(&s[nam eBegin], i - nameBegin);
529 return false;
530 case ':':
531 if (i == nameBegin) {
532 failureReason = "Header name is missing";
533 return false;
534 }
535 *name = AtomicString::fromUTF8(&s[nameBegin], i - nameBegin);
536 if (name->isNull()) {
537 failureReason = "Invalid UTF-8 sequence in header name";
538 return false;
539 }
540 *position = i;
541 return true;
542 default:
543 break;
544 }
545 }
546 failureReason = "Unterminated header name";
547 return false;
548 }
549
550 static bool parseHTTPHeaderValue(const char* s, size_t start, size_t size, Strin g& failureReason, size_t* position, AtomicString* value)
551 {
552 size_t i = start;
553 for (; i < size && s[i] == ' '; ++i) {
554 }
555 size_t valueBegin = i;
556
557 for (; i < size && s[i] != '\r'; ++i) {
558 if (s[i] == '\n') {
559 failureReason = "Unexpected LF in value at " + trimInputSample(&s[va lueBegin], i - valueBegin);
560 return false;
561 }
562 }
563 if (i == size) {
564 failureReason = "Unterminated header value";
565 return false;
566 }
567
568 ASSERT(i < size && s[i] == '\r');
569 if (i + 1 >= size || s[i + 1] != '\n') {
570 failureReason = "LF doesn't follow CR after value at " + trimInputSample (&s[i + 1], size - i - 1);
571 return false;
572 }
573
574 *value = AtomicString::fromUTF8(&s[valueBegin], i - valueBegin);
575 if (i != valueBegin && value->isNull()) {
576 failureReason = "Invalid UTF-8 sequence in header value";
577 return false;
578 }
579
580 // 2 for strlen("\r\n")
581 *position = i + 2;
582 return true;
583 }
584
585 // Note that the header is already parsed and re-formatted in chromium side.
586 // We assume that the input is more restricted than RFC2616.
587 size_t parseHTTPHeader(const char* s, size_t size, String& failureReason, Atomic String& name, AtomicString& value)
588 {
589 name = nullAtom;
590 value = nullAtom;
591 if (size >= 1 && s[0] == '\r') {
592 if (size >= 2 && s[1] == '\n') {
593 // Skip an empty line.
594 return 2;
595 }
596 failureReason = "LF doesn't follow CR at " + trimInputSample(0, size);
597 return 0;
598 }
599 size_t current = 0;
600 if (!parseHTTPHeaderName(s, current, size, failureReason, &current, &name)) {
601 return 0;
602 }
603 ASSERT(s[current] == ':');
604 ++current;
605
606 if (!parseHTTPHeaderValue(s, current, size, failureReason, &current, &value) ) {
607 return 0;
608 }
609
610 return current;
611 }
612
613 size_t parseHTTPRequestBody(const char* data, size_t length, Vector<unsigned cha r>& body)
614 {
615 body.clear();
616 body.append(data, length);
617
618 return length;
619 }
620
621 static bool isCacheHeaderSeparator(UChar c)
622 {
623 // See RFC 2616, Section 2.2
624 switch (c) {
625 case '(':
626 case ')':
627 case '<':
628 case '>':
629 case '@':
630 case ',':
631 case ';':
632 case ':':
633 case '\\':
634 case '"':
635 case '/':
636 case '[':
637 case ']':
638 case '?':
639 case '=':
640 case '{':
641 case '}':
642 case ' ':
643 case '\t':
644 return true;
645 default:
646 return false;
647 }
648 }
649
650 static bool isControlCharacter(UChar c)
651 {
652 return c < ' ' || c == 127;
653 }
654
655 static inline String trimToNextSeparator(const String& str)
656 {
657 return str.substring(0, str.find(isCacheHeaderSeparator));
658 }
659
660 static void parseCacheHeader(const String& header, Vector<pair<String, String> > & result)
661 {
662 const String safeHeader = header.removeCharacters(isControlCharacter);
663 unsigned max = safeHeader.length();
664 for (unsigned pos = 0; pos < max; /* pos incremented in loop */) {
665 size_t nextCommaPosition = safeHeader.find(',', pos);
666 size_t nextEqualSignPosition = safeHeader.find('=', pos);
667 if (nextEqualSignPosition != kNotFound && (nextEqualSignPosition < nextC ommaPosition || nextCommaPosition == kNotFound)) {
668 // Get directive name, parse right hand side of equal sign, then add to map
669 String directive = trimToNextSeparator(safeHeader.substring(pos, nex tEqualSignPosition - pos).stripWhiteSpace());
670 pos += nextEqualSignPosition - pos + 1;
671
672 String value = safeHeader.substring(pos, max - pos).stripWhiteSpace( );
673 if (value[0] == '"') {
674 // The value is a quoted string
675 size_t nextDoubleQuotePosition = value.find('"', 1);
676 if (nextDoubleQuotePosition != kNotFound) {
677 // Store the value as a quoted string without quotes
678 result.append(pair<String, String>(directive, value.substrin g(1, nextDoubleQuotePosition - 1).stripWhiteSpace()));
679 pos += (safeHeader.find('"', pos) - pos) + nextDoubleQuotePo sition + 1;
680 // Move past next comma, if there is one
681 size_t nextCommaPosition2 = safeHeader.find(',', pos);
682 if (nextCommaPosition2 != kNotFound)
683 pos += nextCommaPosition2 - pos + 1;
684 else
685 return; // Parse error if there is anything left with no comma
686 } else {
687 // Parse error; just use the rest as the value
688 result.append(pair<String, String>(directive, trimToNextSepa rator(value.substring(1, value.length() - 1).stripWhiteSpace())));
689 return;
690 }
691 } else {
692 // The value is a token until the next comma
693 size_t nextCommaPosition2 = value.find(',');
694 if (nextCommaPosition2 != kNotFound) {
695 // The value is delimited by the next comma
696 result.append(pair<String, String>(directive, trimToNextSepa rator(value.substring(0, nextCommaPosition2).stripWhiteSpace())));
697 pos += (safeHeader.find(',', pos) - pos) + 1;
698 } else {
699 // The rest is the value; no change to value needed
700 result.append(pair<String, String>(directive, trimToNextSepa rator(value)));
701 return;
702 }
703 }
704 } else if (nextCommaPosition != kNotFound && (nextCommaPosition < nextEq ualSignPosition || nextEqualSignPosition == kNotFound)) {
705 // Add directive to map with empty string as value
706 result.append(pair<String, String>(trimToNextSeparator(safeHeader.su bstring(pos, nextCommaPosition - pos).stripWhiteSpace()), ""));
707 pos += nextCommaPosition - pos + 1;
708 } else {
709 // Add last directive to map with empty string as value
710 result.append(pair<String, String>(trimToNextSeparator(safeHeader.su bstring(pos, max - pos).stripWhiteSpace()), ""));
711 return;
712 }
713 }
714 }
715
716 CacheControlHeader parseCacheControlDirectives(const AtomicString& cacheControlV alue, const AtomicString& pragmaValue)
717 {
718 CacheControlHeader cacheControlHeader;
719 cacheControlHeader.parsed = true;
720 cacheControlHeader.maxAge = std::numeric_limits<double>::quiet_NaN();
721
722 DEFINE_STATIC_LOCAL(const AtomicString, noCacheDirective, ("no-cache", Atomi cString::ConstructFromLiteral));
723 DEFINE_STATIC_LOCAL(const AtomicString, noStoreDirective, ("no-store", Atomi cString::ConstructFromLiteral));
724 DEFINE_STATIC_LOCAL(const AtomicString, mustRevalidateDirective, ("must-reva lidate", AtomicString::ConstructFromLiteral));
725 DEFINE_STATIC_LOCAL(const AtomicString, maxAgeDirective, ("max-age", AtomicS tring::ConstructFromLiteral));
726
727 if (!cacheControlValue.isEmpty()) {
728 Vector<pair<String, String> > directives;
729 parseCacheHeader(cacheControlValue, directives);
730
731 size_t directivesSize = directives.size();
732 for (size_t i = 0; i < directivesSize; ++i) {
733 // RFC2616 14.9.1: A no-cache directive with a value is only meaning ful for proxy caches.
734 // It should be ignored by a browser level cache.
735 if (equalIgnoringCase(directives[i].first, noCacheDirective) && dire ctives[i].second.isEmpty()) {
736 cacheControlHeader.containsNoCache = true;
737 } else if (equalIgnoringCase(directives[i].first, noStoreDirective)) {
738 cacheControlHeader.containsNoStore = true;
739 } else if (equalIgnoringCase(directives[i].first, mustRevalidateDire ctive)) {
740 cacheControlHeader.containsMustRevalidate = true;
741 } else if (equalIgnoringCase(directives[i].first, maxAgeDirective)) {
742 if (!std::isnan(cacheControlHeader.maxAge)) {
743 // First max-age directive wins if there are multiple ones.
744 continue;
745 }
746 bool ok;
747 double maxAge = directives[i].second.toDouble(&ok);
748 if (ok)
749 cacheControlHeader.maxAge = maxAge;
750 }
751 }
752 }
753
754 if (!cacheControlHeader.containsNoCache) {
755 // Handle Pragma: no-cache
756 // This is deprecated and equivalent to Cache-control: no-cache
757 // Don't bother tokenizing the value, it is not important
758 cacheControlHeader.containsNoCache = pragmaValue.lower().contains(noCach eDirective);
759 }
760 return cacheControlHeader;
761 }
762
763 }
OLDNEW
« no previous file with comments | « sky/engine/platform/network/HTTPParsers.h ('k') | sky/engine/platform/network/HTTPParsersTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698