10
10
import zlib
11
11
12
12
from ..common .decoder import DeflateDecoder
13
- from .util import pop_from_key_value_set
13
+ from .. common . headers import HTTPHeaderMap
14
14
15
15
log = logging .getLogger (__name__ )
16
16
17
17
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 ]
51
29
52
30
53
31
class HTTP20Response (object ):
@@ -63,14 +41,15 @@ def __init__(self, headers, stream):
63
41
#: HTTP/2, and so is always the empty string.
64
42
self .reason = ''
65
43
66
- status = pop_from_key_value_set (headers , ':status' )[0 ]
44
+ status = headers [b':status' ][0 ]
45
+ strip_headers (headers )
67
46
68
47
#: The status code returned by the server.
69
48
self .status = int (status )
70
49
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
74
53
75
54
# The response trailers. These are always intially ``None``.
76
55
self ._trailers = None
@@ -88,13 +67,29 @@ def __init__(self, headers, stream):
88
67
# This 16 + MAX_WBITS nonsense is to force gzip. See this
89
68
# Stack Overflow answer for more:
90
69
# 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', []) :
92
71
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', []) :
94
73
self ._decompressobj = DeflateDecoder ()
95
74
else :
96
75
self ._decompressobj = None
97
76
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
+
98
93
def read (self , amt = None , decode_content = True ):
99
94
"""
100
95
Reads the response body, or up to the next ``amt`` bytes.
@@ -132,78 +127,14 @@ def read(self, amt=None, decode_content=True):
132
127
data += self ._decompressobj .flush ()
133
128
134
129
if self ._stream .response_headers :
135
- self ._headers .merge (self ._stream .response_headers )
130
+ self .headers .merge (self ._stream .response_headers )
136
131
137
132
# We're at the end. Close the connection.
138
133
if not data :
139
134
self .close ()
140
135
141
136
return data
142
137
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
-
207
138
def fileno (self ):
208
139
"""
209
140
Return the ``fileno`` of the underlying socket. This function is
@@ -234,43 +165,21 @@ class HTTP20Push(object):
234
165
push mechanism.
235
166
"""
236
167
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
- )
241
168
#: The scheme of the simulated request
242
- self .scheme = scheme
169
+ self .scheme = request_headers [ b': scheme' ][ 0 ]
243
170
#: 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 ]
245
172
#: The authority of the simulated request (usually host:port)
246
- self .authority = authority
173
+ self .authority = request_headers [ b': authority' ][ 0 ]
247
174
#: The path of the simulated request
248
- self .path = path
175
+ self .path = request_headers [ b': path' ][ 0 ]
249
176
250
- self ._request_headers = Headers (request_headers )
251
- self ._stream = stream
177
+ strip_headers (request_headers )
252
178
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
270
181
271
- :returns: A list of ``(header, value)`` tuples.
272
- """
273
- return self ._request_headers .getheaders ()
182
+ self ._stream = stream
274
183
275
184
def getresponse (self ):
276
185
"""
0 commit comments