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