Repository: pika/pika

Issue:
InvalidFieldTypeException when receiving messages with int values in headers dict
Pika throws an InvalidFieldTypeException on messages produced by haigha Python package with int values in the headers dictionary. 

Notes:
RabbitMQ can handle the message generated by _haigha_ (See attached screenshot)
Pika can process similar messages that are published by pika

An example for a failing application_headers: 
```
{'compressed': False, 'sequence': 5}
```

Stack trace:
```
File " \venv\lib\site-packages\pika\adapters\blocking_connection.py", line 1688, in _basic_consume_impl
    self._flush_output(ok_result.is_ready)
File " \venv\lib\site-packages\pika\adapters\blocking_connection.py", line 1327, in _flush_output
    self._connection._flush_output(lambda: self.is_closed, *waiters)
File " \venv\lib\site-packages\pika\adapters\blocking_connection.py", line 523, in _flush_output
    raise self._closed_result.value.error
pika.exceptions.StreamLostError: Stream connection lost: InvalidFieldTypeException
![pika + haigha messages](https://user-images.githubusercontent.com/47942606/56371619-18635680-6206-11e9-804a-fcf1a2e0d1de.PNG)

: Unsupported field kind b''
```

Attachments:
haigha_publisher.py : Publish messages that cause the error in pika
receiver_2.py: pika receiver code
publisher.py: pika publish code - receiver handles it
pika + haigha messages.PNG: A screenshot of the messages sent by haigha and pika.
[examples.zip](https://github.com/pika/pika/files/3095058/examples.zip)

The following application_header schemas were received succesfully by pika:
```
{'compressed': False, },
{'compressed': False, 'sequence': '5'},
{'compressed': False, 'sequence': None},
{'compressed': False, 'encoding': 'json'},
{'compressed': False, 'encoding': 'json', 'sequence': ''},
{'compressed': False, 'encoding': 'json', 'sequence': '5'}
```

Comments:

Patch:
diff --git a/pika/connection.py b/pika/connection.py
index e5f3cfa..aeddae3 100644
--- a/pika/connection.py
+++ b/pika/connection.py
@@ -1995,7 +1995,8 @@ class Connection(pika.compat.AbstractBase):
         """
         LOGGER.info(
             'AMQP stack terminated, failed to connect, or aborted: '
-            'error-arg=%r; pending-error=%r', error, self._error)
+            'opened=%r, error-arg=%r; pending-error=%r',
+            self._opened, error, self._error)
 
         if error is not None:
             if self._error is not None:
@@ -2016,11 +2017,11 @@ class Connection(pika.compat.AbstractBase):
                                [spec.Connection.Close, spec.Connection.Start])
 
         if self.params.blocked_connection_timeout is not None:
-            self._remove_callbacks(
-                0, [spec.Connection.Blocked, spec.Connection.Unblocked])
+            self._remove_callbacks(0,
+                    [spec.Connection.Blocked, spec.Connection.Unblocked])
 
         if not self._opened and isinstance(self._error,
-                                           exceptions.StreamLostError):
+                (exceptions.StreamLostError, exceptions.ConnectionClosedByBroker)):
             # Heuristically deduce error based on connection state
             if self.connection_state == self.CONNECTION_PROTOCOL:
                 LOGGER.error('Probably incompatible Protocol Versions')
diff --git a/pika/data.py b/pika/data.py
index d4264d2..e50a7b2 100644
--- a/pika/data.py
+++ b/pika/data.py
@@ -273,9 +273,11 @@ def decode_value(encoded, offset): # pylint: disable=R0912,R0915
         offset += 4
         value = decimal.Decimal(raw) * (decimal.Decimal(10)**-decimals)
 
-    # Short String
+    # https://github.com/pika/pika/issues/1205
+    # Short Signed Int
     elif kind == b's':
-        value, offset = decode_short_string(encoded, offset)
+        value = struct.unpack_from('>h', encoded, offset)[0]
+        offset += 2
 
     # Long String
     elif kind == b'S':


Test patch:
diff --git a/tests/acceptance/async_adapter_tests.py b/tests/acceptance/async_adapter_tests.py
index 5c8d542..b451541 100644
--- a/tests/acceptance/async_adapter_tests.py
+++ b/tests/acceptance/async_adapter_tests.py
@@ -910,14 +910,14 @@ class TestZ_PublishAndGet(BoundQueueTestCase, AsyncAdapters):  # pylint: disable
 
 
 class TestZ_AccessDenied(AsyncTestCase, AsyncAdapters):  # pylint: disable=C0103
-    DESCRIPTION = "Unknown vhost results in ConnectionClosedByBroker."
+    DESCRIPTION = "Unknown vhost results in ProbableAccessDeniedError."
 
     def start(self, *args, **kwargs):  # pylint: disable=W0221
         self.parameters.virtual_host = str(uuid.uuid4())
         self.error_captured = None
         super(TestZ_AccessDenied, self).start(*args, **kwargs)
         self.assertIsInstance(self.error_captured,
-                              pika.exceptions.ConnectionClosedByBroker)
+                              pika.exceptions.ProbableAccessDeniedError)
 
     def on_open_error(self, connection, error):
         self.error_captured = error
diff --git a/tests/unit/data_tests.py b/tests/unit/data_tests.py
index fccd0a9..db8601f 100644
--- a/tests/unit/data_tests.py
+++ b/tests/unit/data_tests.py
@@ -35,16 +35,19 @@ class DataTests(unittest.TestCase):
     FIELD_TBL_ENCODED += b'\x05bytesx\x00\x00\x00\x06foobar' if PY3 else b'\x05bytesS\x00\x00\x00\x06foobar'
 
     FIELD_TBL_VALUE = OrderedDict(
-        [('array', [1, 2, 3]), ('boolval', True), ('decimal',
-                                                   decimal.Decimal('3.14')),
-         ('decimal_too', decimal.Decimal('100')), ('dictval', {
-             'foo': 'bar'
-         }), ('intval', 1), ('bigint', 2592000000), ('longval',
-                                                     long(912598613)), ('null',
-                                                                        None),
-         ('strval', 'Test'), ('timestampval',
-                              datetime.datetime(2006, 11, 21, 16, 30,
-                                                10)), ('unicode', u'utf8=✓'),
+        [
+            ('array', [1, 2, 3]),
+            ('boolval', True),
+            ('decimal', decimal.Decimal('3.14')),
+            ('decimal_too', decimal.Decimal('100')),
+            ('dictval', { 'foo': 'bar' }),
+            ('intval', 1),
+            ('bigint', 2592000000),
+            ('longval', long(912598613)),
+            ('null', None),
+            ('strval', 'Test'),
+            ('timestampval', datetime.datetime(2006, 11, 21, 16, 30, 10)),
+            ('unicode', u'utf8=✓'),
             ('bytes', b'foobar'),
         ])
 
@@ -56,6 +59,15 @@ class DataTests(unittest.TestCase):
         result = data.decode_table(input, 0)
         self.assertEqual(result, ({'bytes': b'foobar'}, 21))
 
+    # b'\x08shortints\x04\xd2'
+    # ('shortint', 1234),
+    def test_decode_shortint(self):
+        input = (
+                b'\x00\x00\x00\x01'
+                b'\x08shortints\x04\xd2'
+            )
+        result = data.decode_table(input, 0)
+        self.assertEqual(result, ({'shortint': 1234}, 16))
 
     def test_encode_table(self):
         result = []