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