Hello community,

here is the log from the commit of package python-h2 for openSUSE:Factory 
checked in at 2019-09-23 12:08:55
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-h2 (Old)
 and      /work/SRC/openSUSE:Factory/.python-h2.new.7948 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-h2"

Mon Sep 23 12:08:55 2019 rev:6 rq:730347 version:3.1.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-h2/python-h2.changes      2019-07-30 
13:06:18.970373386 +0200
+++ /work/SRC/openSUSE:Factory/.python-h2.new.7948/python-h2.changes    
2019-09-23 12:08:56.973890121 +0200
@@ -1,0 +2,7 @@
+Thu Sep 12 10:04:22 UTC 2019 - Tomáš Chvátal <tchva...@suse.com>
+
+- Update to 3.1.1:
+  * Ignore WINDOW_UPDATE and RST_STREAM frames received after stream closure.
+- Drop patch pytest5.patch
+
+-------------------------------------------------------------------

Old:
----
  h2-3.1.0.tar.gz
  pytest5.patch

New:
----
  h2-3.1.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-h2.spec ++++++
--- /var/tmp/diff_new_pack.4QZCQ5/_old  2019-09-23 12:08:58.457889876 +0200
+++ /var/tmp/diff_new_pack.4QZCQ5/_new  2019-09-23 12:08:58.457889876 +0200
@@ -18,14 +18,13 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-h2
-Version:        3.1.0
+Version:        3.1.1
 Release:        0
 Summary:        HTTP/2 State-Machine based protocol implementation
 License:        MIT
 Group:          Development/Languages/Python
 URL:            https://github.com/python-hyper/hyper-h2
 Source0:        
https://files.pythonhosted.org/packages/source/h/h2/h2-%{version}.tar.gz
-Patch0:         pytest5.patch
 BuildRequires:  %{python_module hpack >= 2.3}
 BuildRequires:  %{python_module hyperframe >= 5.2.0}
 BuildRequires:  %{python_module hypothesis}
@@ -51,7 +50,6 @@
 
 %prep
 %setup -q -n h2-%{version}
-%patch0 -p1
 
 %build
 %python_build

++++++ h2-3.1.0.tar.gz -> h2-3.1.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/HISTORY.rst new/h2-3.1.1/HISTORY.rst
--- old/h2-3.1.0/HISTORY.rst    2019-01-22 18:41:49.000000000 +0100
+++ new/h2-3.1.1/HISTORY.rst    2019-08-02 15:10:28.000000000 +0200
@@ -1,6 +1,16 @@
 Release History
 ===============
 
+3.1.1 (2019-08-02)
+------------------
+
+Bugfixes
+~~~~~~~~
+
+- Ignore WINDOW_UPDATE and RST_STREAM frames received after stream
+  closure.
+
+
 3.1.0 (2019-01-22)
 ------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/MANIFEST.in new/h2-3.1.1/MANIFEST.in
--- old/h2-3.1.0/MANIFEST.in    2019-01-22 18:41:43.000000000 +0100
+++ new/h2-3.1.1/MANIFEST.in    2019-08-02 15:10:17.000000000 +0200
@@ -1,7 +1,8 @@
 include README.rst LICENSE CONTRIBUTORS.rst HISTORY.rst tox.ini 
test_requirements.txt .coveragerc Makefile
-recursive-include test *.py
+recursive-include test *.py *.sh
 graft docs
 prune docs/build
 graft visualizer
 recursive-include examples *.py *.crt *.key *.pem *.csr
 recursive-include utils *.sh
+recursive-include _travis *.sh
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/PKG-INFO new/h2-3.1.1/PKG-INFO
--- old/h2-3.1.0/PKG-INFO       2019-01-22 18:42:26.000000000 +0100
+++ new/h2-3.1.1/PKG-INFO       2019-08-02 15:10:46.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: h2
-Version: 3.1.0
+Version: 3.1.1
 Summary: HTTP/2 State-Machine based protocol implementation
 Home-page: http://hyper.rtfd.org
 Author: Cory Benfield
@@ -76,6 +76,16 @@
         Release History
         ===============
         
