Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(205950)

Side by Side Diff: Lib/http/client.py

Issue 16723: io.TextIOWrapper on urllib.request.urlopen terminates prematurely
Patch Set: Created 6 years, 10 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | Lib/test/test_httplib.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 """HTTP/1.1 client library 1 """HTTP/1.1 client library
2 2
3 <intro stuff goes here> 3 <intro stuff goes here>
4 <other stuff, too> 4 <other stuff, too>
5 5
6 HTTPConnection goes through a number of "states", which define when a client 6 HTTPConnection goes through a number of "states", which define when a client
7 may legally make another request or fetch the response for a particular 7 may legally make another request or fetch the response for a particular
8 request. This diagram details these state transitions: 8 request. This diagram details these state transitions:
9 9
10 (null) 10 (null)
(...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 try: 325 try:
326 version, status, reason = line.split(None, 2) 326 version, status, reason = line.split(None, 2)
327 except ValueError: 327 except ValueError:
328 try: 328 try:
329 version, status = line.split(None, 1) 329 version, status = line.split(None, 1)
330 reason = "" 330 reason = ""
331 except ValueError: 331 except ValueError:
332 # empty version will cause next test to fail. 332 # empty version will cause next test to fail.
333 version = "" 333 version = ""
334 if not version.startswith("HTTP/"): 334 if not version.startswith("HTTP/"):
335 self.close() 335 self._close_conn()
336 raise BadStatusLine(line) 336 raise BadStatusLine(line)
337 337
338 # The status code is a three-digit number 338 # The status code is a three-digit number
339 try: 339 try:
340 status = int(status) 340 status = int(status)
341 if status < 100 or status > 999: 341 if status < 100 or status > 999:
342 raise BadStatusLine(line) 342 raise BadStatusLine(line)
343 except ValueError: 343 except ValueError:
344 raise BadStatusLine(line) 344 raise BadStatusLine(line)
345 return version, status, reason 345 return version, status, reason
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
447 return False 447 return False
448 448
449 # Proxy-Connection is a netscape hack. 449 # Proxy-Connection is a netscape hack.
450 pconn = self.headers.get("proxy-connection") 450 pconn = self.headers.get("proxy-connection")
451 if pconn and "keep-alive" in pconn.lower(): 451 if pconn and "keep-alive" in pconn.lower():
452 return False 452 return False
453 453
454 # otherwise, assume it will close 454 # otherwise, assume it will close
455 return True 455 return True
456 456
457 def _close_conn(self):
458 self.fp.close()
459 self.fp = None
460
457 def close(self): 461 def close(self):
462 super().close() # set "closed" flag
458 if self.fp: 463 if self.fp:
459 self.fp.close() 464 self._close_conn()
460 self.fp = None
461 465
462 # These implementations are for the benefit of io.BufferedReader. 466 # These implementations are for the benefit of io.BufferedReader.
463 467
464 # XXX This class should probably be revised to act more like 468 # XXX This class should probably be revised to act more like
465 # the "raw stream" that BufferedReader expects. 469 # the "raw stream" that BufferedReader expects.
466 470
467 @property
468 def closed(self):
469 return self.isclosed()
470
471 def flush(self): 471 def flush(self):
472 self.fp.flush() 472 super().flush()
473 if self.fp:
474 self.fp.flush()
473 475
474 def readable(self): 476 def readable(self):
475 return True 477 return True
476 478
477 # End of "raw stream" methods 479 # End of "raw stream" methods
478 480
479 def isclosed(self): 481 def isclosed(self):
480 # NOTE: it is possible that we will not ever call self.close(). This 482 # NOTE: it is possible that we will not ever call self.close(). This
481 # case occurs when will_close is TRUE, length is None, and we 483 # case occurs when will_close is TRUE, length is None, and we
482 # read up to the last byte, but NOT past it. 484 # read up to the last byte, but NOT past it.
483 # 485 #
484 # IMPLIES: if will_close is FALSE, then self.close() will ALWAYS be 486 # IMPLIES: if will_close is FALSE, then self.close() will ALWAYS be
485 # called, meaning self.isclosed() is meaningful. 487 # called, meaning self.isclosed() is meaningful.
486 return self.fp is None 488 return self.fp is None
487 489
488 def read(self, amt=None): 490 def read(self, amt=None):
489 if self.fp is None: 491 if self.fp is None:
490 return b"" 492 return b""
491 493
492 if self._method == "HEAD": 494 if self._method == "HEAD":
493 self.close() 495 self._close_conn()
494 return b"" 496 return b""
495 497
496 if amt is not None: 498 if amt is not None:
497 # Amount is given, so call base class version 499 # Amount is given, so call base class version
498 # (which is implemented in terms of self.readinto) 500 # (which is implemented in terms of self.readinto)
499 return super(HTTPResponse, self).read(amt) 501 return super(HTTPResponse, self).read(amt)
500 else: 502 else:
501 # Amount is not given (unbounded read) so we must check self.length 503 # Amount is not given (unbounded read) so we must check self.length
502 # and self.chunked 504 # and self.chunked
503 505
504 if self.chunked: 506 if self.chunked:
505 return self._readall_chunked() 507 return self._readall_chunked()
506 508
507 if self.length is None: 509 if self.length is None:
508 s = self.fp.read() 510 s = self.fp.read()
509 else: 511 else:
510 s = self._safe_read(self.length) 512 s = self._safe_read(self.length)
511 self.length = 0 513 self.length = 0
512 self.close() # we read everything 514 self._close_conn() # we read everything
513 return s 515 return s
514 516
515 def readinto(self, b): 517 def readinto(self, b):
516 if self.fp is None: 518 if self.fp is None:
517 return 0 519 return 0
518 520
519 if self._method == "HEAD": 521 if self._method == "HEAD":
520 self.close() 522 self._close_conn()
521 return 0 523 return 0
522 524
523 if self.chunked: 525 if self.chunked:
524 return self._readinto_chunked(b) 526 return self._readinto_chunked(b)
525 527
526 if self.length is not None: 528 if self.length is not None:
527 if len(b) > self.length: 529 if len(b) > self.length:
528 # clip the read to the "end of response" 530 # clip the read to the "end of response"
529 b = memoryview(b)[0:self.length] 531 b = memoryview(b)[0:self.length]
530 532
531 # we do not use _safe_read() here because this may be a .will_close 533 # we do not use _safe_read() here because this may be a .will_close
532 # connection, and the user is reading more bytes than will be provided 534 # connection, and the user is reading more bytes than will be provided
533 # (for example, reading in 1k chunks) 535 # (for example, reading in 1k chunks)
534 n = self.fp.readinto(b) 536 n = self.fp.readinto(b)
535 if self.length is not None: 537 if self.length is not None:
536 self.length -= n 538 self.length -= n
537 if not self.length: 539 if not self.length:
538 self.close() 540 self._close_conn()
539 else: 541 else:
540 if not n: 542 if not n:
541 self.close() 543 self._close_conn()
542 return n 544 return n
543 545
544 def _read_next_chunk_size(self): 546 def _read_next_chunk_size(self):
545 # Read the next chunk size from the file 547 # Read the next chunk size from the file
546 line = self.fp.readline(_MAXLINE + 1) 548 line = self.fp.readline(_MAXLINE + 1)
547 if len(line) > _MAXLINE: 549 if len(line) > _MAXLINE:
548 raise LineTooLong("chunk size") 550 raise LineTooLong("chunk size")
549 i = line.find(b";") 551 i = line.find(b";")
550 if i >= 0: 552 if i >= 0:
551 line = line[:i] # strip chunk-extensions 553 line = line[:i] # strip chunk-extensions
552 try: 554 try:
553 return int(line, 16) 555 return int(line, 16)
554 except ValueError: 556 except ValueError:
555 # close the connection as protocol synchronisation is 557 # close the connection as protocol synchronisation is
556 # probably lost 558 # probably lost
557 self.close() 559 self._close_conn()
558 raise 560 raise
559 561
560 def _read_and_discard_trailer(self): 562 def _read_and_discard_trailer(self):
561 # read and discard trailer up to the CRLF terminator 563 # read and discard trailer up to the CRLF terminator
562 ### note: we shouldn't have any trailers! 564 ### note: we shouldn't have any trailers!
563 while True: 565 while True:
564 line = self.fp.readline(_MAXLINE + 1) 566 line = self.fp.readline(_MAXLINE + 1)
565 if len(line) > _MAXLINE: 567 if len(line) > _MAXLINE:
566 raise LineTooLong("trailer line") 568 raise LineTooLong("trailer line")
567 if not line: 569 if not line:
(...skipping 17 matching lines...) Expand all
585 raise IncompleteRead(b''.join(value)) 587 raise IncompleteRead(b''.join(value))
586 value.append(self._safe_read(chunk_left)) 588 value.append(self._safe_read(chunk_left))
587 589
588 # we read the whole chunk, get another 590 # we read the whole chunk, get another
589 self._safe_read(2) # toss the CRLF at the end of the chunk 591 self._safe_read(2) # toss the CRLF at the end of the chunk
590 chunk_left = None 592 chunk_left = None
591 593
592 self._read_and_discard_trailer() 594 self._read_and_discard_trailer()
593 595
594 # we read everything; close the "file" 596 # we read everything; close the "file"
595 self.close() 597 self._close_conn()
596 598
597 return b''.join(value) 599 return b''.join(value)
598 600
599 def _readinto_chunked(self, b): 601 def _readinto_chunked(self, b):
600 assert self.chunked != _UNKNOWN 602 assert self.chunked != _UNKNOWN
601 chunk_left = self.chunk_left 603 chunk_left = self.chunk_left
602 604
603 total_bytes = 0 605 total_bytes = 0
604 mvb = memoryview(b) 606 mvb = memoryview(b)
605 while True: 607 while True:
(...skipping 20 matching lines...) Expand all
626 mvb = mvb[n:] 628 mvb = mvb[n:]
627 total_bytes += n 629 total_bytes += n
628 630
629 # we read the whole chunk, get another 631 # we read the whole chunk, get another
630 self._safe_read(2) # toss the CRLF at the end of the chunk 632 self._safe_read(2) # toss the CRLF at the end of the chunk
631 chunk_left = None 633 chunk_left = None
632 634
633 self._read_and_discard_trailer() 635 self._read_and_discard_trailer()
634 636
635 # we read everything; close the "file" 637 # we read everything; close the "file"
636 self.close() 638 self._close_conn()
637 639
638 return total_bytes 640 return total_bytes
639 641
640 def _safe_read(self, amt): 642 def _safe_read(self, amt):
641 """Read the number of bytes requested, compensating for partial reads. 643 """Read the number of bytes requested, compensating for partial reads.
642 644
643 Normally, we have a blocking socket, but a read() can be interrupted 645 Normally, we have a blocking socket, but a read() can be interrupted
644 by a signal (resulting in a partial read). 646 by a signal (resulting in a partial read).
645 647
646 Note that we cannot distinguish between EOF and an interrupt when zero 648 Note that we cannot distinguish between EOF and an interrupt when zero
(...skipping 620 matching lines...) Expand 10 before | Expand all | Expand 10 after
1267 self.args = line, 1269 self.args = line,
1268 self.line = line 1270 self.line = line
1269 1271
1270 class LineTooLong(HTTPException): 1272 class LineTooLong(HTTPException):
1271 def __init__(self, line_type): 1273 def __init__(self, line_type):
1272 HTTPException.__init__(self, "got more than %d bytes when reading %s" 1274 HTTPException.__init__(self, "got more than %d bytes when reading %s"
1273 % (_MAXLINE, line_type)) 1275 % (_MAXLINE, line_type))
1274 1276
1275 # for backwards compatibility 1277 # for backwards compatibility
1276 error = HTTPException 1278 error = HTTPException
OLDNEW
« no previous file with comments | « no previous file | Lib/test/test_httplib.py » ('j') | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+