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 "http2frames_p.h"
5
6#include <QtNetwork/qabstractsocket.h>
7
8#include <algorithm>
9#include <utility>
10
11QT_BEGIN_NAMESPACE
12
13namespace Http2
14{
15
16// HTTP/2 frames are defined by RFC7540, clauses 4 and 6.
17
18Frame::Frame()
19 : buffer(frameHeaderSize)
20{
21}
22
23FrameType Frame::type() const
24{
25 Q_ASSERT(buffer.size() >= frameHeaderSize);
26
27 if (int(buffer[3]) >= int(FrameType::LAST_FRAME_TYPE))
28 return FrameType::LAST_FRAME_TYPE;
29
30 return FrameType(buffer[3]);
31}
32
33quint32 Frame::streamID() const
34{
35 Q_ASSERT(buffer.size() >= frameHeaderSize);
36 return qFromBigEndian<quint32>(src: &buffer[5]);
37}
38
39FrameFlags Frame::flags() const
40{
41 Q_ASSERT(buffer.size() >= frameHeaderSize);
42 return FrameFlags(buffer[4]);
43}
44
45quint32 Frame::payloadSize() const
46{
47 Q_ASSERT(buffer.size() >= frameHeaderSize);
48 return buffer[0] << 16 | buffer[1] << 8 | buffer[2];
49}
50
51uchar Frame::padding() const
52{
53 Q_ASSERT(validateHeader() == FrameStatus::goodFrame);
54
55 if (!flags().testFlag(flag: FrameFlag::PADDED))
56 return 0;
57
58 switch (type()) {
59 case FrameType::DATA:
60 case FrameType::PUSH_PROMISE:
61 case FrameType::HEADERS:
62 Q_ASSERT(buffer.size() > frameHeaderSize);
63 return buffer[frameHeaderSize];
64 default:
65 return 0;
66 }
67}
68
69bool Frame::priority(quint32 *streamID, uchar *weight) const
70{
71 Q_ASSERT(validatePayload() == FrameStatus::goodFrame);
72
73 if (buffer.size() <= frameHeaderSize)
74 return false;
75
76 const uchar *src = &buffer[0] + frameHeaderSize;
77 if (type() == FrameType::HEADERS && flags().testFlag(flag: FrameFlag::PADDED))
78 ++src;
79
80 if ((type() == FrameType::HEADERS && flags().testFlag(flag: FrameFlag::PRIORITY))
81 || type() == FrameType::PRIORITY) {
82 if (streamID)
83 *streamID = qFromBigEndian<quint32>(src);
84 if (weight)
85 *weight = src[4];
86 return true;
87 }
88
89 return false;
90}
91
92FrameStatus Frame::validateHeader() const
93{
94 // Should be called only on a frame with
95 // a complete header.
96 Q_ASSERT(buffer.size() >= frameHeaderSize);
97
98 const auto framePayloadSize = payloadSize();
99 // 4.2 Frame Size
100 if (framePayloadSize > maxPayloadSize)
101 return FrameStatus::sizeError;
102
103 switch (type()) {
104 case FrameType::SETTINGS:
105 // SETTINGS ACK can not have any payload.
106 // The payload of a SETTINGS frame consists of zero
107 // or more parameters, each consisting of an unsigned
108 // 16-bit setting identifier and an unsigned 32-bit value.
109 // Thus the payload size must be a multiple of 6.
110 if (flags().testFlag(flag: FrameFlag::ACK) ? framePayloadSize : framePayloadSize % 6)
111 return FrameStatus::sizeError;
112 break;
113 case FrameType::PRIORITY:
114 // 6.3 PRIORITY
115 if (framePayloadSize != 5)
116 return FrameStatus::sizeError;
117 break;
118 case FrameType::PING:
119 // 6.7 PING
120 if (framePayloadSize != 8)
121 return FrameStatus::sizeError;
122 break;
123 case FrameType::GOAWAY:
124 // 6.8 GOAWAY
125 if (framePayloadSize < 8)
126 return FrameStatus::sizeError;
127 break;
128 case FrameType::RST_STREAM:
129 case FrameType::WINDOW_UPDATE:
130 // 6.4 RST_STREAM, 6.9 WINDOW_UPDATE
131 if (framePayloadSize != 4)
132 return FrameStatus::sizeError;
133 break;
134 case FrameType::PUSH_PROMISE:
135 // 6.6 PUSH_PROMISE
136 if (framePayloadSize < 4)
137 return FrameStatus::sizeError;
138 default:
139 // DATA/HEADERS/CONTINUATION will be verified
140 // when we have payload.
141 // Frames of unknown types are ignored (5.1)
142 break;
143 }
144
145 return FrameStatus::goodFrame;
146}
147
148FrameStatus Frame::validatePayload() const
149{
150 // Should be called only on a complete frame with a valid header.
151 Q_ASSERT(validateHeader() == FrameStatus::goodFrame);
152
153 // Ignored, 5.1
154 if (type() == FrameType::LAST_FRAME_TYPE)
155 return FrameStatus::goodFrame;
156
157 auto size = payloadSize();
158 Q_ASSERT(buffer.size() >= frameHeaderSize && size == buffer.size() - frameHeaderSize);
159
160 const uchar *src = size ? &buffer[0] + frameHeaderSize : nullptr;
161 const auto frameFlags = flags();
162 switch (type()) {
163 // 6.1 DATA, 6.2 HEADERS
164 case FrameType::DATA:
165 case FrameType::HEADERS:
166 if (frameFlags.testFlag(flag: FrameFlag::PADDED)) {
167 if (!size || size < src[0])
168 return FrameStatus::sizeError;
169 size -= src[0];
170 }
171 if (type() == FrameType::HEADERS && frameFlags.testFlag(flag: FrameFlag::PRIORITY)) {
172 if (size < 5)
173 return FrameStatus::sizeError;
174 }
175 break;
176 // 6.6 PUSH_PROMISE
177 case FrameType::PUSH_PROMISE:
178 if (frameFlags.testFlag(flag: FrameFlag::PADDED)) {
179 if (!size || size < src[0])
180 return FrameStatus::sizeError;
181 size -= src[0];
182 }
183
184 if (size < 4)
185 return FrameStatus::sizeError;
186 break;
187 default:
188 break;
189 }
190
191 return FrameStatus::goodFrame;
192}
193
194
195quint32 Frame::dataSize() const
196{
197 Q_ASSERT(validatePayload() == FrameStatus::goodFrame);
198
199 quint32 size = payloadSize();
200 if (flags().testFlag(flag: FrameFlag::PADDED)) {
201 const uchar pad = padding();
202 // + 1 one for a byte with padding number itself:
203 size -= pad + 1;
204 }
205
206 if (priority())
207 size -= 5;
208
209 return size;
210}
211
212quint32 Frame::hpackBlockSize() const
213{
214 Q_ASSERT(validatePayload() == FrameStatus::goodFrame);
215
216 const auto frameType = type();
217 Q_ASSERT(frameType == FrameType::HEADERS ||
218 frameType == FrameType::PUSH_PROMISE ||
219 frameType == FrameType::CONTINUATION);
220
221 quint32 size = dataSize();
222 if (frameType == FrameType::PUSH_PROMISE) {
223 Q_ASSERT(size >= 4);
224 size -= 4;
225 }
226
227 return size;
228}
229
230const uchar *Frame::dataBegin() const
231{
232 Q_ASSERT(validatePayload() == FrameStatus::goodFrame);
233 if (buffer.size() <= frameHeaderSize)
234 return nullptr;
235
236 const uchar *src = &buffer[0] + frameHeaderSize;
237 if (flags().testFlag(flag: FrameFlag::PADDED))
238 ++src;
239
240 if (priority())
241 src += 5;
242
243 return src;
244}
245
246const uchar *Frame::hpackBlockBegin() const
247{
248 Q_ASSERT(validatePayload() == FrameStatus::goodFrame);
249
250 const auto frameType = type();
251 Q_ASSERT(frameType == FrameType::HEADERS ||
252 frameType == FrameType::PUSH_PROMISE ||
253 frameType == FrameType::CONTINUATION);
254
255 const uchar *begin = dataBegin();
256 if (frameType == FrameType::PUSH_PROMISE)
257 begin += 4; // That's a promised stream, skip it.
258 return begin;
259}
260
261FrameStatus FrameReader::read(QAbstractSocket &socket)
262{
263 if (offset < frameHeaderSize) {
264 if (!readHeader(socket))
265 return FrameStatus::incompleteFrame;
266
267 const auto status = frame.validateHeader();
268 if (status != FrameStatus::goodFrame) {
269 // No need to read any payload.
270 return status;
271 }
272
273 if (Http2PredefinedParameters::maxPayloadSize < frame.payloadSize())
274 return FrameStatus::sizeError;
275
276 frame.buffer.resize(new_size: frame.payloadSize() + frameHeaderSize);
277 }
278
279 if (offset < frame.buffer.size() && !readPayload(socket))
280 return FrameStatus::incompleteFrame;
281
282 // Reset the offset, our frame can be re-used
283 // now (re-read):
284 offset = 0;
285
286 return frame.validatePayload();
287}
288
289bool FrameReader::readHeader(QAbstractSocket &socket)
290{
291 Q_ASSERT(offset < frameHeaderSize);
292
293 auto &buffer = frame.buffer;
294 if (buffer.size() < frameHeaderSize)
295 buffer.resize(new_size: frameHeaderSize);
296
297 const auto chunkSize = socket.read(data: reinterpret_cast<char *>(&buffer[offset]),
298 maxlen: frameHeaderSize - offset);
299 if (chunkSize > 0)
300 offset += chunkSize;
301
302 return offset == frameHeaderSize;
303}
304
305bool FrameReader::readPayload(QAbstractSocket &socket)
306{
307 Q_ASSERT(offset < frame.buffer.size());
308 Q_ASSERT(frame.buffer.size() > frameHeaderSize);
309
310 auto &buffer = frame.buffer;
311 // Casts and ugliness - to deal with MSVC. Values are guaranteed to fit into quint32.
312 const auto chunkSize = socket.read(data: reinterpret_cast<char *>(&buffer[offset]),
313 maxlen: qint64(buffer.size() - offset));
314 if (chunkSize > 0)
315 offset += quint32(chunkSize);
316
317 return offset == buffer.size();
318}
319
320FrameWriter::FrameWriter()
321{
322}
323
324FrameWriter::FrameWriter(FrameType type, FrameFlags flags, quint32 streamID)
325{
326 start(type, flags, streamID);
327}
328
329void FrameWriter::setOutboundFrame(Frame &&newFrame)
330{
331 frame = std::move(newFrame);
332 updatePayloadSize();
333}
334
335void FrameWriter::start(FrameType type, FrameFlags flags, quint32 streamID)
336{
337 auto &buffer = frame.buffer;
338
339 buffer.resize(new_size: frameHeaderSize);
340 // The first three bytes - payload size, which is 0 for now.
341 buffer[0] = 0;
342 buffer[1] = 0;
343 buffer[2] = 0;
344
345 buffer[3] = uchar(type);
346 buffer[4] = uchar(flags);
347
348 qToBigEndian(src: streamID, dest: &buffer[5]);
349}
350
351void FrameWriter::setPayloadSize(quint32 size)
352{
353 auto &buffer = frame.buffer;
354
355 Q_ASSERT(buffer.size() >= frameHeaderSize);
356 Q_ASSERT(size <= maxPayloadSize);
357
358 buffer[0] = size >> 16;
359 buffer[1] = size >> 8;
360 buffer[2] = size;
361}
362
363void FrameWriter::setType(FrameType type)
364{
365 Q_ASSERT(frame.buffer.size() >= frameHeaderSize);
366 frame.buffer[3] = uchar(type);
367}
368
369void FrameWriter::setFlags(FrameFlags flags)
370{
371 Q_ASSERT(frame.buffer.size() >= frameHeaderSize);
372 frame.buffer[4] = uchar(flags);
373}
374
375void FrameWriter::addFlag(FrameFlag flag)
376{
377 setFlags(frame.flags() | flag);
378}
379
380void FrameWriter::append(const uchar *begin, const uchar *end)
381{
382 Q_ASSERT(begin && end);
383 Q_ASSERT(begin < end);
384
385 frame.buffer.insert(position: frame.buffer.end(), first: begin, last: end);
386 updatePayloadSize();
387}
388
389void FrameWriter::updatePayloadSize()
390{
391 const quint32 size = quint32(frame.buffer.size() - frameHeaderSize);
392 Q_ASSERT(size <= maxPayloadSize);
393 setPayloadSize(size);
394}
395
396bool FrameWriter::write(QAbstractSocket &socket) const
397{
398 auto &buffer = frame.buffer;
399 Q_ASSERT(buffer.size() >= frameHeaderSize);
400 // Do some sanity check first:
401
402 Q_ASSERT(int(frame.type()) < int(FrameType::LAST_FRAME_TYPE));
403 Q_ASSERT(frame.validateHeader() == FrameStatus::goodFrame);
404
405 const auto nWritten = socket.write(data: reinterpret_cast<const char *>(&buffer[0]),
406 len: buffer.size());
407 return nWritten != -1 && size_type(nWritten) == buffer.size();
408}
409
410bool FrameWriter::writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit)
411{
412 auto &buffer = frame.buffer;
413 Q_ASSERT(buffer.size() >= frameHeaderSize);
414
415 if (sizeLimit > quint32(maxPayloadSize))
416 sizeLimit = quint32(maxPayloadSize);
417
418 if (quint32(buffer.size() - frameHeaderSize) <= sizeLimit) {
419 addFlag(flag: FrameFlag::END_HEADERS);
420 updatePayloadSize();
421 return write(socket);
422 }
423
424 // Our HPACK block does not fit into the size limit, remove
425 // END_HEADERS bit from the first frame, we'll later set
426 // it on the last CONTINUATION frame:
427 setFlags(frame.flags() & ~FrameFlags(FrameFlag::END_HEADERS));
428 // Write a frame's header (not controlled by sizeLimit) and
429 // as many bytes of payload as we can within sizeLimit,
430 // then send CONTINUATION frames, as needed.
431 setPayloadSize(sizeLimit);
432 const quint32 firstChunkSize = frameHeaderSize + sizeLimit;
433 qint64 written = socket.write(data: reinterpret_cast<const char *>(&buffer[0]),
434 len: firstChunkSize);
435
436 if (written != qint64(firstChunkSize))
437 return false;
438
439 FrameWriter continuationWriter(FrameType::CONTINUATION, FrameFlag::EMPTY, frame.streamID());
440 quint32 offset = firstChunkSize;
441
442 while (offset != buffer.size()) {
443 const auto chunkSize = std::min(a: sizeLimit, b: quint32(buffer.size() - offset));
444 if (chunkSize + offset == buffer.size())
445 continuationWriter.addFlag(flag: FrameFlag::END_HEADERS);
446 continuationWriter.setPayloadSize(chunkSize);
447 if (!continuationWriter.write(socket))
448 return false;
449 written = socket.write(data: reinterpret_cast<const char *>(&buffer[offset]),
450 len: chunkSize);
451 if (written != qint64(chunkSize))
452 return false;
453
454 offset += chunkSize;
455 }
456
457 return true;
458}
459
460bool FrameWriter::writeDATA(QAbstractSocket &socket, quint32 sizeLimit,
461 const uchar *src, quint32 size)
462{
463 // With DATA frame(s) we always have:
464 // 1) frame's header (9 bytes)
465 // 2) a separate payload (from QNonContiguousByteDevice).
466 // We either fit within a sizeLimit, or split into several
467 // DATA frames.
468
469 Q_ASSERT(src);
470
471 if (sizeLimit > quint32(maxPayloadSize))
472 sizeLimit = quint32(maxPayloadSize);
473 // We NEVER set END_STREAM, since QHttp2ProtocolHandler works with
474 // QNonContiguousByteDevice and this 'writeDATA' is probably
475 // not the last one for a given request.
476 // This has to be done externally (sending an empty DATA frame with END_STREAM).
477 for (quint32 offset = 0; offset != size;) {
478 const auto chunkSize = std::min(a: size - offset, b: sizeLimit);
479 setPayloadSize(chunkSize);
480 // Frame's header first:
481 if (!write(socket))
482 return false;
483 // Payload (if any):
484 if (chunkSize) {
485 const auto written = socket.write(data: reinterpret_cast<const char*>(src + offset),
486 len: chunkSize);
487 if (written != qint64(chunkSize))
488 return false;
489 }
490
491 offset += chunkSize;
492 }
493
494 return true;
495}
496
497} // Namespace Http2
498
499QT_END_NAMESPACE
500

source code of qtbase/src/network/access/http2/http2frames.cpp