Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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.isEmpty() && (header == "\n" || header == "\r\n");
60 if (result.size() >= maxFieldCount)
61 return false;
62 QByteArrayView name = header.first(colon);
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;
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(name, 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 return fields.value(name, defaultValue).toByteArray();
140}
141
143{
144 const QList<QByteArray> allValues = headerFieldValues(name);
145 if (allValues.isEmpty())
146 return defaultValue;
147 return allValues.join(", ");
148}
149
151{
152 return fields.values(name);
153}
154
159
165
167{
168 fields.insert(0, name, data);
169}
170
175
177{
178 fields.clear();
179}
180
182{
183 return statusCode;
184}
185
187{
188 statusCode = code;
189}
190
192{
193 return majorVersion;
194}
195
197{
198 majorVersion = version;
199}
200
202{
203 return minorVersion;
204}
205
207{
208 minorVersion = version;
209}
210
212{
213 return reasonPhrase;
214}
215
217{
218 reasonPhrase = reason;
219}
220
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)
const QHttpHeaders & headers() const
QList< QByteArray > headerFieldValues(QByteArrayView name) const
QByteArray combinedHeaderValue(QByteArrayView name, const QByteArray &defaultValue=QByteArray()) const
QString getReasonPhrase() const
void appendHeaderField(const QByteArray &name, const QByteArray &data)
void setMajorVersion(int version)
void setStatusCode(int code)
void removeHeaderField(QByteArrayView name)
bool parseHeaders(QByteArrayView headers)
void setHeaderField(const QByteArray &name, const QByteArray &data)
bool parseStatus(QByteArrayView status)
void setMinorVersion(int version)
QByteArray firstHeaderField(QByteArrayView name, const QByteArray &defaultValue=QByteArray()) const
void prependHeaderField(const QByteArray &name, const QByteArray &data)
Q_NETWORK_EXPORT void clear()
Clears all header entries.
bool isEmpty() const noexcept
Returns true if the headers have size 0; otherwise returns false.
Q_NETWORK_EXPORT bool insert(qsizetype i, QAnyStringView name, QAnyStringView value)
Inserts a header entry at index i, with name and value.
Q_NETWORK_EXPORT bool append(QAnyStringView name, QAnyStringView value)
Appends a header entry with name and value and returns true if successful.
Q_NETWORK_EXPORT void removeAll(QAnyStringView name)
Removes the header name.
Q_NETWORK_EXPORT QList< QByteArray > values(QAnyStringView name) const
Returns the values of header name in a list.
Q_NETWORK_EXPORT QByteArrayView value(QAnyStringView name, QByteArrayView defaultValue={}) const noexcept
Returns the value of the (first) header name, or defaultValue if it doesn't exist.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
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:731
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
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:5506
QString trimmed() const &
Definition qstring.h:447
Combined button and popup list for selecting options.
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]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34