From 80ecd3b822973352fa75d7166bf689c4284f8d38 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Fri, 18 Apr 2014 21:57:16 +0300 Subject: [PATCH 01/15] asyncio: Initial prototype implementation. --- asyncio/asyncio.py | 100 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 asyncio/asyncio.py diff --git a/asyncio/asyncio.py b/asyncio/asyncio.py new file mode 100644 index 000000000..9f346fe89 --- /dev/null +++ b/asyncio/asyncio.py @@ -0,0 +1,100 @@ +import time +import heapq + + +def coroutine(f): + return f + + +def get_event_loop(): + return EventLoop() + + +class EventLoop: + + def __init__(self): + self.q = [] + self.cnt = 0 + + def time(self): + return time.time() + + def call_soon(self, callback, *args): + self.call_at(0, callback, *args) + + def call_later(self, delay, callback, *args): + self.call_at(self.time() + delay, callback, *args) + + def call_at(self, time, callback, *args): +# self.q.append((callback, args)) + # self.cnt is workaround per heapq docs +# print("Scheduling", (time, self.cnt, callback, args)) + heapq.heappush(self.q, (time, self.cnt, callback, args)) +# print(self.q) + self.cnt += 1 + +# def run_forever(self): +# while self.q: +# c = self.q.pop(0) +# c[0](*c[1]) + + def run_forever(self): + while self.q: +# t, cnt, cb, args = self.q.pop(0) + t, cnt, cb, args = heapq.heappop(self.q) + tnow = self.time() + delay = t - tnow + if delay > 0: +# print("Sleeping for:", delay) + time.sleep(delay) + delay = 0 + try: + ret = next(cb) +# print("ret:", ret) + if isinstance(ret, Sleep): + delay = ret.args[0] + except StopIteration as e: + print(c, "finished") + continue + #self.q.append(c) + self.call_later(delay, cb, *args) + + def run_until_complete(self, coro): + val = None + while True: + try: + ret = coro.send(val) + except StopIteration as e: + print(e) + break + print("ret:", ret) + if isinstance(ret, SysCall): + ret.handle() + + def close(self): + pass + + +class SysCall: + + def __init__(self, call, *args): + self.call = call + self.args = args + +class Sleep(SysCall): + + def handle(self): + time.sleep(self.args[0]) + + +def sleep(secs): + yield Sleep("sleep", secs) + +def sleep2(secs): + t = time.time() +# print("Started sleep:", t, "targetting:", t + secs) + while time.time() < t + secs: + time.sleep(0.01) + yield None +# print("Finished sleeping", secs) +# time.sleep(secs) From 78fef25eeaa38dae6675e9b3051dc1baf3b8db13 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Sun, 20 Apr 2014 00:01:57 +0300 Subject: [PATCH 02/15] asyncio: Implement subclass implementing filedes watching interface. --- asyncio/asyncio.py | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/asyncio/asyncio.py b/asyncio/asyncio.py index 9f346fe89..d076f824b 100644 --- a/asyncio/asyncio.py +++ b/asyncio/asyncio.py @@ -6,10 +6,6 @@ def coroutine(f): return f -def get_event_loop(): - return EventLoop() - - class EventLoop: def __init__(self): @@ -38,6 +34,10 @@ def call_at(self, time, callback, *args): # c = self.q.pop(0) # c[0](*c[1]) + def wait(self, delay): +# print("Sleeping for:", delay) + time.sleep(delay) + def run_forever(self): while self.q: # t, cnt, cb, args = self.q.pop(0) @@ -45,8 +45,7 @@ def run_forever(self): tnow = self.time() delay = t - tnow if delay > 0: -# print("Sleeping for:", delay) - time.sleep(delay) + self.wait(delay) delay = 0 try: ret = next(cb) @@ -74,6 +73,26 @@ def run_until_complete(self, coro): def close(self): pass +import select + +class EpollEventLoop(EventLoop): + + def __init__(self): + EventLoop.__init__(self) + self.poller = select.epoll(1) + + def add_reader(self, fd, cb, *args): + self.poller.register(fd, select.EPOLLIN, (cb, args)) + + def add_writer(self, fd, cb, *args): + self.poller.register(fd, select.EPOLLOUT, (cb, args)) + + def wait(self, delay): + res = self.poller.poll(int(delay * 1000)) + print("poll: ", res) + for cb, ev in res: + cb[0](*cb[1]) + class SysCall: @@ -87,6 +106,9 @@ def handle(self): time.sleep(self.args[0]) +def get_event_loop(): + return EpollEventLoop() + def sleep(secs): yield Sleep("sleep", secs) From 25bcef1b0da72d580b272e63289b9bb04a34bb1e Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Sun, 20 Apr 2014 00:47:43 +0300 Subject: [PATCH 03/15] asyncio: Recover eventloop's ability to work with callbacks. Actually, coroutine support for call_soon() is a hack, in big asyncio coroutine should be wrapped in Task object. --- asyncio/asyncio.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/asyncio/asyncio.py b/asyncio/asyncio.py index d076f824b..5713459e2 100644 --- a/asyncio/asyncio.py +++ b/asyncio/asyncio.py @@ -46,17 +46,20 @@ def run_forever(self): delay = t - tnow if delay > 0: self.wait(delay) - delay = 0 - try: - ret = next(cb) -# print("ret:", ret) - if isinstance(ret, Sleep): - delay = ret.args[0] - except StopIteration as e: - print(c, "finished") - continue - #self.q.append(c) - self.call_later(delay, cb, *args) + if callable(cb): + cb(*args) + else: + delay = 0 + try: + ret = next(cb) +# print("ret:", ret) + if isinstance(ret, Sleep): + delay = ret.args[0] + except StopIteration as e: + print(c, "finished") + continue + #self.q.append(c) + self.call_later(delay, cb, *args) def run_until_complete(self, coro): val = None From e2403bde0a29b58d2107eb03edd241dc609b7397 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Sun, 20 Apr 2014 05:58:12 +0300 Subject: [PATCH 04/15] asyncio: Make run_forever() actually run forever. --- asyncio/asyncio.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/asyncio/asyncio.py b/asyncio/asyncio.py index 5713459e2..e5c174a5b 100644 --- a/asyncio/asyncio.py +++ b/asyncio/asyncio.py @@ -39,13 +39,17 @@ def wait(self, delay): time.sleep(delay) def run_forever(self): - while self.q: -# t, cnt, cb, args = self.q.pop(0) - t, cnt, cb, args = heapq.heappop(self.q) - tnow = self.time() - delay = t - tnow - if delay > 0: - self.wait(delay) + while True: + if self.q: + t, cnt, cb, args = heapq.heappop(self.q) + tnow = self.time() + delay = t - tnow + if delay > 0: + self.wait(delay) + else: + self.wait(-1) + # Assuming IO completion scheduled some tasks + continue if callable(cb): cb(*args) else: From 65f0a05f2ac68a5f7466910f09d0abb7a7f4019a Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Sun, 20 Apr 2014 06:01:04 +0300 Subject: [PATCH 05/15] asyncio: EpollEventLoop.wait(): support infinite wait. --- asyncio/asyncio.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/asyncio/asyncio.py b/asyncio/asyncio.py index e5c174a5b..238565cb0 100644 --- a/asyncio/asyncio.py +++ b/asyncio/asyncio.py @@ -95,7 +95,11 @@ def add_writer(self, fd, cb, *args): self.poller.register(fd, select.EPOLLOUT, (cb, args)) def wait(self, delay): - res = self.poller.poll(int(delay * 1000)) + print("epoll.wait", delay) + if delay == -1: + res = self.poller.poll(-1) + else: + res = self.poller.poll(int(delay * 1000)) print("poll: ", res) for cb, ev in res: cb[0](*cb[1]) From ca9ea0d1b8cc3e506129112c825d36b5ce86ad7b Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Sun, 20 Apr 2014 06:05:50 +0300 Subject: [PATCH 06/15] asyncio: Support read/write syscalls, and route vals both ways between coros. --- asyncio/asyncio.py | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/asyncio/asyncio.py b/asyncio/asyncio.py index 238565cb0..e2e266e73 100644 --- a/asyncio/asyncio.py +++ b/asyncio/asyncio.py @@ -55,12 +55,22 @@ def run_forever(self): else: delay = 0 try: - ret = next(cb) -# print("ret:", ret) - if isinstance(ret, Sleep): - delay = ret.args[0] + if args == (): + args = (None,) + print("Send args:", args) + ret = cb.send(*args) + print("ret:", ret) + if isinstance(ret, SysCall): + if isinstance(ret, Sleep): + delay = ret.args[0] + elif isinstance(ret, IORead): + self.add_reader(ret.obj.fileno(), lambda f: self.call_soon(cb, f), ret.obj) + continue + elif isinstance(ret, IOWrite): + self.add_writer(ret.obj.fileno(), lambda f: self.call_soon(cb, f), ret.obj) + continue except StopIteration as e: - print(c, "finished") + print(cb, "finished") continue #self.q.append(c) self.call_later(delay, cb, *args) @@ -116,6 +126,16 @@ class Sleep(SysCall): def handle(self): time.sleep(self.args[0]) +class IORead(SysCall): + + def __init__(self, obj): + self.obj = obj + +class IOWrite(SysCall): + + def __init__(self, obj): + self.obj = obj + def get_event_loop(): return EpollEventLoop() From a0a699ecda35a9153c9400d19f5a4af6411cd9e9 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Sun, 20 Apr 2014 06:08:49 +0300 Subject: [PATCH 07/15] asyncio: Start adding asyncio stream interface. --- asyncio/asyncio.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/asyncio/asyncio.py b/asyncio/asyncio.py index e2e266e73..d7e183c25 100644 --- a/asyncio/asyncio.py +++ b/asyncio/asyncio.py @@ -151,3 +151,47 @@ def sleep2(secs): yield None # print("Finished sleeping", secs) # time.sleep(secs) + + +import microsocket as _socket + +class StreamReader: + + def __init__(self, s): + self.s = s + + def readline(self): + print("readline") + s = yield IORead(self.s) + print("after IORead") + res = self.s.readline() + print("readline res:", res) + return res + + +class StreamWriter: + + def __init__(self, s): + self.s = s + + def write(self, buf): + print("Write!") + res = self.s.write(str(buf)) + print("write res:", res) + s = yield IOWrite(self.s) + print("returning write res:", res) + + +def open_connection(host, port): + s = _socket.socket() + s.setblocking(False) + ai = _socket.getaddrinfo(host, port) + addr = ai[0][4] + try: + s.connect(addr) + except OSError as e: + print(e.args[0]) + print("After connect") + s = yield IOWrite(s) + print("After iowait:", s) + return StreamReader(s), StreamWriter(s) From 56ff510f9d383c309fb3f87e7d19e20fb3cac780 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Sun, 20 Apr 2014 06:09:45 +0300 Subject: [PATCH 08/15] asyncio: Add dumb debug output. --- asyncio/asyncio.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/asyncio/asyncio.py b/asyncio/asyncio.py index d7e183c25..5eec357a2 100644 --- a/asyncio/asyncio.py +++ b/asyncio/asyncio.py @@ -99,9 +99,11 @@ def __init__(self): self.poller = select.epoll(1) def add_reader(self, fd, cb, *args): + print("add_reader") self.poller.register(fd, select.EPOLLIN, (cb, args)) def add_writer(self, fd, cb, *args): + print("add_writer") self.poller.register(fd, select.EPOLLOUT, (cb, args)) def wait(self, delay): @@ -112,6 +114,7 @@ def wait(self, delay): res = self.poller.poll(int(delay * 1000)) print("poll: ", res) for cb, ev in res: + print("Calling %s%s" % (cb[0], cb[1])) cb[0](*cb[1]) From 9301c152bd1048ac861090d412f465253019f19e Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Tue, 22 Apr 2014 02:51:04 +0300 Subject: [PATCH 09/15] asyncio: Use logging and errno modules. --- asyncio/asyncio.py | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/asyncio/asyncio.py b/asyncio/asyncio.py index 5eec357a2..5efcca088 100644 --- a/asyncio/asyncio.py +++ b/asyncio/asyncio.py @@ -1,7 +1,12 @@ +import __main__ import time import heapq +import errno +import logging +log = logging.getLogger("asyncio") + def coroutine(f): return f @@ -24,7 +29,7 @@ def call_later(self, delay, callback, *args): def call_at(self, time, callback, *args): # self.q.append((callback, args)) # self.cnt is workaround per heapq docs -# print("Scheduling", (time, self.cnt, callback, args)) + log.debug("Scheduling %s", (time, self.cnt, callback, args)) heapq.heappush(self.q, (time, self.cnt, callback, args)) # print(self.q) self.cnt += 1 @@ -42,6 +47,8 @@ def run_forever(self): while True: if self.q: t, cnt, cb, args = heapq.heappop(self.q) + log.debug("Next task to run: %s", (t, cnt, cb, args)) +# __main__.mem_info() tnow = self.time() delay = t - tnow if delay > 0: @@ -57,20 +64,22 @@ def run_forever(self): try: if args == (): args = (None,) - print("Send args:", args) + log.debug("Gen send args: %s", args) ret = cb.send(*args) - print("ret:", ret) + log.debug("Gen yield result: %s", ret) if isinstance(ret, SysCall): if isinstance(ret, Sleep): delay = ret.args[0] elif isinstance(ret, IORead): +# self.add_reader(ret.obj.fileno(), lambda self, c, f: self.call_soon(c, f), self, cb, ret.obj) +# self.add_reader(ret.obj.fileno(), lambda c, f: self.call_soon(c, f), cb, ret.obj) self.add_reader(ret.obj.fileno(), lambda f: self.call_soon(cb, f), ret.obj) continue elif isinstance(ret, IOWrite): self.add_writer(ret.obj.fileno(), lambda f: self.call_soon(cb, f), ret.obj) continue except StopIteration as e: - print(cb, "finished") + log.debug("Gen finished: %s", cb) continue #self.q.append(c) self.call_later(delay, cb, *args) @@ -99,22 +108,22 @@ def __init__(self): self.poller = select.epoll(1) def add_reader(self, fd, cb, *args): - print("add_reader") + log.debug("add_reader%s", (fd, cb, args)) self.poller.register(fd, select.EPOLLIN, (cb, args)) def add_writer(self, fd, cb, *args): - print("add_writer") + log.debug("add_writer%s", (fd, cb, args)) self.poller.register(fd, select.EPOLLOUT, (cb, args)) def wait(self, delay): - print("epoll.wait", delay) + log.debug("epoll.wait(%d)", delay) if delay == -1: res = self.poller.poll(-1) else: res = self.poller.poll(int(delay * 1000)) - print("poll: ", res) + log.debug("epoll result: %s", res) for cb, ev in res: - print("Calling %s%s" % (cb[0], cb[1])) + log.debug("Calling IO callback: %s%s", cb[0], cb[1]) cb[0](*cb[1]) @@ -164,11 +173,11 @@ def __init__(self, s): self.s = s def readline(self): - print("readline") + log.debug("StreamReader.readline()") s = yield IORead(self.s) - print("after IORead") + log.debug("StreamReader.readline(): after IORead: %s", s) res = self.s.readline() - print("readline res:", res) + log.debug("StreamReader.readline(): res: %s", res) return res @@ -186,6 +195,7 @@ def write(self, buf): def open_connection(host, port): + log.debug("open_connection(%s, %s)", host, port) s = _socket.socket() s.setblocking(False) ai = _socket.getaddrinfo(host, port) @@ -193,8 +203,9 @@ def open_connection(host, port): try: s.connect(addr) except OSError as e: - print(e.args[0]) - print("After connect") + if e.args[0] != errno.EINPROGRESS: + raise + log.debug("open_connection: After connect") s = yield IOWrite(s) - print("After iowait:", s) + log.debug("open_connection: After iowait: %s", s) return StreamReader(s), StreamWriter(s) From aff251b1295c741cd577654be2f425a5b5662940 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Thu, 24 Apr 2014 01:18:51 +0300 Subject: [PATCH 10/15] asyncio: Add remove_reader()/remove_writer(). --- asyncio/asyncio.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/asyncio/asyncio.py b/asyncio/asyncio.py index 5efcca088..2c7a5e901 100644 --- a/asyncio/asyncio.py +++ b/asyncio/asyncio.py @@ -111,10 +111,18 @@ def add_reader(self, fd, cb, *args): log.debug("add_reader%s", (fd, cb, args)) self.poller.register(fd, select.EPOLLIN, (cb, args)) + def remove_reader(self, fd): + log.debug("remove_reader(%s)", fd) + self.poller.unregister(fd) + def add_writer(self, fd, cb, *args): log.debug("add_writer%s", (fd, cb, args)) self.poller.register(fd, select.EPOLLOUT, (cb, args)) + def remove_writer(self, fd): + log.debug("remove_writer(%s)", fd) + self.poller.unregister(fd) + def wait(self, delay): log.debug("epoll.wait(%d)", delay) if delay == -1: From d63d73d9fb42bcee7ccaf55f223d83e0fe8ffe80 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Thu, 24 Apr 2014 01:26:55 +0300 Subject: [PATCH 11/15] asyncio: Add asyncio.async() dummy factory function. Not Task bloat implemented (so far?), so just identity function for CPython compatibility. --- asyncio/asyncio.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/asyncio/asyncio.py b/asyncio/asyncio.py index 2c7a5e901..ccc5f3ab3 100644 --- a/asyncio/asyncio.py +++ b/asyncio/asyncio.py @@ -160,6 +160,10 @@ def __init__(self, obj): def get_event_loop(): return EpollEventLoop() +def async(coro): + # We don't have Task bloat, so op is null + return coro + def sleep(secs): yield Sleep("sleep", secs) From efc5bac0dda128e84c0b393d7f571dee48b473ba Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Thu, 24 Apr 2014 01:35:10 +0300 Subject: [PATCH 12/15] asyncio: Add basic loop.call_soon() test. --- asyncio/test_call_soon.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 asyncio/test_call_soon.py diff --git a/asyncio/test_call_soon.py b/asyncio/test_call_soon.py new file mode 100644 index 000000000..8ab5cb879 --- /dev/null +++ b/asyncio/test_call_soon.py @@ -0,0 +1,13 @@ +import asyncio +import time + + +def cb(): + print("callback") + time.sleep(0.5) + loop.call_soon(cb) + + +loop = asyncio.get_event_loop() +loop.call_soon(cb) +loop.run_forever() From 916eb337277a3109fde7582d33c753ff50a0ede6 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Thu, 24 Apr 2014 02:13:21 +0300 Subject: [PATCH 13/15] asyncio: Handle end of stream condition properly. By removing any IO watches for associated file handle. The way it's implemented tries to preserve OS-like separation between event loop and tasks. So, stream to finish watching fd for IO also issues syscall, instead of calling methods on loop instance directly. Calling method on loop would be more efficient, but will require storing reference to loop in each stream. And those separation matters... --- asyncio/asyncio.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/asyncio/asyncio.py b/asyncio/asyncio.py index ccc5f3ab3..5dbefba0e 100644 --- a/asyncio/asyncio.py +++ b/asyncio/asyncio.py @@ -7,6 +7,10 @@ log = logging.getLogger("asyncio") +IO_READ = 1 +IO_WRITE = 2 + + def coroutine(f): return f @@ -78,6 +82,12 @@ def run_forever(self): elif isinstance(ret, IOWrite): self.add_writer(ret.obj.fileno(), lambda f: self.call_soon(cb, f), ret.obj) continue + elif isinstance(ret, IODone): + if ret.op == IO_READ: + self.remove_reader(ret.obj.fileno()) + elif ret.op == IO_WRITE: + self.remove_writer(ret.obj.fileno()) + continue except StopIteration as e: log.debug("Gen finished: %s", cb) continue @@ -156,6 +166,12 @@ class IOWrite(SysCall): def __init__(self, obj): self.obj = obj +class IODone(SysCall): + + def __init__(self, op, obj): + self.op = op + self.obj = obj + def get_event_loop(): return EpollEventLoop() @@ -189,6 +205,8 @@ def readline(self): s = yield IORead(self.s) log.debug("StreamReader.readline(): after IORead: %s", s) res = self.s.readline() + if not res: + yield IODone(IO_READ, self.s) log.debug("StreamReader.readline(): res: %s", res) return res From 18c0b2c2f0a4a19866b79779b2e2964781392b7c Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Sun, 20 Apr 2014 06:27:43 +0300 Subject: [PATCH 14/15] asyncio: Add basic asyncio stream interface test. --- asyncio/test_http_client.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 asyncio/test_http_client.py diff --git a/asyncio/test_http_client.py b/asyncio/test_http_client.py new file mode 100644 index 000000000..cffc61e7d --- /dev/null +++ b/asyncio/test_http_client.py @@ -0,0 +1,25 @@ +import asyncio + +@asyncio.coroutine +def print_http_headers(url): + reader, writer = yield from asyncio.open_connection(url, 80) + print(reader, writer) + print("================") + query = "GET / HTTP/1.0\n\n" + print(query.encode('latin-1')) + yield from writer.write(query) + while True: + line = yield from reader.readline() +# 1/0 + if not line: + break + if line: + print(line) + +url = "google.com" +loop = asyncio.get_event_loop() +#task = asyncio.async(print_http_headers(url)) +#loop.run_until_complete(task) +loop.call_soon(print_http_headers(url)) +loop.run_forever() +loop.close() From dbc45a241b3db69c1b61adbd77cc23bc80098eb9 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky <pfalcon@users.sourceforge.net> Date: Thu, 24 Apr 2014 02:45:39 +0300 Subject: [PATCH 15/15] Add changes to expose uPy segfault. --- asyncio/asyncio.py | 4 ++-- asyncio/test_http_client.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/asyncio/asyncio.py b/asyncio/asyncio.py index 5dbefba0e..a6868543c 100644 --- a/asyncio/asyncio.py +++ b/asyncio/asyncio.py @@ -205,8 +205,8 @@ def readline(self): s = yield IORead(self.s) log.debug("StreamReader.readline(): after IORead: %s", s) res = self.s.readline() - if not res: - yield IODone(IO_READ, self.s) +# if not res: +# yield IODone(IO_READ, self.s) log.debug("StreamReader.readline(): res: %s", res) return res diff --git a/asyncio/test_http_client.py b/asyncio/test_http_client.py index cffc61e7d..0c96caf41 100644 --- a/asyncio/test_http_client.py +++ b/asyncio/test_http_client.py @@ -5,7 +5,8 @@ def print_http_headers(url): reader, writer = yield from asyncio.open_connection(url, 80) print(reader, writer) print("================") - query = "GET / HTTP/1.0\n\n" + query = "GET / HTTP/1.0\r\nHost: foo\r\n\r\n" +# query = "GET / HTTP/1.0\r\n\r\n" print(query.encode('latin-1')) yield from writer.write(query) while True: