##// END OF EJS Templates
http: reuse Python's implementation of read/readline/readinto...
Joerg Sonnenberger -
r52722:c1ed5ee2 default
parent child Browse files
Show More
@@ -380,22 +380,9 b' class HTTPHandler(KeepAliveHandler, urlr'
380
380
381 class HTTPResponse(httplib.HTTPResponse):
381 class HTTPResponse(httplib.HTTPResponse):
382 # we need to subclass HTTPResponse in order to
382 # we need to subclass HTTPResponse in order to
383 # 1) add readline(), readlines(), and readinto() methods
383 # 1) add close_connection() methods
384 # 2) add close_connection() methods
384 # 2) add info() and geturl() methods
385 # 3) add info() and geturl() methods
385 # 3) add accounting for read(), readlines() and readinto()
386
387 # in order to add readline(), read must be modified to deal with a
388 # buffer. example: readline must read a buffer and then spit back
389 # one line at a time. The only real alternative is to read one
390 # BYTE at a time (ick). Once something has been read, it can't be
391 # put back (ok, maybe it can, but that's even uglier than this),
392 # so if you THEN do a normal read, you must first take stuff from
393 # the buffer.
394
395 # the read method wraps the original to accommodate buffering,
396 # although read() never adds to the buffer.
397 # Both readline and readlines have been stolen with almost no
398 # modification from socket.py
399
386
400 def __init__(self, sock, debuglevel=0, strict=0, method=None):
387 def __init__(self, sock, debuglevel=0, strict=0, method=None):
401 httplib.HTTPResponse.__init__(
388 httplib.HTTPResponse.__init__(
@@ -411,9 +398,6 b' class HTTPResponse(httplib.HTTPResponse)'
411 self._url = None # (same)
398 self._url = None # (same)
412 self._connection = None # (same)
399 self._connection = None # (same)
413
400
414 _raw_read = httplib.HTTPResponse.read
415 _raw_readinto = getattr(httplib.HTTPResponse, 'readinto', None)
416
417 # Python 2.7 has a single close() which closes the socket handle.
401 # Python 2.7 has a single close() which closes the socket handle.
418 # This method was effectively renamed to _close_conn() in Python 3. But
402 # This method was effectively renamed to _close_conn() in Python 3. But
419 # there is also a close(). _close_conn() is called by methods like
403 # there is also a close(). _close_conn() is called by methods like
@@ -442,23 +426,7 b' class HTTPResponse(httplib.HTTPResponse)'
442 return self._url
426 return self._url
443
427
444 def read(self, amt=None):
428 def read(self, amt=None):
445 # the _rbuf test is only in this first if for speed. It's not
429 data = super().read(amt)
446 # logically necessary
447 if self._rbuf and amt is not None:
448 L = len(self._rbuf)
449 if amt > L:
450 amt -= L
451 else:
452 s = self._rbuf[:amt]
453 self._rbuf = self._rbuf[amt:]
454 return s
455 # Careful! http.client.HTTPResponse.read() on Python 3 is
456 # implemented using readinto(), which can duplicate self._rbuf
457 # if it's not empty.
458 s = self._rbuf
459 self._rbuf = b''
460 data = self._raw_read(amt)
461
462 self.receivedbytescount += len(data)
430 self.receivedbytescount += len(data)
463 try:
431 try:
464 self._connection.receivedbytescount += len(data)
432 self._connection.receivedbytescount += len(data)
@@ -468,137 +436,32 b' class HTTPResponse(httplib.HTTPResponse)'
468 self._handler.parent.receivedbytescount += len(data)
436 self._handler.parent.receivedbytescount += len(data)
469 except AttributeError:
437 except AttributeError:
470 pass
438 pass
471
439 return data
472 s += data
473 return s
474
475 # stolen from Python SVN #68532 to fix issue1088
476 def _read_chunked(self, amt):
477 chunk_left = self.chunk_left
478 parts = []
479
480 while True:
481 if chunk_left is None:
482 line = self.fp.readline()
483 i = line.find(b';')
484 if i >= 0:
485 line = line[:i] # strip chunk-extensions
486 try:
487 chunk_left = int(line, 16)
488 except ValueError:
489 # close the connection as protocol synchronization is
490 # probably lost
491 self.close()
492 raise httplib.IncompleteRead(b''.join(parts))
493 if chunk_left == 0:
494 break
495 if amt is None:
496 parts.append(self._safe_read(chunk_left))
497 elif amt < chunk_left:
498 parts.append(self._safe_read(amt))
499 self.chunk_left = chunk_left - amt
500 return b''.join(parts)
501 elif amt == chunk_left:
502 parts.append(self._safe_read(amt))
503 self._safe_read(2) # toss the CRLF at the end of the chunk
504 self.chunk_left = None
505 return b''.join(parts)
506 else:
507 parts.append(self._safe_read(chunk_left))
508 amt -= chunk_left
509
510 # we read the whole chunk, get another
511 self._safe_read(2) # toss the CRLF at the end of the chunk
512 chunk_left = None
513
514 # read and discard trailer up to the CRLF terminator
515 ### note: we shouldn't have any trailers!
516 while True:
517 line = self.fp.readline()
518 if not line:
519 # a vanishingly small number of sites EOF without
520 # sending the trailer
521 break
522 if line == b'\r\n':
523 break
524
525 # we read everything; close the "file"
526 self.close()
527
528 return b''.join(parts)
529
440
530 def readline(self):
441 def readline(self):
531 # Fast path for a line is already available in read buffer.
442 data = super().readline()
532 i = self._rbuf.find(b'\n')
443 self.receivedbytescount += len(data)
533 if i >= 0:
444 try:
534 i += 1
445 self._connection.receivedbytescount += len(data)
535 line = self._rbuf[:i]
446 except AttributeError:
536 self._rbuf = self._rbuf[i:]
447 pass
537 return line
448 try:
538
449 self._handler.parent.receivedbytescount += len(data)
539 # No newline in local buffer. Read until we find one.
450 except AttributeError:
540 # readinto read via readinto will already return _rbuf
451 pass
541 if self._raw_readinto is None:
452 return data
542 chunks = [self._rbuf]
543 else:
544 chunks = []
545 i = -1
546 readsize = self._rbufsize
547 while True:
548 new = self._raw_read(readsize)
549 if not new:
550 break
551
552 self.receivedbytescount += len(new)
553 self._connection.receivedbytescount += len(new)
554 try:
555 self._handler.parent.receivedbytescount += len(new)
556 except AttributeError:
557 pass
558
559 chunks.append(new)
560 i = new.find(b'\n')
561 if i >= 0:
562 break
563
564 # We either have exhausted the stream or have a newline in chunks[-1].
565
566 # EOF
567 if i == -1:
568 self._rbuf = b''
569 return b''.join(chunks)
570
571 i += 1
572 self._rbuf = chunks[-1][i:]
573 chunks[-1] = chunks[-1][:i]
574 return b''.join(chunks)
575
453
576 def readinto(self, dest):
454 def readinto(self, dest):
577 if self._raw_readinto is None:
455 got = super().readinto(dest)
578 res = self.read(len(dest))
579 if not res:
580 return 0
581 dest[0 : len(res)] = res
582 return len(res)
583 total = len(dest)
584 have = len(self._rbuf)
585 if have >= total:
586 dest[0:total] = self._rbuf[:total]
587 self._rbuf = self._rbuf[total:]
588 return total
589 mv = memoryview(dest)
590 got = self._raw_readinto(mv[have:total])
591
592 self.receivedbytescount += got
456 self.receivedbytescount += got
593 self._connection.receivedbytescount += got
594 try:
457 try:
595 self._handler.receivedbytescount += got
458 self._connection.receivedbytescount += got
596 except AttributeError:
459 except AttributeError:
597 pass
460 pass
598
461 try:
599 dest[0:have] = self._rbuf
462 self._handler.parent.receivedbytescount += got
600 got += len(self._rbuf)
463 except AttributeError:
601 self._rbuf = b''
464 pass
602 return got
465 return got
603
466
604
467
General Comments 0
You need to be logged in to leave comments. Login now