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

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