31const 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};
37Frame configurationToSettingsFrame(
const QHttp2Configuration &config)
40 FrameWriter builder(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID);
42 builder.append(Settings::ENABLE_PUSH_ID);
43 builder.append(
int(config.serverPushEnabled()));
46 if (config.streamReceiveWindowSize() != defaultSessionWindowSize) {
47 builder.append(Settings::INITIAL_WINDOW_SIZE_ID);
48 builder.append(config.streamReceiveWindowSize());
51 if (config.maxFrameSize() != minPayloadLimit) {
52 builder.append(Settings::MAX_FRAME_SIZE_ID);
53 builder.append(config.maxFrameSize());
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);
65 return builder.outboundFrame();
68QByteArray settingsFrameToBase64(
const Frame &frame)
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())));
80 return wrapper.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
83void appendProtocolUpgradeHeaders(
const QHttp2Configuration &config, QHttpNetworkRequest *request)
88 const QByteArray connectionHeader = request->headerField(
"Connection");
89 const auto separator = connectionHeader.isEmpty() ? QByteArrayView() : QByteArrayView(
", ");
91 QByteArray value = connectionHeader + separator +
"Upgrade, HTTP2-Settings";
92 request->setHeaderField(
"Connection", value);
94 request->setHeaderField(
"Upgrade",
"h2c");
96 const Frame frame(configurationToSettingsFrame(config));
98 request->setHeaderField(
"HTTP2-Settings", settingsFrameToBase64(frame));
101void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error,
102 QString &errorMessage)
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);
111 const Http2Error http2Error = Http2Error(errorCode);
113 switch (http2Error) {
115 error = QNetworkReply::RemoteHostClosedError;
116 errorMessage =
"Remote host signaled shutdown"_L1;
119 error = QNetworkReply::ProtocolFailure;
120 errorMessage =
"HTTP/2 protocol error"_L1;
123 error = QNetworkReply::InternalServerError;
124 errorMessage =
"Internal server error"_L1;
126 case FLOW_CONTROL_ERROR:
127 error = QNetworkReply::ProtocolFailure;
128 errorMessage =
"Flow control error"_L1;
130 case SETTINGS_TIMEOUT:
131 error = QNetworkReply::TimeoutError;
132 errorMessage =
"SETTINGS ACK timeout error"_L1;
135 error = QNetworkReply::ProtocolFailure;
136 errorMessage =
"Server received frame(s) on a half-closed stream"_L1;
138 case FRAME_SIZE_ERROR:
139 error = QNetworkReply::ProtocolFailure;
140 errorMessage =
"Server received a frame with an invalid size"_L1;
143 error = QNetworkReply::ProtocolFailure;
144 errorMessage =
"Server refused a stream"_L1;
147 error = QNetworkReply::ProtocolFailure;
148 errorMessage =
"Stream is no longer needed"_L1;
150 case COMPRESSION_ERROR:
151 error = QNetworkReply::ProtocolFailure;
152 errorMessage =
"Server is unable to maintain the "
153 "header compression context for the connection"_L1;
157 error = QNetworkReply::UnknownNetworkError;
158 errorMessage =
"The connection established in response "
159 "to a CONNECT request was reset or abnormally closed"_L1;
161 case ENHANCE_YOUR_CALM:
162 error = QNetworkReply::UnknownServerError;
163 errorMessage =
"Server dislikes our behavior, excessive load detected."_L1;
165 case INADEQUATE_SECURITY:
166 error = QNetworkReply::ContentAccessDenied;
167 errorMessage =
"The underlying transport has properties "
168 "that do not meet minimum security "
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;
194bool is_protocol_upgraded(
const QHttpNetworkReply &reply)
196 if (reply.statusCode() != 101)
199 const auto values = reply.header().values(QHttpHeaders::WellKnownHeader::Upgrade);
201 for (
const auto &v : values) {
202 if (v.compare(
"h2c", Qt::CaseInsensitive) == 0)
209std::vector<uchar> assemble_hpack_block(
const std::vector<Frame> &frames)
211 std::vector<uchar> hpackBlock;
214 for (
const auto &frame : frames) {
215 if (qAddOverflow(total, size_t{frame.hpackBlockSize()}, &total))
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;