+        3.1.1 (2019-08-02)
+        ------------------
+        
+        Bugfixes
+        ~~~~~~~~
+        
+        - Ignore WINDOW_UPDATE and RST_STREAM frames received after stream
+          closure.
+        
+        
         3.1.0 (2019-01-22)
         ------------------
         
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/_travis/install.sh 
new/h2-3.1.1/_travis/install.sh
--- old/h2-3.1.0/_travis/install.sh     1970-01-01 01:00:00.000000000 +0100
+++ new/h2-3.1.1/_travis/install.sh     2019-08-02 15:10:17.000000000 +0200
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+set -e
+set -x
+
+pip install -U pip setuptools
+pip install -U tox
+
+if [ $TOXENV = "h2spec" ]; then
+    # For some reason it helps to have this here.
+    echo $(curl -s 
https://api.github.com/repos/summerwind/h2spec/releases/latest)
+    # We want to get the latest release of h2spec. We do that by asking the
+    # Github API for it, and then parsing the JSON for the appropriate kind of
+    # binary. Happily, the binary is always called "h2spec" so we don't need
+    # even more shenanigans to get this to work.
+    TARBALL=$(curl -s 
https://api.github.com/repos/summerwind/h2spec/releases/latest | jq 
--raw-output '.assets[] | .browser_download_url | 
select(endswith("linux_amd64.tar.gz"))')
+    curl -L "$TARBALL" -o h2spec.tgz
+    tar xvf h2spec.tgz
+    mkdir bin
+    mv h2spec ./bin/
+fi
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/docs/source/basic-usage.rst 
new/h2-3.1.1/docs/source/basic-usage.rst
--- old/h2-3.1.0/docs/source/basic-usage.rst    2019-01-22 18:41:43.000000000 
+0100
+++ new/h2-3.1.1/docs/source/basic-usage.rst    2019-08-02 15:10:17.000000000 
+0200
@@ -221,13 +221,11 @@
 
 .. code-block:: console
 
-    $ hyper GET http://http2bin.org/get
+    $ hyper GET https://nghttp2.org/httpbin/get
     {'args': {},
-     'headers': {'Connection': 'keep-alive',
-                 'Host': 'http2bin.org',
-                 'Via': '2 http2bin.org'},
+     'headers': {'Host': 'nghttp2.org'},
      'origin': '10.0.0.2',
-     'url': 'http://http2bin.org/get'}
+     'url': 'https://nghttp2.org/httpbin/get'}
 
 Assuming it works, you're now ready to start sending HTTP/2 data.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/docs/source/conf.py 
new/h2-3.1.1/docs/source/conf.py
--- old/h2-3.1.0/docs/source/conf.py    2019-01-22 18:41:49.000000000 +0100
+++ new/h2-3.1.1/docs/source/conf.py    2019-08-02 15:10:28.000000000 +0200
@@ -55,9 +55,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = '3.1.0'
+version = '3.1.1'
 # The full version, including alpha/beta/rc tags.
-release = '3.1.0'
+release = '3.1.1'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/docs/source/twisted-head-example.rst 
new/h2-3.1.1/docs/source/twisted-head-example.rst
--- old/h2-3.1.0/docs/source/twisted-head-example.rst   2019-01-22 
18:41:43.000000000 +0100
+++ new/h2-3.1.1/docs/source/twisted-head-example.rst   2019-08-02 
15:10:17.000000000 +0200
@@ -5,7 +5,7 @@
 networking framework.
 
 This client is fairly simple: it makes a hard-coded HEAD request to
-http2bin.org and prints out the response data. Its purpose is to demonstrate
+nghttp2.org/httpbin/ and prints out the response data. Its purpose is to 
demonstrate
 how to write a very basic HTTP/2 client implementation.
 
 .. literalinclude:: ../../examples/twisted/head_request.py
@@ -14,4 +14,4 @@
    :encoding: utf-8
 
 
-.. _Twisted: https://twistedmatrix.com/
\ No newline at end of file
+.. _Twisted: https://twistedmatrix.com/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/docs/source/twisted-post-example.rst 
new/h2-3.1.1/docs/source/twisted-post-example.rst
--- old/h2-3.1.0/docs/source/twisted-post-example.rst   2019-01-22 
18:41:43.000000000 +0100
+++ new/h2-3.1.1/docs/source/twisted-post-example.rst   2019-08-02 
15:10:17.000000000 +0200
@@ -5,7 +5,7 @@
 networking framework.
 
 This client is fairly simple: it makes a hard-coded POST request to
-http2bin.org and prints out the response data, sending a file that is provided
+nghttp2.org/httpbin/post and prints out the response data, sending a file that 
is provided
 on the command line or the script itself. Its purpose is to demonstrate how to
 write a HTTP/2 client implementation that handles flow control.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/examples/asyncio/asyncio-server.py 
new/h2-3.1.1/examples/asyncio/asyncio-server.py
--- old/h2-3.1.0/examples/asyncio/asyncio-server.py     2019-01-22 
18:41:43.000000000 +0100
+++ new/h2-3.1.1/examples/asyncio/asyncio-server.py     2019-08-02 
15:10:17.000000000 +0200
@@ -8,10 +8,6 @@
 This example demonstrates handling requests with bodies, as well as handling
 those without. In particular, it demonstrates the fact that DataReceived may
 be called multiple times, and that applications must handle that possibility.
-
-Please note that this example does not handle flow control, and so only works
-properly for relatively small requests. Please see other examples to understand
-how flow control should work.
 """
 import asyncio
 import io
@@ -23,10 +19,12 @@
 from h2.config import H2Configuration
 from h2.connection import H2Connection
 from h2.events import (
-    ConnectionTerminated, DataReceived, RequestReceived, StreamEnded
+    ConnectionTerminated, DataReceived, RemoteSettingsChanged,
+    RequestReceived, StreamEnded, StreamReset, WindowUpdated
 )
 from h2.errors import ErrorCodes
-from h2.exceptions import ProtocolError
+from h2.exceptions import ProtocolError, StreamClosedError
+from h2.settings import SettingCodes
 
 
 RequestData = collections.namedtuple('RequestData', ['headers', 'data'])
@@ -38,12 +36,18 @@
         self.conn = H2Connection(config=config)
         self.transport = None
         self.stream_data = {}
+        self.flow_control_futures = {}
 
     def connection_made(self, transport: asyncio.Transport):
         self.transport = transport
         self.conn.initiate_connection()
         self.transport.write(self.conn.data_to_send())
 
+    def connection_lost(self, exc):
+        for future in self.flow_control_futures.values():
+            future.cancel()
+        self.flow_control_futures = {}
+
     def data_received(self, data: bytes):
         try:
             events = self.conn.receive_data(data)
@@ -61,6 +65,13 @@
                     self.stream_complete(event.stream_id)
                 elif isinstance(event, ConnectionTerminated):
                     self.transport.close()
+                elif isinstance(event, StreamReset):
+                    self.stream_reset(event.stream_id)
+                elif isinstance(event, WindowUpdated):
+                    self.window_updated(event.stream_id, event.delta)
+                elif isinstance(event, RemoteSettingsChanged):
+                    if SettingCodes.INITIAL_WINDOW_SIZE in 
event.changed_settings:
+                        self.window_updated(None, 0)
 
                 self.transport.write(self.conn.data_to_send())
 
@@ -68,11 +79,6 @@
         headers = collections.OrderedDict(headers)
         method = headers[':method']
 
-        # We only support GET and POST.
-        if method not in ('GET', 'POST'):
-            self.return_405(headers, stream_id)
-            return
-
         # Store off the request data.
         request_data = RequestData(headers, io.BytesIO())
         self.stream_data[stream_id] = request_data
@@ -101,18 +107,7 @@
             ('server', 'asyncio-h2'),
         )
         self.conn.send_headers(stream_id, response_headers)
-        self.conn.send_data(stream_id, data, end_stream=True)
-
-    def return_405(self, headers: List[Tuple[str, str]], stream_id: int):
-        """
-        We don't support the given method, so we want to return a 405 response.
-        """
-        response_headers = (
-            (':status', '405'),
-            ('content-length', '0'),
-            ('server', 'asyncio-h2'),
-        )
-        self.conn.send_headers(stream_id, response_headers, end_stream=True)
+        asyncio.ensure_future(self.send_data(data, stream_id))
 
     def receive_data(self, data: bytes, stream_id: int):
         """
@@ -128,12 +123,72 @@
         else:
             stream_data.data.write(data)
 
+    def stream_reset(self, stream_id):
+        """
+        A stream reset was sent. Stop sending data.
+        """
+        if stream_id in self.flow_control_futures:
+            future = self.flow_control_futures.pop(stream_id)
+            future.cancel()
+
+    async def send_data(self, data, stream_id):
+        """
+        Send data according to the flow control rules.
+        """
+        while data:
+            while self.conn.local_flow_control_window(stream_id) < 1:
+                try:
+                    await self.wait_for_flow_control(stream_id)
+                except asyncio.CancelledError:
+                    return
+
+            chunk_size = min(
+                self.conn.local_flow_control_window(stream_id),
+                len(data),
+                self.conn.max_outbound_frame_size,
+            )
+
+            try:
+                self.conn.send_data(
+                    stream_id,
+                    data[:chunk_size],
+                    end_stream=(chunk_size == len(data))
+                )
+            except (StreamClosedError, ProtocolError):
+                # The stream got closed and we didn't get told. We're done
+                # here.
+                break
+
+            self.transport.write(self.conn.data_to_send())
+            data = data[chunk_size:]
+
+    async def wait_for_flow_control(self, stream_id):
+        """
+        Waits for a Future that fires when the flow control window is opened.
+        """
+        f = asyncio.Future()
+        self.flow_control_futures[stream_id] = f
+        await f
+
+    def window_updated(self, stream_id, delta):
+        """
+        A window update frame was received. Unblock some number of flow control
+        Futures.
+        """
+        if stream_id and stream_id in self.flow_control_futures:
+            f = self.flow_control_futures.pop(stream_id)
+            f.set_result(delta)
+        elif not stream_id:
+            for f in self.flow_control_futures.values():
+                f.set_result(delta)
+
+            self.flow_control_futures = {}
+
 
 ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
 ssl_context.options |= (
     ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_COMPRESSION
 )
-ssl_context.set_ciphers("ECDHE+AESGCM")
 ssl_context.load_cert_chain(certfile="cert.crt", keyfile="cert.key")
 ssl_context.set_alpn_protocols(["h2"])
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/examples/asyncio/wsgi-server.py 
new/h2-3.1.1/examples/asyncio/wsgi-server.py
--- old/h2-3.1.0/examples/asyncio/wsgi-server.py        2019-01-22 
18:41:43.000000000 +0100
+++ new/h2-3.1.1/examples/asyncio/wsgi-server.py        2019-08-02 
15:10:17.000000000 +0200
@@ -5,7 +5,7 @@
 
 A fully-functional WSGI server, written using hyper-h2. Requires asyncio.
 
-To test it, try installing httpin from pip (``pip install httpbin``) and then
+To test it, try installing httpbin from pip (``pip install httpbin``) and then
 running the server (``python asyncio-server.py httpbin:app``).
 
 This server does not support HTTP/1.1: it is a HTTP/2-only WSGI server. The
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/examples/curio/curio-server.py 
new/h2-3.1.1/examples/curio/curio-server.py
--- old/h2-3.1.0/examples/curio/curio-server.py 2019-01-22 18:41:43.000000000 
+0100
+++ new/h2-3.1.1/examples/curio/curio-server.py 2019-08-02 15:10:17.000000000 
+0200
@@ -150,7 +150,7 @@
         Send the data portion of a file. Handles flow control rules.
         """
         while True:
-            while not self.conn.local_flow_control_window(stream_id):
+            while self.conn.local_flow_control_window(stream_id) < 1:
                 await self.wait_for_flow_control(stream_id)
 
             chunk_size = min(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/examples/twisted/head_request.py 
new/h2-3.1.1/examples/twisted/head_request.py
--- old/h2-3.1.0/examples/twisted/head_request.py       2019-01-22 
18:41:43.000000000 +0100
+++ new/h2-3.1.1/examples/twisted/head_request.py       2019-08-02 
15:10:17.000000000 +0200
@@ -23,8 +23,8 @@
 )
 
 
-AUTHORITY = u'http2bin.org'
-PATH = '/'
+AUTHORITY = u'nghttp2.org'
+PATH = '/httpbin/'
 SIZE = 4096
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/examples/twisted/post_request.py 
new/h2-3.1.1/examples/twisted/post_request.py
--- old/h2-3.1.0/examples/twisted/post_request.py       2019-01-22 
18:41:43.000000000 +0100
+++ new/h2-3.1.1/examples/twisted/post_request.py       2019-08-02 
15:10:17.000000000 +0200
@@ -30,8 +30,8 @@
 )
 
 
-AUTHORITY = u'http2bin.org'
-PATH = '/post'
+AUTHORITY = u'nghttp2.org'
+PATH = '/httpbin/post'
 
 
 class H2Protocol(Protocol):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/examples/twisted/twisted-server.py 
new/h2-3.1.1/examples/twisted/twisted-server.py
--- old/h2-3.1.0/examples/twisted/twisted-server.py     2019-01-22 
18:41:43.000000000 +0100
+++ new/h2-3.1.1/examples/twisted/twisted-server.py     2019-08-02 
15:10:17.000000000 +0200
@@ -20,6 +20,7 @@
 from h2.events import (
     RequestReceived, DataReceived, WindowUpdated
 )
+from h2.exceptions import ProtocolError
 
 
 def close_file(file, d):
@@ -46,17 +47,23 @@
         if not self.known_proto:
             self.known_proto = True
 
-        events = self.conn.receive_data(data)
-        if self.conn.data_to_send:
-            self.transport.write(self.conn.data_to_send())
+        try:
+            events = self.conn.receive_data(data)
+        except ProtocolError:
+            if self.conn.data_to_send:
+                self.transport.write(self.conn.data_to_send())
+            self.transport.loseConnection()
+        else:
+            for event in events:
+                if isinstance(event, RequestReceived):
+                    self.requestReceived(event.headers, event.stream_id)
+                elif isinstance(event, DataReceived):
+                    self.dataFrameReceived(event.stream_id)
+                elif isinstance(event, WindowUpdated):
+                    self.windowUpdated(event)
 
-        for event in events:
-            if isinstance(event, RequestReceived):
-                self.requestReceived(event.headers, event.stream_id)
-            elif isinstance(event, DataReceived):
-                self.dataFrameReceived(event.stream_id)
-            elif isinstance(event, WindowUpdated):
-                self.windowUpdated(event)
+            if self.conn.data_to_send:
+                self.transport.write(self.conn.data_to_send())
 
     def requestReceived(self, headers, stream_id):
         headers = dict(headers)  # Invalid conversion, fix later.
@@ -86,7 +93,9 @@
 
     def sendFile(self, file_path, stream_id):
         filesize = os.stat(file_path).st_size
-        content_type, content_encoding = mimetypes.guess_type(file_path)
+        content_type, content_encoding = mimetypes.guess_type(
+            file_path.decode('utf-8')
+        )
         response_headers = [
             (':status', '200'),
             ('content-length', str(filesize)),
@@ -159,10 +168,11 @@
         self.root = root
 
     def buildProtocol(self, addr):
+        print(H2Protocol)
         return H2Protocol(self.root)
 
 
-root = sys.argv[1]
+root = sys.argv[1].encode('utf-8')
 
 with open('server.crt', 'r') as f:
     cert_data = f.read()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/h2/__init__.py new/h2-3.1.1/h2/__init__.py
--- old/h2-3.1.0/h2/__init__.py 2019-01-22 18:41:49.000000000 +0100
+++ new/h2-3.1.1/h2/__init__.py 2019-08-02 15:10:28.000000000 +0200
@@ -5,4 +5,4 @@
 
 A HTTP/2 implementation.
 """
-__version__ = '3.1.0'
+__version__ = '3.1.1'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/h2/connection.py 
new/h2-3.1.1/h2/connection.py
--- old/h2-3.1.0/h2/connection.py       2019-01-22 18:41:43.000000000 +0100
+++ new/h2-3.1.1/h2/connection.py       2019-08-02 15:10:17.000000000 +0200
@@ -350,7 +350,7 @@
         self._header_frames = []
 
         # Data that needs to be sent.
-        self._data_to_send = b''
+        self._data_to_send = bytearray()
 
         # Keeps track of how streams are closed.
         # Used to ensure that we don't blow up in the face of frames that were
@@ -1353,11 +1353,11 @@
         :rtype: ``bytes``
         """
         if amount is None:
-            data = self._data_to_send
-            self._data_to_send = b''
+            data = bytes(self._data_to_send)
+            self._data_to_send = bytearray()
             return data
         else:
-            data = self._data_to_send[:amount]
+            data = bytes(self._data_to_send[:amount])
             self._data_to_send = self._data_to_send[amount:]
             return data
 
@@ -1371,7 +1371,7 @@
         This method should not normally be used, but is made available to avoid
         exposing implementation details.
         """
-        self._data_to_send = b''
+        self._data_to_send = bytearray()
 
     def _acknowledge_settings(self):
         """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/h2/stream.py new/h2-3.1.1/h2/stream.py
--- old/h2-3.1.0/h2/stream.py   2019-01-22 18:41:43.000000000 +0100
+++ new/h2-3.1.1/h2/stream.py   2019-08-02 15:10:17.000000000 +0200
@@ -378,41 +378,6 @@
         """
         raise ProtocolError("Attempted to push on closed stream.")
 
-    def window_on_closed_stream(self, previous_state):
-        """
-        Called when a WINDOW_UPDATE frame is received on an already-closed
-        stream.
-
-        If we sent an END_STREAM frame, we just ignore the frame, as instructed
-        in RFC 7540 Section 5.1. Technically we should eventually consider
-        WINDOW_UPDATE in this state an error, but we don't have access to a
-        clock so we just always allow it. If we closed the stream for any other
-        reason, we behave as we do for receiving any other frame on a closed
-        stream.
-        """
-        assert self.stream_closed_by is not None
-
-        if self.stream_closed_by == StreamClosedBy.SEND_END_STREAM:
-            return []
-        return self.recv_on_closed_stream(previous_state)
-
-    def reset_on_closed_stream(self, previous_state):
-        """
-        Called when a RST_STREAM frame is received on an already-closed stream.
-
-        If we sent an END_STREAM frame, we just ignore the frame, as instructed
-        in RFC 7540 Section 5.1. Technically we should eventually consider
-        RST_STREAM in this state an error, but we don't have access to a clock
-        so we just always allow it. If we closed the stream for any other
-        reason, we behave as we do for receiving any other frame on a closed
-        stream.
-        """
-        assert self.stream_closed_by is not None
-
-        if self.stream_closed_by is StreamClosedBy.SEND_END_STREAM:
-            return []
-        return self.recv_on_closed_stream(previous_state)
-
     def send_informational_response(self, previous_state):
         """
         Called when an informational header block is sent (that is, a block
@@ -740,11 +705,12 @@
 
     # > WINDOW_UPDATE or RST_STREAM frames can be received in this state
     # > for a short period after a DATA or HEADERS frame containing a
-    # > END_STREAM flag is sent.
+    # > END_STREAM flag is sent, as instructed in RFC 7540 Section 5.1. But we
+    # > don't have access to a clock so we just always allow it.
     (StreamState.CLOSED, StreamInputs.RECV_WINDOW_UPDATE):
-        (H2StreamStateMachine.window_on_closed_stream, StreamState.CLOSED),
+        (None, StreamState.CLOSED),
     (StreamState.CLOSED, StreamInputs.RECV_RST_STREAM):
-        (H2StreamStateMachine.reset_on_closed_stream, StreamState.CLOSED),
+        (None, StreamState.CLOSED),
 
     # > A receiver MUST treat the receipt of a PUSH_PROMISE on a stream that is
     # > neither "open" nor "half-closed (local)" as a connection error of type
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/h2.egg-info/PKG-INFO 
new/h2-3.1.1/h2.egg-info/PKG-INFO
--- old/h2-3.1.0/h2.egg-info/PKG-INFO   2019-01-22 18:42:26.000000000 +0100
+++ new/h2-3.1.1/h2.egg-info/PKG-INFO   2019-08-02 15:10:46.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: h2
-Version: 3.1.0
+Version: 3.1.1
 Summary: HTTP/2 State-Machine based protocol implementation
 Home-page: http://hyper.rtfd.org
 Author: Cory Benfield
@@ -76,6 +76,16 @@
         Release History
         ===============
         
+        3.1.1 (2019-08-02)
+        ------------------
+        
+        Bugfixes
+        ~~~~~~~~
+        
+        - Ignore WINDOW_UPDATE and RST_STREAM frames received after stream
+          closure.
+        
+        
         3.1.0 (2019-01-22)
         ------------------
         
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/h2.egg-info/SOURCES.txt 
new/h2-3.1.1/h2.egg-info/SOURCES.txt
--- old/h2-3.1.0/h2.egg-info/SOURCES.txt        2019-01-22 18:42:26.000000000 
+0100
+++ new/h2-3.1.1/h2.egg-info/SOURCES.txt        2019-08-02 15:10:46.000000000 
+0200
@@ -9,6 +9,7 @@
 setup.py
 test_requirements.txt
 tox.ini
+_travis/install.sh
 docs/Makefile
 docs/make.bat
 docs/source/advanced-usage.rst
@@ -76,6 +77,7 @@
 h2.egg-info/top_level.txt
 test/conftest.py
 test/coroutine_tests.py
+test/h2spectest.sh
 test/helpers.py
 test/test_basic_logic.py
 test/test_closed_streams.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/test/h2spectest.sh 
new/h2-3.1.1/test/h2spectest.sh
--- old/h2-3.1.0/test/h2spectest.sh     1970-01-01 01:00:00.000000000 +0100
+++ new/h2-3.1.1/test/h2spectest.sh     2019-08-02 15:10:17.000000000 +0200
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+# A test script that runs the example Python Twisted server and then runs
+# h2spec against it. Prints the output of h2spec. This script does not expect
+# to be run directly, but instead via `tox -e h2spec`.
+
+set -x
+
+# Kill all background jobs on exit.
+trap 'kill $(jobs -p)' EXIT
+
+pushd examples/asyncio
+python asyncio-server.py  &
+popd
+
+# Wait briefly to let the server start up
+sleep 2
+
+# Go go go!
+h2spec -k -t -v -p 8443 $@
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/test/test_closed_streams.py 
new/h2-3.1.1/test/test_closed_streams.py
--- old/h2-3.1.0/test/test_closed_streams.py    2019-01-22 18:41:43.000000000 
+0100
+++ new/h2-3.1.1/test/test_closed_streams.py    2019-08-02 15:10:17.000000000 
+0200
@@ -107,6 +107,77 @@
         # The streams dictionary should be empty.
         assert not c.streams
 
+    def test_receive_rst_stream_on_closed_stream(self, frame_factory):
+        """
+        RST_STREAM frame should be ignored if stream is in a closed state.
+        See RFC 7540 Section 5.1 (closed state)
+        """
+        c = h2.connection.H2Connection()
+        c.initiate_connection()
+
+        # Client sends request
+        c.send_headers(1, self.example_request_headers)
+
+        # Some time passes and client sends DATA frame and closes stream,
+        # so it is in a half-closed state
+        c.send_data(1, b'some data', end_stream=True)
+
+        # Server received HEADERS frame but DATA frame is still on the way.
+        # Stream is in open state on the server-side. In this state server is
+        # allowed to end stream and reset it - this trick helps immediately
+        # close stream on the server-side.
+        headers_frame = frame_factory.build_headers_frame(
+            [(':status', '200')],
+            flags=['END_STREAM'],
+            stream_id=1,
+        )
+        events = c.receive_data(headers_frame.serialize())
+        assert len(events) == 2
+        response_received, stream_ended = events
+        assert isinstance(response_received, h2.events.ResponseReceived)
+        assert isinstance(stream_ended, h2.events.StreamEnded)
+
+        rst_stream_frame = frame_factory.build_rst_stream_frame(stream_id=1)
+        events = c.receive_data(rst_stream_frame.serialize())
+        assert not events
+
+    def test_receive_window_update_on_closed_stream(self, frame_factory):
+        """
+        WINDOW_UPDATE frame should be ignored if stream is in a closed state.
+        See RFC 7540 Section 5.1 (closed state)
+        """
+        c = h2.connection.H2Connection()
+        c.initiate_connection()
+
+        # Client sends request
+        c.send_headers(1, self.example_request_headers)
+
+        # Some time passes and client sends DATA frame and closes stream,
+        # so it is in a half-closed state
+        c.send_data(1, b'some data', end_stream=True)
+
+        # Server received HEADERS frame but DATA frame is still on the way.
+        # Stream is in open state on the server-side. In this state server is
+        # allowed to end stream and after that acknowledge received data by
+        # sending WINDOW_UPDATE frames.
+        headers_frame = frame_factory.build_headers_frame(
+            [(':status', '200')],
+            flags=['END_STREAM'],
+            stream_id=1,
+        )
+        events = c.receive_data(headers_frame.serialize())
+        assert len(events) == 2
+        response_received, stream_ended = events
+        assert isinstance(response_received, h2.events.ResponseReceived)
+        assert isinstance(stream_ended, h2.events.StreamEnded)
+
+        window_update_frame = frame_factory.build_window_update_frame(
+            stream_id=1,
+            increment=1,
+        )
+        events = c.receive_data(window_update_frame.serialize())
+        assert not events
+
 
 class TestStreamsClosedByEndStream(object):
     example_request_headers = [
@@ -274,7 +345,6 @@
             lambda self, ff: ff.build_headers_frame(
                 self.example_request_headers, flags=['END_STREAM']),
             lambda self, ff: ff.build_data_frame(b'hello'),
-            lambda self, ff: ff.build_window_update_frame(1, 1),
         ]
     )
     def test_resets_further_frames_after_recv_reset(self,
@@ -334,7 +404,6 @@
             lambda self, ff: ff.build_headers_frame(
                 self.example_request_headers, flags=['END_STREAM']),
             lambda self, ff: ff.build_data_frame(b'hello'),
-            lambda self, ff: ff.build_window_update_frame(1, 1),
         ]
     )
     def test_resets_further_frames_after_send_reset(self,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/test/test_head_request.py 
new/h2-3.1.1/test/test_head_request.py
--- old/h2-3.1.0/test/test_head_request.py      2019-01-22 18:41:43.000000000 
+0100
+++ new/h2-3.1.1/test/test_head_request.py      2019-08-02 15:10:17.000000000 
+0200
@@ -8,49 +8,48 @@
 
 
 class TestHeadRequest(object):
-
-        example_request_headers = [
-            (b':authority', b'example.com'),
-            (b':path', b'/'),
-            (b':scheme', b'https'),
-            (b':method', b'HEAD'),
-        ]
-
-        example_response_headers = [
-            (b':status', b'200'),
-            (b'server', b'fake-serv/0.1.0'),
-            (b'content_length', b'1'),
-        ]
-
-        def test_non_zero_content_and_no_body(self, frame_factory):
-
-            c = h2.connection.H2Connection()
-            c.initiate_connection()
-            c.send_headers(1, self.example_request_headers, end_stream=True)
-
-            f = frame_factory.build_headers_frame(
-                self.example_response_headers,
-                flags=['END_STREAM']
-            )
-            events = c.receive_data(f.serialize())
-
-            assert len(events) == 2
-            event = events[0]
-
-            assert isinstance(event, h2.events.ResponseReceived)
-            assert event.stream_id == 1
-            assert event.headers == self.example_response_headers
-
-        def test_reject_non_zero_content_and_body(self, frame_factory):
-            c = h2.connection.H2Connection()
-            c.initiate_connection()
-            c.send_headers(1, self.example_request_headers)
-
-            headers = frame_factory.build_headers_frame(
-                self.example_response_headers
-            )
-            data = frame_factory.build_data_frame(data=b'\x01')
-
-            c.receive_data(headers.serialize())
-            with pytest.raises(h2.exceptions.InvalidBodyLengthError):
-                c.receive_data(data.serialize())
+    example_request_headers = [
+        (b':authority', b'example.com'),
+        (b':path', b'/'),
+        (b':scheme', b'https'),
+        (b':method', b'HEAD'),
+    ]
+
+    example_response_headers = [
+        (b':status', b'200'),
+        (b'server', b'fake-serv/0.1.0'),
+        (b'content_length', b'1'),
+    ]
+
+    def test_non_zero_content_and_no_body(self, frame_factory):
+
+        c = h2.connection.H2Connection()
+        c.initiate_connection()
+        c.send_headers(1, self.example_request_headers, end_stream=True)
+
+        f = frame_factory.build_headers_frame(
+            self.example_response_headers,
+            flags=['END_STREAM']
+        )
+        events = c.receive_data(f.serialize())
+
+        assert len(events) == 2
+        event = events[0]
+
+        assert isinstance(event, h2.events.ResponseReceived)
+        assert event.stream_id == 1
+        assert event.headers == self.example_response_headers
+
+    def test_reject_non_zero_content_and_body(self, frame_factory):
+        c = h2.connection.H2Connection()
+        c.initiate_connection()
+        c.send_headers(1, self.example_request_headers)
+
+        headers = frame_factory.build_headers_frame(
+            self.example_response_headers
+        )
+        data = frame_factory.build_data_frame(data=b'\x01')
+
+        c.receive_data(headers.serialize())
+        with pytest.raises(h2.exceptions.InvalidBodyLengthError):
+            c.receive_data(data.serialize())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/test/test_invalid_headers.py 
new/h2-3.1.1/test/test_invalid_headers.py
--- old/h2-3.1.0/test/test_invalid_headers.py   2019-01-22 18:41:43.000000000 
+0100
+++ new/h2-3.1.1/test/test_invalid_headers.py   2019-08-02 15:10:17.000000000 
+0200
@@ -204,7 +204,7 @@
         # Raise exception if pseudo header in trailer
         with pytest.raises(h2.exceptions.ProtocolError) as e:
             c.receive_data(trailer)
-        assert "pseudo-header in trailer" in str(e)
+        assert "pseudo-header in trailer" in str(e.value)
 
         # Test appropriate response frame is generated
         expected_frame = frame_factory.build_goaway_frame(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/test_requirements.txt 
new/h2-3.1.1/test_requirements.txt
--- old/h2-3.1.0/test_requirements.txt  2019-01-22 18:41:43.000000000 +0100
+++ new/h2-3.1.1/test_requirements.txt  2019-08-02 15:10:17.000000000 +0200
@@ -1,5 +1,5 @@
-pytest==3.4.2
-pytest-cov==2.5.1
-coverage==4.5.1
-pytest-xdist==1.22.2
+pytest==4.6.2
+pytest-cov==2.7.1
+coverage==4.5.3
+pytest-xdist==1.29.0
 hypothesis
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/h2-3.1.0/tox.ini new/h2-3.1.1/tox.ini
--- old/h2-3.1.0/tox.ini        2019-01-22 18:41:43.000000000 +0100
+++ new/h2-3.1.1/tox.ini        2019-08-02 15:10:17.000000000 +0200
@@ -11,17 +11,9 @@
 # temporarily disable coverage testing on PyPy due to performance problems
 commands= py.test {posargs} {toxinidir}/test/
 
-[testenv:py27-twistedMaster]
-# This is a validation test that confirms that Twisted's test cases haven't
-# broken.
-deps =
-    twisted[tls, http2, conch]
-    sphinx
-commands = python -m twisted.trial --reporter=text twisted
-
 [testenv:lint]
 basepython=python3.7
-deps = flake8==3.5.0
+deps = flake8==3.7.8
 commands = flake8 --max-complexity 10 h2 test
 
 [testenv:docs]
@@ -35,15 +27,22 @@
 
 [testenv:graphs]
 basepython=python2.7
-deps = graphviz==0.8.2
+deps = graphviz==0.11.1
 commands =
     python visualizer/visualize.py -i docs/source/_static
 
 [testenv:packaging]
 basepython=python3.7
 deps =
-    check-manifest==0.36
-    readme-renderer==17.3
+    check-manifest==0.39
+    readme-renderer==24.0
 commands =
     check-manifest
     python setup.py check --metadata --restructuredtext --strict
+
+[testenv:h2spec]
+basepython=python3.6
+deps = twisted[tls]==17.1.0
+whitelist_externals = {toxinidir}/test/h2spectest.sh
+commands =
+    {toxinidir}/test/h2spectest.sh


Reply via email to