# HG changeset patch # User Brendan Cully # Date 2013-02-01 23:00:23 # Node ID 36733ab7fa05aa948c5c13e43e1c97a3aadb897b # Parent 1fde25ad93966891897d75241c7850051b5bc568 http2: sane readline It turns out that it pays off to read more than a byte at a time with a select in between :) diff --git a/mercurial/httpclient/__init__.py b/mercurial/httpclient/__init__.py --- a/mercurial/httpclient/__init__.py +++ b/mercurial/httpclient/__init__.py @@ -125,24 +125,16 @@ class HTTPResponse(object): This may block until either a line ending is found or the response is complete. """ - # TODO: move this into the reader interface where it can be - # smarter (and probably avoid copies) - bytes = [] - while not bytes: - try: - bytes = [self._reader.read(1)] - except _readers.ReadNotReady: - self._select() - while bytes[-1] != '\n' and not self.complete(): + blocks = [] + while True: + self._reader.readto('\n', blocks) + + if blocks and blocks[-1][-1] == '\n' or self.complete(): + break + self._select() - bytes.append(self._reader.read(1)) - if bytes[-1] != '\n': - next = self._reader.read(1) - while next and next != '\n': - bytes.append(next) - next = self._reader.read(1) - bytes.append(next) - return ''.join(bytes) + + return ''.join(blocks) def read(self, length=None): # if length is None, unbounded read diff --git a/mercurial/httpclient/_readers.py b/mercurial/httpclient/_readers.py --- a/mercurial/httpclient/_readers.py +++ b/mercurial/httpclient/_readers.py @@ -96,6 +96,29 @@ class AbstractReader(object): return result + def readto(self, delimstr, blocks = None): + """return available data chunks up to the first one in which delimstr + occurs. No data will be returned after delimstr -- the chunk in which + it occurs will be split and the remainder pushed back onto the available + data queue. If blocks is supplied chunks will be added to blocks, otherwise + a new list will be allocated. + """ + if blocks is None: + blocks = [] + + while self._done_chunks: + b = self.popchunk() + i = b.find(delimstr) + len(delimstr) + if i: + if i < len(b): + self.pushchunk(b[i:]) + blocks.append(b[:i]) + break + else: + blocks.append(b) + + return blocks + def _load(self, data): # pragma: no cover """Subclasses must implement this.