Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qhttpheaderparser.cpp
Go to the documentation of this file.
1
// Copyright (C) 2022 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
// Qt-Security score:critical reason:data-parser
4
5
#
include
"qhttpheaderparser_p.h"
6
7
#
include
<
algorithm
>
8
9
QT_BEGIN_NAMESPACE
10
11
QHttpHeaderParser::QHttpHeaderParser()
12
: statusCode(100)
// Required by tst_QHttpNetworkConnection::ignoresslerror(failure)
13
, majorVersion(0)
14
, minorVersion(0)
15
{
16
}
17
18
void
QHttpHeaderParser::clear()
19
{
20
statusCode = 100;
21
majorVersion = 0;
22
minorVersion = 0;
23
reasonPhrase.clear();
24
fields.clear();
25
}
26
27
static
bool
fieldNameCheck
(QByteArrayView name)
28
{
29
const
auto
fieldNameChar = [](
char
c) {
30
constexpr
QByteArrayView otherCharacters(
"!#$%&'*+-.^_`|~"
);
31
return
(
'a'
<= c && c <=
'z'
) || (
'A'
<= c && c <=
'Z'
) || (
'0'
<= c && c <=
'9'
)
32
|| otherCharacters.contains(c);
33
};
34
35
return
!name.empty() &&
std
::all_of(name.begin(), name.end(), fieldNameChar);
36
}
37
38
bool
QHttpHeaderParser::parseHeaders(QByteArrayView header)
39
{
40
// see rfc2616, sec 4 for information about HTTP/1.1 headers.
41
// allows relaxed parsing here, accepts both CRLF & LF line endings
42
Q_ASSERT(fields.isEmpty());
43
const
auto
hSpaceStart = [](QByteArrayView h) {
44
return
h.startsWith(
' '
) || h.startsWith(
'\t'
);
45
};
46
// Headers, if non-empty, start with a non-space and end with a newline:
47
if
(hSpaceStart(header) || (!header.empty() && !header.endsWith(
'\n'
)))
48
return
false
;
49
50
while
(
int
tail = header.endsWith(
"\n\r\n"
) ? 2 : header.endsWith(
"\n\n"
) ? 1 : 0)
51
header.chop(tail);
52
53
if
(header.size() - (header.endsWith(
"\r\n"
) ? 2 : 1) > maxTotalSize)
54
return
false
;
55
56
QHttpHeaders result;
57
while
(!header.empty()) {
58
const
qsizetype colon = header.indexOf(
':'
);
59
if
(colon == -1)
// if no colon check if empty headers
60
return
result.isEmpty() && (header ==
"\n"
|| header ==
"\r\n"
);
61
if
(result.size() >= maxFieldCount)
62
return
false
;
63
QByteArrayView name = header.first(colon);
64
if
(!fieldNameCheck(name))
65
return
false
;
66
header = header.sliced(colon + 1);
67
QByteArray value;
68
qsizetype valueSpace = maxFieldSize - name.size() - 1;
69
do
{
70
const
qsizetype endLine = header.indexOf(
'\n'
);
71
Q_ASSERT(endLine != -1);
72
auto
line = header.first(endLine);
// includes space
73
valueSpace -= line.size() - (line.endsWith(
'\r'
) ? 1 : 0);
74
if
(valueSpace < 0)
75
return
false
;
76
line = line.trimmed();
77
if
(!line.empty()) {
78
if
(value.size())
79
value +=
' '
+ line;
80
else
81
value = line.toByteArray();
82
}
83
header = header.sliced(endLine + 1);
84
}
while
(hSpaceStart(header));
85
Q_ASSERT(name.size() + 1 + value.size() <= maxFieldSize);
86
result.append(name, value);
87
}
88
89
fields = result;
90
return
true
;
91
}
92
93
bool
QHttpHeaderParser::parseStatus(QByteArrayView status)
94
{
95
// from RFC 2616:
96
// Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
97
// HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
98
// that makes: 'HTTP/n.n xxx Message'
99
// byte count: 0123456789012
100
101
static
const
int
minLength = 11;
102
static
const
int
dotPos = 6;
103
static
const
int
spacePos = 8;
104
static
const
char
httpMagic[] =
"HTTP/"
;
105
106
if
(status.size() < minLength
107
|| !status.startsWith(httpMagic)
108
|| status.at(dotPos) !=
'.'
109
|| status.at(spacePos) !=
' '
) {
110
// I don't know how to parse this status line
111
return
false
;
112
}
113
114
// optimize for the valid case: defer checking until the end
115
majorVersion = status.at(dotPos - 1) -
'0'
;
116
minorVersion = status.at(dotPos + 1) -
'0'
;
117
118
int
i = spacePos;
119
qsizetype j = status.indexOf(
' '
, i + 1);
120
const
QByteArrayView code = j > i ? status.sliced(i + 1, j - i - 1)
121
: status.sliced(i + 1);
122
123
bool
ok =
false
;
124
statusCode = code.toInt(&ok);
125
126
if
(j > i) {
127
const
auto
phrase = status.sliced(j + 1);
128
129
if
(phrase.size() > HeaderConstants::MAX_REASON_PHRASE_SIZE)
130
return
false
;
131
for
(
char
c : phrase) {
132
// most of the classic control characters are below 0x20
133
// except DEL, which is 0x7f
134
// tab is 0x09, but it is permitted in the reason phrase
135
if
(c !=
'\t'
&& (uchar(c) < 0x20 || c == 0x7f))
136
return
false
;
137
}
138
reasonPhrase = QString::fromLatin1(phrase);
139
}
else
{
140
reasonPhrase = QString();
141
}
142
143
return
ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
144
}
145
146
const
QHttpHeaders& QHttpHeaderParser::headers()
const
&
147
{
148
return
fields;
149
}
150
151
QHttpHeaders QHttpHeaderParser::headers() &&
152
{
153
return
std::move(fields);
154
}
155
156
QByteArray QHttpHeaderParser::firstHeaderField(QByteArrayView name,
157
const
QByteArray &defaultValue)
const
158
{
159
return
fields.value(name, defaultValue).toByteArray();
160
}
161
162
QByteArray QHttpHeaderParser::combinedHeaderValue(QByteArrayView name,
const
QByteArray &defaultValue)
const
163
{
164
const
QList<QByteArray> allValues = headerFieldValues(name);
165
if
(allValues.isEmpty())
166
return
defaultValue;
167
return
allValues.join(
", "
);
168
}
169
170
QList<QByteArray> QHttpHeaderParser::headerFieldValues(QByteArrayView name)
const
171
{
172
return
fields.values(name);
173
}
174
175
void
QHttpHeaderParser::removeHeaderField(QByteArrayView name)
176
{
177
fields.removeAll(name);
178
}
179
180
void
QHttpHeaderParser::setHeaderField(
const
QByteArray &name,
const
QByteArray &data)
181
{
182
removeHeaderField(name);
183
fields.append(name, data);
184
}
185
186
void
QHttpHeaderParser::prependHeaderField(
const
QByteArray &name,
const
QByteArray &data)
187
{
188
fields.insert(0, name, data);
189
}
190
191
void
QHttpHeaderParser::appendHeaderField(
const
QByteArray &name,
const
QByteArray &data)
192
{
193
fields.append(name, data);
194
}
195
196
void
QHttpHeaderParser::clearHeaders()
197
{
198
fields.clear();
199
}
200
201
int
QHttpHeaderParser::getStatusCode()
const
202
{
203
return
statusCode;
204
}
205
206
void
QHttpHeaderParser::setStatusCode(
int
code)
207
{
208
statusCode = code;
209
}
210
211
int
QHttpHeaderParser::getMajorVersion()
const
212
{
213
return
majorVersion;
214
}
215
216
void
QHttpHeaderParser::setMajorVersion(
int
version)
217
{
218
majorVersion = version;
219
}
220
221
int
QHttpHeaderParser::getMinorVersion()
const
222
{
223
return
minorVersion;
224
}
225
226
void
QHttpHeaderParser::setMinorVersion(
int
version)
227
{
228
minorVersion = version;
229
}
230
231
QString QHttpHeaderParser::getReasonPhrase()
const
232
{
233
return
reasonPhrase;
234
}
235
236
void
QHttpHeaderParser::setReasonPhrase(
const
QString &reason)
237
{
238
reasonPhrase = reason;
239
}
240
241
QT_END_NAMESPACE
std
[33]
Definition
src_corelib_tools_qhash.cpp:421
fieldNameCheck
static bool fieldNameCheck(QByteArrayView name)
Definition
qhttpheaderparser.cpp:27
qtbase
src
network
access
qhttpheaderparser.cpp
Generated on
for Qt by
1.18.0