30const char Http2clientPreface[clientPrefaceLength] =
31 {0x50, 0x52, 0x49, 0x20, 0x2a, 0x20,
32 0x48, 0x54, 0x54, 0x50, 0x2f, 0x32,
33 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a,
34 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a};
36Frame configurationToSettingsFrame(
const QHttp2Configuration &config)
39 FrameWriter builder(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID);
41 builder.append(Settings::ENABLE_PUSH_ID);
42 builder.append(
int(config.serverPushEnabled()));
45 if (config.streamReceiveWindowSize() != defaultSessionWindowSize) {
46 builder.append(Settings::INITIAL_WINDOW_SIZE_ID);
47 builder.append(config.streamReceiveWindowSize());
50 if (config.maxFrameSize() != minPayloadLimit) {
51 builder.append(Settings::MAX_FRAME_SIZE_ID);
52 builder.append(config.maxFrameSize());
57 return builder.outboundFrame();
60QByteArray settingsFrameToBase64(
const Frame &frame)
64 Q_ASSERT(frame.payloadSize() && !(frame.payloadSize() % 6));
65 const char *src =
reinterpret_cast<
const char *>(frame.dataBegin());
66 const QByteArray wrapper(QByteArray::fromRawData(src,
int(frame.dataSize())));
72 return wrapper.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
75void appendProtocolUpgradeHeaders(
const QHttp2Configuration &config, QHttpNetworkRequest *request)
80 const QByteArray connectionHeader = request->headerField(
"Connection");
81 const auto separator = connectionHeader.isEmpty() ? QByteArrayView() : QByteArrayView(
", ");
83 QByteArray value = connectionHeader + separator +
"Upgrade, HTTP2-Settings";
84 request->setHeaderField(
"Connection", value);
86 request->setHeaderField(
"Upgrade",
"h2c");
88 const Frame frame(configurationToSettingsFrame(config));
90 request->setHeaderField(
"HTTP2-Settings", settingsFrameToBase64(frame));
93void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error,
94 QString &errorMessage)
96 if (errorCode > quint32(HTTP_1_1_REQUIRED)) {
97 error = QNetworkReply::ProtocolFailure;
98 errorMessage =
"RST_STREAM with unknown error code (%1)"_L1;
99 errorMessage = errorMessage.arg(errorCode);
103 const Http2Error http2Error = Http2Error(errorCode);
105 switch (http2Error) {
107 error = QNetworkReply::RemoteHostClosedError;
108 errorMessage =
"Remote host signaled shutdown"_L1;
111 error = QNetworkReply::ProtocolFailure;
112 errorMessage =
"HTTP/2 protocol error"_L1;
115 error = QNetworkReply::InternalServerError;
116 errorMessage =
"Internal server error"_L1;
118 case FLOW_CONTROL_ERROR:
119 error = QNetworkReply::ProtocolFailure;
120 errorMessage =
"Flow control error"_L1;
122 case SETTINGS_TIMEOUT:
123 error = QNetworkReply::TimeoutError;
124 errorMessage =
"SETTINGS ACK timeout error"_L1;
127 error = QNetworkReply::ProtocolFailure;
128 errorMessage =
"Server received frame(s) on a half-closed stream"_L1;
130 case FRAME_SIZE_ERROR:
131 error = QNetworkReply::ProtocolFailure;
132 errorMessage =
"Server received a frame with an invalid size"_L1;
135 error = QNetworkReply::ProtocolFailure;
136 errorMessage =
"Server refused a stream"_L1;
139 error = QNetworkReply::ProtocolFailure;
140 errorMessage =
"Stream is no longer needed"_L1;
142 case COMPRESSION_ERROR:
143 error = QNetworkReply::ProtocolFailure;
144 errorMessage =
"Server is unable to maintain the "
145 "header compression context for the connection"_L1;
149 error = QNetworkReply::UnknownNetworkError;
150 errorMessage =
"The connection established in response "
151 "to a CONNECT request was reset or abnormally closed"_L1;
153 case ENHANCE_YOUR_CALM:
154 error = QNetworkReply::UnknownServerError;
155 errorMessage =
"Server dislikes our behavior, excessive load detected."_L1;
157 case INADEQUATE_SECURITY:
158 error = QNetworkReply::ContentAccessDenied;
159 errorMessage =
"The underlying transport has properties "
160 "that do not meet minimum security "
163 case HTTP_1_1_REQUIRED:
164 error = QNetworkReply::ProtocolFailure;
165 errorMessage =
"Server requires that HTTP/1.1 "
166 "be used instead of HTTP/2."_L1;
186bool is_protocol_upgraded(
const QHttpNetworkReply &reply)
188 if (reply.statusCode() != 101)
192 for (
const auto &v : reply.header().values(QHttpHeaders::WellKnownHeader::Upgrade)) {
193 if (v.compare(
"h2c", Qt::CaseInsensitive) == 0)
200std::vector<uchar> assemble_hpack_block(
const std::vector<Frame> &frames)
202 std::vector<uchar> hpackBlock;
205 for (
const auto &frame : frames) {
206 if (qAddOverflow(total, size_t{frame.hpackBlockSize()}, &total))
213 hpackBlock.resize(total);
214 auto dst = hpackBlock.begin();
215 for (
const auto &frame : frames) {
216 if (
const auto hpackBlockSize = frame.hpackBlockSize()) {
217 const uchar *src = frame.hpackBlockBegin();
218 std::copy(src, src + hpackBlockSize, dst);
219 dst += hpackBlockSize;