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
http2protocol.cpp
Go to the documentation of this file.
1
// Copyright (C) 2016 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:network-protocol
4
5
#
include
"http2protocol_p.h"
6
#
include
"http2frames_p.h"
7
8
#
include
"private/qhttpnetworkrequest_p.h"
9
#
include
"private/qhttpnetworkreply_p.h"
10
11
#
include
<
access
/
qhttp2configuration
.
h
>
12
#
include
<
access
/
qhttp2configuration_p
.
h
>
13
14
#
include
<
QtCore
/
qbytearray
.
h
>
15
#
include
<
QtCore
/
qstring
.
h
>
16
17
QT_BEGIN_NAMESPACE
18
19
using
namespace
Qt::StringLiterals;
20
21
QT_IMPL_METATYPE_EXTERN_TAGGED(Http2::Settings, Http2__Settings)
22
23
Q_LOGGING_CATEGORY(QT_HTTP2,
"qt.network.http2"
)
24
25
namespace
Http2
26
{
27
28
// 3.5 HTTP/2 Connection Preface:
29
// "That is, the connection preface starts with the string
30
// PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n)."
31
const
char
Http2clientPreface[clientPrefaceLength] =
32
{0x50, 0x52, 0x49, 0x20, 0x2a, 0x20,
33
0x48, 0x54, 0x54, 0x50, 0x2f, 0x32,
34
0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a,
35
0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a};
36
37
Frame configurationToSettingsFrame(
const
QHttp2Configuration &config)
38
{
39
// 6.5 SETTINGS
40
FrameWriter builder(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID);
41
// Server push:
42
builder.append(Settings::ENABLE_PUSH_ID);
43
builder.append(
int
(config.serverPushEnabled()));
44
45
// Stream receive window size (if it's a default value, don't include):
46
if
(config.streamReceiveWindowSize() != defaultSessionWindowSize) {
47
builder.append(Settings::INITIAL_WINDOW_SIZE_ID);
48
builder.append(config.streamReceiveWindowSize());
49
}
50
51
if
(config.maxFrameSize() != minPayloadLimit) {
52
builder.append(Settings::MAX_FRAME_SIZE_ID);
53
builder.append(config.maxFrameSize());
54
}
55
56
if
(
const
quint32 maxHeaderListSize =
57
QHttp2ConfigurationPrivate::get(config)->maxHeaderListSize;
58
maxHeaderListSize != (std::numeric_limits<quint32>::max)()) {
59
builder.append(Settings::MAX_HEADER_LIST_SIZE_ID);
60
builder.append(maxHeaderListSize);
61
}
62
// TODO: In future, if the need is proven, we can
63
// also send the decoding table size.
64
// For now, defaults suffice.
65
return
builder.outboundFrame();
66
}
67
68
QByteArray settingsFrameToBase64(
const
Frame &frame)
69
{
70
// SETTINGS frame's payload consists of pairs:
71
// 2-byte-identifier | 4-byte-value == multiple of 6.
72
Q_ASSERT(frame.payloadSize() && !(frame.payloadSize() % 6));
73
const
char
*src =
reinterpret_cast
<
const
char
*>(frame.dataBegin());
74
const
QByteArray wrapper(QByteArray::fromRawData(src,
int
(frame.dataSize())));
75
// 3.2.1
76
// The content of the HTTP2-Settings header field is the payload
77
// of a SETTINGS frame (Section 6.5), encoded as a base64url string
78
// (that is, the URL- and filename-safe Base64 encoding described in
79
// Section 5 of [RFC4648], with any trailing '=' characters omitted).
80
return
wrapper.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
81
}
82
83
void
appendProtocolUpgradeHeaders(
const
QHttp2Configuration &config, QHttpNetworkRequest *request)
84
{
85
Q_ASSERT(request);
86
// RFC 2616, 14.10
87
// RFC 7540, 3.2
88
const
QByteArray connectionHeader = request->headerField(
"Connection"
);
89
const
auto
separator = connectionHeader.isEmpty() ? QByteArrayView() : QByteArrayView(
", "
);
90
// We _append_ 'Upgrade':
91
QByteArray value = connectionHeader + separator +
"Upgrade, HTTP2-Settings"
;
92
request->setHeaderField(
"Connection"
, value);
93
// This we just (re)write.
94
request->setHeaderField(
"Upgrade"
,
"h2c"
);
95
96
const
Frame frame(configurationToSettingsFrame(config));
97
// This we just (re)write.
98
request->setHeaderField(
"HTTP2-Settings"
, settingsFrameToBase64(frame));
99
}
100
101
void
qt_error(quint32 errorCode, QNetworkReply::NetworkError &error,
102
QString &errorMessage)
103
{
104
if
(errorCode > quint32(HTTP_1_1_REQUIRED)) {
105
error = QNetworkReply::ProtocolFailure;
106
errorMessage =
"RST_STREAM with unknown error code (%1)"_L1
;
107
errorMessage = errorMessage.arg(errorCode);
108
return
;
109
}
110
111
const
Http2Error http2Error = Http2Error(errorCode);
112
113
switch
(http2Error) {
114
case
HTTP2_NO_ERROR:
115
error = QNetworkReply::RemoteHostClosedError;
116
errorMessage =
"Remote host signaled shutdown"_L1
;
117
break
;
118
case
PROTOCOL_ERROR:
119
error = QNetworkReply::ProtocolFailure;
120
errorMessage =
"HTTP/2 protocol error"_L1
;
121
break
;
122
case
INTERNAL_ERROR:
123
error = QNetworkReply::InternalServerError;
124
errorMessage =
"Internal server error"_L1
;
125
break
;
126
case
FLOW_CONTROL_ERROR:
127
error = QNetworkReply::ProtocolFailure;
128
errorMessage =
"Flow control error"_L1
;
129
break
;
130
case
SETTINGS_TIMEOUT:
131
error = QNetworkReply::TimeoutError;
132
errorMessage =
"SETTINGS ACK timeout error"_L1
;
133
break
;
134
case
STREAM_CLOSED:
135
error = QNetworkReply::ProtocolFailure;
136
errorMessage =
"Server received frame(s) on a half-closed stream"_L1
;
137
break
;
138
case
FRAME_SIZE_ERROR:
139
error = QNetworkReply::ProtocolFailure;
140
errorMessage =
"Server received a frame with an invalid size"_L1
;
141
break
;
142
case
REFUSE_STREAM:
143
error = QNetworkReply::ProtocolFailure;
144
errorMessage =
"Server refused a stream"_L1
;
145
break
;
146
case
CANCEL:
147
error = QNetworkReply::ProtocolFailure;
148
errorMessage =
"Stream is no longer needed"_L1
;
149
break
;
150
case
COMPRESSION_ERROR:
151
error = QNetworkReply::ProtocolFailure;
152
errorMessage =
"Server is unable to maintain the "
153
"header compression context for the connection"_L1
;
154
break
;
155
case
CONNECT_ERROR:
156
// TODO: in Qt6 we'll have to add more error codes in QNetworkReply.
157
error = QNetworkReply::UnknownNetworkError;
158
errorMessage =
"The connection established in response "
159
"to a CONNECT request was reset or abnormally closed"_L1
;
160
break
;
161
case
ENHANCE_YOUR_CALM:
162
error = QNetworkReply::UnknownServerError;
163
errorMessage =
"Server dislikes our behavior, excessive load detected."_L1
;
164
break
;
165
case
INADEQUATE_SECURITY:
166
error = QNetworkReply::ContentAccessDenied;
167
errorMessage =
"The underlying transport has properties "
168
"that do not meet minimum security "
169
"requirements"_L1
;
170
break
;
171
case
HTTP_1_1_REQUIRED:
172
error = QNetworkReply::ProtocolFailure;
173
errorMessage =
"Server requires that HTTP/1.1 "
174
"be used instead of HTTP/2."_L1
;
175
}
176
}
177
178
QString qt_error_string(quint32 errorCode)
179
{
180
QNetworkReply::NetworkError error = QNetworkReply::NoError;
181
QString message;
182
qt_error(errorCode, error, message);
183
return
message;
184
}
185
186
QNetworkReply::NetworkError qt_error(quint32 errorCode)
187
{
188
QNetworkReply::NetworkError error = QNetworkReply::NoError;
189
QString message;
190
qt_error(errorCode, error, message);
191
return
error;
192
}
193
194
bool
is_protocol_upgraded(
const
QHttpNetworkReply &reply)
195
{
196
if
(reply.statusCode() != 101)
197
return
false
;
198
199
const
auto
values = reply.header().values(QHttpHeaders::WellKnownHeader::Upgrade);
200
// Do some minimal checks here - we expect 'Upgrade: h2c' to be found.
201
for
(
const
auto
&v : values) {
202
if
(v.compare(
"h2c"
, Qt::CaseInsensitive) == 0)
203
return
true
;
204
}
205
206
return
false
;
207
}
208
209
std::vector<uchar> assemble_hpack_block(
const
std::vector<Frame> &frames)
210
{
211
std::vector<uchar> hpackBlock;
212
213
size_t total = 0;
214
for
(
const
auto
&frame : frames) {
215
if
(qAddOverflow(total, size_t{frame.hpackBlockSize()}, &total))
216
return
hpackBlock;
217
}
218
219
if
(!total)
220
return
hpackBlock;
221
222
hpackBlock.resize(total);
223
auto
dst = hpackBlock.begin();
224
for
(
const
auto
&frame : frames) {
225
if
(
const
auto
hpackBlockSize = frame.hpackBlockSize()) {
226
const
uchar *src = frame.hpackBlockBegin();
227
std::copy(src, src + hpackBlockSize, dst);
228
dst += hpackBlockSize;
229
}
230
}
231
232
return
hpackBlock;
233
}
234
235
236
}
// namespace Http2
237
238
QT_END_NAMESPACE
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
qtbase
src
network
access
http2
http2protocol.cpp
Generated on
for Qt by
1.18.0