FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[Original HTTPS Page]
cpython/Lib/http/client.py at 3.15 · python/cpython · GitHub
python
cpython
Repository navigation
Code
Issues
5k+
(5k+)
Pull requests
2.6k
(2.6k)
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
cpython
/
Lib
/
http
/
client.py
Copy path
More file actions
More file actions
Latest commit
History
History
History
1614 lines (1364 loc) · 59.1 KB
Breadcrumbs
cpython
/
Lib
/
http
/
client.py
Copy path
File metadata and controls
1614 lines (1364 loc) · 59.1 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
r"""HTTP/1.1 client library
<intro stuff goes here>
<other stuff, too>
HTTPConnection goes through a number of "states", which define when a client
may legally make another request or fetch the response for a particular
request. This diagram details these state transitions:
(null)
|
| HTTPConnection()
v
Idle
|
| putrequest()
v
Request-started
|
| ( putheader() )* endheaders()
v
Request-sent
|\_____________________________
| | getresponse() raises
| response = getresponse() | ConnectionError
v v
Unread-response Idle
[Response-headers-read]
|\____________________
| |
| response.read() | putrequest()
v v
Idle Req-started-unread-response
______/|
/ |
response.read() | | ( putheader() )* endheaders()
v v
Request-started Req-sent-unread-response
|
| response.read()
v
Request-sent
This diagram presents the following rules:
-- a second request may not be started until {response-headers-read}
-- a response [object] cannot be retrieved until {request-sent}
-- there is no differentiation between an unread response body and a
partially read response body
Note: this enforcement is applied by the HTTPConnection class. The
HTTPResponse class does not enforce this state machine, which
implies sophisticated clients may accelerate the request/response
pipeline. Caution should be taken, though: accelerating the states
beyond the above pattern may imply knowledge of the server's
connection-close behavior for certain requests. For example, it
is impossible to tell whether the server will close the connection
UNTIL the response headers have been read; this means that further
requests cannot be placed into the pipeline until it is known that
the server will NOT be closing the connection.
Logical State __state __response
------------- ------- ----------
Idle _CS_IDLE None
Request-started _CS_REQ_STARTED None
Request-sent _CS_REQ_SENT None
Unread-response _CS_IDLE <response_class>
Req-started-unread-response _CS_REQ_STARTED <response_class>
Req-sent-unread-response _CS_REQ_SENT <response_class>
"""
import
email
.
parser
import
email
.
message
import
errno
import
http
import
io
import
re
import
socket
import
sys
import
collections
.
abc
from
urllib
.
parse
import
urlsplit
# HTTPMessage, parse_headers(), and the HTTP status code constants are
# intentionally omitted for simplicity
__all__
=
[
"HTTPResponse"
,
"HTTPConnection"
,
"HTTPException"
,
"NotConnected"
,
"UnknownProtocol"
,
"UnknownTransferEncoding"
,
"UnimplementedFileMode"
,
"IncompleteRead"
,
"InvalidURL"
,
"ImproperConnectionState"
,
"CannotSendRequest"
,
"CannotSendHeader"
,
"ResponseNotReady"
,
"BadStatusLine"
,
"LineTooLong"
,
"RemoteDisconnected"
,
"error"
,
"responses"
]
HTTP_PORT
=
80
HTTPS_PORT
=
443
_UNKNOWN
=
'UNKNOWN'
# connection states
_CS_IDLE
=
'Idle'
_CS_REQ_STARTED
=
'Request-started'
_CS_REQ_SENT
=
'Request-sent'
# hack to maintain backwards compatibility
globals
().
update
(
http
.
HTTPStatus
.
__members__
)
# another hack to maintain backwards compatibility
# Mapping status codes to official W3C names
responses
=
{
v
:
v
.
phrase
for
v
in
http
.
HTTPStatus
.
__members__
.
values
()}
# maximal line length when calling readline().
_MAXLINE
=
65536
_MAXHEADERS
=
100
# maximal number of interim (1xx) responses tolerated before the final
# response. Real servers send at most a few; without a bound, a server
# streaming "100 Continue" responses would hang getresponse() forever.
# A socket timeout cannot detect that as data keeps arriving within every
# timeout window.
_MAXINTERIMRESPONSES
=
100
# Data larger than this will be read in chunks, to prevent extreme
# overallocation.
_MIN_READ_BUF_SIZE
=
1
<<
20
# Header name/value ABNF (http://tools.ietf.org/html/rfc7230#section-3.2)
#
# VCHAR = %x21-7E
# obs-text = %x80-FF
# header-field = field-name ":" OWS field-value OWS
# field-name = token
# field-value = *( field-content / obs-fold )
# field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
# field-vchar = VCHAR / obs-text
#
# obs-fold = CRLF 1*( SP / HTAB )
# ; obsolete line folding
# ; see Section 3.2.4
# token = 1*tchar
#
# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
# / DIGIT / ALPHA
# ; any VCHAR, except delimiters
#
# VCHAR defined in http://tools.ietf.org/html/rfc5234#appendix-B.1
# the patterns for both name and value are more lenient than RFC
# definitions to allow for backwards compatibility
_is_legal_header_name
=
re
.
compile
(
rb'[^:\s][^:\r\n]*'
).
fullmatch
_is_illegal_header_value
=
re
.
compile
(
rb'\n(?![ \t])|\r(?![ \t\n])'
).
search
# These characters are not allowed within HTTP URL paths.
# See https://tools.ietf.org/html/rfc3986#section-3.3 and the
# https://tools.ietf.org/html/rfc3986#appendix-A pchar definition.
# Prevents CVE-2019-9740. Includes control characters such as \r\n.
# We don't restrict chars above \x7f as putrequest() limits us to ASCII.
_contains_disallowed_url_pchar_re
=
re
.
compile
(
'[
\x00
-
\x20
\x7f
]'
)
# Arguably only these _should_ allowed:
# _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$")
# We are more lenient for assumed real world compatibility purposes.
# These characters are not allowed within HTTP method names
# to prevent http header injection.
_contains_disallowed_method_pchar_re
=
re
.
compile
(
'[
\x00
-
\x1f
]'
)
# We always set the Content-Length header for these methods because some
# servers will otherwise respond with a 411
_METHODS_EXPECTING_BODY
=
{
'PATCH'
,
'POST'
,
'PUT'
}
def
_encode
(
data
,
name
=
'data'
):
"""Call data.encode("latin-1") but show a better error message."""
try
:
return
data
.
encode
(
"latin-1"
)
except
UnicodeEncodeError
as
err
:
raise
UnicodeEncodeError
(
err
.
encoding
,
err
.
object
,
err
.
start
,
err
.
end
,
"%s (%.20r) is not valid Latin-1. Use %s.encode('utf-8') "
"if you want to send it encoded in UTF-8."
%
(
name
.
title
(),
data
[
err
.
start
:
err
.
end
],
name
))
from
None
def
_strip_ipv6_iface
(
enc_name
:
bytes
)
->
bytes
:
"""Remove interface scope from IPv6 address."""
enc_name
,
percent
,
_
=
enc_name
.
partition
(
b"%"
)
if
percent
:
assert
enc_name
.
startswith
(
b'['
),
enc_name
enc_name
+=
b']'
return
enc_name
class
HTTPMessage
(
email
.
message
.
Message
):
# The getallmatchingheaders() method was only used by the CGI handler
# that was removed in Python 3.15. However, since the public API was not
# properly defined, it will be kept for backwards compatibility reasons.
def
getallmatchingheaders
(
self
,
name
):
"""Find all header lines matching a given header name.
Look through the list of headers and find all lines matching a given
header name (and their continuation lines). A list of the lines is
returned, without interpretation. If the header does not occur, an
empty list is returned. If the header occurs multiple times, all
occurrences are returned. Case is not important in the header name.
"""
name
=
name
.
lower
()
+
':'
n
=
len
(
name
)
lst
=
[]
hit
=
0
for
line
in
self
.
keys
():
if
line
[:
n
].
lower
()
==
name
:
hit
=
1
elif
not
line
[:
1
].
isspace
():
hit
=
0
if
hit
:
lst
.
append
(
line
)
return
lst
def
_read_headers
(
fp
,
max_headers
):
"""Reads potential header lines into a list from a file pointer.
Length of line is limited by _MAXLINE, and number of
headers is limited by max_headers.
"""
headers
=
[]
if
max_headers
is
None
:
max_headers
=
_MAXHEADERS
while
True
:
line
=
fp
.
readline
(
_MAXLINE
+
1
)
if
len
(
line
)
>
_MAXLINE
:
raise
LineTooLong
(
"header line"
)
if
line
in
(
b'
\r
\n
'
,
b'
\n
'
,
b''
):
break
headers
.
append
(
line
)
if
len
(
headers
)
>
max_headers
:
raise
HTTPException
(
f"got more than
{
max_headers
}
headers"
)
return
headers
def
_parse_header_lines
(
header_lines
,
_class
=
HTTPMessage
):
"""
Parses only RFC 5322 headers from header lines.
email Parser wants to see strings rather than bytes.
But a TextIOWrapper around self.rfile would buffer too many bytes
from the stream, bytes which we later need to read as bytes.
So we read the correct bytes here, as bytes, for email Parser
to parse.
"""
hstring
=
b''
.
join
(
header_lines
).
decode
(
'iso-8859-1'
)
return
email
.
parser
.
Parser
(
_class
=
_class
).
parsestr
(
hstring
)
def
parse_headers
(
fp
,
_class
=
HTTPMessage
,
*
,
_max_headers
=
None
):
"""Parses only RFC 5322 headers from a file pointer."""
headers
=
_read_headers
(
fp
,
_max_headers
)
return
_parse_header_lines
(
headers
,
_class
)
class
HTTPResponse
(
io
.
BufferedIOBase
):
# See RFC 2616 sec 19.6 and RFC 1945 sec 6 for details.
# The bytes from the socket object are iso-8859-1 strings.
# See RFC 2616 sec 2.2 which notes an exception for MIME-encoded
# text following RFC 2047. The basic status line parsing only
# accepts iso-8859-1.
def
__init__
(
self
,
sock
,
debuglevel
=
0
,
method
=
None
,
url
=
None
):
# If the response includes a content-length header, we need to
# make sure that the client doesn't read more than the
# specified number of bytes. If it does, it will block until
# the server times out and closes the connection. This will
# happen if a self.fp.read() is done (without a size) whether
# self.fp is buffered or not. So, no self.fp.read() by
# clients unless they know what they are doing.
self
.
fp
=
sock
.
makefile
(
"rb"
)
self
.
debuglevel
=
debuglevel
self
.
_method
=
method
# The HTTPResponse object is returned via urllib. The clients
# of http and urllib expect different attributes for the
# headers. headers is used here and supports urllib. msg is
# provided as a backwards compatibility layer for http
# clients.
self
.
headers
=
self
.
msg
=
None
# from the Status-Line of the response
self
.
version
=
_UNKNOWN
# HTTP-Version
self
.
status
=
_UNKNOWN
# Status-Code
self
.
reason
=
_UNKNOWN
# Reason-Phrase
self
.
chunked
=
_UNKNOWN
# is "chunked" being used?
self
.
chunk_left
=
_UNKNOWN
# bytes left to read in current chunk
self
.
length
=
_UNKNOWN
# number of bytes left in response
self
.
will_close
=
_UNKNOWN
# conn will close at end of response
self
.
_max_headers
=
None
# configured header count limit
def
_read_status
(
self
):
line
=
str
(
self
.
fp
.
readline
(
_MAXLINE
+
1
),
"iso-8859-1"
)
if
len
(
line
)
>
_MAXLINE
:
raise
LineTooLong
(
"status line"
)
if
self
.
debuglevel
>
0
:
print
(
"reply:"
,
repr
(
line
))
if
not
line
:
# Presumably, the server closed the connection before
# sending a valid response.
raise
RemoteDisconnected
(
"Remote end closed connection without"
" response"
)
try
:
version
,
status
,
reason
=
line
.
split
(
None
,
2
)
except
ValueError
:
try
:
version
,
status
=
line
.
split
(
None
,
1
)
reason
=
""
except
ValueError
:
# empty version will cause next test to fail.
version
=
""
if
not
version
.
startswith
(
"HTTP/"
):
self
.
_close_conn
()
raise
BadStatusLine
(
line
)
# The status code is a three-digit number
try
:
status
=
int
(
status
)
if
status
<
100
or
status
>
999
:
raise
BadStatusLine
(
line
)
except
ValueError
:
raise
BadStatusLine
(
line
)
return
version
,
status
,
reason
def
begin
(
self
,
*
,
_max_headers
=
None
):
if
self
.
headers
is
not
None
:
# we've already started reading the response
return
# Trailers of a chunked response are read by read() long after
# begin() returns, so remember the configured header count limit
# for _read_and_discard_trailer() to enforce.
self
.
_max_headers
=
_max_headers
# read until we get a non-100 response
for
_
in
range
(
_MAXINTERIMRESPONSES
):
version
,
status
,
reason
=
self
.
_read_status
()
if
status
!=
CONTINUE
:
break
# skip the header from the 100 response
skipped_headers
=
_read_headers
(
self
.
fp
,
_max_headers
)
if
self
.
debuglevel
>
0
:
print
(
"headers:"
,
skipped_headers
)
del
skipped_headers
else
:
raise
HTTPException
(
f"got more than
{
_MAXINTERIMRESPONSES
}
interim responses"
)
self
.
code
=
self
.
status
=
status
self
.
reason
=
reason
.
strip
()
if
version
in
(
"HTTP/1.0"
,
"HTTP/0.9"
):
# Some servers might still return "0.9", treat it as 1.0 anyway
self
.
version
=
10
elif
version
.
startswith
(
"HTTP/1."
):
self
.
version
=
11
# use HTTP/1.1 code for HTTP/1.x where x>=1
else
:
raise
UnknownProtocol
(
version
)
self
.
headers
=
self
.
msg
=
parse_headers
(
self
.
fp
,
_max_headers
=
_max_headers
)
if
self
.
debuglevel
>
0
:
for
hdr
,
val
in
self
.
headers
.
items
():
print
(
"header:"
,
hdr
+
":"
,
val
)
# are we using the chunked-style of transfer encoding?
tr_enc
=
self
.
headers
.
get
(
"transfer-encoding"
)
if
tr_enc
and
tr_enc
.
lower
()
==
"chunked"
:
self
.
chunked
=
True
self
.
chunk_left
=
None
else
:
self
.
chunked
=
False
# will the connection close at the end of the response?
self
.
will_close
=
self
.
_check_close
()
# do we have a Content-Length?
# NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
self
.
length
=
None
length
=
self
.
headers
.
get
(
"content-length"
)
if
length
and
not
self
.
chunked
:
try
:
self
.
length
=
int
(
length
)
except
ValueError
:
self
.
length
=
None
else
:
if
self
.
length
<
0
:
# ignore nonsensical negative lengths
self
.
length
=
None
else
:
self
.
length
=
None
# does the body have a fixed length? (of zero)
if
(
status
==
NO_CONTENT
or
status
==
NOT_MODIFIED
or
100
<=
status
<
200
or
# 1xx codes
self
.
_method
==
"HEAD"
):
self
.
length
=
0
# if the connection remains open, and we aren't using chunked, and
# a content-length was not provided, then assume that the connection
# WILL close.
if
(
not
self
.
will_close
and
not
self
.
chunked
and
self
.
length
is
None
):
self
.
will_close
=
True
def
_check_close
(
self
):
conn
=
self
.
headers
.
get
(
"connection"
)
if
self
.
version
==
11
:
# An HTTP/1.1 proxy is assumed to stay open unless
# explicitly closed.
if
conn
and
"close"
in
conn
.
lower
():
return
True
return
False
# Some HTTP/1.0 implementations have support for persistent
# connections, using rules different than HTTP/1.1.
# For older HTTP, Keep-Alive indicates persistent connection.
if
self
.
headers
.
get
(
"keep-alive"
):
return
False
# At least Akamai returns a "Connection: Keep-Alive" header,
# which was supposed to be sent by the client.
if
conn
and
"keep-alive"
in
conn
.
lower
():
return
False
# Proxy-Connection is a netscape hack.
pconn
=
self
.
headers
.
get
(
"proxy-connection"
)
if
pconn
and
"keep-alive"
in
pconn
.
lower
():
return
False
# otherwise, assume it will close
return
True
def
_close_conn
(
self
):
fp
=
self
.
fp
self
.
fp
=
None
fp
.
close
()
def
close
(
self
):
try
:
super
().
close
()
# set "closed" flag
finally
:
if
self
.
fp
:
self
.
_close_conn
()
# These implementations are for the benefit of io.BufferedReader.
# XXX This class should probably be revised to act more like
# the "raw stream" that BufferedReader expects.
def
flush
(
self
):
super
().
flush
()
if
self
.
fp
:
self
.
fp
.
flush
()
def
readable
(
self
):
"""Always returns True"""
return
True
# End of "raw stream" methods
def
isclosed
(
self
):
"""True if the connection is closed."""
# NOTE: it is possible that we will not ever call self.close(). This
# case occurs when will_close is TRUE, length is None, and we
# read up to the last byte, but NOT past it.
#
# IMPLIES: if will_close is FALSE, then self.close() will ALWAYS be
# called, meaning self.isclosed() is meaningful.
return
self
.
fp
is
None
def
read
(
self
,
amt
=
None
):
"""Read and return the response body, or up to the next amt bytes."""
if
self
.
fp
is
None
:
return
b""
if
self
.
_method
==
"HEAD"
:
self
.
_close_conn
()
return
b""
if
self
.
chunked
:
return
self
.
_read_chunked
(
amt
)
if
amt
is
not
None
and
amt
>=
0
:
if
self
.
length
is
not
None
and
amt
>
self
.
length
:
# clip the read to the "end of response"
amt
=
self
.
length
s
=
self
.
fp
.
read
(
amt
)
if
not
s
and
amt
:
# Ideally, we would raise IncompleteRead if the content-length
# wasn't satisfied, but it might break compatibility.
self
.
_close_conn
()
elif
self
.
length
is
not
None
:
self
.
length
-=
len
(
s
)
if
not
self
.
length
:
self
.
_close_conn
()
return
s
else
:
# Amount is not given (unbounded read) so we must check self.length
if
self
.
length
is
None
:
s
=
self
.
fp
.
read
()
else
:
try
:
s
=
self
.
_safe_read
(
self
.
length
)
except
IncompleteRead
:
self
.
_close_conn
()
raise
self
.
length
=
0
self
.
_close_conn
()
# we read everything
return
s
def
readinto
(
self
,
b
):
"""Read up to len(b) bytes into bytearray b and return the number
of bytes read.
"""
if
self
.
fp
is
None
:
return
0
if
self
.
_method
==
"HEAD"
:
self
.
_close_conn
()
return
0
if
self
.
chunked
:
return
self
.
_readinto_chunked
(
b
)
if
self
.
length
is
not
None
:
if
len
(
b
)
>
self
.
length
:
# clip the read to the "end of response"
b
=
memoryview
(
b
)[
0
:
self
.
length
]
# we do not use _safe_read() here because this may be a .will_close
# connection, and the user is reading more bytes than will be provided
# (for example, reading in 1k chunks)
n
=
self
.
fp
.
readinto
(
b
)
if
not
n
and
b
:
# Ideally, we would raise IncompleteRead if the content-length
# wasn't satisfied, but it might break compatibility.
self
.
_close_conn
()
elif
self
.
length
is
not
None
:
self
.
length
-=
n
if
not
self
.
length
:
self
.
_close_conn
()
return
n
def
_read_next_chunk_size
(
self
):
# Read the next chunk size from the file
line
=
self
.
fp
.
readline
(
_MAXLINE
+
1
)
if
len
(
line
)
>
_MAXLINE
:
raise
LineTooLong
(
"chunk size"
)
i
=
line
.
find
(
b";"
)
if
i
>=
0
:
line
=
line
[:
i
]
# strip chunk-extensions
try
:
return
int
(
line
,
16
)
except
ValueError
:
# close the connection as protocol synchronisation is
# probably lost
self
.
_close_conn
()
raise
def
_read_and_discard_trailer
(
self
):
# read and discard trailer up to the CRLF terminator
### note: we shouldn't have any trailers!
max_trailers
=
self
.
_max_headers
if
max_trailers
is
None
:
max_trailers
=
_MAXHEADERS
trailers_read
=
0
while
True
:
line
=
self
.
fp
.
readline
(
_MAXLINE
+
1
)
if
len
(
line
)
>
_MAXLINE
:
raise
LineTooLong
(
"trailer line"
)
if
not
line
:
# a vanishingly small number of sites EOF without
# sending the trailer
break
if
line
in
(
b'
\r
\n
'
,
b'
\n
'
,
b''
):
break
# Bound the trailer count just as response headers are bounded.
# A server streaming trailer lines forever would otherwise hang
# the client; a socket timeout cannot detect that as data keeps
# arriving within every timeout window.
trailers_read
+=
1
if
trailers_read
>
max_trailers
:
raise
HTTPException
(
f"got more than
{
max_trailers
}
trailers"
)
def
_get_chunk_left
(
self
):
# return self.chunk_left, reading a new chunk if necessary.
# chunk_left == 0: at the end of the current chunk, need to close it
# chunk_left == None: No current chunk, should read next.
# This function returns non-zero or None if the last chunk has
# been read.
chunk_left
=
self
.
chunk_left
if
not
chunk_left
:
# Can be 0 or None
if
chunk_left
is
not
None
:
# We are at the end of chunk, discard chunk end
self
.
_safe_read
(
2
)
# toss the CRLF at the end of the chunk
try
:
chunk_left
=
self
.
_read_next_chunk_size
()
except
ValueError
:
raise
IncompleteRead
(
b''
)
if
chunk_left
==
0
:
# last chunk: 1*("0") [ chunk-extension ] CRLF
self
.
_read_and_discard_trailer
()
# we read everything; close the "file"
self
.
_close_conn
()
chunk_left
=
None
self
.
chunk_left
=
chunk_left
return
chunk_left
def
_read_chunked
(
self
,
amt
=
None
):
assert
self
.
chunked
!=
_UNKNOWN
if
amt
is
not
None
and
amt
<
0
:
amt
=
None
value
=
[]
try
:
while
(
chunk_left
:=
self
.
_get_chunk_left
())
is
not
None
:
if
amt
is
not
None
and
amt
<=
chunk_left
:
value
.
append
(
self
.
_safe_read
(
amt
))
self
.
chunk_left
=
chunk_left
-
amt
break
value
.
append
(
self
.
_safe_read
(
chunk_left
))
if
amt
is
not
None
:
amt
-=
chunk_left
self
.
chunk_left
=
0
return
b''
.
join
(
value
)
except
IncompleteRead
as
exc
:
raise
IncompleteRead
(
b''
.
join
(
value
))
from
exc
def
_readinto_chunked
(
self
,
b
):
assert
self
.
chunked
!=
_UNKNOWN
total_bytes
=
0
mvb
=
memoryview
(
b
)
try
:
while
True
:
chunk_left
=
self
.
_get_chunk_left
()
if
chunk_left
is
None
:
return
total_bytes
if
len
(
mvb
)
<=
chunk_left
:
n
=
self
.
_safe_readinto
(
mvb
)
self
.
chunk_left
=
chunk_left
-
n
return
total_bytes
+
n
temp_mvb
=
mvb
[:
chunk_left
]
n
=
self
.
_safe_readinto
(
temp_mvb
)
mvb
=
mvb
[
n
:]
total_bytes
+=
n
self
.
chunk_left
=
0
except
IncompleteRead
:
raise
IncompleteRead
(
bytes
(
b
[
0
:
total_bytes
]))
def
_safe_read
(
self
,
amt
):
"""Read the number of bytes requested.
This function should be used when <amt> bytes "should" be present for
reading. If the bytes are truly not available (due to EOF), then the
IncompleteRead exception can be used to detect the problem.
"""
cursize
=
min
(
amt
,
_MIN_READ_BUF_SIZE
)
data
=
self
.
fp
.
read
(
cursize
)
if
len
(
data
)
>=
amt
:
return
data
if
len
(
data
)
<
cursize
:
raise
IncompleteRead
(
data
,
amt
-
len
(
data
))
data
=
io
.
BytesIO
(
data
)
data
.
seek
(
0
,
2
)
while
True
:
# This is a geometric increase in read size (never more than
# doubling out the current length of data per loop iteration).
delta
=
min
(
cursize
,
amt
-
cursize
)
data
.
write
(
self
.
fp
.
read
(
delta
))
if
data
.
tell
()
>=
amt
:
return
data
.
getvalue
()
cursize
+=
delta
if
data
.
tell
()
<
cursize
:
raise
IncompleteRead
(
data
.
getvalue
(),
amt
-
data
.
tell
())
def
_safe_readinto
(
self
,
b
):
"""Same as _safe_read, but for reading into a buffer."""
amt
=
len
(
b
)
n
=
self
.
fp
.
readinto
(
b
)
if
n
<
amt
:
raise
IncompleteRead
(
bytes
(
b
[:
n
]),
amt
-
n
)
return
n
def
read1
(
self
,
n
=
-
1
):
"""Read with at most one underlying system call. If at least one
byte is buffered, return that instead.
"""
if
self
.
fp
is
None
or
self
.
_method
==
"HEAD"
:
return
b""
if
self
.
chunked
:
return
self
.
_read1_chunked
(
n
)
if
self
.
length
is
not
None
and
(
n
<
0
or
n
>
self
.
length
):
n
=
self
.
length
result
=
self
.
fp
.
read1
(
n
)
if
not
result
and
n
:
self
.
_close_conn
()
elif
self
.
length
is
not
None
:
self
.
length
-=
len
(
result
)
if
not
self
.
length
:
self
.
_close_conn
()
return
result
def
peek
(
self
,
n
=
-
1
):
# Having this enables IOBase.readline() to read more than one
# byte at a time
if
self
.
fp
is
None
or
self
.
_method
==
"HEAD"
:
return
b""
if
self
.
chunked
:
return
self
.
_peek_chunked
(
n
)
return
self
.
fp
.
peek
(
n
)
def
readline
(
self
,
limit
=
-
1
):
if
self
.
fp
is
None
or
self
.
_method
==
"HEAD"
:
return
b""
if
self
.
chunked
:
# Fallback to IOBase readline which uses peek() and read()
return
super
().
readline
(
limit
)
if
self
.
length
is
not
None
and
(
limit
<
0
or
limit
>
self
.
length
):
limit
=
self
.
length
result
=
self
.
fp
.
readline
(
limit
)
if
not
result
and
limit
:
self
.
_close_conn
()
elif
self
.
length
is
not
None
:
self
.
length
-=
len
(
result
)
if
not
self
.
length
:
self
.
_close_conn
()
return
result
def
_read1_chunked
(
self
,
n
):
# Strictly speaking, _get_chunk_left() may cause more than one read,
# but that is ok, since that is to satisfy the chunked protocol.
chunk_left
=
self
.
_get_chunk_left
()
if
chunk_left
is
None
or
n
==
0
:
return
b''
if
not
(
0
<=
n
<=
chunk_left
):
n
=
chunk_left
# if n is negative or larger than chunk_left
read
=
self
.
fp
.
read1
(
n
)
self
.
chunk_left
-=
len
(
read
)
if
not
read
:
raise
IncompleteRead
(
b""
)
return
read
def
_peek_chunked
(
self
,
n
):
# Strictly speaking, _get_chunk_left() may cause more than one read,
# but that is ok, since that is to satisfy the chunked protocol.
try
:
chunk_left
=
self
.
_get_chunk_left
()
except
IncompleteRead
:
return
b''
# peek doesn't worry about protocol
if
chunk_left
is
None
:
return
b''
# eof
# peek is allowed to return more than requested. Just request the
# entire chunk, and truncate what we get.
return
self
.
fp
.
peek
(
chunk_left
)[:
chunk_left
]
def
fileno
(
self
):
return
self
.
fp
.
fileno
()
def
getheader
(
self
,
name
,
default
=
None
):
'''Returns the value of the header matching *name*.
If there are multiple matching headers, the values are
combined into a single string separated by commas and spaces.
If no matching header is found, returns *default* or None if
the *default* is not specified.
If the headers are unknown, raises http.client.ResponseNotReady.
'''
if
self
.
headers
is
None
:
raise
ResponseNotReady
()
headers
=
self
.
headers
.
get_all
(
name
)
or
default
if
isinstance
(
headers
,
str
)
or
not
hasattr
(
headers
,
'__iter__'
):
return
headers
else
:
return
', '
.
join
(
headers
)
def
getheaders
(
self
):
"""Return list of (header, value) tuples."""
if
self
.
headers
is
None
:
raise
ResponseNotReady
()
return
list
(
self
.
headers
.
items
())
# We override IOBase.__iter__ so that it doesn't check for closed-ness
def
__iter__
(
self
):
return
self
# For compatibility with old-style urllib responses.
def
info
(
self
):
'''Returns an instance of the class mimetools.Message containing
meta-information associated with the URL.
When the method is HTTP, these headers are those returned by
the server at the head of the retrieved HTML page (including
Content-Length and Content-Type).
When the method is FTP, a Content-Length header will be
present if (as is now usual) the server passed back a file
length in response to the FTP retrieval request. A
Content-Type header will be present if the MIME type can be
guessed.
When the method is local-file, returned headers will include
a Date representing the file's last-modified time, a
Content-Length giving file size, and a Content-Type
containing a guess at the file's type. See also the
description of the mimetools module.
'''
return
self
.
headers
def
geturl
(
self
):
'''Return the real URL of the page.
In some cases, the HTTP server redirects a client to another
URL. The urlopen() function handles this transparently, but in
some cases the caller needs to know which URL the client was
redirected to. The geturl() method can be used to get at this
redirected URL.
'''
return
self
.
url
def
getcode
(
self
):
'''Return the HTTP status code that was sent with the response,
or None if the URL is not an HTTP URL.
'''
return
self
.
status
def
_create_https_context
(
http_version
):
# Function also used by urllib.request to be able to set the check_hostname
# attribute on a context object.
context
=
ssl
.
_create_default_https_context
()
# send ALPN extension to indicate HTTP/1.1 protocol
if
http_version
==
11
:
context
.
set_alpn_protocols
([
'http/1.1'
])
# enable PHA for TLS 1.3 connections if available
if
context
.
post_handshake_auth
is
not
None
:
context
.
post_handshake_auth
=
True
return
context
class
HTTPConnection
:
_http_vsn
=
11
_http_vsn_str
=
'HTTP/1.1'
response_class
=
HTTPResponse
default_port
=
HTTP_PORT
auto_open
=
1
debuglevel
=
0
@
staticmethod
def
_is_textIO
(
stream
):
"""Test whether a file-like object is a text or a binary stream.
"""
return
isinstance
(
stream
,
io
.
TextIOBase
)
@
staticmethod
def
_get_content_length
(
body
,
method
):
"""Get the content-length based on the body.
If the body is None, we set Content-Length: 0 for methods that expect
a body (RFC 7230, Section 3.3.2). We also set the Content-Length for
any method if the body is a str or bytes-like object and not a file.
"""
if
body
is
None
:
# do an explicit check for not None here to distinguish
# between unset and set but empty
if
method
.
upper
()
in
_METHODS_EXPECTING_BODY
:
return
0
else
:
return
None
if
hasattr
(
body
,
'read'
):
# file-like object.
return
None
try
:
# does it implement the buffer protocol (bytes, bytearray, array)?
mv
=
memoryview
(
body
)
return
mv
.
nbytes
except
TypeError
:
pass
if
isinstance
(
body
,
str
):
return
len
(
body
)
return
None
def
__init__
(
self
,
host
,
port
=
None
,
timeout
=
socket
.
_GLOBAL_DEFAULT_TIMEOUT
,
source_address
=
None
,
blocksize
=
8192
,
*
,
max_response_headers
=
None
):
self
.
timeout
=
timeout
self
.
source_address
=
source_address
self
.
blocksize
=
blocksize
self
.
sock
=
None
self
.
_buffer
=
[]
self
.
__response
=
None
self
.
__state
=
_CS_IDLE
self
.
_method
=
None
self
.
_tunnel_host
=
None
self
.
_tunnel_port
=
None
self
.
_tunnel_headers
=
{}
self
.
_raw_proxy_headers
=
None
self
.
max_response_headers
=
max_response_headers
(
self
.
host
,
self
.
port
)
=
self
.
_get_hostport
(
host
,
port
)
self
.
_validate_host
(
self
.
host
)
# This is stored as an instance variable to allow unit
# tests to replace it with a suitable mockup
self
.
_create_connection
=
socket
.
create_connection
def
set_tunnel
(
self
,
host
,
port
=
None
,
headers
=
None
):
"""Set up host and port for HTTP CONNECT tunnelling.
In a connection that uses HTTP CONNECT tunnelling, the host passed to
the constructor is used as a proxy server that relays all communication
to the endpoint passed to `set_tunnel`. This done by sending an HTTP
CONNECT request to the proxy server when the connection is established.
This method must be called before the HTTP connection has been
established.
The headers argument should be a mapping of extra HTTP headers to send
with the CONNECT request.
As HTTP/1.1 is used for HTTP CONNECT tunnelling request, as per the RFC
(https://tools.ietf.org/html/rfc7231#section-4.3.6), a HTTP Host:
header must be provided, matching the authority-form of the request
target provided as the destination for the CONNECT request. If a
HTTP Host: header is not provided via the headers argument, one
is generated and transmitted automatically.
"""
if
self
.
sock
:
raise
RuntimeError
(
"Can't set up tunnel for established connection"
)
self
.
_tunnel_host
,
self
.
_tunnel_port
=
self
.
_get_hostport
(
host
,
port
)
if
headers
:
self
.
_tunnel_headers
=
headers
.
copy
()
else
:
self
.
_tunnel_headers
.
clear
()
if
not
any
(
header
.
lower
()
==
"host"
for
header
in
self
.
_tunnel_headers
):
encoded_host
=
self
.
_tunnel_host
.
encode
(
"idna"
).
decode
(
"ascii"
)
self
.
_tunnel_headers
[
"Host"
]
=
"%s:%d"
%
(
encoded_host
,
self
.
_tunnel_port
)
def
_get_hostport
(
self
,
host
,
port
):
if
port
is
None
:
i
=
host
.
rfind
(
':'
)
j
=
host
.
rfind
(
']'
)
# ipv6 addresses have [...]
if
i
>
j
:
try
:
port
=
int
(
host
[
i
+
1
:])
except
ValueError
:
if
host
[
i
+
1
:]
==
""
:
# http://foo.com:/ == http://foo.com/
port
=
self
.
default_port
else
:
raise
InvalidURL
(
"nonnumeric port: '%s'"
%
host
[
i
+
1
:])
host
=
host
[:
i
]
else
:
port
=
self
.
default_port
if
host
and
host
[
0
]
==
'['
and
host
[
-
1
]
==
']'
:
host
=
host
[
1
:
-
1
]
return
(
host
,
port
)
def
set_debuglevel
(
self
,
level
):
self
.
debuglevel
=
level
def
_wrap_ipv6
(
self
,
ip
):
View remainder of file in raw view
Back
|
FazBrowse Home
|
New Git URL