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