1
- # -*- coding: utf-8 -*-
2
1
"""
3
2
hpack/hpack
4
3
~~~~~~~~~~~
5
4
6
5
Implements the HPACK header compression algorithm as detailed by the IETF.
7
6
"""
8
7
import logging
8
+ from typing import Any , Generator , Union
9
9
10
10
from .table import HeaderTable , table_entry_size
11
11
from .exceptions import (
16
16
REQUEST_CODES , REQUEST_CODES_LENGTH
17
17
)
18
18
from .huffman_table import decode_huffman
19
- from .struct import HeaderTuple , NeverIndexedHeaderTuple
19
+ from .struct import HeaderTuple , NeverIndexedHeaderTuple , Headers
20
20
21
21
log = logging .getLogger (__name__ )
22
22
29
29
# as prefix numbers are not zero indexed.
30
30
_PREFIX_BIT_MAX_NUMBERS = [(2 ** i ) - 1 for i in range (9 )]
31
31
32
- try : # pragma: no cover
33
- basestring = basestring
34
- except NameError : # pragma: no cover
35
- basestring = (str , bytes )
36
-
37
-
38
32
# We default the maximum header list we're willing to accept to 64kB. That's a
39
33
# lot of headers, but if applications want to raise it they can do.
40
34
DEFAULT_MAX_HEADER_LIST_SIZE = 2 ** 16
41
35
42
36
43
- def _unicode_if_needed (header , raw ) :
37
+ def _unicode_if_needed (header : HeaderTuple , raw : bool ) -> HeaderTuple :
44
38
"""
45
39
Provides a header as a unicode string if raw is False, otherwise returns
46
40
it as a bytestring.
47
41
"""
48
- name = bytes (header [0 ])
49
- value = bytes (header [1 ])
42
+ name = bytes (header [0 ]) # type: ignore
43
+ value = bytes (header [1 ]) # type: ignore
50
44
if not raw :
51
- name = name .decode ('utf-8' )
52
- value = value . decode ( 'utf-8' )
53
- return header .__class__ (name , value )
45
+ return header . __class__ ( name .decode ('utf-8' ), value . decode ( 'utf-8' ) )
46
+ else :
47
+ return header .__class__ (name , value )
54
48
55
49
56
- def encode_integer (integer , prefix_bits ) :
50
+ def encode_integer (integer : int , prefix_bits : int ) -> bytearray :
57
51
"""
58
52
This encodes an integer according to the wacky integer encoding rules
59
53
defined in the HPACK spec.
@@ -87,7 +81,7 @@ def encode_integer(integer, prefix_bits):
87
81
return bytearray (elements )
88
82
89
83
90
- def decode_integer (data , prefix_bits ) :
84
+ def decode_integer (data : bytes , prefix_bits : int ) -> tuple [ int , int ] :
91
85
"""
92
86
This decodes an integer according to the wacky integer encoding rules
93
87
defined in the HPACK spec. Returns a tuple of the decoded integer and the
@@ -128,7 +122,8 @@ def decode_integer(data, prefix_bits):
128
122
return number , index
129
123
130
124
131
- def _dict_to_iterable (header_dict ):
125
+ def _dict_to_iterable (header_dict : Union [dict [bytes , bytes ], dict [str , str ]]) \
126
+ -> Generator [Union [tuple [bytes , bytes ], tuple [str , str ]], None , None ]:
132
127
"""
133
128
This converts a dictionary to an iterable of two-tuples. This is a
134
129
HPACK-specific function because it pulls "special-headers" out first and
@@ -140,19 +135,19 @@ def _dict_to_iterable(header_dict):
140
135
key = lambda k : not _to_bytes (k ).startswith (b':' )
141
136
)
142
137
for key in keys :
143
- yield key , header_dict [key ]
138
+ yield key , header_dict [key ] # type: ignore
144
139
145
140
146
- def _to_bytes (value ) :
141
+ def _to_bytes (value : Union [ bytes , str , Any ]) -> bytes :
147
142
"""
148
143
Convert anything to bytes through a UTF-8 encoded string
149
144
"""
150
145
t = type (value )
151
146
if t is bytes :
152
- return value
147
+ return value # type: ignore
153
148
if t is not str :
154
149
value = str (value )
155
- return value .encode ("utf-8" )
150
+ return value .encode ("utf-8" ) # type: ignore
156
151
157
152
158
153
class Encoder :
@@ -161,27 +156,29 @@ class Encoder:
161
156
HTTP/2 header blocks.
162
157
"""
163
158
164
- def __init__ (self ):
159
+ def __init__ (self ) -> None :
165
160
self .header_table = HeaderTable ()
166
161
self .huffman_coder = HuffmanEncoder (
167
162
REQUEST_CODES , REQUEST_CODES_LENGTH
168
163
)
169
- self .table_size_changes = []
164
+ self .table_size_changes : list [ int ] = []
170
165
171
166
@property
172
- def header_table_size (self ):
167
+ def header_table_size (self ) -> int :
173
168
"""
174
169
Controls the size of the HPACK header table.
175
170
"""
176
171
return self .header_table .maxsize
177
172
178
173
@header_table_size .setter
179
- def header_table_size (self , value ) :
174
+ def header_table_size (self , value : int ) -> None :
180
175
self .header_table .maxsize = value
181
176
if self .header_table .resized :
182
177
self .table_size_changes .append (value )
183
178
184
- def encode (self , headers , huffman = True ):
179
+ def encode (self ,
180
+ headers : Headers ,
181
+ huffman : bool = True ) -> bytes :
185
182
"""
186
183
Takes a set of headers and encodes them into a HPACK-encoded header
187
184
block.
@@ -256,13 +253,13 @@ def encode(self, headers, huffman=True):
256
253
header = (_to_bytes (header [0 ]), _to_bytes (header [1 ]))
257
254
header_block .append (self .add (header , sensitive , huffman ))
258
255
259
- header_block = b'' .join (header_block )
256
+ encoded = b'' .join (header_block )
260
257
261
- log .debug ("Encoded header block to %s" , header_block )
258
+ log .debug ("Encoded header block to %s" , encoded )
262
259
263
- return header_block
260
+ return encoded
264
261
265
- def add (self , to_add , sensitive , huffman = False ):
262
+ def add (self , to_add : tuple [ bytes , bytes ], sensitive : bool , huffman : bool = False ) -> bytes :
266
263
"""
267
264
This function takes a header key-value tuple and serializes it.
268
265
"""
@@ -311,15 +308,15 @@ def add(self, to_add, sensitive, huffman=False):
311
308
312
309
return encoded
313
310
314
- def _encode_indexed (self , index ) :
311
+ def _encode_indexed (self , index : int ) -> bytes :
315
312
"""
316
313
Encodes a header using the indexed representation.
317
314
"""
318
315
field = encode_integer (index , 7 )
319
316
field [0 ] |= 0x80 # we set the top bit
320
317
return bytes (field )
321
318
322
- def _encode_literal (self , name , value , indexbit , huffman = False ):
319
+ def _encode_literal (self , name : bytes , value : bytes , indexbit : bytes , huffman : bool = False ) -> bytes :
323
320
"""
324
321
Encodes a header with a literal name and literal value. If ``indexing``
325
322
is True, the header will be added to the header table: otherwise it
@@ -340,7 +337,7 @@ def _encode_literal(self, name, value, indexbit, huffman=False):
340
337
[indexbit , bytes (name_len ), name , bytes (value_len ), value ]
341
338
)
342
339
343
- def _encode_indexed_literal (self , index , value , indexbit , huffman = False ):
340
+ def _encode_indexed_literal (self , index : int , value : bytes , indexbit : bytes , huffman : bool = False ) -> bytes :
344
341
"""
345
342
Encodes a header with an indexed name and a literal value and performs
346
343
incremental indexing.
@@ -362,16 +359,16 @@ def _encode_indexed_literal(self, index, value, indexbit, huffman=False):
362
359
363
360
return b'' .join ([bytes (prefix ), bytes (value_len ), value ])
364
361
365
- def _encode_table_size_change (self ):
362
+ def _encode_table_size_change (self ) -> bytes :
366
363
"""
367
364
Produces the encoded form of all header table size change context
368
365
updates.
369
366
"""
370
367
block = b''
371
368
for size_bytes in self .table_size_changes :
372
- size_bytes = encode_integer (size_bytes , 5 )
373
- size_bytes [0 ] |= 0x20
374
- block += bytes (size_bytes )
369
+ b = encode_integer (size_bytes , 5 )
370
+ b [0 ] |= 0x20
371
+ block += bytes (b )
375
372
self .table_size_changes = []
376
373
return block
377
374
@@ -397,7 +394,7 @@ class Decoder:
397
394
Defaults to 64kB.
398
395
:type max_header_list_size: ``int``
399
396
"""
400
- def __init__ (self , max_header_list_size = DEFAULT_MAX_HEADER_LIST_SIZE ):
397
+ def __init__ (self , max_header_list_size : int = DEFAULT_MAX_HEADER_LIST_SIZE ) -> None :
401
398
self .header_table = HeaderTable ()
402
399
403
400
#: The maximum decompressed size we will allow for any single header
@@ -426,17 +423,17 @@ def __init__(self, max_header_list_size=DEFAULT_MAX_HEADER_LIST_SIZE):
426
423
self .max_allowed_table_size = self .header_table .maxsize
427
424
428
425
@property
429
- def header_table_size (self ):
426
+ def header_table_size (self ) -> int :
430
427
"""
431
428
Controls the size of the HPACK header table.
432
429
"""
433
430
return self .header_table .maxsize
434
431
435
432
@header_table_size .setter
436
- def header_table_size (self , value ) :
433
+ def header_table_size (self , value : int ) -> None :
437
434
self .header_table .maxsize = value
438
435
439
- def decode (self , data , raw = False ):
436
+ def decode (self , data : bytes , raw : bool = False ) -> Headers :
440
437
"""
441
438
Takes an HPACK-encoded header block and decodes it into a header set.
442
439
@@ -454,7 +451,7 @@ def decode(self, data, raw=False):
454
451
log .debug ("Decoding %s" , data )
455
452
456
453
data_mem = memoryview (data )
457
- headers = []
454
+ headers : list [ HeaderTuple ] = []
458
455
data_len = len (data )
459
456
inflated_size = 0
460
457
current_index = 0
@@ -501,7 +498,7 @@ def decode(self, data, raw=False):
501
498
502
499
if header :
503
500
headers .append (header )
504
- inflated_size += table_entry_size (* header )
501
+ inflated_size += table_entry_size (header [ 0 ], header [ 1 ] )
505
502
506
503
if inflated_size > self .max_header_list_size :
507
504
raise OversizedHeaderListError (
@@ -521,7 +518,7 @@ def decode(self, data, raw=False):
521
518
except UnicodeDecodeError :
522
519
raise HPACKDecodingError ("Unable to decode headers as UTF-8." )
523
520
524
- def _assert_valid_table_size (self ):
521
+ def _assert_valid_table_size (self ) -> None :
525
522
"""
526
523
Check that the table size set by the encoder is lower than the maximum
527
524
we expect to have.
@@ -531,7 +528,7 @@ def _assert_valid_table_size(self):
531
528
"Encoder did not shrink table size to within the max"
532
529
)
533
530
534
- def _update_encoding_context (self , data ) :
531
+ def _update_encoding_context (self , data : bytes ) -> int :
535
532
"""
536
533
Handles a byte that updates the encoding context.
537
534
"""
@@ -544,7 +541,7 @@ def _update_encoding_context(self, data):
544
541
self .header_table_size = new_size
545
542
return consumed
546
543
547
- def _decode_indexed (self , data ) :
544
+ def _decode_indexed (self , data : bytes ) -> tuple [ HeaderTuple , int ] :
548
545
"""
549
546
Decodes a header represented using the indexed representation.
550
547
"""
@@ -553,13 +550,13 @@ def _decode_indexed(self, data):
553
550
log .debug ("Decoded %s, consumed %d" , header , consumed )
554
551
return header , consumed
555
552
556
- def _decode_literal_no_index (self , data ) :
553
+ def _decode_literal_no_index (self , data : bytes ) -> tuple [ HeaderTuple , int ] :
557
554
return self ._decode_literal (data , False )
558
555
559
- def _decode_literal_index (self , data ) :
556
+ def _decode_literal_index (self , data : bytes ) -> tuple [ HeaderTuple , int ] :
560
557
return self ._decode_literal (data , True )
561
558
562
- def _decode_literal (self , data , should_index ) :
559
+ def _decode_literal (self , data : bytes , should_index : bool ) -> tuple [ HeaderTuple , int ] :
563
560
"""
564
561
Decodes a header represented with a literal.
565
562
"""
@@ -577,7 +574,7 @@ def _decode_literal(self, data, should_index):
577
574
high_byte = data [0 ]
578
575
indexed_name = high_byte & 0x0F
579
576
name_len = 4
580
- not_indexable = high_byte & 0x10
577
+ not_indexable = bool ( high_byte & 0x10 )
581
578
582
579
if indexed_name :
583
580
# Indexed header name.
@@ -616,6 +613,7 @@ def _decode_literal(self, data, should_index):
616
613
617
614
# If we have been told never to index the header field, encode that in
618
615
# the tuple we use.
616
+ header : HeaderTuple
619
617
if not_indexable :
620
618
header = NeverIndexedHeaderTuple (name , value )
621
619
else :
0 commit comments