| OLD | NEW |
| (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 "config.h" | |
| 34 #include "core/platform/network/HTTPParsers.h" | |
| 35 | |
| 36 #include "wtf/DateMath.h" | |
| 37 #include "wtf/text/CString.h" | |
| 38 #include "wtf/text/StringBuilder.h" | |
| 39 #include "wtf/text/WTFString.h" | |
| 40 #include "wtf/unicode/CharacterNames.h" | |
| 41 | |
| 42 using namespace WTF; | |
| 43 | |
| 44 namespace WebCore { | |
| 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 String s = String(p, std::min<size_t>(length, maxInputSampleSize)); | |
| 133 if (length > maxInputSampleSize) | |
| 134 s.append(horizontalEllipsis); | |
| 135 return s; | |
| 136 } | |
| 137 | |
| 138 ContentDispositionType contentDispositionType(const String& contentDisposition) | |
| 139 { | |
| 140 if (contentDisposition.isEmpty()) | |
| 141 return ContentDispositionNone; | |
| 142 | |
| 143 Vector<String> parameters; | |
| 144 contentDisposition.split(';', parameters); | |
| 145 | |
| 146 if (parameters.isEmpty()) | |
| 147 return ContentDispositionNone; | |
| 148 | |
| 149 String dispositionType = parameters[0]; | |
| 150 dispositionType.stripWhiteSpace(); | |
| 151 | |
| 152 if (equalIgnoringCase(dispositionType, "inline")) | |
| 153 return ContentDispositionInline; | |
| 154 | |
| 155 // Some broken sites just send bogus headers like | |
| 156 // | |
| 157 // Content-Disposition: ; filename="file" | |
| 158 // Content-Disposition: filename="file" | |
| 159 // Content-Disposition: name="file" | |
| 160 // | |
| 161 // without a disposition token... screen those out. | |
| 162 if (!isValidHTTPToken(dispositionType)) | |
| 163 return ContentDispositionNone; | |
| 164 | |
| 165 // We have a content-disposition of "attachment" or unknown. | |
| 166 // RFC 2183, section 2.8 says that an unknown disposition | |
| 167 // value should be treated as "attachment" | |
| 168 return ContentDispositionAttachment; | |
| 169 } | |
| 170 | |
| 171 bool parseHTTPRefresh(const String& refresh, bool fromHttpEquivMeta, double& del
ay, String& url) | |
| 172 { | |
| 173 unsigned len = refresh.length(); | |
| 174 unsigned pos = 0; | |
| 175 | |
| 176 if (!skipWhiteSpace(refresh, pos, fromHttpEquivMeta)) | |
| 177 return false; | |
| 178 | |
| 179 while (pos != len && refresh[pos] != ',' && refresh[pos] != ';') | |
| 180 ++pos; | |
| 181 | |
| 182 if (pos == len) { // no URL | |
| 183 url = String(); | |
| 184 bool ok; | |
| 185 delay = refresh.stripWhiteSpace().toDouble(&ok); | |
| 186 return ok; | |
| 187 } else { | |
| 188 bool ok; | |
| 189 delay = refresh.left(pos).stripWhiteSpace().toDouble(&ok); | |
| 190 if (!ok) | |
| 191 return false; | |
| 192 | |
| 193 ++pos; | |
| 194 skipWhiteSpace(refresh, pos, fromHttpEquivMeta); | |
| 195 unsigned urlStartPos = pos; | |
| 196 if (refresh.find("url", urlStartPos, false) == urlStartPos) { | |
| 197 urlStartPos += 3; | |
| 198 skipWhiteSpace(refresh, urlStartPos, fromHttpEquivMeta); | |
| 199 if (refresh[urlStartPos] == '=') { | |
| 200 ++urlStartPos; | |
| 201 skipWhiteSpace(refresh, urlStartPos, fromHttpEquivMeta); | |
| 202 } else | |
| 203 urlStartPos = pos; // e.g. "Refresh: 0; url.html" | |
| 204 } | |
| 205 | |
| 206 unsigned urlEndPos = len; | |
| 207 | |
| 208 if (refresh[urlStartPos] == '"' || refresh[urlStartPos] == '\'') { | |
| 209 UChar quotationMark = refresh[urlStartPos]; | |
| 210 urlStartPos++; | |
| 211 while (urlEndPos > urlStartPos) { | |
| 212 urlEndPos--; | |
| 213 if (refresh[urlEndPos] == quotationMark) | |
| 214 break; | |
| 215 } | |
| 216 | |
| 217 // https://bugs.webkit.org/show_bug.cgi?id=27868 | |
| 218 // Sometimes there is no closing quote for the end of the URL even t
hough there was an opening quote. | |
| 219 // If we looped over the entire alleged URL string back to the openi
ng quote, just go ahead and use everything | |
| 220 // after the opening quote instead. | |
| 221 if (urlEndPos == urlStartPos) | |
| 222 urlEndPos = len; | |
| 223 } | |
| 224 | |
| 225 url = refresh.substring(urlStartPos, urlEndPos - urlStartPos).stripWhite
Space(); | |
| 226 return true; | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 double parseDate(const String& value) | |
| 231 { | |
| 232 return parseDateFromNullTerminatedCharacters(value.utf8().data()); | |
| 233 } | |
| 234 | |
| 235 // FIXME: This function doesn't comply with RFC 6266. | |
| 236 // For example, this function doesn't handle the interaction between " and ; | |
| 237 // that arises from quoted-string, nor does this function properly unquote | |
| 238 // attribute values. Further this function appears to process parameter names | |
| 239 // in a case-sensitive manner. (There are likely other bugs as well.) | |
| 240 String filenameFromHTTPContentDisposition(const String& value) | |
| 241 { | |
| 242 Vector<String> keyValuePairs; | |
| 243 value.split(';', keyValuePairs); | |
| 244 | |
| 245 unsigned length = keyValuePairs.size(); | |
| 246 for (unsigned i = 0; i < length; i++) { | |
| 247 size_t valueStartPos = keyValuePairs[i].find('='); | |
| 248 if (valueStartPos == kNotFound) | |
| 249 continue; | |
| 250 | |
| 251 String key = keyValuePairs[i].left(valueStartPos).stripWhiteSpace(); | |
| 252 | |
| 253 if (key.isEmpty() || key != "filename") | |
| 254 continue; | |
| 255 | |
| 256 String value = keyValuePairs[i].substring(valueStartPos + 1).stripWhiteS
pace(); | |
| 257 | |
| 258 // Remove quotes if there are any | |
| 259 if (value[0] == '\"') | |
| 260 value = value.substring(1, value.length() - 2); | |
| 261 | |
| 262 return value; | |
| 263 } | |
| 264 | |
| 265 return String(); | |
| 266 } | |
| 267 | |
| 268 String extractMIMETypeFromMediaType(const String& mediaType) | |
| 269 { | |
| 270 StringBuilder mimeType; | |
| 271 unsigned length = mediaType.length(); | |
| 272 mimeType.reserveCapacity(length); | |
| 273 for (unsigned i = 0; i < length; i++) { | |
| 274 UChar c = mediaType[i]; | |
| 275 | |
| 276 if (c == ';') | |
| 277 break; | |
| 278 | |
| 279 // While RFC 2616 does not allow it, other browsers allow multiple value
s in the HTTP media | |
| 280 // type header field, Content-Type. In such cases, the media type string
passed here may contain | |
| 281 // the multiple values separated by commas. For now, this code ignores t
ext after the first comma, | |
| 282 // which prevents it from simply failing to parse such types altogether.
Later for better | |
| 283 // compatibility we could consider using the first or last valid MIME ty
pe instead. | |
| 284 // See https://bugs.webkit.org/show_bug.cgi?id=25352 for more discussion
. | |
| 285 if (c == ',') | |
| 286 break; | |
| 287 | |
| 288 // FIXME: The following is not correct. RFC 2616 allows linear white spa
ce before and | |
| 289 // after the MIME type, but not within the MIME type itself. And linear
white space | |
| 290 // includes only a few specific ASCII characters; a small subset of isSp
aceOrNewline. | |
| 291 // See https://bugs.webkit.org/show_bug.cgi?id=8644 for a bug tracking p
art of this. | |
| 292 if (isSpaceOrNewline(c)) | |
| 293 continue; | |
| 294 | |
| 295 mimeType.append(c); | |
| 296 } | |
| 297 | |
| 298 if (mimeType.length() == length) | |
| 299 return mediaType; | |
| 300 return mimeType.toString(); | |
| 301 } | |
| 302 | |
| 303 String extractCharsetFromMediaType(const String& mediaType) | |
| 304 { | |
| 305 unsigned int pos, len; | |
| 306 findCharsetInMediaType(mediaType, pos, len); | |
| 307 return mediaType.substring(pos, len); | |
| 308 } | |
| 309 | |
| 310 void findCharsetInMediaType(const String& mediaType, unsigned int& charsetPos, u
nsigned int& charsetLen, unsigned int start) | |
| 311 { | |
| 312 charsetPos = start; | |
| 313 charsetLen = 0; | |
| 314 | |
| 315 size_t pos = start; | |
| 316 unsigned length = mediaType.length(); | |
| 317 | |
| 318 while (pos < length) { | |
| 319 pos = mediaType.find("charset", pos, false); | |
| 320 if (pos == kNotFound || !pos) { | |
| 321 charsetLen = 0; | |
| 322 return; | |
| 323 } | |
| 324 | |
| 325 // is what we found a beginning of a word? | |
| 326 if (mediaType[pos-1] > ' ' && mediaType[pos-1] != ';') { | |
| 327 pos += 7; | |
| 328 continue; | |
| 329 } | |
| 330 | |
| 331 pos += 7; | |
| 332 | |
| 333 // skip whitespace | |
| 334 while (pos != length && mediaType[pos] <= ' ') | |
| 335 ++pos; | |
| 336 | |
| 337 if (mediaType[pos++] != '=') // this "charset" substring wasn't a parame
ter name, but there may be others | |
| 338 continue; | |
| 339 | |
| 340 while (pos != length && (mediaType[pos] <= ' ' || mediaType[pos] == '"'
|| mediaType[pos] == '\'')) | |
| 341 ++pos; | |
| 342 | |
| 343 // we don't handle spaces within quoted parameter values, because charse
t names cannot have any | |
| 344 unsigned endpos = pos; | |
| 345 while (pos != length && mediaType[endpos] > ' ' && mediaType[endpos] !=
'"' && mediaType[endpos] != '\'' && mediaType[endpos] != ';') | |
| 346 ++endpos; | |
| 347 | |
| 348 charsetPos = pos; | |
| 349 charsetLen = endpos - pos; | |
| 350 return; | |
| 351 } | |
| 352 } | |
| 353 | |
| 354 ReflectedXSSDisposition parseXSSProtectionHeader(const String& header, String& f
ailureReason, unsigned& failurePosition, String& reportURL) | |
| 355 { | |
| 356 DEFINE_STATIC_LOCAL(String, failureReasonInvalidToggle, ("expected 0 or 1"))
; | |
| 357 DEFINE_STATIC_LOCAL(String, failureReasonInvalidSeparator, ("expected semico
lon")); | |
| 358 DEFINE_STATIC_LOCAL(String, failureReasonInvalidEquals, ("expected equals si
gn")); | |
| 359 DEFINE_STATIC_LOCAL(String, failureReasonInvalidMode, ("invalid mode directi
ve")); | |
| 360 DEFINE_STATIC_LOCAL(String, failureReasonInvalidReport, ("invalid report dir
ective")); | |
| 361 DEFINE_STATIC_LOCAL(String, failureReasonDuplicateMode, ("duplicate mode dir
ective")); | |
| 362 DEFINE_STATIC_LOCAL(String, failureReasonDuplicateReport, ("duplicate report
directive")); | |
| 363 DEFINE_STATIC_LOCAL(String, failureReasonInvalidDirective, ("unrecognized di
rective")); | |
| 364 | |
| 365 unsigned pos = 0; | |
| 366 | |
| 367 if (!skipWhiteSpace(header, pos, false)) | |
| 368 return ReflectedXSSUnset; | |
| 369 | |
| 370 if (header[pos] == '0') | |
| 371 return AllowReflectedXSS; | |
| 372 | |
| 373 if (header[pos++] != '1') { | |
| 374 failureReason = failureReasonInvalidToggle; | |
| 375 return ReflectedXSSInvalid; | |
| 376 } | |
| 377 | |
| 378 ReflectedXSSDisposition result = FilterReflectedXSS; | |
| 379 bool modeDirectiveSeen = false; | |
| 380 bool reportDirectiveSeen = false; | |
| 381 | |
| 382 while (1) { | |
| 383 // At end of previous directive: consume whitespace, semicolon, and whit
espace. | |
| 384 if (!skipWhiteSpace(header, pos, false)) | |
| 385 return result; | |
| 386 | |
| 387 if (header[pos++] != ';') { | |
| 388 failureReason = failureReasonInvalidSeparator; | |
| 389 failurePosition = pos; | |
| 390 return ReflectedXSSInvalid; | |
| 391 } | |
| 392 | |
| 393 if (!skipWhiteSpace(header, pos, false)) | |
| 394 return result; | |
| 395 | |
| 396 // At start of next directive. | |
| 397 if (skipToken(header, pos, "mode")) { | |
| 398 if (modeDirectiveSeen) { | |
| 399 failureReason = failureReasonDuplicateMode; | |
| 400 failurePosition = pos; | |
| 401 return ReflectedXSSInvalid; | |
| 402 } | |
| 403 modeDirectiveSeen = true; | |
| 404 if (!skipEquals(header, pos)) { | |
| 405 failureReason = failureReasonInvalidEquals; | |
| 406 failurePosition = pos; | |
| 407 return ReflectedXSSInvalid; | |
| 408 } | |
| 409 if (!skipToken(header, pos, "block")) { | |
| 410 failureReason = failureReasonInvalidMode; | |
| 411 failurePosition = pos; | |
| 412 return ReflectedXSSInvalid; | |
| 413 } | |
| 414 result = BlockReflectedXSS; | |
| 415 } else if (skipToken(header, pos, "report")) { | |
| 416 if (reportDirectiveSeen) { | |
| 417 failureReason = failureReasonDuplicateReport; | |
| 418 failurePosition = pos; | |
| 419 return ReflectedXSSInvalid; | |
| 420 } | |
| 421 reportDirectiveSeen = true; | |
| 422 if (!skipEquals(header, pos)) { | |
| 423 failureReason = failureReasonInvalidEquals; | |
| 424 failurePosition = pos; | |
| 425 return ReflectedXSSInvalid; | |
| 426 } | |
| 427 size_t startPos = pos; | |
| 428 if (!skipValue(header, pos)) { | |
| 429 failureReason = failureReasonInvalidReport; | |
| 430 failurePosition = pos; | |
| 431 return ReflectedXSSInvalid; | |
| 432 } | |
| 433 reportURL = header.substring(startPos, pos - startPos); | |
| 434 failurePosition = startPos; // If later semantic check deems unaccep
table. | |
| 435 } else { | |
| 436 failureReason = failureReasonInvalidDirective; | |
| 437 failurePosition = pos; | |
| 438 return ReflectedXSSInvalid; | |
| 439 } | |
| 440 } | |
| 441 } | |
| 442 | |
| 443 ContentTypeOptionsDisposition parseContentTypeOptionsHeader(const String& header
) | |
| 444 { | |
| 445 if (header.stripWhiteSpace().lower() == "nosniff") | |
| 446 return ContentTypeOptionsNosniff; | |
| 447 return ContentTypeOptionsNone; | |
| 448 } | |
| 449 | |
| 450 String 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 | |
| 458 XFrameOptionsDisposition 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 | |
| 488 bool 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 specif
ies the last N bytes, is provided. | |
| 508 // Example: | |
| 509 // -500 | |
| 510 if (!index) { | |
| 511 String suffixLengthString = byteRange.substring(index + 1).stripWhiteSpa
ce(); | |
| 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 | |
| 548 size_t parseHTTPRequestLine(const char* data, size_t length, String& failureReas
on, 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++, consumedLen
gth++) { | |
| 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, bu
t 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 subtra
ct 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 | |
| 608 size_t parseHTTPHeader(const char* start, size_t length, String& failureReason,
AtomicString& nameStr, String& valueStr) | |
| 609 { | |
| 610 const char* p = start; | |
| 611 const char* end = start + length; | |
| 612 | |
| 613 Vector<char> name; | |
| 614 Vector<char> value; | |
| 615 nameStr = AtomicString(); | |
| 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.d
ata(), name.size()); | |
| 628 return 0; | |
| 629 case '\n': | |
| 630 failureReason = "Unexpected LF in name at " + trimInputSample(name.d
ata(), 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 failureReason = "Unexpected LF in value at " + trimInputSample(value
.data(), value.size()); | |
| 652 return 0; | |
| 653 default: | |
| 654 value.append(*p); | |
| 655 } | |
| 656 if (*p == '\r') { | |
| 657 ++p; | |
| 658 break; | |
| 659 } | |
| 660 } | |
| 661 if (p >= end || *p != '\n') { | |
| 662 failureReason = "CR doesn't follow LF after value at " + trimInputSample
(p, end - p); | |
| 663 return 0; | |
| 664 } | |
| 665 nameStr = AtomicString::fromUTF8(name.data(), name.size()); | |
| 666 valueStr = String::fromUTF8(value.data(), value.size()); | |
| 667 if (nameStr.isNull()) { | |
| 668 failureReason = "Invalid UTF-8 sequence in header name"; | |
| 669 return 0; | |
| 670 } | |
| 671 if (valueStr.isNull()) { | |
| 672 failureReason = "Invalid UTF-8 sequence in header value"; | |
| 673 return 0; | |
| 674 } | |
| 675 return p - start; | |
| 676 } | |
| 677 | |
| 678 size_t parseHTTPRequestBody(const char* data, size_t length, Vector<unsigned cha
r>& body) | |
| 679 { | |
| 680 body.clear(); | |
| 681 body.append(data, length); | |
| 682 | |
| 683 return length; | |
| 684 } | |
| 685 | |
| 686 } | |
| OLD | NEW |