1010import zlib
1111
1212from ..common .decoder import DeflateDecoder
13- from .util import pop_from_key_value_set
13+ from .. common . headers import HTTPHeaderMap
1414
1515log = logging .getLogger (__name__ )
1616
1717
18- class Headers (object ):
19- def __init__ (self , pairs ):
20- # This conversion to dictionary is unwise, as there may be repeated
21- # keys, but it's acceptable for an early alpha.
22- self ._headers = dict (pairs )
23- self ._strip_headers ()
24-
25- def getheader (self , name , default = None ):
26- return self ._headers .get (name , default )
27-
28- def getheaders (self ):
29- return list (self ._headers .items ())
30-
31- def items (self ):
32- return self ._headers .items ()
33-
34- def merge (self , headers ):
35- for n , v in headers :
36- self ._headers [n ] = v
37- self ._strip_headers ()
38-
39- def _strip_headers (self ):
40- """
41- Strips the headers attached to the instance of any header beginning
42- with a colon that ``hyper`` doesn't understand. This method logs at
43- warning level about the deleted headers, for discoverability.
44- """
45- # Convert to list to ensure that we don't mutate the headers while
46- # we iterate over them.
47- for name in list (self ._headers .keys ()):
48- if name .startswith (':' ):
49- val = self ._headers .pop (name )
50- log .warning ("Unknown reserved header: %s: %s" , name , val )
18+ def strip_headers (headers ):
19+ """
20+ Strips the headers attached to the instance of any header beginning
21+ with a colon that ``hyper`` doesn't understand. This method logs at
22+ warning level about the deleted headers, for discoverability.
23+ """
24+ # Convert to list to ensure that we don't mutate the headers while
25+ # we iterate over them.
26+ for name in list (headers .keys ()):
27+ if name .startswith (b':' ):
28+ del headers [name ]
5129
5230
5331class HTTP20Response (object ):
@@ -63,14 +41,15 @@ def __init__(self, headers, stream):
6341 #: HTTP/2, and so is always the empty string.
6442 self .reason = ''
6543
66- status = pop_from_key_value_set (headers , ':status' )[0 ]
44+ status = headers [b':status' ][0 ]
45+ strip_headers (headers )
6746
6847 #: The status code returned by the server.
6948 self .status = int (status )
7049
71- # The response headers. These are determined upon creation, assigned
72- # once, and never assigned again.
73- self ._headers = Headers ( headers )
50+ #: The response headers. These are determined upon creation, assigned
51+ #: once, and never assigned again.
52+ self .headers = headers
7453
7554 # The response trailers. These are always intially ``None``.
7655 self ._trailers = None
@@ -88,13 +67,29 @@ def __init__(self, headers, stream):
8867 # This 16 + MAX_WBITS nonsense is to force gzip. See this
8968 # Stack Overflow answer for more:
9069 # http://stackoverflow.com/a/2695466/1401686
91- if self ._headers . getheader ( 'content-encoding' ) == 'gzip' :
70+ if b'gzip' in self .headers . get ( b 'content-encoding', []) :
9271 self ._decompressobj = zlib .decompressobj (16 + zlib .MAX_WBITS )
93- elif self ._headers . getheader ( 'content-encoding' ) == 'deflate' :
72+ elif b'deflate' in self .headers . get ( b 'content-encoding', []) :
9473 self ._decompressobj = DeflateDecoder ()
9574 else :
9675 self ._decompressobj = None
9776
77+ @property
78+ def trailers (self ):
79+ """
80+ Trailers on the HTTP message, if any.
81+
82+ .. warning:: Note that this property requires that the stream is
83+ totally exhausted. This means that, if you have not
84+ completely read from the stream, all stream data will be
85+ read into memory.
86+ """
87+ if self ._trailers is None :
88+ self ._trailers = self ._stream .gettrailers () or HTTPHeaderMap ()
89+ strip_headers (self ._trailers )
90+
91+ return self ._trailers
92+
9893 def read (self , amt = None , decode_content = True ):
9994 """
10095 Reads the response body, or up to the next ``amt`` bytes.
@@ -132,78 +127,14 @@ def read(self, amt=None, decode_content=True):
132127 data += self ._decompressobj .flush ()
133128
134129 if self ._stream .response_headers :
135- self ._headers .merge (self ._stream .response_headers )
130+ self .headers .merge (self ._stream .response_headers )
136131
137132 # We're at the end. Close the connection.
138133 if not data :
139134 self .close ()
140135
141136 return data
142137
143- def getheader (self , name , default = None ):
144- """
145- Return the value of the header ``name``, or ``default`` if there is no
146- header matching ``name``. If there is more than one header with the
147- value ``name``, return all of the values joined by ', '. If ``default``
148- is any iterable other than a single string, its elements are similarly
149- returned joined by commas.
150-
151- :param name: The name of the header to get the value of.
152- :param default: (optional) The return value if the header wasn't sent.
153- :returns: The value of the header.
154- """
155- return self ._headers .getheader (name , default )
156-
157- def getheaders (self ):
158- """
159- Get all the headers sent on the response.
160-
161- :returns: A list of ``(header, value)`` tuples.
162- """
163- return list (self ._headers .items ())
164-
165- def gettrailer (self , name , default = None ):
166- """
167- Return the value of the trailer ``name``, or ``default`` if there is no
168- trailer matching ``name``. If there is more than one trailer with the
169- value ``name``, return all of the values joined by ', '. If ``default``
170- is any iterable other than a single string, its elements are similarly
171- returned joined by commas.
172-
173- .. warning:: Note that this method requires that the stream is
174- totally exhausted. This means that, if you have not
175- completely read from the stream, all stream data will be
176- read into memory.
177-
178- :param name: The name of the trailer to get the value of.
179- :param default: (optional) The return value if the trailer wasn't sent.
180- :returns: The value of the trailer.
181- """
182- # We need to get the trailers.
183- if self ._trailers is None :
184- trailers = self ._stream .gettrailers () or []
185- self ._trailers = Headers (trailers )
186-
187- return self ._trailers .getheader (name , default )
188-
189- def gettrailers (self ):
190- """
191- Get all the trailers sent on the response.
192-
193- .. warning:: Note that this method requires that the stream is
194- totally exhausted. This means that, if you have not
195- completely read from the stream, all stream data will be
196- read into memory.
197-
198- :returns: A list of ``(header, value)`` tuples.
199- """
200- # We need to get the trailers.
201- if self ._trailers is None :
202- trailers = self ._stream .gettrailers () or []
203- self ._trailers = Headers (trailers )
204-
205- return list (self ._trailers .items ())
206-
207138 def fileno (self ):
208139 """
209140 Return the ``fileno`` of the underlying socket. This function is
@@ -234,43 +165,21 @@ class HTTP20Push(object):
234165 push mechanism.
235166 """
236167 def __init__ (self , request_headers , stream ):
237- scheme , method , authority , path = (
238- pop_from_key_value_set (request_headers ,
239- ':scheme' , ':method' , ':authority' , ':path' )
240- )
241168 #: The scheme of the simulated request
242- self .scheme = scheme
169+ self .scheme = request_headers [ b': scheme' ][ 0 ]
243170 #: The method of the simulated request (must be safe and cacheable, e.g. GET)
244- self .method = method
171+ self .method = request_headers [ b': method' ][ 0 ]
245172 #: The authority of the simulated request (usually host:port)
246- self .authority = authority
173+ self .authority = request_headers [ b': authority' ][ 0 ]
247174 #: The path of the simulated request
248- self .path = path
175+ self .path = request_headers [ b': path' ][ 0 ]
249176
250- self ._request_headers = Headers (request_headers )
251- self ._stream = stream
177+ strip_headers (request_headers )
252178
253- def getrequestheader (self , name , default = None ):
254- """
255- Return the value of the simulated request header ``name``, or ``default``
256- if there is no header matching ``name``. If there is more than one header
257- with the value ``name``, return all of the values joined by ``', '``.
258- If ``default`` is any iterable other than a single string, its elements
259- are similarly returned joined by commas.
260-
261- :param name: The name of the header to get the value of.
262- :param default: (optional) The return value if the header wasn't sent.
263- :returns: The value of the header.
264- """
265- return self ._request_headers .getheader (name , default )
266-
267- def getrequestheaders (self ):
268- """
269- Get all the simulated request headers.
179+ #: The headers the server attached to the simulated request.
180+ self .request_headers = request_headers
270181
271- :returns: A list of ``(header, value)`` tuples.
272- """
273- return self ._request_headers .getheaders ()
182+ self ._stream = stream
274183
275184 def getresponse (self ):
276185 """
0 commit comments