87
87
from six .moves import urllib
88
88
89
89
from . import connections , errors , __version__
90
- from ._compat import bton , ntou
90
+ from ._compat import bton
91
91
from ._compat import IS_PPC
92
92
from .workers import threadpool
93
93
from .makefile import MakeFile , StreamWriter
94
94
95
95
__all__ = (
96
96
'HTTPRequest' , 'HTTPConnection' , 'HTTPServer' ,
97
- 'HeaderReader' , 'DropUnderscoreHeaderReader' ,
98
- 'SizeCheckWrapper' , 'KnownLengthRFile' , 'ChunkedRFile' ,
97
+ 'KnownLengthRFile' , 'ChunkedRFile' ,
99
98
'Gateway' , 'get_ssl_adapter_class' ,
100
99
)
101
100
166
165
logging .statistics = {}
167
166
168
167
169
- class HeaderReader :
170
- """Object for reading headers from an HTTP request.
171
-
172
- Interface and default implementation.
173
- """
174
-
175
- def __call__ (self , rfile , hdict = None ):
176
- """
177
- Read headers from the given stream into the given header dict.
178
-
179
- If hdict is None, a new header dict is created. Returns the populated
180
- header dict.
181
-
182
- Headers which are repeated are folded together using a comma if their
183
- specification so dictates.
184
-
185
- This function raises ValueError when the read bytes violate the HTTP
186
- spec.
187
- You should probably return "400 Bad Request" if this happens.
188
- """
189
- if hdict is None :
190
- hdict = {}
191
-
192
- while True :
193
- line = rfile .readline ()
194
- if not line :
195
- # No more data--illegal end of headers
196
- raise ValueError ('Illegal end of headers.' )
197
-
198
- if line == CRLF :
199
- # Normal end of headers
200
- break
201
- if not line .endswith (CRLF ):
202
- raise ValueError ('HTTP requires CRLF terminators' )
203
-
204
- if line [0 ] in (SPACE , TAB ):
205
- # It's a continuation line.
206
- v = line .strip ()
207
- else :
208
- try :
209
- k , v = line .split (COLON , 1 )
210
- except ValueError :
211
- raise ValueError ('Illegal header line.' )
212
- v = v .strip ()
213
- k = self ._transform_key (k )
214
- hname = k
215
-
216
- if not self ._allow_header (k ):
217
- continue
218
-
219
- if k in comma_separated_headers :
220
- existing = hdict .get (hname )
221
- if existing :
222
- v = b', ' .join ((existing , v ))
223
- hdict [hname ] = v
224
-
225
- return hdict
226
-
227
- def _allow_header (self , key_name ):
228
- return True
229
-
230
- def _transform_key (self , key_name ):
231
- # TODO: what about TE and WWW-Authenticate?
232
- return key_name .strip ().title ()
233
-
234
-
235
- class DropUnderscoreHeaderReader (HeaderReader ):
236
- """Custom HeaderReader to exclude any headers with underscores in them."""
237
-
238
- def _allow_header (self , key_name ):
239
- orig = super (DropUnderscoreHeaderReader , self )._allow_header (key_name )
240
- return orig and '_' not in key_name
241
-
242
-
243
- class SizeCheckWrapper :
244
- """Wraps a file-like object, raising MaxSizeExceeded if too large.
245
-
246
- :param rfile: ``file`` of a limited size
247
- :param int maxlen: maximum length of the file being read
248
- """
249
-
250
- def __init__ (self , rfile , maxlen ):
251
- """Initialize SizeCheckWrapper instance."""
252
- self .rfile = rfile
253
- self .maxlen = maxlen
254
- self .bytes_read = 0
255
-
256
- def _check_length (self ):
257
- if self .maxlen and self .bytes_read > self .maxlen :
258
- raise errors .MaxSizeExceeded ()
259
-
260
- def read (self , size = None ):
261
- """Read a chunk from ``rfile`` buffer and return it.
262
-
263
- :param int size: amount of data to read
264
-
265
- :returns: chunk from ``rfile``, limited by size if specified
266
- :rtype: bytes
267
- """
268
- data = self .rfile .read (size )
269
- self .bytes_read += len (data )
270
- self ._check_length ()
271
- return data
272
-
273
- def readline (self , size = None ):
274
- """Read a single line from ``rfile`` buffer and return it.
275
-
276
- :param int size: minimum amount of data to read
277
-
278
- :returns: one line from ``rfile``
279
- :rtype: bytes
280
- """
281
- if size is not None :
282
- data = self .rfile .readline (size )
283
- self .bytes_read += len (data )
284
- self ._check_length ()
285
- return data
286
-
287
- # User didn't specify a size ...
288
- # We read the line in chunks to make sure it's not a 100MB line !
289
- res = []
290
- while True :
291
- data = self .rfile .readline (256 )
292
- self .bytes_read += len (data )
293
- self ._check_length ()
294
- res .append (data )
295
- # See https://github.com/cherrypy/cherrypy/issues/421
296
- if len (data ) < 256 or data [- 1 :] == LF :
297
- return EMPTY .join (res )
298
-
299
- def readlines (self , sizehint = 0 ):
300
- """Read all lines from ``rfile`` buffer and return them.
301
-
302
- :param int sizehint: hint of minimum amount of data to read
303
-
304
- :returns: lines of bytes read from ``rfile``
305
- :rtype: list[bytes]
306
- """
307
- # Shamelessly stolen from StringIO
308
- total = 0
309
- lines = []
310
- line = self .readline (sizehint )
311
- while line :
312
- lines .append (line )
313
- total += len (line )
314
- if 0 < sizehint <= total :
315
- break
316
- line = self .readline (sizehint )
317
- return lines
318
-
319
- def close (self ):
320
- """Release resources allocated for ``rfile``."""
321
- self .rfile .close ()
322
-
323
- def __iter__ (self ):
324
- """Return file iterator."""
325
- return self
326
-
327
- def __next__ (self ):
328
- """Generate next file chunk."""
329
- data = next (self .rfile )
330
- self .bytes_read += len (data )
331
- self ._check_length ()
332
- return data
333
-
334
- next = __next__
335
-
336
-
337
168
class RFile (ABC ):
338
169
def __init__ (self , rfile , wfile , conn ):
339
170
self .rfile = rfile
@@ -358,7 +189,7 @@ def _handle_input(self, data):
358
189
if isinstance (evt , h11 .Data ):
359
190
return bytes (evt .data )
360
191
else :
361
- return b""
192
+ return b''
362
193
363
194
@abstractmethod
364
195
def read (self , size = None ):
@@ -704,11 +535,6 @@ class HTTPRequest:
704
535
705
536
This value is set automatically inside send_headers."""
706
537
707
- header_reader = HeaderReader ()
708
- """
709
- A HeaderReader instance or compatible reader.
710
- """
711
-
712
538
def __init__ (self , server , conn , proxy_mode = False , strict_mode = True ):
713
539
"""Initialize HTTP request container instance.
714
540
@@ -745,7 +571,7 @@ def __init__(self, server, conn, proxy_mode=False, strict_mode=True):
745
571
self .strict_mode = strict_mode
746
572
747
573
def _process_next_h11_event (self , read_new = True ):
748
- """Instruct h11 to process data in its buffer and return any events it has available
574
+ """Instruct h11 to process data in its buffer and return any events it has available.
749
575
750
576
:param read_new: If the method should attempt to read new lines to find an event
751
577
:type read_new: bool
@@ -771,20 +597,22 @@ def parse_request(self):
771
597
if isinstance (req_line , h11 .Request ):
772
598
self .started_request = True
773
599
self .uri = req_line .target
774
- scheme , authority , path , query , fragment = urllib .parse .urlsplit (self .uri )
775
- self .qs = query
600
+ scheme , authority , path , qs , fragment = urllib .parse .urlsplit (self .uri ) # noqa
601
+ self .qs = qs
776
602
self .method = req_line .method
777
- self .request_protocol = b" HTTP/%s" % req_line .http_version
603
+ self .request_protocol = b' HTTP/%s' % req_line .http_version
778
604
if req_line .http_version > b'1.1' :
779
- self .simple_response (505 , " Cannot fulfill request" )
780
- self .response_protocol = b" HTTP/1.1"
605
+ self .simple_response (505 , ' Cannot fulfill request' )
606
+ self .response_protocol = b' HTTP/1.1'
781
607
# TODO: oneliner-ify this
782
608
self .inheaders = {}
783
609
for header in req_line .headers :
784
610
self .inheaders [header [0 ]] = header [1 ]
785
611
786
- if (b'transfer-encoding' in self .inheaders and
787
- self .inheaders [b'transfer-encoding' ].lower () == b"chunked" ):
612
+ if (
613
+ b'transfer-encoding' in self .inheaders and
614
+ self .inheaders [b'transfer-encoding' ].lower () == b'chunked'
615
+ ):
788
616
self .chunked_read = True
789
617
790
618
uri_is_absolute_form = scheme or authority
@@ -803,8 +631,9 @@ def parse_request(self):
803
631
self .simple_response (405 )
804
632
return False
805
633
806
- # `urlsplit()` above parses "example.com:3128" as path part of URI.
807
- # this is a workaround, which makes it detect netloc correctly
634
+ # `urlsplit()` above parses "example.com:3128" as path part
635
+ # of URI. This is a workaround, which makes it detect
636
+ # netloc correctly
808
637
uri_split = urllib .parse .urlsplit (b'' .join ((b'//' , self .uri )))
809
638
_scheme , _authority , _path , _qs , _fragment = uri_split
810
639
_port = EMPTY
@@ -844,7 +673,8 @@ def parse_request(self):
844
673
"""Absolute URI is only allowed within proxies."""
845
674
self .simple_response (
846
675
400 ,
847
- 'Absolute URI not allowed if server is not a proxy.' ,
676
+ 'Absolute URI not allowed'
677
+ ' if server is not a proxy.' ,
848
678
)
849
679
return False
850
680
@@ -876,7 +706,10 @@ def parse_request(self):
876
706
return False
877
707
path = QUOTED_SLASH .join (atoms )
878
708
if fragment :
879
- self .simple_response (400 , "Illegal #fragment in Request-URI." )
709
+ self .simple_response (
710
+ 400 ,
711
+ 'Illegal #fragment in Request-URI.' ,
712
+ )
880
713
881
714
if not path .startswith (FORWARD_SLASH ):
882
715
path = FORWARD_SLASH + path
@@ -887,10 +720,8 @@ def parse_request(self):
887
720
self .close_connection = True
888
721
else :
889
722
# TODO
890
- raise NotImplementedError (" Only expecting Request object here" )
723
+ raise NotImplementedError (' Only expecting Request object here' )
891
724
except h11 .RemoteProtocolError as e :
892
- # NEED INFO: Should we adjust the tests to match h11's exception text
893
- # Or should we continue to shim like this:
894
725
err_map = {
895
726
'bad Content-Length' : 'Malformed Content-Length Header.' ,
896
727
'illegal request line' : 'Malformed Request-Line.' ,
@@ -904,7 +735,10 @@ def respond(self):
904
735
"""Call the gateway and write its iterable output."""
905
736
mrbs = self .server .max_request_body_size
906
737
if self .chunked_read :
907
- self .rfile = ChunkedRFile (self .h_conn , self .conn .rfile , self .conn .wfile , mrbs )
738
+ self .rfile = ChunkedRFile (
739
+ self .h_conn ,
740
+ self .conn .rfile , self .conn .wfile , mrbs ,
741
+ )
908
742
else :
909
743
cl = int (self .inheaders .get (b'content-length' , 0 ))
910
744
if mrbs and mrbs < cl :
@@ -915,29 +749,36 @@ def respond(self):
915
749
'maximum allowed bytes.' ,
916
750
)
917
751
return
918
- self .rfile = KnownLengthRFile (self .h_conn , self .conn .rfile , self .conn .wfile , cl )
752
+ self .rfile = KnownLengthRFile (
753
+ self .h_conn ,
754
+ self .conn .rfile , self .conn .wfile , cl ,
755
+ )
919
756
920
- # client may still be in send body, lets find out and figure out what to do with that
921
- # if self.h_conn.their_state == h11.SEND_BODY:
922
757
if self .h_conn .client_is_waiting_for_100_continue :
923
758
mini_headers = ()
924
- go_ahead = h11 .InformationalResponse (status_code = 100 , headers = mini_headers )
759
+ go_ahead = h11 .InformationalResponse (
760
+ status_code = 100 ,
761
+ headers = mini_headers ,
762
+ )
925
763
bytes_out = self .h_conn .send (go_ahead )
926
764
self .conn .wfile .write (bytes_out )
927
765
try :
928
766
self .server .gateway (self ).respond ()
929
767
self .ready and self .ensure_headers_sent ()
930
768
except errors .MaxSizeExceeded as e :
931
- self .simple_response (413 , " Request Entity Too Large" )
769
+ self .simple_response (413 , ' Request Entity Too Large' )
932
770
self .close_connection = True
933
771
return
934
772
935
- while self .h_conn .their_state is h11 .SEND_BODY and self .h_conn .our_state is not h11 .ERROR :
936
- # empty their buffer ?
773
+ while (
774
+ self .h_conn .their_state is h11 .SEND_BODY
775
+ and self .h_conn .our_state is not h11 .ERROR
776
+ ):
937
777
data = self .rfile .read ()
938
778
self ._process_next_h11_event (read_new = False )
939
779
if data == EMPTY and self .h_conn .their_state is h11 .SEND_BODY :
940
- # they didn't send a full body, kill connection, set our state to ERROR
780
+ # they didn't send a full body, kill connection,
781
+ # set our state to ERROR
941
782
self .h_conn .send_failed ()
942
783
943
784
# If we haven't sent our end-of-message data, send it now
@@ -946,7 +787,10 @@ def respond(self):
946
787
self .conn .wfile .write (bytes_out )
947
788
948
789
# prep for next req cycle if it's available
949
- if self .h_conn .our_state is h11 .DONE and self .h_conn .their_state is h11 .DONE :
790
+ if (
791
+ self .h_conn .our_state is h11 .DONE
792
+ and self .h_conn .their_state is h11 .DONE
793
+ ):
950
794
self .h_conn .start_next_cycle ()
951
795
self .close_connection = False
952
796
else :
@@ -959,14 +803,14 @@ def simple_response(self, status, msg=None):
959
803
msg = ''
960
804
status = str (status )
961
805
headers = [
962
- ('Content-Type' , 'text/plain' )
806
+ ('Content-Type' , 'text/plain' ),
963
807
]
964
808
965
809
self .outheaders = headers
966
810
self .status = status
967
811
self .send_headers ()
968
812
if msg :
969
- self .write (bytes (msg , encoding = " ascii" ))
813
+ self .write (bytes (msg , encoding = ' ascii' ))
970
814
971
815
evt = h11 .EndOfMessage ()
972
816
bytes_out = self .h_conn .send (evt )
@@ -1005,8 +849,10 @@ def send_headers(self):
1005
849
self .server .server_name .encode ('ISO-8859-1' ),
1006
850
))
1007
851
1008
- res = h11 .Response (status_code = status , headers = self .outheaders ,
1009
- http_version = self .response_protocol [5 :], reason = self .status [3 :])
852
+ res = h11 .Response (
853
+ status_code = status , headers = self .outheaders ,
854
+ http_version = self .response_protocol [5 :], reason = self .status [3 :],
855
+ )
1010
856
res_bytes = self .h_conn .send (res )
1011
857
self .conn .wfile .write (res_bytes )
1012
858
0 commit comments