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
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
4#include "http2protocol_p.h"
5#include "http2frames_p.h"
6
7#include "private/qhttpnetworkrequest_p.h"
8#include "private/qhttpnetworkreply_p.h"
9
11
12#include <QtCore/qbytearray.h>
13#include <QtCore/qstring.h>
14
16
17using namespace Qt::StringLiterals;
18
20
21Q_LOGGING_CATEGORY(QT_HTTP2, "qt.network.http2")
22
23namespace Http2
24{
25
26// 3.5 HTTP/2 Connection Preface:
27// "That is, the connection preface starts with the string
28// PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n)."
30 {0x50, 0x52, 0x49, 0x20, 0x2a, 0x20,
31 0x48, 0x54, 0x54, 0x50, 0x2f, 0x32,
32 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a,
33 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a};
34
36{
37 // 6.5 SETTINGS
38 FrameWriter builder(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID);
39 // Server push:
40 builder.append(Settings::ENABLE_PUSH_ID);
41 builder.append(int(config.serverPushEnabled()));
42
43 // Stream receive window size (if it's a default value, don't include):
44 if (config.streamReceiveWindowSize() != defaultSessionWindowSize) {
45 builder.append(Settings::INITIAL_WINDOW_SIZE_ID);
46 builder.append(config.streamReceiveWindowSize());
47 }
48
49 if (config.maxFrameSize() != minPayloadLimit) {
50 builder.append(Settings::MAX_FRAME_SIZE_ID);
51 builder.append(config.maxFrameSize());
52 }
53 // TODO: In future, if the need is proven, we can
54 // also send decoding table size and header list size.
55 // For now, defaults suffice.
56 return builder.outboundFrame();
57}
58
60{
61 // SETTINGS frame's payload consists of pairs:
62 // 2-byte-identifier | 4-byte-value == multiple of 6.
63 Q_ASSERT(frame.payloadSize() && !(frame.payloadSize() % 6));
64 const char *src = reinterpret_cast<const char *>(frame.dataBegin());
65 const QByteArray wrapper(QByteArray::fromRawData(src, int(frame.dataSize())));
66 // 3.2.1
67 // The content of the HTTP2-Settings header field is the payload
68 // of a SETTINGS frame (Section 6.5), encoded as a base64url string
69 // (that is, the URL- and filename-safe Base64 encoding described in
70 // Section 5 of [RFC4648], with any trailing '=' characters omitted).
72}
73
75{
77 // RFC 2616, 14.10
78 // RFC 7540, 3.2
79 const QByteArray connectionHeader = request->headerField("Connection");
80 const auto separator = connectionHeader.isEmpty() ? QByteArrayView() : QByteArrayView(", ");
81 // We _append_ 'Upgrade':
82 QByteArray value = connectionHeader + separator + "Upgrade, HTTP2-Settings";
83 request->setHeaderField("Connection", value);
84 // This we just (re)write.
85 request->setHeaderField("Upgrade", "h2c");
86
88 // This we just (re)write.
89 request->setHeaderField("HTTP2-Settings", settingsFrameToBase64(frame));
90}
91
94{
95 if (errorCode > quint32(HTTP_1_1_REQUIRED)) {
97 errorMessage = "RST_STREAM with unknown error code (%1)"_L1;
98 errorMessage = errorMessage.arg(errorCode);
99 return;
100 }
101
102 const Http2Error http2Error = Http2Error(errorCode);
103
104 switch (http2Error) {
105 case HTTP2_NO_ERROR:
107 errorMessage.clear();
108 break;
109 case PROTOCOL_ERROR:
111 errorMessage = "HTTP/2 protocol error"_L1;
112 break;
113 case INTERNAL_ERROR:
115 errorMessage = "Internal server error"_L1;
116 break;
119 errorMessage = "Flow control error"_L1;
120 break;
121 case SETTINGS_TIMEOUT:
123 errorMessage = "SETTINGS ACK timeout error"_L1;
124 break;
125 case STREAM_CLOSED:
127 errorMessage = "Server received frame(s) on a half-closed stream"_L1;
128 break;
129 case FRAME_SIZE_ERROR:
131 errorMessage = "Server received a frame with an invalid size"_L1;
132 break;
133 case REFUSE_STREAM:
135 errorMessage = "Server refused a stream"_L1;
136 break;
137 case CANCEL:
139 errorMessage = "Stream is no longer needed"_L1;
140 break;
143 errorMessage = "Server is unable to maintain the "
144 "header compression context for the connection"_L1;
145 break;
146 case CONNECT_ERROR:
147 // TODO: in Qt6 we'll have to add more error codes in QNetworkReply.
149 errorMessage = "The connection established in response "
150 "to a CONNECT request was reset or abnormally closed"_L1;
151 break;
154 errorMessage = "Server dislikes our behavior, excessive load detected."_L1;
155 break;
158 errorMessage = "The underlying transport has properties "
159 "that do not meet minimum security "
160 "requirements"_L1;
161 break;
164 errorMessage = "Server requires that HTTP/1.1 "
165 "be used instead of HTTP/2."_L1;
166 }
167}
168
176
184
186{
187 if (reply.statusCode() != 101)
188 return false;
189
190 // Do some minimal checks here - we expect 'Upgrade: h2c' to be found.
191 for (const auto &v : reply.header().values(QHttpHeaders::WellKnownHeader::Upgrade)) {
192 if (v.compare("h2c", Qt::CaseInsensitive) == 0)
193 return true;
194 }
195
196 return false;
197}
198
199std::vector<uchar> assemble_hpack_block(const std::vector<Frame> &frames)
200{
201 std::vector<uchar> hpackBlock;
202
203 size_t total = 0;
204 for (const auto &frame : frames) {
205 if (qAddOverflow(total, size_t{frame.hpackBlockSize()}, &total))
206 return hpackBlock;
207 }
208
209 if (!total)
210 return hpackBlock;
211
212 hpackBlock.resize(total);
213 auto dst = hpackBlock.begin();
214 for (const auto &frame : frames) {
215 if (const auto hpackBlockSize = frame.hpackBlockSize()) {
216 const uchar *src = frame.hpackBlockBegin();
217 std::copy(src, src + hpackBlockSize, dst);
218 dst += hpackBlockSize;
219 }
220 }
221
222 return hpackBlock;
223}
224
225
226} // namespace Http2
227
void append(ValueType val)
Frame & outboundFrame()
\inmodule QtCore
Definition qbytearray.h:57
@ Base64UrlEncoding
Definition qbytearray.h:74
@ OmitTrailingEquals
Definition qbytearray.h:77
static QByteArray fromRawData(const char *data, qsizetype size)
Constructs a QByteArray that uses the first size bytes of the data array.
Definition qbytearray.h:409
The QHttp2Configuration class controls HTTP/2 parameters and settings.
QVariant header(QNetworkRequest::KnownHeaders header) const
Returns the value of the known header header, if that header was sent by the remote server.
NetworkError
Indicates all possible error conditions found during the processing of the request.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
std::vector< uchar > assemble_hpack_block(const std::vector< Frame > &frames)
const char Http2clientPreface[clientPrefaceLength]
bool is_protocol_upgraded(const QHttpNetworkReply &reply)
void appendProtocolUpgradeHeaders(const QHttp2Configuration &config, QHttpNetworkRequest *request)
@ defaultSessionWindowSize
@ connectionStreamID
@ clientPrefaceLength
@ minPayloadLimit
Frame configurationToSettingsFrame(const QHttp2Configuration &config)
QByteArray settingsFrameToBase64(const Frame &frame)
@ COMPRESSION_ERROR
@ INADEQUATE_SECURITY
@ ENHANCE_YOUR_CALM
@ FLOW_CONTROL_ERROR
@ HTTP_1_1_REQUIRED
void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, QString &errorMessage)
Combined button and popup list for selecting options.
@ CaseInsensitive
DBusConnection const char DBusError * error
EGLConfig config
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
Q_DECL_COLD_FUNCTION Q_CORE_EXPORT QString qt_error_string(int errorCode=-1)
#define Q_LOGGING_CATEGORY(name,...)
#define QT_IMPL_METATYPE_EXTERN_TAGGED(TYPE, TAG)
Definition qmetatype.h:1384
std::enable_if_t< std::is_unsigned_v< T >, bool > qAddOverflow(T v1, T v2, T *r)
Definition qnumeric.h:113
GLsizei const GLfloat * v
[13]
GLenum src
GLenum GLenum dst
GLuint GLsizei const GLchar * message
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &errorSource, qsizetype errorPosition)
Definition qurl.cpp:3517
QFrame frame
[0]
QNetworkRequest request(url)
QNetworkReply * reply
void wrapper()