OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) | 2 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) |
3 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. | 3 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
4 * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ | 4 * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ |
5 * Copyright (C) 2009 Google Inc. All rights reserved. | 5 * Copyright (C) 2009 Google Inc. All rights reserved. |
6 * Copyright (C) 2011 Apple Inc. All Rights Reserved. | 6 * Copyright (C) 2011 Apple Inc. All Rights Reserved. |
7 * | 7 * |
8 * Redistribution and use in source and binary forms, with or without | 8 * Redistribution and use in source and binary forms, with or without |
9 * modification, are permitted provided that the following conditions | 9 * modification, are permitted provided that the following conditions |
10 * are met: | 10 * are met: |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
151 if (c <= 0x20 || c >= 0x7F | 151 if (c <= 0x20 || c >= 0x7F |
152 || c == '(' || c == ')' || c == '<' || c == '>' || c == '@' | 152 || c == '(' || c == ')' || c == '<' || c == '>' || c == '@' |
153 || c == ',' || c == ';' || c == ':' || c == '\\' || c == '"' | 153 || c == ',' || c == ';' || c == ':' || c == '\\' || c == '"' |
154 || c == '/' || c == '[' || c == ']' || c == '?' || c == '=' | 154 || c == '/' || c == '[' || c == ']' || c == '?' || c == '=' |
155 || c == '{' || c == '}') | 155 || c == '{' || c == '}') |
156 return false; | 156 return false; |
157 } | 157 } |
158 return true; | 158 return true; |
159 } | 159 } |
160 | 160 |
161 static const size_t maxInputSampleSize = 128; | |
162 static String trimInputSample(const char* p, size_t length) | |
163 { | |
164 if (length > maxInputSampleSize) | |
165 return String(p, maxInputSampleSize) + horizontalEllipsisCharacter; | |
166 return String(p, length); | |
167 } | |
168 | |
169 ContentDispositionType contentDispositionType(const String& contentDisposition) | 161 ContentDispositionType contentDispositionType(const String& contentDisposition) |
170 { | 162 { |
171 if (contentDisposition.isEmpty()) | 163 if (contentDisposition.isEmpty()) |
172 return ContentDispositionNone; | 164 return ContentDispositionNone; |
173 | 165 |
174 Vector<String> parameters; | 166 Vector<String> parameters; |
175 contentDisposition.split(';', parameters); | 167 contentDisposition.split(';', parameters); |
176 | 168 |
177 if (parameters.isEmpty()) | 169 if (parameters.isEmpty()) |
178 return ContentDispositionNone; | 170 return ContentDispositionNone; |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
257 url = refresh.substring(urlStartPos, urlEndPos - urlStartPos).stripWhite
Space(); | 249 url = refresh.substring(urlStartPos, urlEndPos - urlStartPos).stripWhite
Space(); |
258 return true; | 250 return true; |
259 } | 251 } |
260 } | 252 } |
261 | 253 |
262 double parseDate(const String& value) | 254 double parseDate(const String& value) |
263 { | 255 { |
264 return parseDateFromNullTerminatedCharacters(value.utf8().data()); | 256 return parseDateFromNullTerminatedCharacters(value.utf8().data()); |
265 } | 257 } |
266 | 258 |
267 // FIXME: This function doesn't comply with RFC 6266. | |
268 // For example, this function doesn't handle the interaction between " and ; | |
269 // that arises from quoted-string, nor does this function properly unquote | |
270 // attribute values. Further this function appears to process parameter names | |
271 // in a case-sensitive manner. (There are likely other bugs as well.) | |
272 String filenameFromHTTPContentDisposition(const String& value) | |
273 { | |
274 Vector<String> keyValuePairs; | |
275 value.split(';', keyValuePairs); | |
276 | |
277 unsigned length = keyValuePairs.size(); | |
278 for (unsigned i = 0; i < length; i++) { | |
279 size_t valueStartPos = keyValuePairs[i].find('='); | |
280 if (valueStartPos == kNotFound) | |
281 continue; | |
282 | |
283 String key = keyValuePairs[i].left(valueStartPos).stripWhiteSpace(); | |
284 | |
285 if (key.isEmpty() || key != "filename") | |
286 continue; | |
287 | |
288 String value = keyValuePairs[i].substring(valueStartPos + 1).stripWhiteS
pace(); | |
289 | |
290 // Remove quotes if there are any | |
291 if (value[0] == '\"') | |
292 value = value.substring(1, value.length() - 2); | |
293 | |
294 return value; | |
295 } | |
296 | |
297 return String(); | |
298 } | |
299 | |
300 AtomicString extractMIMETypeFromMediaType(const AtomicString& mediaType) | 259 AtomicString extractMIMETypeFromMediaType(const AtomicString& mediaType) |
301 { | 260 { |
302 StringBuilder mimeType; | 261 StringBuilder mimeType; |
303 unsigned length = mediaType.length(); | 262 unsigned length = mediaType.length(); |
304 mimeType.reserveCapacity(length); | 263 mimeType.reserveCapacity(length); |
305 for (unsigned i = 0; i < length; i++) { | 264 for (unsigned i = 0; i < length; i++) { |
306 UChar c = mediaType[i]; | 265 UChar c = mediaType[i]; |
307 | 266 |
308 if (c == ';') | 267 if (c == ';') |
309 break; | 268 break; |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
472 } | 431 } |
473 } | 432 } |
474 | 433 |
475 ContentTypeOptionsDisposition parseContentTypeOptionsHeader(const String& header
) | 434 ContentTypeOptionsDisposition parseContentTypeOptionsHeader(const String& header
) |
476 { | 435 { |
477 if (header.stripWhiteSpace().lower() == "nosniff") | 436 if (header.stripWhiteSpace().lower() == "nosniff") |
478 return ContentTypeOptionsNosniff; | 437 return ContentTypeOptionsNosniff; |
479 return ContentTypeOptionsNone; | 438 return ContentTypeOptionsNone; |
480 } | 439 } |
481 | 440 |
482 String extractReasonPhraseFromHTTPStatusLine(const String& statusLine) | |
483 { | |
484 size_t spacePos = statusLine.find(' '); | |
485 // Remove status code from the status line. | |
486 spacePos = statusLine.find(' ', spacePos + 1); | |
487 return statusLine.substring(spacePos + 1); | |
488 } | |
489 | |
490 XFrameOptionsDisposition parseXFrameOptionsHeader(const String& header) | 441 XFrameOptionsDisposition parseXFrameOptionsHeader(const String& header) |
491 { | 442 { |
492 XFrameOptionsDisposition result = XFrameOptionsNone; | 443 XFrameOptionsDisposition result = XFrameOptionsNone; |
493 | 444 |
494 if (header.isEmpty()) | 445 if (header.isEmpty()) |
495 return result; | 446 return result; |
496 | 447 |
497 Vector<String> headers; | 448 Vector<String> headers; |
498 header.split(',', headers); | 449 header.split(',', headers); |
499 | 450 |
(...skipping 10 matching lines...) Expand all Loading... |
510 currentValue = XFrameOptionsInvalid; | 461 currentValue = XFrameOptionsInvalid; |
511 | 462 |
512 if (result == XFrameOptionsNone) | 463 if (result == XFrameOptionsNone) |
513 result = currentValue; | 464 result = currentValue; |
514 else if (result != currentValue) | 465 else if (result != currentValue) |
515 return XFrameOptionsConflict; | 466 return XFrameOptionsConflict; |
516 } | 467 } |
517 return result; | 468 return result; |
518 } | 469 } |
519 | 470 |
520 bool parseRange(const String& range, long long& rangeOffset, long long& rangeEnd
, long long& rangeSuffixLength) | |
521 { | |
522 // The format of "Range" header is defined in RFC 2616 Section 14.35.1. | |
523 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1 | |
524 // We don't support multiple range requests. | |
525 | |
526 rangeOffset = rangeEnd = rangeSuffixLength = -1; | |
527 | |
528 // The "bytes" unit identifier should be present. | |
529 static const char bytesStart[] = "bytes="; | |
530 if (!range.startsWith(bytesStart, TextCaseInsensitive)) | |
531 return false; | |
532 String byteRange = range.substring(sizeof(bytesStart) - 1); | |
533 | |
534 // The '-' character needs to be present. | |
535 int index = byteRange.find('-'); | |
536 if (index == -1) | |
537 return false; | |
538 | |
539 // If the '-' character is at the beginning, the suffix length, which specif
ies the last N bytes, is provided. | |
540 // Example: | |
541 // -500 | |
542 if (!index) { | |
543 String suffixLengthString = byteRange.substring(index + 1).stripWhiteSpa
ce(); | |
544 bool ok; | |
545 long long value = suffixLengthString.toInt64Strict(&ok); | |
546 if (ok) | |
547 rangeSuffixLength = value; | |
548 return true; | |
549 } | |
550 | |
551 // Otherwise, the first-byte-position and the last-byte-position are provied
. | |
552 // Examples: | |
553 // 0-499 | |
554 // 500- | |
555 String firstBytePosStr = byteRange.left(index).stripWhiteSpace(); | |
556 bool ok; | |
557 long long firstBytePos = firstBytePosStr.toInt64Strict(&ok); | |
558 if (!ok) | |
559 return false; | |
560 | |
561 String lastBytePosStr = byteRange.substring(index + 1).stripWhiteSpace(); | |
562 long long lastBytePos = -1; | |
563 if (!lastBytePosStr.isEmpty()) { | |
564 lastBytePos = lastBytePosStr.toInt64Strict(&ok); | |
565 if (!ok) | |
566 return false; | |
567 } | |
568 | |
569 if (firstBytePos < 0 || !(lastBytePos == -1 || lastBytePos >= firstBytePos)) | |
570 return false; | |
571 | |
572 rangeOffset = firstBytePos; | |
573 rangeEnd = lastBytePos; | |
574 return true; | |
575 } | |
576 | |
577 // HTTP/1.1 - RFC 2616 | |
578 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1 | |
579 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF | |
580 size_t parseHTTPRequestLine(const char* data, size_t length, String& failureReas
on, String& method, String& url, HTTPVersion& httpVersion) | |
581 { | |
582 method = String(); | |
583 url = String(); | |
584 httpVersion = Unknown; | |
585 | |
586 const char* space1 = 0; | |
587 const char* space2 = 0; | |
588 const char* p; | |
589 size_t consumedLength; | |
590 | |
591 for (p = data, consumedLength = 0; consumedLength < length; p++, consumedLen
gth++) { | |
592 if (*p == ' ') { | |
593 if (!space1) | |
594 space1 = p; | |
595 else if (!space2) | |
596 space2 = p; | |
597 } else if (*p == '\n') { | |
598 break; | |
599 } | |
600 } | |
601 | |
602 // Haven't finished header line. | |
603 if (consumedLength == length) { | |
604 failureReason = "Incomplete Request Line"; | |
605 return 0; | |
606 } | |
607 | |
608 // RequestLine does not contain 3 parts. | |
609 if (!space1 || !space2) { | |
610 failureReason = "Request Line does not appear to contain: <Method> <Url>
<HTTPVersion>."; | |
611 return 0; | |
612 } | |
613 | |
614 // The line must end with "\r\n". | |
615 const char* end = p + 1; | |
616 if (*(end - 2) != '\r') { | |
617 failureReason = "Request line does not end with CRLF"; | |
618 return 0; | |
619 } | |
620 | |
621 // Request Method. | |
622 method = String(data, space1 - data); // For length subtract 1 for space, bu
t add 1 for data being the first character. | |
623 | |
624 // Request URI. | |
625 url = String(space1 + 1, space2 - space1 - 1); // For length subtract 1 for
space. | |
626 | |
627 // HTTP Version. | |
628 String httpVersionString(space2 + 1, end - space2 - 3); // For length subtra
ct 1 for space, and 2 for "\r\n". | |
629 if (httpVersionString.length() != 8 || !httpVersionString.startsWith("HTTP/1
.")) | |
630 httpVersion = Unknown; | |
631 else if (httpVersionString[7] == '0') | |
632 httpVersion = HTTP_1_0; | |
633 else if (httpVersionString[7] == '1') | |
634 httpVersion = HTTP_1_1; | |
635 else | |
636 httpVersion = Unknown; | |
637 | |
638 return end - data; | |
639 } | |
640 | |
641 static bool parseHTTPHeaderName(const char* s, size_t start, size_t size, String
& failureReason, size_t* position, AtomicString* name) | |
642 { | |
643 size_t nameBegin = start; | |
644 for (size_t i = start; i < size; ++i) { | |
645 switch (s[i]) { | |
646 case '\r': | |
647 failureReason = "Unexpected CR in name at " + trimInputSample(&s[nam
eBegin], i - nameBegin); | |
648 return false; | |
649 case '\n': | |
650 failureReason = "Unexpected LF in name at " + trimInputSample(&s[nam
eBegin], i - nameBegin); | |
651 return false; | |
652 case ':': | |
653 if (i == nameBegin) { | |
654 failureReason = "Header name is missing"; | |
655 return false; | |
656 } | |
657 *name = AtomicString::fromUTF8(&s[nameBegin], i - nameBegin); | |
658 if (name->isNull()) { | |
659 failureReason = "Invalid UTF-8 sequence in header name"; | |
660 return false; | |
661 } | |
662 *position = i; | |
663 return true; | |
664 default: | |
665 break; | |
666 } | |
667 } | |
668 failureReason = "Unterminated header name"; | |
669 return false; | |
670 } | |
671 | |
672 static bool parseHTTPHeaderValue(const char* s, size_t start, size_t size, Strin
g& failureReason, size_t* position, AtomicString* value) | |
673 { | |
674 size_t i = start; | |
675 for (; i < size && s[i] == ' '; ++i) { | |
676 } | |
677 size_t valueBegin = i; | |
678 | |
679 for (; i < size && s[i] != '\r'; ++i) { | |
680 if (s[i] == '\n') { | |
681 failureReason = "Unexpected LF in value at " + trimInputSample(&s[va
lueBegin], i - valueBegin); | |
682 return false; | |
683 } | |
684 } | |
685 if (i == size) { | |
686 failureReason = "Unterminated header value"; | |
687 return false; | |
688 } | |
689 | |
690 ASSERT(i < size && s[i] == '\r'); | |
691 if (i + 1 >= size || s[i + 1] != '\n') { | |
692 failureReason = "LF doesn't follow CR after value at " + trimInputSample
(&s[i + 1], size - i - 1); | |
693 return false; | |
694 } | |
695 | |
696 *value = AtomicString::fromUTF8(&s[valueBegin], i - valueBegin); | |
697 if (i != valueBegin && value->isNull()) { | |
698 failureReason = "Invalid UTF-8 sequence in header value"; | |
699 return false; | |
700 } | |
701 | |
702 // 2 for strlen("\r\n") | |
703 *position = i + 2; | |
704 return true; | |
705 } | |
706 | |
707 // Note that the header is already parsed and re-formatted in chromium side. | |
708 // We assume that the input is more restricted than RFC2616. | |
709 size_t parseHTTPHeader(const char* s, size_t size, String& failureReason, Atomic
String& name, AtomicString& value) | |
710 { | |
711 name = nullAtom; | |
712 value = nullAtom; | |
713 if (size >= 1 && s[0] == '\r') { | |
714 if (size >= 2 && s[1] == '\n') { | |
715 // Skip an empty line. | |
716 return 2; | |
717 } | |
718 failureReason = "LF doesn't follow CR at " + trimInputSample(0, size); | |
719 return 0; | |
720 } | |
721 size_t current = 0; | |
722 if (!parseHTTPHeaderName(s, current, size, failureReason, ¤t, &name))
{ | |
723 return 0; | |
724 } | |
725 ASSERT(s[current] == ':'); | |
726 ++current; | |
727 | |
728 if (!parseHTTPHeaderValue(s, current, size, failureReason, ¤t, &value)
) { | |
729 return 0; | |
730 } | |
731 | |
732 return current; | |
733 } | |
734 | |
735 size_t parseHTTPRequestBody(const char* data, size_t length, Vector<unsigned cha
r>& body) | |
736 { | |
737 body.clear(); | |
738 body.append(data, length); | |
739 | |
740 return length; | |
741 } | |
742 | |
743 static bool isCacheHeaderSeparator(UChar c) | 471 static bool isCacheHeaderSeparator(UChar c) |
744 { | 472 { |
745 // See RFC 2616, Section 2.2 | 473 // See RFC 2616, Section 2.2 |
746 switch (c) { | 474 switch (c) { |
747 case '(': | 475 case '(': |
748 case ')': | 476 case ')': |
749 case '<': | 477 case '<': |
750 case '>': | 478 case '>': |
751 case '@': | 479 case '@': |
752 case ',': | 480 case ',': |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
895 | 623 |
896 void parseCommaDelimitedHeader(const String& headerValue, CommaDelimitedHeaderSe
t& headerSet) | 624 void parseCommaDelimitedHeader(const String& headerValue, CommaDelimitedHeaderSe
t& headerSet) |
897 { | 625 { |
898 Vector<String> results; | 626 Vector<String> results; |
899 headerValue.split(",", results); | 627 headerValue.split(",", results); |
900 for (auto& value : results) | 628 for (auto& value : results) |
901 headerSet.add(value.stripWhiteSpace(isWhitespace)); | 629 headerSet.add(value.stripWhiteSpace(isWhitespace)); |
902 } | 630 } |
903 | 631 |
904 } | 632 } |
OLD | NEW |