| 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 |