mercurial/httpclient/_readers.py
changeset 19182 fae47ecaa952
parent 19038 36733ab7fa05
child 27601 1ad9da968a2e
equal deleted inserted replaced
19181:8c2fdf7d5645 19182:fae47ecaa952
    31 This module is package-private. It is not expected that these will
    31 This module is package-private. It is not expected that these will
    32 have any clients outside of httpplus.
    32 have any clients outside of httpplus.
    33 """
    33 """
    34 
    34 
    35 import httplib
    35 import httplib
    36 import itertools
       
    37 import logging
    36 import logging
    38 
    37 
    39 logger = logging.getLogger(__name__)
    38 logger = logging.getLogger(__name__)
    40 
    39 
    41 
    40 
    57     def __init__(self):
    56     def __init__(self):
    58         self._finished = False
    57         self._finished = False
    59         self._done_chunks = []
    58         self._done_chunks = []
    60         self.available_data = 0
    59         self.available_data = 0
    61 
    60 
    62     def addchunk(self, data):
    61     def _addchunk(self, data):
    63         self._done_chunks.append(data)
    62         self._done_chunks.append(data)
    64         self.available_data += len(data)
    63         self.available_data += len(data)
    65 
    64 
    66     def pushchunk(self, data):
    65     def _pushchunk(self, data):
    67         self._done_chunks.insert(0, data)
    66         self._done_chunks.insert(0, data)
    68         self.available_data += len(data)
    67         self.available_data += len(data)
    69 
    68 
    70     def popchunk(self):
    69     def _popchunk(self):
    71         b = self._done_chunks.pop(0)
    70         b = self._done_chunks.pop(0)
    72         self.available_data -= len(b)
    71         self.available_data -= len(b)
    73 
    72 
    74         return b
    73         return b
    75 
    74 
    76     def done(self):
    75     def done(self):
       
    76         """Returns true if the response body is entirely read."""
    77         return self._finished
    77         return self._finished
    78 
    78 
    79     def read(self, amt):
    79     def read(self, amt):
       
    80         """Read amt bytes from the response body."""
    80         if self.available_data < amt and not self._finished:
    81         if self.available_data < amt and not self._finished:
    81             raise ReadNotReady()
    82             raise ReadNotReady()
    82         blocks = []
    83         blocks = []
    83         need = amt
    84         need = amt
    84         while self._done_chunks:
    85         while self._done_chunks:
    85             b = self.popchunk()
    86             b = self._popchunk()
    86             if len(b) > need:
    87             if len(b) > need:
    87                 nb = b[:need]
    88                 nb = b[:need]
    88                 self.pushchunk(b[need:])
    89                 self._pushchunk(b[need:])
    89                 b = nb
    90                 b = nb
    90             blocks.append(b)
    91             blocks.append(b)
    91             need -= len(b)
    92             need -= len(b)
    92             if need == 0:
    93             if need == 0:
    93                 break
    94                 break
   105         """
   106         """
   106         if blocks is None:
   107         if blocks is None:
   107             blocks = []
   108             blocks = []
   108 
   109 
   109         while self._done_chunks:
   110         while self._done_chunks:
   110             b = self.popchunk()
   111             b = self._popchunk()
   111             i = b.find(delimstr) + len(delimstr)
   112             i = b.find(delimstr) + len(delimstr)
   112             if i:
   113             if i:
   113                 if i < len(b):
   114                 if i < len(b):
   114                     self.pushchunk(b[i:])
   115                     self._pushchunk(b[i:])
   115                 blocks.append(b[:i])
   116                 blocks.append(b[:i])
   116                 break
   117                 break
   117             else:
   118             else:
   118                 blocks.append(b)
   119                 blocks.append(b)
   119 
   120 
   152     """
   153     """
   153     def _load(self, data):
   154     def _load(self, data):
   154         if data:
   155         if data:
   155             assert not self._finished, (
   156             assert not self._finished, (
   156                 'tried to add data (%r) to a closed reader!' % data)
   157                 'tried to add data (%r) to a closed reader!' % data)
   157         logger.debug('%s read an additional %d data', self.name, len(data))
   158         logger.debug('%s read an additional %d data',
   158         self.addchunk(data)
   159                      self.name, len(data)) # pylint: disable=E1101
       
   160         self._addchunk(data)
   159 
   161 
   160 
   162 
   161 class CloseIsEndReader(AbstractSimpleReader):
   163 class CloseIsEndReader(AbstractSimpleReader):
   162     """Reader for responses that specify Connection: Close for length."""
   164     """Reader for responses that specify Connection: Close for length."""
   163     name = 'close-is-end'
   165     name = 'close-is-end'
   170 class ContentLengthReader(AbstractSimpleReader):
   172 class ContentLengthReader(AbstractSimpleReader):
   171     """Reader for responses that specify an exact content length."""
   173     """Reader for responses that specify an exact content length."""
   172     name = 'content-length'
   174     name = 'content-length'
   173 
   175 
   174     def __init__(self, amount):
   176     def __init__(self, amount):
   175         AbstractReader.__init__(self)
   177         AbstractSimpleReader.__init__(self)
   176         self._amount = amount
   178         self._amount = amount
   177         if amount == 0:
   179         if amount == 0:
   178             self._finished = True
   180             self._finished = True
   179         self._amount_seen = 0
   181         self._amount_seen = 0
   180 
   182 
   197     def _load(self, data):
   199     def _load(self, data):
   198         assert not self._finished, 'tried to add data to a closed reader!'
   200         assert not self._finished, 'tried to add data to a closed reader!'
   199         logger.debug('chunked read an additional %d data', len(data))
   201         logger.debug('chunked read an additional %d data', len(data))
   200         position = 0
   202         position = 0
   201         if self._leftover_data:
   203         if self._leftover_data:
   202             logger.debug('chunked reader trying to finish block from leftover data')
   204             logger.debug(
       
   205                 'chunked reader trying to finish block from leftover data')
   203             # TODO: avoid this string concatenation if possible
   206             # TODO: avoid this string concatenation if possible
   204             data = self._leftover_data + data
   207             data = self._leftover_data + data
   205             position = self._leftover_skip_amt
   208             position = self._leftover_skip_amt
   206             self._leftover_data = ''
   209             self._leftover_data = ''
   207             self._leftover_skip_amt = 0
   210             self._leftover_skip_amt = 0
   222                 return
   225                 return
   223             if amt == 0:
   226             if amt == 0:
   224                 self._finished = True
   227                 self._finished = True
   225                 logger.debug('closing chunked reader due to chunk of length 0')
   228                 logger.debug('closing chunked reader due to chunk of length 0')
   226                 return
   229                 return
   227             self.addchunk(data[block_start:block_start + amt])
   230             self._addchunk(data[block_start:block_start + amt])
   228             position = block_start + amt + len(self._eol)
   231             position = block_start + amt + len(self._eol)
   229 # no-check-code
   232 # no-check-code