1- # -*- coding: utf-8 -*-
21"""
32hpack/hpack
43~~~~~~~~~~~
54
65Implements the HPACK header compression algorithm as detailed by the IETF.
76"""
87import logging
8+ from typing import Any , Union
99
1010from .table import HeaderTable , table_entry_size
1111from .exceptions import (
1616 REQUEST_CODES , REQUEST_CODES_LENGTH
1717)
1818from .huffman_table import decode_huffman
19- from .struct import HeaderTuple , NeverIndexedHeaderTuple
19+ from .struct import HeaderTuple , NeverIndexedHeaderTuple , Headers
2020
2121log = logging .getLogger (__name__ )
2222
2929# as prefix numbers are not zero indexed.
3030_PREFIX_BIT_MAX_NUMBERS = [(2 ** i ) - 1 for i in range (9 )]
3131
32- try : # pragma: no cover
33- basestring = basestring
34- except NameError : # pragma: no cover
35- basestring = (str , bytes )
36-
37-
3832# We default the maximum header list we're willing to accept to 64kB. That's a
3933# lot of headers, but if applications want to raise it they can do.
4034DEFAULT_MAX_HEADER_LIST_SIZE = 2 ** 16
4135
4236
43- def _unicode_if_needed (header , raw ) :
37+ def _unicode_if_needed (header : HeaderTuple , raw : bool ) -> HeaderTuple :
4438 """
4539 Provides a header as a unicode string if raw is False, otherwise returns
4640 it as a bytestring.
4741 """
48- name = bytes (header [0 ])
49- value = bytes (header [1 ])
42+ name = bytes (header [0 ]) # type: ignore
43+ value = bytes (header [1 ]) # type: ignore
5044 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 )
5448
5549
56- def encode_integer (integer , prefix_bits ) :
50+ def encode_integer (integer : int , prefix_bits : int ) -> bytearray :
5751 """
5852 This encodes an integer according to the wacky integer encoding rules
5953 defined in the HPACK spec.
@@ -87,7 +81,7 @@ def encode_integer(integer, prefix_bits):
8781 return bytearray (elements )
8882
8983
90- def decode_integer (data , prefix_bits ) :
84+ def decode_integer (data : bytes , prefix_bits : int ) -> tuple [ int , int ] :
9185 """
9286 This decodes an integer according to the wacky integer encoding rules
9387 defined in the HPACK spec. Returns a tuple of the decoded integer and the
@@ -128,7 +122,7 @@ def decode_integer(data, prefix_bits):
128122 return number , index
129123
130124
131- def _dict_to_iterable (header_dict ) :
125+ def _dict_to_iterable (header_dict : dict [ Union [ bytes , str ], Union [ bytes , str ]]) -> Headers :
132126 """
133127 This converts a dictionary to an iterable of two-tuples. This is a
134128 HPACK-specific function because it pulls "special-headers" out first and
@@ -143,16 +137,16 @@ def _dict_to_iterable(header_dict):
143137 yield key , header_dict [key ]
144138
145139
146- def _to_bytes (value ) :
140+ def _to_bytes (value : Union [ bytes , str , Any ]) -> bytes :
147141 """
148142 Convert anything to bytes through a UTF-8 encoded string
149143 """
150144 t = type (value )
151145 if t is bytes :
152- return value
146+ return value # type: ignore
153147 if t is not str :
154148 value = str (value )
155- return value .encode ("utf-8" )
149+ return value .encode ("utf-8" ) # type: ignore
156150
157151
158152class Encoder :
@@ -161,27 +155,29 @@ class Encoder:
161155 HTTP/2 header blocks.
162156 """
163157
164- def __init__ (self ):
158+ def __init__ (self ) -> None :
165159 self .header_table = HeaderTable ()
166160 self .huffman_coder = HuffmanEncoder (
167161 REQUEST_CODES , REQUEST_CODES_LENGTH
168162 )
169- self .table_size_changes = []
163+ self .table_size_changes : list [ int ] = []
170164
171165 @property
172- def header_table_size (self ):
166+ def header_table_size (self ) -> int :
173167 """
174168 Controls the size of the HPACK header table.
175169 """
176170 return self .header_table .maxsize
177171
178172 @header_table_size .setter
179- def header_table_size (self , value ) :
173+ def header_table_size (self , value : int ) -> None :
180174 self .header_table .maxsize = value
181175 if self .header_table .resized :
182176 self .table_size_changes .append (value )
183177
184- def encode (self , headers , huffman = True ):
178+ def encode (self ,
179+ headers : Headers ,
180+ huffman : bool = True ) -> bytes :
185181 """
186182 Takes a set of headers and encodes them into a HPACK-encoded header
187183 block.
@@ -256,13 +252,13 @@ def encode(self, headers, huffman=True):
256252 header = (_to_bytes (header [0 ]), _to_bytes (header [1 ]))
257253 header_block .append (self .add (header , sensitive , huffman ))
258254
259- header_block = b'' .join (header_block )
255+ encoded = b'' .join (header_block )
260256
261- log .debug ("Encoded header block to %s" , header_block )
257+ log .debug ("Encoded header block to %s" , encoded )
262258
263- return header_block
259+ return encoded
264260
265- def add (self , to_add , sensitive , huffman = False ):
261+ def add (self , to_add : tuple [ bytes , bytes ], sensitive : bool , huffman : bool = False ) -> bytes :
266262 """
267263 This function takes a header key-value tuple and serializes it.
268264 """
@@ -311,15 +307,15 @@ def add(self, to_add, sensitive, huffman=False):
311307
312308 return encoded
313309
314- def _encode_indexed (self , index ) :
310+ def _encode_indexed (self , index : int ) -> bytes :
315311 """
316312 Encodes a header using the indexed representation.
317313 """
318314 field = encode_integer (index , 7 )
319315 field [0 ] |= 0x80 # we set the top bit
320316 return bytes (field )
321317
322- def _encode_literal (self , name , value , indexbit , huffman = False ):
318+ def _encode_literal (self , name : bytes , value : bytes , indexbit : bytes , huffman : bool = False ) -> bytes :
323319 """
324320 Encodes a header with a literal name and literal value. If ``indexing``
325321 is True, the header will be added to the header table: otherwise it
@@ -340,7 +336,7 @@ def _encode_literal(self, name, value, indexbit, huffman=False):
340336 [indexbit , bytes (name_len ), name , bytes (value_len ), value ]
341337 )
342338
343- def _encode_indexed_literal (self , index , value , indexbit , huffman = False ):
339+ def _encode_indexed_literal (self , index : int , value : bytes , indexbit : bytes , huffman : bool = False ) -> bytes :
344340 """
345341 Encodes a header with an indexed name and a literal value and performs
346342 incremental indexing.
@@ -362,16 +358,16 @@ def _encode_indexed_literal(self, index, value, indexbit, huffman=False):
362358
363359 return b'' .join ([bytes (prefix ), bytes (value_len ), value ])
364360
365- def _encode_table_size_change (self ):
361+ def _encode_table_size_change (self ) -> bytes :
366362 """
367363 Produces the encoded form of all header table size change context
368364 updates.
369365 """
370366 block = b''
371367 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 )
368+ b = encode_integer (size_bytes , 5 )
369+ b [0 ] |= 0x20
370+ block += bytes (b )
375371 self .table_size_changes = []
376372 return block
377373
@@ -397,7 +393,7 @@ class Decoder:
397393 Defaults to 64kB.
398394 :type max_header_list_size: ``int``
399395 """
400- def __init__ (self , max_header_list_size = DEFAULT_MAX_HEADER_LIST_SIZE ):
396+ def __init__ (self , max_header_list_size : int = DEFAULT_MAX_HEADER_LIST_SIZE ) -> None :
401397 self .header_table = HeaderTable ()
402398
403399 #: The maximum decompressed size we will allow for any single header
@@ -426,17 +422,17 @@ def __init__(self, max_header_list_size=DEFAULT_MAX_HEADER_LIST_SIZE):
426422 self .max_allowed_table_size = self .header_table .maxsize
427423
428424 @property
429- def header_table_size (self ):
425+ def header_table_size (self ) -> int :
430426 """
431427 Controls the size of the HPACK header table.
432428 """
433429 return self .header_table .maxsize
434430
435431 @header_table_size .setter
436- def header_table_size (self , value ) :
432+ def header_table_size (self , value : int ) -> None :
437433 self .header_table .maxsize = value
438434
439- def decode (self , data , raw = False ):
435+ def decode (self , data : bytes , raw : bool = False ) -> Headers :
440436 """
441437 Takes an HPACK-encoded header block and decodes it into a header set.
442438
@@ -454,7 +450,7 @@ def decode(self, data, raw=False):
454450 log .debug ("Decoding %s" , data )
455451
456452 data_mem = memoryview (data )
457- headers = []
453+ headers : list [ HeaderTuple ] = []
458454 data_len = len (data )
459455 inflated_size = 0
460456 current_index = 0
@@ -501,7 +497,7 @@ def decode(self, data, raw=False):
501497
502498 if header :
503499 headers .append (header )
504- inflated_size += table_entry_size (* header )
500+ inflated_size += table_entry_size (header [ 0 ], header [ 1 ] )
505501
506502 if inflated_size > self .max_header_list_size :
507503 raise OversizedHeaderListError (
@@ -521,7 +517,7 @@ def decode(self, data, raw=False):
521517 except UnicodeDecodeError :
522518 raise HPACKDecodingError ("Unable to decode headers as UTF-8." )
523519
524- def _assert_valid_table_size (self ):
520+ def _assert_valid_table_size (self ) -> None :
525521 """
526522 Check that the table size set by the encoder is lower than the maximum
527523 we expect to have.
@@ -531,7 +527,7 @@ def _assert_valid_table_size(self):
531527 "Encoder did not shrink table size to within the max"
532528 )
533529
534- def _update_encoding_context (self , data ) :
530+ def _update_encoding_context (self , data : bytes ) -> int :
535531 """
536532 Handles a byte that updates the encoding context.
537533 """
@@ -544,7 +540,7 @@ def _update_encoding_context(self, data):
544540 self .header_table_size = new_size
545541 return consumed
546542
547- def _decode_indexed (self , data ) :
543+ def _decode_indexed (self , data : bytes ) -> tuple [ HeaderTuple , int ] :
548544 """
549545 Decodes a header represented using the indexed representation.
550546 """
@@ -553,13 +549,13 @@ def _decode_indexed(self, data):
553549 log .debug ("Decoded %s, consumed %d" , header , consumed )
554550 return header , consumed
555551
556- def _decode_literal_no_index (self , data ) :
552+ def _decode_literal_no_index (self , data : bytes ) -> tuple [ HeaderTuple , int ] :
557553 return self ._decode_literal (data , False )
558554
559- def _decode_literal_index (self , data ) :
555+ def _decode_literal_index (self , data : bytes ) -> tuple [ HeaderTuple , int ] :
560556 return self ._decode_literal (data , True )
561557
562- def _decode_literal (self , data , should_index ) :
558+ def _decode_literal (self , data : bytes , should_index : bool ) -> tuple [ HeaderTuple , int ] :
563559 """
564560 Decodes a header represented with a literal.
565561 """
@@ -577,7 +573,7 @@ def _decode_literal(self, data, should_index):
577573 high_byte = data [0 ]
578574 indexed_name = high_byte & 0x0F
579575 name_len = 4
580- not_indexable = high_byte & 0x10
576+ not_indexable = bool ( high_byte & 0x10 )
581577
582578 if indexed_name :
583579 # Indexed header name.
@@ -616,6 +612,7 @@ def _decode_literal(self, data, should_index):
616612
617613 # If we have been told never to index the header field, encode that in
618614 # the tuple we use.
615+ header : HeaderTuple
619616 if not_indexable :
620617 header = NeverIndexedHeaderTuple (name , value )
621618 else :
0 commit comments