From 606ea1f836523825737951ecf4c2a9d2f2be4e97 Mon Sep 17 00:00:00 2001 From: boyxuper Date: Tue, 8 Mar 2016 18:32:36 +0800 Subject: [PATCH 1/8] do NOT getpeercert() when secure=False on tornado2 --- http2/tornado2.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/http2/tornado2.py b/http2/tornado2.py index 7ad5e74..25c1044 100644 --- a/http2/tornado2.py +++ b/http2/tornado2.py @@ -448,9 +448,10 @@ def _get_ssl_options(cls, cert_options): return ssl_options def _on_connect(self, io_stream, ready_callback, close_callback): - if not self._verify_cert(io_stream.socket.getpeercert()): - io_stream.close() - return + if self.secure: + if not self._verify_cert(io_stream.socket.getpeercert()): + io_stream.close() + return io_stream.set_close_callback(lambda: close_callback(io_stream, io_stream.error)) self.io_loop.add_callback(functools.partial(ready_callback, io_stream)) From 5202dc643cbd7ef9124655b7ab4d80c5bcef04ee Mon Sep 17 00:00:00 2001 From: boyxuper Date: Tue, 8 Mar 2016 20:37:12 +0800 Subject: [PATCH 2/8] add backports.ssl_match_hostname dependency for tornado2 --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 11a92e8..fae6629 100644 --- a/setup.py +++ b/setup.py @@ -22,6 +22,7 @@ install_requires=[ 'h2>=2.1.0', 'tornado>=2.4.1', + 'backports.ssl_match_hostname', ], license="Apache Software License", zip_safe=False, From c634debc7aacdc2a66c398badfa6e4908e6d4d21 Mon Sep 17 00:00:00 2001 From: boyxuper Date: Tue, 8 Mar 2016 22:03:01 +0800 Subject: [PATCH 3/8] patch struct.unpack to adapt memoryview object for python < 2.7.5 --- http2/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/http2/__init__.py b/http2/__init__.py index 00c46bd..7dce165 100644 --- a/http2/__init__.py +++ b/http2/__init__.py @@ -1,5 +1,19 @@ # -*- coding: utf-8 -*- +import sys + + +if sys.version < (2, 7, 5): + import struct + _unpack = struct.unpack + + def unpack(fmt, data): + if isinstance(data, memoryview): + return _unpack(fmt, data.tobytes()) + else: + return _unpack(fmt, data) + struct.unpack = unpack + try: from tornado import version_info except ImportError: From 9621c7cc5e324316adcec2f95ba1bea3164f6196 Mon Sep 17 00:00:00 2001 From: boyxuper Date: Wed, 9 Mar 2016 10:41:44 +0800 Subject: [PATCH 4/8] fix argument order when invoking SimpleAsyncHTTP2Client._on_connection_close --- http2/tornado2.py | 5 +++-- http2/tornado4.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/http2/tornado2.py b/http2/tornado2.py index 25c1044..c4448aa 100644 --- a/http2/tornado2.py +++ b/http2/tornado2.py @@ -232,6 +232,7 @@ def _on_connection_close(self, io_stream, reason): if connection is not None: connection.on_connection_close(io_stream.error) + # schedule back-off self.connection_backoff = min( self.connection_backoff + 1, self.MAX_CONNECTION_BACKOFF) now_time = time.time() @@ -262,10 +263,10 @@ def _on_connection_close(self, io_stream, reason): def _connection_terminated(self, event): self._on_connection_close( - 'Server requested: ERR 0x%x' % event.error_code, self.io_stream) + self.io_stream, 'Server requested, code: 0x%x' % event.error_code) def _on_connection_ready(self, io_stream): - # back-off + # reset back-off self.next_connect_time = max(time.time(), self.next_connect_time) self.connection_backoff = 0 diff --git a/http2/tornado4.py b/http2/tornado4.py index 09a4265..655212c 100644 --- a/http2/tornado4.py +++ b/http2/tornado4.py @@ -129,6 +129,7 @@ def _on_connection_close(self, io_stream, reason): if connection is not None: connection.on_connection_close(io_stream.error) + # schedule back-off self.connection_backoff = min( self.connection_backoff + 1, self.MAX_CONNECTION_BACKOFF) now_time = time.time() @@ -159,10 +160,10 @@ def _on_connection_close(self, io_stream, reason): def _connection_terminated(self, event): self._on_connection_close( - 'Server requested: ERR 0x%x' % event.error_code, self.io_stream) + self.io_stream, 'Server requested, code: 0x%x' % event.error_code) def _on_connection_ready(self, io_stream): - # back-off + # reset back-off self.next_connect_time = max(time.time(), self.next_connect_time) self.connection_backoff = 0 From 2786dd296e4d0a4673883e36d94ee6aed577b10c Mon Sep 17 00:00:00 2001 From: boyxuper Date: Wed, 9 Mar 2016 10:50:16 +0800 Subject: [PATCH 5/8] improve exception handling in _HTTP2ConnectionContext._on_connection_streaming() --- http2/tornado2.py | 16 +++++++++------- http2/tornado4.py | 18 +++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/http2/tornado2.py b/http2/tornado2.py index c4448aa..8b1b7b6 100644 --- a/http2/tornado2.py +++ b/http2/tornado2.py @@ -521,22 +521,24 @@ def _on_connection_streaming(self, data): try: events = self.h2_conn.receive_data(data) - except h2.exceptions.ProtocolError as err: + except Exception as err: try: - self._flush_to_stream() - finally: + if isinstance(err, h2.exceptions.ProtocolError): + self._flush_to_stream() self.io_stream.close() + finally: self.on_connection_close(err) return if events: try: self._process_events(events) - except Exception as err: - self.io_stream.close() - self.on_connection_close(err) - else: self._flush_to_stream() + except Exception as err: + try: + self.io_stream.close() + finally: + self.on_connection_close(err) def _flush_to_stream(self): """flush h2 connection data to IOStream""" diff --git a/http2/tornado4.py b/http2/tornado4.py index 655212c..5df9fc6 100644 --- a/http2/tornado4.py +++ b/http2/tornado4.py @@ -320,24 +320,24 @@ def _on_connection_streaming(self, data): try: events = self.h2_conn.receive_data(data) - except h2.exceptions.ProtocolError as err: + except Exception as err: try: - self._flush_to_stream() + if isinstance(err, h2.exceptions.ProtocolError): + self._flush_to_stream() + self.io_stream.close() finally: - # import traceback; traceback.print_exc() - self.io_stream.close(exc_info=True) self.on_connection_close(err) return if events: try: self._process_events(events) - except Exception as err: - # import traceback; traceback.print_exc() - self.io_stream.close(exc_info=True) - self.on_connection_close(err) - else: self._flush_to_stream() + except Exception as err: + try: + self.io_stream.close() + finally: + self.on_connection_close(err) def _flush_to_stream(self): """flush h2 connection data to IOStream""" From 510a8c0199b6561c1c4daff4bd400cc7c869e206 Mon Sep 17 00:00:00 2001 From: boyxuper Date: Wed, 9 Mar 2016 11:33:48 +0800 Subject: [PATCH 6/8] add comment for patching struct.unpack --- http2/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/http2/__init__.py b/http2/__init__.py index 7dce165..f541b1c 100644 --- a/http2/__init__.py +++ b/http2/__init__.py @@ -3,6 +3,8 @@ import sys +# patch struct.unpack to accept memoryview object +# for python < 2.7.5 if sys.version < (2, 7, 5): import struct _unpack = struct.unpack @@ -14,6 +16,7 @@ def unpack(fmt, data): return _unpack(fmt, data) struct.unpack = unpack + try: from tornado import version_info except ImportError: From 89cdf9faad25be77c94e4b6489a4584400f50aec Mon Sep 17 00:00:00 2001 From: boyxuper Date: Wed, 9 Mar 2016 11:34:41 +0800 Subject: [PATCH 7/8] move version variable into __init__.py --- http2/__init__.py | 2 ++ setup.py | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/http2/__init__.py b/http2/__init__.py index f541b1c..1f83db3 100644 --- a/http2/__init__.py +++ b/http2/__init__.py @@ -2,6 +2,8 @@ import sys +__version__ = '0.2.2' + # patch struct.unpack to accept memoryview object # for python < 2.7.5 diff --git a/setup.py b/setup.py index fae6629..71db2c4 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,8 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- + +import re + try: from setuptools import setup @@ -6,9 +10,20 @@ from distutils.core import setup +# Get the version +version_regex = r'__version__ = ["\']([^"\']*)["\']' +with open('http2/__init__.py', 'r') as f: + text = f.read() + match = re.search(version_regex, text) + + if match: + version = match.group(1) + else: + raise RuntimeError("No version number found!") + setup( name='http2', - version='0.2.1', + version=version, description="HTTP/2 client with hyper-h2 for tornado", author="boyxuper", author_email='boyxuper@gmail.com', From 6887eab45b575b9bff41099d98a596f2ec292478 Mon Sep 17 00:00:00 2001 From: boyxuper Date: Wed, 9 Mar 2016 13:43:42 +0800 Subject: [PATCH 8/8] more detail in logs when connection disconnects --- http2/tornado2.py | 8 ++++---- http2/tornado4.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/http2/tornado2.py b/http2/tornado2.py index 8b1b7b6..79547bc 100644 --- a/http2/tornado2.py +++ b/http2/tornado2.py @@ -242,12 +242,12 @@ def _on_connection_close(self, io_stream, reason): if io_stream is None: logger.info( - 'Connection to %s failed due: %r. Reconnect in %.2f seconds', - self.host, reason, self.next_connect_time - now_time) + 'Connection to %s:%u failed due: %r. Reconnect in %.2f seconds', + self.host, self.port, reason, self.next_connect_time - now_time) else: logger.info( - 'Connection closed due: %r. Reconnect in %.2f seconds', - reason, self.next_connect_time - now_time) + 'Connection to %s:%u closed due: %r. Reconnect in %.2f seconds', + self.host, self.port, reason, self.next_connect_time - now_time) self.io_loop.add_timeout( self.next_connect_time, functools.partial( diff --git a/http2/tornado4.py b/http2/tornado4.py index 5df9fc6..5a142db 100644 --- a/http2/tornado4.py +++ b/http2/tornado4.py @@ -139,12 +139,12 @@ def _on_connection_close(self, io_stream, reason): if io_stream is None: logger.info( - 'Connection to %s failed due: %r. Reconnect in %.2f seconds', - self.host, reason, self.next_connect_time - now_time) + 'Connection to %s:%u failed due: %r. Reconnect in %.2f seconds', + self.host, self.port, reason, self.next_connect_time - now_time) else: logger.info( - 'Connection closed due: %r. Reconnect in %.2f seconds', - reason, self.next_connect_time - now_time) + 'Connection to %s:%u closed due: %r. Reconnect in %.2f seconds', + self.host, self.port, reason, self.next_connect_time - now_time) self.io_loop.add_timeout( self.next_connect_time, functools.partial(