http2: sane readline
It turns out that it pays off to read more than a byte at a time with
a select in between :)
--- a/mercurial/httpclient/__init__.py Fri Feb 01 14:41:35 2013 -0800
+++ b/mercurial/httpclient/__init__.py Fri Feb 01 15:00:23 2013 -0800
@@ -125,24 +125,16 @@
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
--- a/mercurial/httpclient/_readers.py Fri Feb 01 14:41:35 2013 -0800
+++ b/mercurial/httpclient/_readers.py Fri Feb 01 15:00:23 2013 -0800
@@ -96,6 +96,29 @@
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.