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