Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / net / http / http_status_line_validator.cc
blob5b8284cb5a341a68382d5b3a6c1297cce557dba3
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/http/http_status_line_validator.h"
7 #include <vector>
9 #include "base/strings/string_split.h"
10 #include "base/strings/string_util.h"
12 namespace net {
14 using base::StringPiece;
16 HttpStatusLineValidator::StatusLineStatus
17 HttpStatusLineValidator::ValidateStatusLine(const StringPiece& status_line) {
18 std::vector<StringPiece> fields = base::SplitStringPiece(
19 status_line, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
20 // Permissively split fields, meaning:
21 // 1) Extra whitespace separating fields ignored
22 // 2) Extra leading/trailing whitespace removed
23 std::vector<StringPiece> loose_fields = base::SplitStringPiece(
24 status_line, " ", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
26 // Fields: HTTP-version status-code reason-phrase
27 if (fields.empty() || loose_fields.empty())
28 return STATUS_LINE_EMPTY;
30 StatusLineStatus rv = CheckHttpVersionSyntax(fields[0]);
31 if (rv != STATUS_LINE_OK)
32 return rv;
34 if (fields.size() < 2)
35 return STATUS_LINE_MISSING_STATUS_CODE;
37 rv = CheckStatusCodeSyntax(fields[1]);
38 if (rv != STATUS_LINE_OK)
39 return rv;
41 // At this point the field splitting could be wrong, so check for extra
42 // whitespace/padding on the result code.
43 // Note that there is no such thing as "extra whitespace" on the
44 // reason-phrase, since spaces are legal inside it.
45 if (loose_fields[1] != fields[1])
46 return STATUS_LINE_EXCESS_WHITESPACE;
48 // An empty reason phrase will cause there to be only two fields in |fields|
49 // but more than that in |loose_fields|.
50 if (loose_fields.size() < 3)
51 return STATUS_LINE_MISSING_REASON_PHRASE;
52 return CheckReasonPhraseSyntax(fields, 2);
55 HttpStatusLineValidator::StatusLineStatus
56 HttpStatusLineValidator::CheckHttpVersionSyntax(const StringPiece& version) {
57 static const char kProtoName[] = "HTTP";
58 static const char kDigits[] = "0123456789";
59 if (!base::StartsWith(version, kProtoName,
60 base::CompareCase::INSENSITIVE_ASCII)) {
61 return STATUS_LINE_NOT_HTTP;
63 if (!base::StartsWith(version, kProtoName, base::CompareCase::SENSITIVE))
64 return STATUS_LINE_HTTP_CASE_MISMATCH;
65 // Okay, definitely "HTTP" at the start. Now check for the separating slash
66 // and version number components:
67 size_t slash = version.find('/');
68 if (slash != strlen(kProtoName))
69 return STATUS_LINE_HTTP_NO_VERSION;
70 StringPiece rest = version.substr(slash + 1);
71 size_t sep = rest.find('.');
72 if (sep == StringPiece::npos) {
73 return STATUS_LINE_INVALID_VERSION;
75 StringPiece major = rest.substr(0, sep);
76 if (major.length() == 0)
77 return STATUS_LINE_INVALID_VERSION;
78 StringPiece minor = rest.substr(sep + 1);
79 if (minor.length() == 0)
80 return STATUS_LINE_INVALID_VERSION;
81 if (major.find_first_not_of(kDigits) != major.npos ||
82 minor.find_first_not_of(kDigits) != minor.npos) {
83 return STATUS_LINE_INVALID_VERSION;
86 if (major.length() != 1 || minor.length() != 1)
87 return STATUS_LINE_MULTI_DIGIT_VERSION;
89 // It is now known that version looks like:
90 // HTTP/x.y
91 // For single digits x and y.
92 // Check that x == '1' and y == '0' or '1'
93 if (major[0] != '1' || (minor[0] != '0' && minor[0] != '1')) {
94 if (major[0] == '0' && minor[0] == '9')
95 return STATUS_LINE_EXPLICIT_0_9;
96 else
97 return STATUS_LINE_UNKNOWN_VERSION;
99 return STATUS_LINE_OK;
102 HttpStatusLineValidator::StatusLineStatus
103 HttpStatusLineValidator::CheckStatusCodeSyntax(const StringPiece& status_code) {
104 if (status_code.length() < 3)
105 return STATUS_LINE_INVALID_STATUS_CODE;
106 if (!isdigit(status_code[0]) || !isdigit(status_code[1]) ||
107 !isdigit(status_code[2])) {
108 return STATUS_LINE_INVALID_STATUS_CODE;
110 if (status_code.length() > 3)
111 return STATUS_LINE_STATUS_CODE_TRAILING;
112 // The only valid codes are 1xx through 5xx, see RFC 7231 S6
113 if (status_code[0] < '1' || status_code[0] > '5')
114 return STATUS_LINE_RESERVED_STATUS_CODE;
115 return STATUS_LINE_OK;
118 HttpStatusLineValidator::StatusLineStatus
119 HttpStatusLineValidator::CheckReasonPhraseSyntax(
120 const std::vector<StringPiece>& fields,
121 size_t start_index) {
122 for (size_t i = start_index; i < fields.size(); ++i) {
123 for (size_t j = 0; j < fields[i].length(); ++j) {
124 // VCHAR is any "visible" ASCII character, meaning any non-control
125 // character, so >= ' ' but not DEL (\x7f).
126 // obs-text is any character between \x80 and \xff.
127 // HTAB is \t, SP is ' '
128 char c = fields[i][j];
129 if (c == '\x7f' || (c < ' ' && c != '\t'))
130 return STATUS_LINE_REASON_DISALLOWED_CHARACTER;
133 return STATUS_LINE_OK;
136 } // namespace net