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 , Generator , Union
99
1010from .table import HeaderTable , table_entry_size
1111from .exceptions import (
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 """
4842 name = bytes (header [0 ])
4943 value = bytes (header [1 ])
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,8 @@ 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 ]]) \
126+ -> Generator [tuple [Union [bytes , str ], Union [bytes , str ]], None , None ]:
132127 """
133128 This converts a dictionary to an iterable of two-tuples. This is a
134129 HPACK-specific function because it pulls "special-headers" out first and
@@ -143,11 +138,11 @@ def _dict_to_iterable(header_dict):
143138 yield key , header_dict [key ]
144139
145140
146- def _to_bytes (string ) :
141+ def _to_bytes (string : Union [ bytes , str , Any ]) -> bytes :
147142 """
148143 Convert string to bytes.
149144 """
150- if not isinstance (string , basestring ): # pragma: no cover
145+ if not isinstance (string , ( str , bytes ) ): # pragma: no cover
151146 string = str (string )
152147
153148 return string if isinstance (string , bytes ) else string .encode ('utf-8' )
@@ -159,27 +154,29 @@ class Encoder:
159154 HTTP/2 header blocks.
160155 """
161156
162- def __init__ (self ):
157+ def __init__ (self ) -> None :
163158 self .header_table = HeaderTable ()
164159 self .huffman_coder = HuffmanEncoder (
165160 REQUEST_CODES , REQUEST_CODES_LENGTH
166161 )
167- self .table_size_changes = []
162+ self .table_size_changes : list [ int ] = []
168163
169164 @property
170- def header_table_size (self ):
165+ def header_table_size (self ) -> int :
171166 """
172167 Controls the size of the HPACK header table.
173168 """
174169 return self .header_table .maxsize
175170
176171 @header_table_size .setter
177- def header_table_size (self , value ) :
172+ def header_table_size (self , value : int ) -> None :
178173 self .header_table .maxsize = value
179174 if self .header_table .resized :
180175 self .table_size_changes .append (value )
181176
182- def encode (self , headers , huffman = True ):
177+ def encode (self ,
178+ headers : list [Union [HeaderTuple , tuple [bytes , bytes ], dict [Any , Any ]]],
179+ huffman : bool = True ) -> bytes :
183180 """
184181 Takes a set of headers and encodes them into a HPACK-encoded header
185182 block.
@@ -254,13 +251,13 @@ def encode(self, headers, huffman=True):
254251 header = (_to_bytes (header [0 ]), _to_bytes (header [1 ]))
255252 header_block .append (self .add (header , sensitive , huffman ))
256253
257- header_block = b'' .join (header_block )
254+ encoded = b'' .join (header_block )
258255
259- log .debug ("Encoded header block to %s" , header_block )
256+ log .debug ("Encoded header block to %s" , encoded )
260257
261- return header_block
258+ return encoded
262259
263- def add (self , to_add , sensitive , huffman = False ):
260+ def add (self , to_add : tuple [ bytes , bytes ], sensitive : bool , huffman : bool = False ) -> bytes :
264261 """
265262 This function takes a header key-value tuple and serializes it.
266263 """
@@ -309,15 +306,15 @@ def add(self, to_add, sensitive, huffman=False):
309306
310307 return encoded
311308
312- def _encode_indexed (self , index ) :
309+ def _encode_indexed (self , index : int ) -> bytes :
313310 """
314311 Encodes a header using the indexed representation.
315312 """
316313 field = encode_integer (index , 7 )
317314 field [0 ] |= 0x80 # we set the top bit
318315 return bytes (field )
319316
320- def _encode_literal (self , name , value , indexbit , huffman = False ):
317+ def _encode_literal (self , name : bytes , value : bytes , indexbit : bytes , huffman : bool = False ) -> bytes :
321318 """
322319 Encodes a header with a literal name and literal value. If ``indexing``
323320 is True, the header will be added to the header table: otherwise it
@@ -338,7 +335,7 @@ def _encode_literal(self, name, value, indexbit, huffman=False):
338335 [indexbit , bytes (name_len ), name , bytes (value_len ), value ]
339336 )
340337
341- def _encode_indexed_literal (self , index , value , indexbit , huffman = False ):
338+ def _encode_indexed_literal (self , index : int , value : bytes , indexbit : bytes , huffman : bool = False ) -> bytes :
342339 """
343340 Encodes a header with an indexed name and a literal value and performs
344341 incremental indexing.
@@ -360,16 +357,16 @@ def _encode_indexed_literal(self, index, value, indexbit, huffman=False):
360357
361358 return b'' .join ([bytes (prefix ), bytes (value_len ), value ])
362359
363- def _encode_table_size_change (self ):
360+ def _encode_table_size_change (self ) -> bytes :
364361 """
365362 Produces the encoded form of all header table size change context
366363 updates.
367364 """
368365 block = b''
369366 for size_bytes in self .table_size_changes :
370- size_bytes = encode_integer (size_bytes , 5 )
371- size_bytes [0 ] |= 0x20
372- block += bytes (size_bytes )
367+ b = encode_integer (size_bytes , 5 )
368+ b [0 ] |= 0x20
369+ block += bytes (b )
373370 self .table_size_changes = []
374371 return block
375372
@@ -395,7 +392,7 @@ class Decoder:
395392 Defaults to 64kB.
396393 :type max_header_list_size: ``int``
397394 """
398- def __init__ (self , max_header_list_size = DEFAULT_MAX_HEADER_LIST_SIZE ):
395+ def __init__ (self , max_header_list_size : int = DEFAULT_MAX_HEADER_LIST_SIZE ) -> None :
399396 self .header_table = HeaderTable ()
400397
401398 #: The maximum decompressed size we will allow for any single header
@@ -424,17 +421,17 @@ def __init__(self, max_header_list_size=DEFAULT_MAX_HEADER_LIST_SIZE):
424421 self .max_allowed_table_size = self .header_table .maxsize
425422
426423 @property
427- def header_table_size (self ):
424+ def header_table_size (self ) -> int :
428425 """
429426 Controls the size of the HPACK header table.
430427 """
431428 return self .header_table .maxsize
432429
433430 @header_table_size .setter
434- def header_table_size (self , value ) :
431+ def header_table_size (self , value : int ) -> None :
435432 self .header_table .maxsize = value
436433
437- def decode (self , data , raw = False ):
434+ def decode (self , data : bytes , raw : bool = False ) -> list [ Union [ HeaderTuple , NeverIndexedHeaderTuple ]] :
438435 """
439436 Takes an HPACK-encoded header block and decodes it into a header set.
440437
@@ -452,7 +449,7 @@ def decode(self, data, raw=False):
452449 log .debug ("Decoding %s" , data )
453450
454451 data_mem = memoryview (data )
455- headers = []
452+ headers : list [ Union [ HeaderTuple , NeverIndexedHeaderTuple ]] = []
456453 data_len = len (data )
457454 inflated_size = 0
458455 current_index = 0
@@ -499,7 +496,7 @@ def decode(self, data, raw=False):
499496
500497 if header :
501498 headers .append (header )
502- inflated_size += table_entry_size (* header )
499+ inflated_size += table_entry_size (header [ 0 ], header [ 1 ] )
503500
504501 if inflated_size > self .max_header_list_size :
505502 raise OversizedHeaderListError (
@@ -519,7 +516,7 @@ def decode(self, data, raw=False):
519516 except UnicodeDecodeError :
520517 raise HPACKDecodingError ("Unable to decode headers as UTF-8." )
521518
522- def _assert_valid_table_size (self ):
519+ def _assert_valid_table_size (self ) -> None :
523520 """
524521 Check that the table size set by the encoder is lower than the maximum
525522 we expect to have.
@@ -529,7 +526,7 @@ def _assert_valid_table_size(self):
529526 "Encoder did not shrink table size to within the max"
530527 )
531528
532- def _update_encoding_context (self , data ) :
529+ def _update_encoding_context (self , data : bytes ) -> int :
533530 """
534531 Handles a byte that updates the encoding context.
535532 """
@@ -542,7 +539,7 @@ def _update_encoding_context(self, data):
542539 self .header_table_size = new_size
543540 return consumed
544541
545- def _decode_indexed (self , data ) :
542+ def _decode_indexed (self , data : bytes ) -> tuple [ HeaderTuple , int ] :
546543 """
547544 Decodes a header represented using the indexed representation.
548545 """
@@ -551,13 +548,13 @@ def _decode_indexed(self, data):
551548 log .debug ("Decoded %s, consumed %d" , header , consumed )
552549 return header , consumed
553550
554- def _decode_literal_no_index (self , data ) :
551+ def _decode_literal_no_index (self , data : bytes ) -> tuple [ HeaderTuple , int ] :
555552 return self ._decode_literal (data , False )
556553
557- def _decode_literal_index (self , data ) :
554+ def _decode_literal_index (self , data : bytes ) -> tuple [ HeaderTuple , int ] :
558555 return self ._decode_literal (data , True )
559556
560- def _decode_literal (self , data , should_index ) :
557+ def _decode_literal (self , data : bytes , should_index : bool ) -> tuple [ HeaderTuple , int ] :
561558 """
562559 Decodes a header represented with a literal.
563560 """
@@ -575,7 +572,7 @@ def _decode_literal(self, data, should_index):
575572 high_byte = data [0 ]
576573 indexed_name = high_byte & 0x0F
577574 name_len = 4
578- not_indexable = high_byte & 0x10
575+ not_indexable = bool ( high_byte & 0x10 )
579576
580577 if indexed_name :
581578 # Indexed header name.
@@ -614,6 +611,7 @@ def _decode_literal(self, data, should_index):
614611
615612 # If we have been told never to index the header field, encode that in
616613 # the tuple we use.
614+ header : HeaderTuple
617615 if not_indexable :
618616 header = NeverIndexedHeaderTuple (name , value )
619617 else :
0 commit comments