Qt 6.x
The Qt SDK
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
5
6#include <algorithm>
7
9
11 : statusCode(100) // Required by tst_QHttpNetworkConnection::ignoresslerror(failure)
12 , majorVersion(0)
13 , minorVersion(0)
14{
15}
16
18{
19 statusCode = 100;
20 majorVersion = 0;
21 minorVersion = 0;
22 reasonPhrase.clear();
23 fields.clear();
24}
25
27{
28 static constexpr QByteArrayView otherCharacters("!#$%&'*+-.^_`|~");
29 static const auto fieldNameChar = [](char c) {
30 return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')
31 || otherCharacters.contains(c);
32 };
33
34 return !name.empty() && std::all_of(name.begin(), name.end(), fieldNameChar);
35}
36
38{
39 // see rfc2616, sec 4 for information about HTTP/1.1 headers.
40 // allows relaxed parsing here, accepts both CRLF & LF line endings
41 Q_ASSERT(fields.isEmpty());
42 const auto hSpaceStart = [](QByteArrayView h) {
43 return h.startsWith(' ') || h.startsWith('\t');
44 };
45 // Headers, if non-empty, start with a non-space and end with a newline:
46 if (hSpaceStart(header) || (!header.empty() && !header.endsWith('\n')))
47 return false;
48
49 while (int tail = header.endsWith("\n\r\n") ? 2 : header.endsWith("\n\n") ? 1 : 0)
50 header.chop(tail);
51
52 if (header.size() - (header.endsWith("\r\n") ? 2 : 1) > maxTotalSize)
53 return false;
54
56 while (!header.empty()) {
57 const qsizetype colon = header.indexOf(':');
58 if (colon == -1) // if no colon check if empty headers
59 return result.empty() && (header == "\n" || header == "\r\n");
60 if (result.size() >= maxFieldCount)
61 return false;
63 if (!fieldNameCheck(name))
64 return false;
65 header = header.sliced(colon + 1);
67 qsizetype valueSpace = maxFieldSize - name.size() - 1;
68 do {
69 const qsizetype endLine = header.indexOf('\n');
70 Q_ASSERT(endLine != -1);
71 auto line = header.first(endLine); // includes space
72 valueSpace -= line.size() - (line.endsWith('\r') ? 1 : 0);
73 if (valueSpace < 0)
74 return false;
75 line = line.trimmed();
76 if (!line.empty()) {
77 if (value.size())
78 value += ' ' + line.toByteArray();
79 else
80 value = line.toByteArray();
81 }
82 header = header.sliced(endLine + 1);
83 } while (hSpaceStart(header));
84 Q_ASSERT(name.size() + 1 + value.size() <= maxFieldSize);
85 result.append(qMakePair(name.toByteArray(), value));
86 }
87
88 fields = result;
89 return true;
90}
91
93{
94 // from RFC 2616:
95 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
96 // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
97 // that makes: 'HTTP/n.n xxx Message'
98 // byte count: 0123456789012
99
100 static const int minLength = 11;
101 static const int dotPos = 6;
102 static const int spacePos = 8;
103 static const char httpMagic[] = "HTTP/";
104
105 if (status.size() < minLength
106 || !status.startsWith(httpMagic)
107 || status.at(dotPos) != '.'
108 || status.at(spacePos) != ' ') {
109 // I don't know how to parse this status line
110 return false;
111 }
112
113 // optimize for the valid case: defer checking until the end
114 majorVersion = status.at(dotPos - 1) - '0';
115 minorVersion = status.at(dotPos + 1) - '0';
116
117 int i = spacePos;
118 qsizetype j = status.indexOf(' ', i + 1);
119 const QByteArrayView code = j > i ? status.sliced(i + 1, j - i - 1)
120 : status.sliced(i + 1);
121
122 bool ok = false;
123 statusCode = code.toInt(&ok);
124
125 reasonPhrase = j > i ? QString::fromLatin1(status.sliced(j + 1))
126 : QString();
127
128 return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
129}
130
132{
133 return fields;
134}
135
137 const QByteArray &defaultValue) const
138{
139 for (auto it = fields.constBegin(); it != fields.constEnd(); ++it) {
140 if (name.compare(it->first, Qt::CaseInsensitive) == 0)
141 return it->second;
142 }
143 return defaultValue;
144}
145
147{
148 const QList<QByteArray> allValues = headerFieldValues(name);
149 if (allValues.isEmpty())
150 return defaultValue;
151 return allValues.join(", ");
152}
153
155{
157 for (auto it = fields.constBegin(); it != fields.constEnd(); ++it)
158 if (name.compare(it->first, Qt::CaseInsensitive) == 0)
159 result += it->second;
160
161 return result;
162}
163
165{
166 auto firstEqualsName = [&name](const QPair<QByteArray, QByteArray> &header) {
167 return name.compare(header.first, Qt::CaseInsensitive) == 0;
168 };
169 fields.removeIf(firstEqualsName);
170}
171
173{
175 fields.append(qMakePair(name, data));
176}
177
179{
180 fields.prepend(qMakePair(name, data));
181}
182
184{
185 fields.append(qMakePair(name, data));
186}
187
189{
190 fields.clear();
191}
192
194{
195 return statusCode;
196}
197
199{
200 statusCode = code;
201}
202
204{
205 return majorVersion;
206}
207
209{
210 majorVersion = version;
211}
212
214{
215 return minorVersion;
216}
217
219{
220 minorVersion = version;
221}
222
224{
225 return reasonPhrase;
226}
227
229{
230 reasonPhrase = reason;
231}
232
bool contains(QByteArrayView a) const noexcept
bool startsWith(QByteArrayView other) const noexcept
constexpr char at(qsizetype n) const
constexpr QByteArrayView sliced(qsizetype pos) const
constexpr qsizetype size() const noexcept
qsizetype indexOf(QByteArrayView a, qsizetype from=0) const noexcept
\inmodule QtCore
Definition qbytearray.h:57
void setReasonPhrase(const QString &reason)
QString getReasonPhrase() const
void appendHeaderField(const QByteArray &name, const QByteArray &data)
void setMajorVersion(int version)
void setStatusCode(int code)
bool parseHeaders(QByteArrayView headers)
void setHeaderField(const QByteArray &name, const QByteArray &data)
bool parseStatus(QByteArrayView status)
void setMinorVersion(int version)
const QList< QPair< QByteArray, QByteArray > > & headers() const
QByteArray combinedHeaderValue(const QByteArray &name, const QByteArray &defaultValue=QByteArray()) const
void removeHeaderField(const QByteArray &name)
QByteArray firstHeaderField(const QByteArray &name, const QByteArray &defaultValue=QByteArray()) const
QList< QByteArray > headerFieldValues(const QByteArray &name) const
void prependHeaderField(const QByteArray &name, const QByteArray &data)
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
const_iterator constBegin() const noexcept
Definition qlist.h:615
qsizetype removeIf(Predicate pred)
Definition qlist.h:587
void prepend(rvalue_ref t)
Definition qlist.h:456
void append(parameter_type t)
Definition qlist.h:441
const_iterator constEnd() const noexcept
Definition qlist.h:616
void clear()
Definition qlist.h:417
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
int toInt(bool *ok=nullptr, int base=10) const
Returns the string converted to an int using base base, which is 10 by default and must be between 2 ...
Definition qstring.h:660
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6180
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QString first(qsizetype n) const
Definition qstring.h:337
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
QString sliced(qsizetype pos) const
Definition qstring.h:341
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
QString trimmed() const &
Definition qstring.h:380
QSet< QString >::iterator it
Combined button and popup list for selecting options.
@ CaseInsensitive
std::pair< T1, T2 > QPair
static QString header(const QString &name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static bool fieldNameCheck(QByteArrayView name)
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
GLfloat GLfloat GLfloat GLfloat h
const GLubyte * c
GLuint64EXT * result
[6]
constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29