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 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 "config.h" 34#include "HTTPParsers.h" 35 36#include "ContentSecurityPolicy.h" 37#include <wtf/DateMath.h> 38#include <wtf/text/CString.h> 39#include <wtf/text/StringBuilder.h> 40#include <wtf/text/WTFString.h> 41#include <wtf/unicode/CharacterNames.h> 42 43using namespace WTF; 44 45namespace WebCore { 46 47// true if there is more to parse, after incrementing pos past whitespace. 48// Note: Might return pos == str.length() 49static inline bool skipWhiteSpace(const String& str, unsigned& pos, bool fromHttpEquivMeta) 50{ 51 unsigned len = str.length(); 52 53 if (fromHttpEquivMeta) { 54 while (pos < len && str[pos] <= ' ') 55 ++pos; 56 } else { 57 while (pos < len && (str[pos] == '\t' || str[pos] == ' ')) 58 ++pos; 59 } 60 61 return pos < len; 62} 63 64// Returns true if the function can match the whole token (case insensitive) 65// incrementing pos on match, otherwise leaving pos unchanged. 66// Note: Might return pos == str.length() 67static inline bool skipToken(const String& str, unsigned& pos, const char* token) 68{ 69 unsigned len = str.length(); 70 unsigned current = pos; 71 72 while (current < len && *token) { 73 if (toASCIILower(str[current]) != *token++) 74 return false; 75 ++current; 76 } 77 78 if (*token) 79 return false; 80 81 pos = current; 82 return true; 83} 84 85// True if the expected equals sign is seen and there is more to follow. 86static inline bool skipEquals(const String& str, unsigned &pos) 87{ 88 return skipWhiteSpace(str, pos, false) && str[pos++] == '=' && skipWhiteSpace(str, pos, false); 89} 90 91// True if a value present, incrementing pos to next space or semicolon, if any. 92// Note: might return pos == str.length(). 93static inline bool skipValue(const String& str, unsigned& pos) 94{ 95 unsigned start = pos; 96 unsigned len = str.length(); 97 while (pos < len) { 98 if (str[pos] == ' ' || str[pos] == '\t' || str[pos] == ';') 99 break; 100 ++pos; 101 } 102 return pos != start; 103} 104 105bool isValidHTTPHeaderValue(const String& name) 106{ 107 // FIXME: This should really match name against 108 // field-value in section 4.2 of RFC 2616. 109 110 return !name.contains('\r') && !name.contains('\n'); 111} 112 113// See RFC 2616, Section 2.2. 114bool isValidHTTPToken(const String& characters) 115{ 116 if (characters.isEmpty()) 117 return false; 118 for (unsigned i = 0; i < characters.length(); ++i) { 119 UChar c = characters[i]; 120 if (c <= 0x20 || c >= 0x7F 121 || c == '(' || c == ')' || c == '<' || c == '>' || c == '@' 122 || c == ',' || c == ';' || c == ':' || c == '\\' || c == '"' 123 || c == '/' || c == '[' || c == ']' || c == '?' || c == '=' 124 || c == '{' || c == '}') 125 return false; 126 } 127 return true; 128} 129 130static const size_t maxInputSampleSize = 128; 131static String trimInputSample(const char* p, size_t length) 132{ 133 String s = String(p, std::min<size_t>(length, maxInputSampleSize)); 134 if (length > maxInputSampleSize) 135 s.append(horizontalEllipsis); 136 return s; 137} 138 139ContentDispositionType contentDispositionType(const String& contentDisposition) 140{ 141 if (contentDisposition.isEmpty()) 142 return ContentDispositionNone; 143 144 Vector<String> parameters; 145 contentDisposition.split(';', parameters); 146 147 String dispositionType = parameters[0]; 148 dispositionType.stripWhiteSpace(); 149 150 if (equalIgnoringCase(dispositionType, "inline")) 151 return ContentDispositionInline; 152 153 // Some broken sites just send bogus headers like 154 // 155 // Content-Disposition: ; filename="file" 156 // Content-Disposition: filename="file" 157 // Content-Disposition: name="file" 158 // 159 // without a disposition token... screen those out. 160 if (!isValidHTTPToken(dispositionType)) 161 return ContentDispositionNone; 162 163 // We have a content-disposition of "attachment" or unknown. 164 // RFC 2183, section 2.8 says that an unknown disposition 165 // value should be treated as "attachment" 166 return ContentDispositionAttachment; 167} 168 169bool parseHTTPRefresh(const String& refresh, bool fromHttpEquivMeta, double& delay, String& url) 170{ 171 unsigned len = refresh.length(); 172 unsigned pos = 0; 173 174 if (!skipWhiteSpace(refresh, pos, fromHttpEquivMeta)) 175 return false; 176 177 while (pos != len && refresh[pos] != ',' && refresh[pos] != ';') 178 ++pos; 179 180 if (pos == len) { // no URL 181 url = String(); 182 bool ok; 183 delay = refresh.stripWhiteSpace().toDouble(&ok); 184 return ok; 185 } else { 186 bool ok; 187 delay = refresh.left(pos).stripWhiteSpace().toDouble(&ok); 188 if (!ok) 189 return false; 190 191 ++pos; 192 skipWhiteSpace(refresh, pos, fromHttpEquivMeta); 193 unsigned urlStartPos = pos; 194 if (refresh.find("url", urlStartPos, false) == urlStartPos) { 195 urlStartPos += 3; 196 skipWhiteSpace(refresh, urlStartPos, fromHttpEquivMeta); 197 if (refresh[urlStartPos] == '=') { 198 ++urlStartPos; 199 skipWhiteSpace(refresh, urlStartPos, fromHttpEquivMeta); 200 } else 201 urlStartPos = pos; // e.g. "Refresh: 0; url.html" 202 } 203 204 unsigned urlEndPos = len; 205 206 if (refresh[urlStartPos] == '"' || refresh[urlStartPos] == '\'') { 207 UChar quotationMark = refresh[urlStartPos]; 208 urlStartPos++; 209 while (urlEndPos > urlStartPos) { 210 urlEndPos--; 211 if (refresh[urlEndPos] == quotationMark) 212 break; 213 } 214 215 // https://bugs.webkit.org/show_bug.cgi?id=27868 216 // Sometimes there is no closing quote for the end of the URL even though there was an opening quote. 217 // If we looped over the entire alleged URL string back to the opening quote, just go ahead and use everything 218 // after the opening quote instead. 219 if (urlEndPos == urlStartPos) 220 urlEndPos = len; 221 } 222 223 url = refresh.substring(urlStartPos, urlEndPos - urlStartPos).stripWhiteSpace(); 224 return true; 225 } 226} 227 228double parseDate(const String& value) 229{ 230 return parseDateFromNullTerminatedCharacters(value.utf8().data()); 231} 232 233// FIXME: This function doesn't comply with RFC 6266. 234// For example, this function doesn't handle the interaction between " and ; 235// that arises from quoted-string, nor does this function properly unquote 236// attribute values. Further this function appears to process parameter names 237// in a case-sensitive manner. (There are likely other bugs as well.) 238String filenameFromHTTPContentDisposition(const String& value) 239{ 240 Vector<String> keyValuePairs; 241 value.split(';', keyValuePairs); 242 243 unsigned length = keyValuePairs.size(); 244 for (unsigned i = 0; i < length; i++) { 245 size_t valueStartPos = keyValuePairs[i].find('='); 246 if (valueStartPos == notFound) 247 continue; 248 249 String key = keyValuePairs[i].left(valueStartPos).stripWhiteSpace(); 250 251 if (key.isEmpty() || key != "filename") 252 continue; 253 254 String value = keyValuePairs[i].substring(valueStartPos + 1).stripWhiteSpace(); 255 256 // Remove quotes if there are any 257 if (value[0] == '\"') 258 value = value.substring(1, value.length() - 2); 259 260 return value; 261 } 262 263 return String(); 264} 265 266String extractMIMETypeFromMediaType(const String& mediaType) 267{ 268 StringBuilder mimeType; 269 unsigned length = mediaType.length(); 270 mimeType.reserveCapacity(length); 271 for (unsigned i = 0; i < length; i++) { 272 UChar c = mediaType[i]; 273 274 if (c == ';') 275 break; 276 277 // While RFC 2616 does not allow it, other browsers allow multiple values in the HTTP media 278 // type header field, Content-Type. In such cases, the media type string passed here may contain 279 // the multiple values separated by commas. For now, this code ignores text after the first comma, 280 // which prevents it from simply failing to parse such types altogether. Later for better 281 // compatibility we could consider using the first or last valid MIME type instead. 282 // See https://bugs.webkit.org/show_bug.cgi?id=25352 for more discussion. 283 if (c == ',') 284 break; 285 286 // FIXME: The following is not correct. RFC 2616 allows linear white space before and 287 // after the MIME type, but not within the MIME type itself. And linear white space 288 // includes only a few specific ASCII characters; a small subset of isSpaceOrNewline. 289 // See https://bugs.webkit.org/show_bug.cgi?id=8644 for a bug tracking part of this. 290 if (isSpaceOrNewline(c)) 291 continue; 292 293 mimeType.append(c); 294 } 295 296 if (mimeType.length() == length) 297 return mediaType; 298 return mimeType.toString(); 299} 300 301String extractCharsetFromMediaType(const String& mediaType) 302{ 303 unsigned int pos, len; 304 findCharsetInMediaType(mediaType, pos, len); 305 return mediaType.substring(pos, len); 306} 307 308void findCharsetInMediaType(const String& mediaType, unsigned int& charsetPos, unsigned int& charsetLen, unsigned int start) 309{ 310 charsetPos = start; 311 charsetLen = 0; 312 313 size_t pos = start; 314 unsigned length = mediaType.length(); 315 316 while (pos < length) { 317 pos = mediaType.find("charset", pos, false); 318 if (pos == notFound || pos == 0) { 319 charsetLen = 0; 320 return; 321 } 322 323 // is what we found a beginning of a word? 324 if (mediaType[pos-1] > ' ' && mediaType[pos-1] != ';') { 325 pos += 7; 326 continue; 327 } 328 329 pos += 7; 330 331 // skip whitespace 332 while (pos != length && mediaType[pos] <= ' ') 333 ++pos; 334 335 if (mediaType[pos++] != '=') // this "charset" substring wasn't a parameter name, but there may be others 336 continue; 337 338 while (pos != length && (mediaType[pos] <= ' ' || mediaType[pos] == '"' || mediaType[pos] == '\'')) 339 ++pos; 340 341 // we don't handle spaces within quoted parameter values, because charset names cannot have any 342 unsigned endpos = pos; 343 while (pos != length && mediaType[endpos] > ' ' && mediaType[endpos] != '"' && mediaType[endpos] != '\'' && mediaType[endpos] != ';') 344 ++endpos; 345 346 charsetPos = pos; 347 charsetLen = endpos - pos; 348 return; 349 } 350} 351 352ContentSecurityPolicy::ReflectedXSSDisposition parseXSSProtectionHeader(const String& header, String& failureReason, unsigned& failurePosition, String& reportURL) 353{ 354 DEPRECATED_DEFINE_STATIC_LOCAL(String, failureReasonInvalidToggle, (ASCIILiteral("expected 0 or 1"))); 355 DEPRECATED_DEFINE_STATIC_LOCAL(String, failureReasonInvalidSeparator, (ASCIILiteral("expected semicolon"))); 356 DEPRECATED_DEFINE_STATIC_LOCAL(String, failureReasonInvalidEquals, (ASCIILiteral("expected equals sign"))); 357 DEPRECATED_DEFINE_STATIC_LOCAL(String, failureReasonInvalidMode, (ASCIILiteral("invalid mode directive"))); 358 DEPRECATED_DEFINE_STATIC_LOCAL(String, failureReasonInvalidReport, (ASCIILiteral("invalid report directive"))); 359 DEPRECATED_DEFINE_STATIC_LOCAL(String, failureReasonDuplicateMode, (ASCIILiteral("duplicate mode directive"))); 360 DEPRECATED_DEFINE_STATIC_LOCAL(String, failureReasonDuplicateReport, (ASCIILiteral("duplicate report directive"))); 361 DEPRECATED_DEFINE_STATIC_LOCAL(String, failureReasonInvalidDirective, (ASCIILiteral("unrecognized directive"))); 362 363 unsigned pos = 0; 364 365 if (!skipWhiteSpace(header, pos, false)) 366 return ContentSecurityPolicy::ReflectedXSSUnset; 367 368 if (header[pos] == '0') 369 return ContentSecurityPolicy::AllowReflectedXSS; 370 371 if (header[pos++] != '1') { 372 failureReason = failureReasonInvalidToggle; 373 return ContentSecurityPolicy::ReflectedXSSInvalid; 374 } 375 376 ContentSecurityPolicy::ReflectedXSSDisposition result = ContentSecurityPolicy::FilterReflectedXSS; 377 bool modeDirectiveSeen = false; 378 bool reportDirectiveSeen = false; 379 380 while (1) { 381 // At end of previous directive: consume whitespace, semicolon, and whitespace. 382 if (!skipWhiteSpace(header, pos, false)) 383 return result; 384 385 if (header[pos++] != ';') { 386 failureReason = failureReasonInvalidSeparator; 387 failurePosition = pos; 388 return ContentSecurityPolicy::ReflectedXSSInvalid; 389 } 390 391 if (!skipWhiteSpace(header, pos, false)) 392 return result; 393 394 // At start of next directive. 395 if (skipToken(header, pos, "mode")) { 396 if (modeDirectiveSeen) { 397 failureReason = failureReasonDuplicateMode; 398 failurePosition = pos; 399 return ContentSecurityPolicy::ReflectedXSSInvalid; 400 } 401 modeDirectiveSeen = true; 402 if (!skipEquals(header, pos)) { 403 failureReason = failureReasonInvalidEquals; 404 failurePosition = pos; 405 return ContentSecurityPolicy::ReflectedXSSInvalid; 406 } 407 if (!skipToken(header, pos, "block")) { 408 failureReason = failureReasonInvalidMode; 409 failurePosition = pos; 410 return ContentSecurityPolicy::ReflectedXSSInvalid; 411 } 412 result = ContentSecurityPolicy::BlockReflectedXSS; 413 } else if (skipToken(header, pos, "report")) { 414 if (reportDirectiveSeen) { 415 failureReason = failureReasonDuplicateReport; 416 failurePosition = pos; 417 return ContentSecurityPolicy::ReflectedXSSInvalid; 418 } 419 reportDirectiveSeen = true; 420 if (!skipEquals(header, pos)) { 421 failureReason = failureReasonInvalidEquals; 422 failurePosition = pos; 423 return ContentSecurityPolicy::ReflectedXSSInvalid; 424 } 425 size_t startPos = pos; 426 if (!skipValue(header, pos)) { 427 failureReason = failureReasonInvalidReport; 428 failurePosition = pos; 429 return ContentSecurityPolicy::ReflectedXSSInvalid; 430 } 431 reportURL = header.substring(startPos, pos - startPos); 432 failurePosition = startPos; // If later semantic check deems unacceptable. 433 } else { 434 failureReason = failureReasonInvalidDirective; 435 failurePosition = pos; 436 return ContentSecurityPolicy::ReflectedXSSInvalid; 437 } 438 } 439} 440 441#if ENABLE(NOSNIFF) 442ContentTypeOptionsDisposition parseContentTypeOptionsHeader(const String& header) 443{ 444 if (header.stripWhiteSpace().lower() == "nosniff") 445 return ContentTypeOptionsNosniff; 446 return ContentTypeOptionsNone; 447} 448#endif 449 450String extractReasonPhraseFromHTTPStatusLine(const String& statusLine) 451{ 452 size_t spacePos = statusLine.find(' '); 453 // Remove status code from the status line. 454 spacePos = statusLine.find(' ', spacePos + 1); 455 return statusLine.substring(spacePos + 1); 456} 457 458XFrameOptionsDisposition parseXFrameOptionsHeader(const String& header) 459{ 460 XFrameOptionsDisposition result = XFrameOptionsNone; 461 462 if (header.isEmpty()) 463 return result; 464 465 Vector<String> headers; 466 header.split(',', headers); 467 468 for (size_t i = 0; i < headers.size(); i++) { 469 String currentHeader = headers[i].stripWhiteSpace(); 470 XFrameOptionsDisposition currentValue = XFrameOptionsNone; 471 if (equalIgnoringCase(currentHeader, "deny")) 472 currentValue = XFrameOptionsDeny; 473 else if (equalIgnoringCase(currentHeader, "sameorigin")) 474 currentValue = XFrameOptionsSameOrigin; 475 else if (equalIgnoringCase(currentHeader, "allowall")) 476 currentValue = XFrameOptionsAllowAll; 477 else 478 currentValue = XFrameOptionsInvalid; 479 480 if (result == XFrameOptionsNone) 481 result = currentValue; 482 else if (result != currentValue) 483 return XFrameOptionsConflict; 484 } 485 return result; 486} 487 488bool parseRange(const String& range, long long& rangeOffset, long long& rangeEnd, long long& rangeSuffixLength) 489{ 490 // The format of "Range" header is defined in RFC 2616 Section 14.35.1. 491 // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1 492 // We don't support multiple range requests. 493 494 rangeOffset = rangeEnd = rangeSuffixLength = -1; 495 496 // The "bytes" unit identifier should be present. 497 static const char bytesStart[] = "bytes="; 498 if (!range.startsWith(bytesStart, false)) 499 return false; 500 String byteRange = range.substring(sizeof(bytesStart) - 1); 501 502 // The '-' character needs to be present. 503 int index = byteRange.find('-'); 504 if (index == -1) 505 return false; 506 507 // If the '-' character is at the beginning, the suffix length, which specifies the last N bytes, is provided. 508 // Example: 509 // -500 510 if (!index) { 511 String suffixLengthString = byteRange.substring(index + 1).stripWhiteSpace(); 512 bool ok; 513 long long value = suffixLengthString.toInt64Strict(&ok); 514 if (ok) 515 rangeSuffixLength = value; 516 return true; 517 } 518 519 // Otherwise, the first-byte-position and the last-byte-position are provied. 520 // Examples: 521 // 0-499 522 // 500- 523 String firstBytePosStr = byteRange.left(index).stripWhiteSpace(); 524 bool ok; 525 long long firstBytePos = firstBytePosStr.toInt64Strict(&ok); 526 if (!ok) 527 return false; 528 529 String lastBytePosStr = byteRange.substring(index + 1).stripWhiteSpace(); 530 long long lastBytePos = -1; 531 if (!lastBytePosStr.isEmpty()) { 532 lastBytePos = lastBytePosStr.toInt64Strict(&ok); 533 if (!ok) 534 return false; 535 } 536 537 if (firstBytePos < 0 || !(lastBytePos == -1 || lastBytePos >= firstBytePos)) 538 return false; 539 540 rangeOffset = firstBytePos; 541 rangeEnd = lastBytePos; 542 return true; 543} 544 545// HTTP/1.1 - RFC 2616 546// http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1 547// Request-Line = Method SP Request-URI SP HTTP-Version CRLF 548size_t parseHTTPRequestLine(const char* data, size_t length, String& failureReason, String& method, String& url, HTTPVersion& httpVersion) 549{ 550 method = String(); 551 url = String(); 552 httpVersion = Unknown; 553 554 const char* space1 = 0; 555 const char* space2 = 0; 556 const char* p; 557 size_t consumedLength; 558 559 for (p = data, consumedLength = 0; consumedLength < length; p++, consumedLength++) { 560 if (*p == ' ') { 561 if (!space1) 562 space1 = p; 563 else if (!space2) 564 space2 = p; 565 } else if (*p == '\n') 566 break; 567 } 568 569 // Haven't finished header line. 570 if (consumedLength == length) { 571 failureReason = "Incomplete Request Line"; 572 return 0; 573 } 574 575 // RequestLine does not contain 3 parts. 576 if (!space1 || !space2) { 577 failureReason = "Request Line does not appear to contain: <Method> <Url> <HTTPVersion>."; 578 return 0; 579 } 580 581 // The line must end with "\r\n". 582 const char* end = p + 1; 583 if (*(end - 2) != '\r') { 584 failureReason = "Request line does not end with CRLF"; 585 return 0; 586 } 587 588 // Request Method. 589 method = String(data, space1 - data); // For length subtract 1 for space, but add 1 for data being the first character. 590 591 // Request URI. 592 url = String(space1 + 1, space2 - space1 - 1); // For length subtract 1 for space. 593 594 // HTTP Version. 595 String httpVersionString(space2 + 1, end - space2 - 3); // For length subtract 1 for space, and 2 for "\r\n". 596 if (httpVersionString.length() != 8 || !httpVersionString.startsWith("HTTP/1.")) 597 httpVersion = Unknown; 598 else if (httpVersionString[7] == '0') 599 httpVersion = HTTP_1_0; 600 else if (httpVersionString[7] == '1') 601 httpVersion = HTTP_1_1; 602 else 603 httpVersion = Unknown; 604 605 return end - data; 606} 607 608size_t parseHTTPHeader(const char* start, size_t length, String& failureReason, String& nameStr, String& valueStr, bool strict) 609{ 610 const char* p = start; 611 const char* end = start + length; 612 613 Vector<char> name; 614 Vector<char> value; 615 nameStr = String(); 616 valueStr = String(); 617 618 for (; p < end; p++) { 619 switch (*p) { 620 case '\r': 621 if (name.isEmpty()) { 622 if (p + 1 < end && *(p + 1) == '\n') 623 return (p + 2) - start; 624 failureReason = "CR doesn't follow LF at " + trimInputSample(p, end - p); 625 return 0; 626 } 627 failureReason = "Unexpected CR in name at " + trimInputSample(name.data(), name.size()); 628 return 0; 629 case '\n': 630 failureReason = "Unexpected LF in name at " + trimInputSample(name.data(), name.size()); 631 return 0; 632 case ':': 633 break; 634 default: 635 name.append(*p); 636 continue; 637 } 638 if (*p == ':') { 639 ++p; 640 break; 641 } 642 } 643 644 for (; p < end && *p == 0x20; p++) { } 645 646 for (; p < end; p++) { 647 switch (*p) { 648 case '\r': 649 break; 650 case '\n': 651 if (strict) { 652 failureReason = "Unexpected LF in value at " + trimInputSample(value.data(), value.size()); 653 return 0; 654 } 655 break; 656 default: 657 value.append(*p); 658 } 659 if (*p == '\r' || (!strict && *p == '\n')) { 660 ++p; 661 break; 662 } 663 } 664 if (p >= end || (strict && *p != '\n')) { 665 failureReason = "CR doesn't follow LF after value at " + trimInputSample(p, end - p); 666 return 0; 667 } 668 nameStr = String::fromUTF8(name.data(), name.size()); 669 valueStr = String::fromUTF8(value.data(), value.size()); 670 if (nameStr.isNull()) { 671 failureReason = "Invalid UTF-8 sequence in header name"; 672 return 0; 673 } 674 if (valueStr.isNull()) { 675 failureReason = "Invalid UTF-8 sequence in header value"; 676 return 0; 677 } 678 return p - start; 679} 680 681size_t parseHTTPRequestBody(const char* data, size_t length, Vector<unsigned char>& body) 682{ 683 body.clear(); 684 body.append(data, length); 685 686 return length; 687} 688 689} 690