aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml2
-rw-r--r--.travis.yml14
-rw-r--r--netlib/http/http2/connections.py119
-rw-r--r--netlib/http/http2/frame.py651
-rw-r--r--netlib/utils.py21
-rw-r--r--setup.py8
-rw-r--r--test/http/http2/test_connections.py (renamed from test/http/http2/test_protocol.py)202
-rw-r--r--test/http/http2/test_frames.py669
8 files changed, 203 insertions, 1483 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index dbb6d2fa..cd1354c2 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -8,4 +8,4 @@ install:
- "%PYTHON%\\python -c \"from OpenSSL import SSL; print(SSL.SSLeay_version(SSL.SSLEAY_VERSION))\""
build: off # Not a C# project
test_script:
- - "%PYTHON%\\Scripts\\py.test -n 4" \ No newline at end of file
+ - "%PYTHON%\\Scripts\\py.test -n 4 --timeout 10"
diff --git a/.travis.yml b/.travis.yml
index 84f32d71..529c7ed3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,8 +16,16 @@ matrix:
packages:
- libssl-dev
- python: 3.5
- script:
- - py.test -s --cov netlib -k "not http2"
+ - python: 3.5
+ env: OPENSSL=1.0.2
+ addons:
+ apt:
+ sources:
+ # Debian sid currently holds OpenSSL 1.0.2
+ # change this with future releases!
+ - debian-sid
+ packages:
+ - libssl-dev
- python: pypy
- python: pypy
env: OPENSSL=1.0.2
@@ -58,7 +66,7 @@ before_script:
- "openssl version -a"
script:
- - "py.test -s --cov netlib"
+ - "py.test -s --cov netlib --timeout 10"
after_success:
- coveralls
diff --git a/netlib/http/http2/connections.py b/netlib/http/http2/connections.py
index c493abe6..91133121 100644
--- a/netlib/http/http2/connections.py
+++ b/netlib/http/http2/connections.py
@@ -5,7 +5,13 @@ import time
from hpack.hpack import Encoder, Decoder
from ... import utils
from .. import Headers, Response, Request
-from . import frame
+
+from hyperframe import frame
+
+# TODO: remove once hyperframe released a new version > 3.1.1
+# wrapper for deprecated name in old hyperframe release
+frame.SettingsFrame.MAX_FRAME_SIZE = frame.SettingsFrame.SETTINGS_MAX_FRAME_SIZE
+frame.SettingsFrame.MAX_HEADER_LIST_SIZE = frame.SettingsFrame.SETTINGS_MAX_HEADER_LIST_SIZE
class TCPHandler(object):
@@ -34,7 +40,16 @@ class HTTP2Protocol(object):
HTTP_1_1_REQUIRED=0xd
)
- CLIENT_CONNECTION_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
+ CLIENT_CONNECTION_PREFACE = b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'
+
+ HTTP2_DEFAULT_SETTINGS = {
+ frame.SettingsFrame.HEADER_TABLE_SIZE: 4096,
+ frame.SettingsFrame.ENABLE_PUSH: 1,
+ frame.SettingsFrame.MAX_CONCURRENT_STREAMS: None,
+ frame.SettingsFrame.INITIAL_WINDOW_SIZE: 2 ** 16 - 1,
+ frame.SettingsFrame.MAX_FRAME_SIZE: 2 ** 14,
+ frame.SettingsFrame.MAX_HEADER_LIST_SIZE: None,
+ }
def __init__(
self,
@@ -54,7 +69,7 @@ class HTTP2Protocol(object):
self.decoder = decoder or Decoder()
self.unhandled_frame_cb = unhandled_frame_cb
- self.http2_settings = frame.HTTP2_DEFAULT_SETTINGS.copy()
+ self.http2_settings = self.HTTP2_DEFAULT_SETTINGS.copy()
self.current_stream_id = None
self.connection_preface_performed = False
@@ -84,7 +99,7 @@ class HTTP2Protocol(object):
timestamp_end = time.time()
- authority = headers.get(':authority', '')
+ authority = headers.get(':authority', b'')
method = headers.get(':method', 'GET')
scheme = headers.get(':scheme', 'https')
path = headers.get(':path', '/')
@@ -103,6 +118,8 @@ class HTTP2Protocol(object):
form_in = "absolute"
# FIXME: verify if path or :host contains what we need
scheme, host, port, _ = utils.parse_url(path)
+ scheme = scheme.decode('ascii')
+ host = host.decode('ascii')
if host is None:
host = 'localhost'
@@ -112,18 +129,17 @@ class HTTP2Protocol(object):
request = Request(
form_in,
- method,
- scheme,
- host,
+ method.encode('ascii'),
+ scheme.encode('ascii'),
+ host.encode('ascii'),
port,
- path,
- (2, 0),
+ path.encode('ascii'),
+ b'2.0',
headers,
body,
timestamp_start,
timestamp_end,
)
- # FIXME: We should not do this.
request.stream_id = stream_id
return request
@@ -131,7 +147,7 @@ class HTTP2Protocol(object):
def read_response(
self,
__rfile,
- request_method='',
+ request_method=b'',
body_size_limit=None,
include_body=True,
stream_id=None,
@@ -160,9 +176,9 @@ class HTTP2Protocol(object):
timestamp_end = None
response = Response(
- (2, 0),
+ b'2.0',
int(headers.get(':status', 502)),
- "",
+ b'',
headers,
body,
timestamp_start=timestamp_start,
@@ -190,13 +206,13 @@ class HTTP2Protocol(object):
headers = request.headers.copy()
if ':authority' not in headers:
- headers.fields.insert(0, (':authority', bytes(authority)))
+ headers.fields.insert(0, (b':authority', authority.encode('ascii')))
if ':scheme' not in headers:
- headers.fields.insert(0, (':scheme', bytes(request.scheme)))
+ headers.fields.insert(0, (b':scheme', request.scheme.encode('ascii')))
if ':path' not in headers:
- headers.fields.insert(0, (':path', bytes(request.path)))
+ headers.fields.insert(0, (b':path', request.path.encode('ascii')))
if ':method' not in headers:
- headers.fields.insert(0, (':method', bytes(request.method)))
+ headers.fields.insert(0, (b':method', request.method.encode('ascii')))
if hasattr(request, 'stream_id'):
stream_id = request.stream_id
@@ -213,7 +229,7 @@ class HTTP2Protocol(object):
headers = response.headers.copy()
if ':status' not in headers:
- headers.fields.insert(0, (':status', bytes(str(response.status_code))))
+ headers.fields.insert(0, (b':status', str(response.status_code).encode('ascii')))
if hasattr(response, 'stream_id'):
stream_id = response.stream_id
@@ -240,9 +256,9 @@ class HTTP2Protocol(object):
magic = self.tcp_handler.rfile.safe_read(magic_length)
assert magic == self.CLIENT_CONNECTION_PREFACE
- frm = frame.SettingsFrame(state=self, settings={
- frame.SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 0,
- frame.SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS: 1,
+ frm = frame.SettingsFrame(settings={
+ frame.SettingsFrame.ENABLE_PUSH: 0,
+ frame.SettingsFrame.MAX_CONCURRENT_STREAMS: 1,
})
self.send_frame(frm, hide=True)
self._receive_settings(hide=True)
@@ -253,12 +269,12 @@ class HTTP2Protocol(object):
self.tcp_handler.wfile.write(self.CLIENT_CONNECTION_PREFACE)
- self.send_frame(frame.SettingsFrame(state=self), hide=True)
+ self.send_frame(frame.SettingsFrame(), hide=True)
self._receive_settings(hide=True) # server announces own settings
self._receive_settings(hide=True) # server acks my settings
def send_frame(self, frm, hide=False):
- raw_bytes = frm.to_bytes()
+ raw_bytes = frm.serialize()
self.tcp_handler.wfile.write(raw_bytes)
self.tcp_handler.wfile.flush()
if not hide and self.dump_frames: # pragma no cover
@@ -266,19 +282,19 @@ class HTTP2Protocol(object):
def read_frame(self, hide=False):
while True:
- frm = frame.Frame.from_file(self.tcp_handler.rfile, self)
+ frm = utils.http2_read_frame(self.tcp_handler.rfile)
if not hide and self.dump_frames: # pragma no cover
print(frm.human_readable("<<"))
if isinstance(frm, frame.PingFrame):
- raw_bytes = frame.PingFrame(flags=frame.Frame.FLAG_ACK, payload=frm.payload).to_bytes()
+ raw_bytes = frame.PingFrame(flags=['ACK'], payload=frm.payload).serialize()
self.tcp_handler.wfile.write(raw_bytes)
self.tcp_handler.wfile.flush()
continue
- if isinstance(frm, frame.SettingsFrame) and not frm.flags & frame.Frame.FLAG_ACK:
+ if isinstance(frm, frame.SettingsFrame) and 'ACK' not in frm.flags:
self._apply_settings(frm.settings, hide)
- if isinstance(frm, frame.DataFrame) and frm.length > 0:
- self._update_flow_control_window(frm.stream_id, frm.length)
+ if isinstance(frm, frame.DataFrame) and frm.flow_controlled_length > 0:
+ self._update_flow_control_window(frm.stream_id, frm.flow_controlled_length)
return frm
def check_alpn(self):
@@ -321,15 +337,13 @@ class HTTP2Protocol(object):
old_value = '-'
self.http2_settings[setting] = value
- frm = frame.SettingsFrame(
- state=self,
- flags=frame.Frame.FLAG_ACK)
+ frm = frame.SettingsFrame(flags=['ACK'])
self.send_frame(frm, hide)
def _update_flow_control_window(self, stream_id, increment):
- frm = frame.WindowUpdateFrame(stream_id=0, window_size_increment=increment)
+ frm = frame.WindowUpdateFrame(stream_id=0, window_increment=increment)
self.send_frame(frm)
- frm = frame.WindowUpdateFrame(stream_id=stream_id, window_size_increment=increment)
+ frm = frame.WindowUpdateFrame(stream_id=stream_id, window_increment=increment)
self.send_frame(frm)
def _create_headers(self, headers, stream_id, end_stream=True):
@@ -342,43 +356,40 @@ class HTTP2Protocol(object):
header_block_fragment = self.encoder.encode(headers.fields)
- chunk_size = self.http2_settings[frame.SettingsFrame.SETTINGS.SETTINGS_MAX_FRAME_SIZE]
+ chunk_size = self.http2_settings[frame.SettingsFrame.MAX_FRAME_SIZE]
chunks = range(0, len(header_block_fragment), chunk_size)
frms = [frm_cls(
- state=self,
- flags=frame.Frame.FLAG_NO_FLAGS,
+ flags=[],
stream_id=stream_id,
- header_block_fragment=header_block_fragment[i:i+chunk_size]) for frm_cls, i in frame_cls(chunks)]
+ data=header_block_fragment[i:i+chunk_size]) for frm_cls, i in frame_cls(chunks)]
- last_flags = frame.Frame.FLAG_END_HEADERS
+ frms[-1].flags.add('END_HEADERS')
if end_stream:
- last_flags |= frame.Frame.FLAG_END_STREAM
- frms[-1].flags = last_flags
+ frms[0].flags.add('END_STREAM')
if self.dump_frames: # pragma no cover
for frm in frms:
print(frm.human_readable(">>"))
- return [frm.to_bytes() for frm in frms]
+ return [frm.serialize() for frm in frms]
def _create_body(self, body, stream_id):
if body is None or len(body) == 0:
return b''
- chunk_size = self.http2_settings[frame.SettingsFrame.SETTINGS.SETTINGS_MAX_FRAME_SIZE]
+ chunk_size = self.http2_settings[frame.SettingsFrame.MAX_FRAME_SIZE]
chunks = range(0, len(body), chunk_size)
frms = [frame.DataFrame(
- state=self,
- flags=frame.Frame.FLAG_NO_FLAGS,
+ flags=[],
stream_id=stream_id,
- payload=body[i:i+chunk_size]) for i in chunks]
- frms[-1].flags = frame.Frame.FLAG_END_STREAM
+ data=body[i:i+chunk_size]) for i in chunks]
+ frms[-1].flags.add('END_STREAM')
if self.dump_frames: # pragma no cover
for frm in frms:
print(frm.human_readable(">>"))
- return [frm.to_bytes() for frm in frms]
+ return [frm.serialize() for frm in frms]
def _receive_transmission(self, stream_id=None, include_body=True):
if not include_body:
@@ -386,7 +397,7 @@ class HTTP2Protocol(object):
body_expected = True
- header_block_fragment = b''
+ header_blocks = b''
body = b''
while True:
@@ -396,10 +407,10 @@ class HTTP2Protocol(object):
(stream_id is None or frm.stream_id == stream_id)
):
stream_id = frm.stream_id
- header_block_fragment += frm.header_block_fragment
- if frm.flags & frame.Frame.FLAG_END_STREAM:
+ header_blocks += frm.data
+ if 'END_STREAM' in frm.flags:
body_expected = False
- if frm.flags & frame.Frame.FLAG_END_HEADERS:
+ if 'END_HEADERS' in frm.flags:
break
else:
self._handle_unexpected_frame(frm)
@@ -407,14 +418,14 @@ class HTTP2Protocol(object):
while body_expected:
frm = self.read_frame()
if isinstance(frm, frame.DataFrame) and frm.stream_id == stream_id:
- body += frm.payload
- if frm.flags & frame.Frame.FLAG_END_STREAM:
+ body += frm.data
+ if 'END_STREAM' in frm.flags:
break
else:
self._handle_unexpected_frame(frm)
headers = Headers(
- [[str(k), str(v)] for k, v in self.decoder.decode(header_block_fragment)]
+ [[k.encode('ascii'), v.encode('ascii')] for k, v in self.decoder.decode(header_blocks)]
)
return stream_id, headers, body
diff --git a/netlib/http/http2/frame.py b/netlib/http/http2/frame.py
deleted file mode 100644
index 188629d4..00000000
--- a/netlib/http/http2/frame.py
+++ /dev/null
@@ -1,651 +0,0 @@
-from __future__ import absolute_import, print_function, division
-import struct
-from hpack.hpack import Encoder, Decoder
-
-from ...utils import BiDi
-from ...exceptions import HttpSyntaxException
-
-
-ERROR_CODES = BiDi(
- NO_ERROR=0x0,
- PROTOCOL_ERROR=0x1,
- INTERNAL_ERROR=0x2,
- FLOW_CONTROL_ERROR=0x3,
- SETTINGS_TIMEOUT=0x4,
- STREAM_CLOSED=0x5,
- FRAME_SIZE_ERROR=0x6,
- REFUSED_STREAM=0x7,
- CANCEL=0x8,
- COMPRESSION_ERROR=0x9,
- CONNECT_ERROR=0xa,
- ENHANCE_YOUR_CALM=0xb,
- INADEQUATE_SECURITY=0xc,
- HTTP_1_1_REQUIRED=0xd
-)
-
-CLIENT_CONNECTION_PREFACE = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
-
-class Frame(object):
-
- """
- Baseclass Frame
- contains header
- payload is defined in subclasses
- """
-
- FLAG_NO_FLAGS = 0x0
- FLAG_ACK = 0x1
- FLAG_END_STREAM = 0x1
- FLAG_END_HEADERS = 0x4
- FLAG_PADDED = 0x8
- FLAG_PRIORITY = 0x20
-
- def __init__(
- self,
- state=None,
- length=0,
- flags=FLAG_NO_FLAGS,
- stream_id=0x0):
- valid_flags = 0
- for flag in self.VALID_FLAGS:
- valid_flags |= flag
- if flags | valid_flags != valid_flags:
- raise ValueError('invalid flags detected.')
-
- if state is None:
- class State(object):
- pass
-
- state = State()
- state.http2_settings = HTTP2_DEFAULT_SETTINGS.copy()
- state.encoder = Encoder()
- state.decoder = Decoder()
-
- self.state = state
-
- self.length = length
- self.type = self.TYPE
- self.flags = flags
- self.stream_id = stream_id
-
- @classmethod
- def _check_frame_size(cls, length, state):
- if state:
- settings = state.http2_settings
- else:
- settings = HTTP2_DEFAULT_SETTINGS.copy()
-
- max_frame_size = settings[
- SettingsFrame.SETTINGS.SETTINGS_MAX_FRAME_SIZE]
-
- if length > max_frame_size:
- raise HttpSyntaxException(
- "Frame size exceeded: %d, but only %d allowed." % (
- length, max_frame_size))
-
- @classmethod
- def from_file(cls, fp, state=None):
- """
- read a HTTP/2 frame sent by a server or client
- fp is a "file like" object that could be backed by a network
- stream or a disk or an in memory stream reader
- """
- raw_header = fp.safe_read(9)
-
- fields = struct.unpack("!HBBBL", raw_header)
- length = (fields[0] << 8) + fields[1]
- flags = fields[3]
- stream_id = fields[4]
-
- if raw_header[:4] == b'HTTP': # pragma no cover
- raise HttpSyntaxException("Expected HTTP2 Frame, got HTTP/1 connection")
-
- cls._check_frame_size(length, state)
-
- payload = fp.safe_read(length)
- return FRAMES[fields[2]].from_bytes(
- state,
- length,
- flags,
- stream_id,
- payload)
-
- def to_bytes(self):
- payload = self.payload_bytes()
- self.length = len(payload)
-
- self._check_frame_size(self.length, self.state)
-
- b = struct.pack('!HB', (self.length & 0xFFFF00) >> 8, self.length & 0x0000FF)
- b += struct.pack('!B', self.TYPE)
- b += struct.pack('!B', self.flags)
- b += struct.pack('!L', self.stream_id & 0x7FFFFFFF)
- b += payload
-
- return b
-
- def payload_bytes(self): # pragma: no cover
- raise NotImplementedError()
-
- def payload_human_readable(self): # pragma: no cover
- raise NotImplementedError()
-
- def human_readable(self, direction="-"):
- self.length = len(self.payload_bytes())
-
- return "\n".join([
- "%s: %s | length: %d | flags: %#x | stream_id: %d" % (
- direction, self.__class__.__name__, self.length, self.flags, self.stream_id),
- self.payload_human_readable(),
- "===============================================================",
- ])
-
- def __eq__(self, other):
- return self.to_bytes() == other.to_bytes()
-
-
-class DataFrame(Frame):
- TYPE = 0x0
- VALID_FLAGS = [Frame.FLAG_END_STREAM, Frame.FLAG_PADDED]
-
- def __init__(
- self,
- state=None,
- length=0,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- payload=b'',
- pad_length=0):
- super(DataFrame, self).__init__(state, length, flags, stream_id)
- self.payload = payload
- self.pad_length = pad_length
-
- @classmethod
- def from_bytes(cls, state, length, flags, stream_id, payload):
- f = cls(state=state, length=length, flags=flags, stream_id=stream_id)
-
- if f.flags & Frame.FLAG_PADDED:
- f.pad_length = struct.unpack('!B', payload[0])[0]
- f.payload = payload[1:-f.pad_length]
- else:
- f.payload = payload
-
- return f
-
- def payload_bytes(self):
- if self.stream_id == 0x0:
- raise ValueError('DATA frames MUST be associated with a stream.')
-
- b = b''
- if self.flags & self.FLAG_PADDED:
- b += struct.pack('!B', self.pad_length)
-
- b += bytes(self.payload)
-
- if self.flags & self.FLAG_PADDED:
- b += b'\0' * self.pad_length
-
- return b
-
- def payload_human_readable(self):
- return "payload: %s" % str(self.payload)
-
-
-class HeadersFrame(Frame):
- TYPE = 0x1
- VALID_FLAGS = [
- Frame.FLAG_END_STREAM,
- Frame.FLAG_END_HEADERS,
- Frame.FLAG_PADDED,
- Frame.FLAG_PRIORITY]
-
- def __init__(
- self,
- state=None,
- length=0,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- header_block_fragment=b'',
- pad_length=0,
- exclusive=False,
- stream_dependency=0x0,
- weight=0):
- super(HeadersFrame, self).__init__(state, length, flags, stream_id)
-
- self.header_block_fragment = header_block_fragment
- self.pad_length = pad_length
- self.exclusive = exclusive
- self.stream_dependency = stream_dependency
- self.weight = weight
-
- @classmethod
- def from_bytes(cls, state, length, flags, stream_id, payload):
- f = cls(state=state, length=length, flags=flags, stream_id=stream_id)
-
- if f.flags & Frame.FLAG_PADDED:
- f.pad_length = struct.unpack('!B', payload[0])[0]
- f.header_block_fragment = payload[1:-f.pad_length]
- else:
- f.header_block_fragment = payload[0:]
-
- if f.flags & Frame.FLAG_PRIORITY:
- f.stream_dependency, f.weight = struct.unpack(
- '!LB', f.header_block_fragment[:5])
- f.exclusive = bool(f.stream_dependency >> 31)
- f.stream_dependency &= 0x7FFFFFFF
- f.header_block_fragment = f.header_block_fragment[5:]
-
- return f
-
- def payload_bytes(self):
- if self.stream_id == 0x0:
- raise ValueError('HEADERS frames MUST be associated with a stream.')
-
- b = b''
- if self.flags & self.FLAG_PADDED:
- b += struct.pack('!B', self.pad_length)
-
- if self.flags & self.FLAG_PRIORITY:
- b += struct.pack('!LB',
- (int(self.exclusive) << 31) | self.stream_dependency,
- self.weight)
-
- b += self.header_block_fragment
-
- if self.flags & self.FLAG_PADDED:
- b += b'\0' * self.pad_length
-
- return b
-
- def payload_human_readable(self):
- s = []
-
- if self.flags & self.FLAG_PRIORITY:
- s.append("exclusive: %d" % self.exclusive)
- s.append("stream dependency: %#x" % self.stream_dependency)
- s.append("weight: %d" % self.weight)
-
- if self.flags & self.FLAG_PADDED:
- s.append("padding: %d" % self.pad_length)
-
- s.append(
- "header_block_fragment: %s" %
- self.header_block_fragment.encode('hex'))
-
- return "\n".join(s)
-
-
-class PriorityFrame(Frame):
- TYPE = 0x2
- VALID_FLAGS = []
-
- def __init__(
- self,
- state=None,
- length=0,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- exclusive=False,
- stream_dependency=0x0,
- weight=0):
- super(PriorityFrame, self).__init__(state, length, flags, stream_id)
- self.exclusive = exclusive
- self.stream_dependency = stream_dependency
- self.weight = weight
-
- @classmethod
- def from_bytes(cls, state, length, flags, stream_id, payload):
- f = cls(state=state, length=length, flags=flags, stream_id=stream_id)
-
- f.stream_dependency, f.weight = struct.unpack('!LB', payload)
- f.exclusive = bool(f.stream_dependency >> 31)
- f.stream_dependency &= 0x7FFFFFFF
-
- return f
-
- def payload_bytes(self):
- if self.stream_id == 0x0:
- raise ValueError(
- 'PRIORITY frames MUST be associated with a stream.')
-
- return struct.pack(
- '!LB',
- (int(
- self.exclusive) << 31) | self.stream_dependency,
- self.weight)
-
- def payload_human_readable(self):
- s = []
- s.append("exclusive: %d" % self.exclusive)
- s.append("stream dependency: %#x" % self.stream_dependency)
- s.append("weight: %d" % self.weight)
- return "\n".join(s)
-
-
-class RstStreamFrame(Frame):
- TYPE = 0x3
- VALID_FLAGS = []
-
- def __init__(
- self,
- state=None,
- length=0,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- error_code=0x0):
- super(RstStreamFrame, self).__init__(state, length, flags, stream_id)
- self.error_code = error_code
-
- @classmethod
- def from_bytes(cls, state, length, flags, stream_id, payload):
- f = cls(state=state, length=length, flags=flags, stream_id=stream_id)
- f.error_code = struct.unpack('!L', payload)[0]
- return f
-
- def payload_bytes(self):
- if self.stream_id == 0x0:
- raise ValueError(
- 'RST_STREAM frames MUST be associated with a stream.')
-
- return struct.pack('!L', self.error_code)
-
- def payload_human_readable(self):
- return "error code: %#x" % self.error_code
-
-
-class SettingsFrame(Frame):
- TYPE = 0x4
- VALID_FLAGS = [Frame.FLAG_ACK]
-
- SETTINGS = BiDi(
- SETTINGS_HEADER_TABLE_SIZE=0x1,
- SETTINGS_ENABLE_PUSH=0x2,
- SETTINGS_MAX_CONCURRENT_STREAMS=0x3,
- SETTINGS_INITIAL_WINDOW_SIZE=0x4,
- SETTINGS_MAX_FRAME_SIZE=0x5,
- SETTINGS_MAX_HEADER_LIST_SIZE=0x6,
- )
-
- def __init__(
- self,
- state=None,
- length=0,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- settings=None):
- super(SettingsFrame, self).__init__(state, length, flags, stream_id)
-
- if settings is None:
- settings = {}
-
- self.settings = settings
-
- @classmethod
- def from_bytes(cls, state, length, flags, stream_id, payload):
- f = cls(state=state, length=length, flags=flags, stream_id=stream_id)
-
- for i in range(0, len(payload), 6):
- identifier, value = struct.unpack("!HL", payload[i:i + 6])
- f.settings[identifier] = value
-
- return f
-
- def payload_bytes(self):
- if self.stream_id != 0x0:
- raise ValueError(
- 'SETTINGS frames MUST NOT be associated with a stream.')
-
- b = b''
- for identifier, value in self.settings.items():
- b += struct.pack("!HL", identifier & 0xFF, value)
-
- return b
-
- def payload_human_readable(self):
- s = []
-
- for identifier, value in self.settings.items():
- s.append("%s: %#x" % (self.SETTINGS.get_name(identifier), value))
-
- if not s:
- return "settings: None"
- else:
- return "\n".join(s)
-
-
-class PushPromiseFrame(Frame):
- TYPE = 0x5
- VALID_FLAGS = [Frame.FLAG_END_HEADERS, Frame.FLAG_PADDED]
-
- def __init__(
- self,
- state=None,
- length=0,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- promised_stream=0x0,
- header_block_fragment=b'',
- pad_length=0):
- super(PushPromiseFrame, self).__init__(state, length, flags, stream_id)
- self.pad_length = pad_length
- self.promised_stream = promised_stream
- self.header_block_fragment = header_block_fragment
-
- @classmethod
- def from_bytes(cls, state, length, flags, stream_id, payload):
- f = cls(state=state, length=length, flags=flags, stream_id=stream_id)
-
- if f.flags & Frame.FLAG_PADDED:
- f.pad_length, f.promised_stream = struct.unpack('!BL', payload[:5])
- f.header_block_fragment = payload[5:-f.pad_length]
- else:
- f.promised_stream = int(struct.unpack("!L", payload[:4])[0])
- f.header_block_fragment = payload[4:]
-
- f.promised_stream &= 0x7FFFFFFF
-
- return f
-
- def payload_bytes(self):
- if self.stream_id == 0x0:
- raise ValueError(
- 'PUSH_PROMISE frames MUST be associated with a stream.')
-
- if self.promised_stream == 0x0:
- raise ValueError('Promised stream id not valid.')
-
- b = b''
- if self.flags & self.FLAG_PADDED:
- b += struct.pack('!B', self.pad_length)
-
- b += struct.pack('!L', self.promised_stream & 0x7FFFFFFF)
- b += bytes(self.header_block_fragment)
-
- if self.flags & self.FLAG_PADDED:
- b += b'\0' * self.pad_length
-
- return b
-
- def payload_human_readable(self):
- s = []
-
- if self.flags & self.FLAG_PADDED:
- s.append("padding: %d" % self.pad_length)
-
- s.append("promised stream: %#x" % self.promised_stream)
- s.append(
- "header_block_fragment: %s" %
- self.header_block_fragment.encode('hex'))
-
- return "\n".join(s)
-
-
-class PingFrame(Frame):
- TYPE = 0x6
- VALID_FLAGS = [Frame.FLAG_ACK]
-
- def __init__(
- self,
- state=None,
- length=0,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- payload=b''):
- super(PingFrame, self).__init__(state, length, flags, stream_id)
- self.payload = payload
-
- @classmethod
- def from_bytes(cls, state, length, flags, stream_id, payload):
- f = cls(state=state, length=length, flags=flags, stream_id=stream_id)
- f.payload = payload
- return f
-
- def payload_bytes(self):
- if self.stream_id != 0x0:
- raise ValueError(
- 'PING frames MUST NOT be associated with a stream.')
-
- b = self.payload[0:8]
- b += b'\0' * (8 - len(b))
- return b
-
- def payload_human_readable(self):
- return "opaque data: %s" % str(self.payload)
-
-
-class GoAwayFrame(Frame):
- TYPE = 0x7
- VALID_FLAGS = []
-
- def __init__(
- self,
- state=None,
- length=0,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- last_stream=0x0,
- error_code=0x0,
- data=b''):
- super(GoAwayFrame, self).__init__(state, length, flags, stream_id)
- self.last_stream = last_stream
- self.error_code = error_code
- self.data = data
-
- @classmethod
- def from_bytes(cls, state, length, flags, stream_id, payload):
- f = cls(state=state, length=length, flags=flags, stream_id=stream_id)
-
- f.last_stream, f.error_code = struct.unpack("!LL", payload[:8])
- f.last_stream &= 0x7FFFFFFF
- f.data = payload[8:]
-
- return f
-
- def payload_bytes(self):
- if self.stream_id != 0x0:
- raise ValueError(
- 'GOAWAY frames MUST NOT be associated with a stream.')
-
- b = struct.pack('!LL', self.last_stream & 0x7FFFFFFF, self.error_code)
- b += bytes(self.data)
- return b
-
- def payload_human_readable(self):
- s = []
- s.append("last stream: %#x" % self.last_stream)
- s.append("error code: %d" % self.error_code)
- s.append("debug data: %s" % str(self.data))
- return "\n".join(s)
-
-
-class WindowUpdateFrame(Frame):
- TYPE = 0x8
- VALID_FLAGS = []
-
- def __init__(
- self,
- state=None,
- length=0,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- window_size_increment=0x0):
- super(WindowUpdateFrame, self).__init__(state, length, flags, stream_id)
- self.window_size_increment = window_size_increment
-
- @classmethod
- def from_bytes(cls, state, length, flags, stream_id, payload):
- f = cls(state=state, length=length, flags=flags, stream_id=stream_id)
-
- f.window_size_increment = struct.unpack("!L", payload)[0]
- f.window_size_increment &= 0x7FFFFFFF
-
- return f
-
- def payload_bytes(self):
- if self.window_size_increment <= 0 or self.window_size_increment >= 2 ** 31:
- raise ValueError(
- 'Window Size Increment MUST be greater than 0 and less than 2^31.')
-
- return struct.pack('!L', self.window_size_increment & 0x7FFFFFFF)
-
- def payload_human_readable(self):
- return "window size increment: %#x" % self.window_size_increment
-
-
-class ContinuationFrame(Frame):
- TYPE = 0x9
- VALID_FLAGS = [Frame.FLAG_END_HEADERS]
-
- def __init__(
- self,
- state=None,
- length=0,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- header_block_fragment=b''):
- super(ContinuationFrame, self).__init__(state, length, flags, stream_id)
- self.header_block_fragment = header_block_fragment
-
- @classmethod
- def from_bytes(cls, state, length, flags, stream_id, payload):
- f = cls(state=state, length=length, flags=flags, stream_id=stream_id)
- f.header_block_fragment = payload
- return f
-
- def payload_bytes(self):
- if self.stream_id == 0x0:
- raise ValueError(
- 'CONTINUATION frames MUST be associated with a stream.')
-
- return self.header_block_fragment
-
- def payload_human_readable(self):
- s = []
- s.append(
- "header_block_fragment: %s" %
- self.header_block_fragment.encode('hex'))
- return "\n".join(s)
-
-_FRAME_CLASSES = [
- DataFrame,
- HeadersFrame,
- PriorityFrame,
- RstStreamFrame,
- SettingsFrame,
- PushPromiseFrame,
- PingFrame,
- GoAwayFrame,
- WindowUpdateFrame,
- ContinuationFrame
-]
-FRAMES = {cls.TYPE: cls for cls in _FRAME_CLASSES}
-
-
-HTTP2_DEFAULT_SETTINGS = {
- SettingsFrame.SETTINGS.SETTINGS_HEADER_TABLE_SIZE: 4096,
- SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1,
- SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS: None,
- SettingsFrame.SETTINGS.SETTINGS_INITIAL_WINDOW_SIZE: 2 ** 16 - 1,
- SettingsFrame.SETTINGS.SETTINGS_MAX_FRAME_SIZE: 2 ** 14,
- SettingsFrame.SETTINGS.SETTINGS_MAX_HEADER_LIST_SIZE: None,
-}
diff --git a/netlib/utils.py b/netlib/utils.py
index 66225897..1c1b617a 100644
--- a/netlib/utils.py
+++ b/netlib/utils.py
@@ -2,12 +2,12 @@ from __future__ import absolute_import, print_function, division
import os.path
import re
import string
+import codecs
import unicodedata
-
import six
from six.moves import urllib
-
+import hyperframe
def always_bytes(unicode_or_bytes, *encode_args):
if isinstance(unicode_or_bytes, six.text_type):
@@ -366,3 +366,20 @@ def multipartdecode(headers, content):
r.append((key, value))
return r
return []
+
+
+def http2_read_raw_frame(rfile):
+ header = rfile.safe_read(9)
+ length = int(codecs.encode(header[:3], 'hex_codec'), 16)
+
+ if length == 4740180:
+ raise ValueError("Length field looks more like HTTP/1.1: %s" % rfile.peek(20))
+
+ body = rfile.safe_read(length)
+ return [header, body]
+
+def http2_read_frame(rfile):
+ header, body = http2_read_raw_frame(rfile)
+ frame, length = hyperframe.frame.Frame.parse_frame_header(header)
+ frame.parse_body(memoryview(body))
+ return frame
diff --git a/setup.py b/setup.py
index 2da10baa..e842fd74 100644
--- a/setup.py
+++ b/setup.py
@@ -17,11 +17,12 @@ with open(os.path.join(here, 'README.rst'), encoding='utf-8') as f:
deps = {
"pyasn1>=0.1.9, <0.2",
"pyOpenSSL>=0.15.1, <0.16",
- "cryptography>=1.2.1, <1.3",
+ "cryptography>=1.2.2, <1.3",
"passlib>=1.6.5, <1.7",
- "hpack>=2.0.1, <2.1",
+ "hpack>=2.0.1, <3.0",
+ "hyperframe>=3.1.1, <4.0",
"six>=1.10.0, <1.11",
- "certifi>=2015.9.6.2", # no semver here - this should always be on the last release!
+ "certifi>=2015.11.20.1", # no semver here - this should always be on the last release!
"backports.ssl_match_hostname>=3.5.0.1, <3.6",
}
if sys.version_info < (3, 0):
@@ -63,6 +64,7 @@ setup(
"pytest>=2.8.0",
"pytest-xdist>=1.13.1",
"pytest-cov>=2.1.0",
+ "pytest-timeout>=1.0.0",
"coveralls>=0.4.1",
"autopep8>=1.0.3",
"autoflake>=0.6.6",
diff --git a/test/http/http2/test_protocol.py b/test/http/http2/test_connections.py
index 0beec950..22a43266 100644
--- a/test/http/http2/test_protocol.py
+++ b/test/http/http2/test_connections.py
@@ -1,11 +1,14 @@
import OpenSSL
import mock
+import codecs
-from netlib import tcp, http, tutils, tservers
+from hyperframe.frame import *
+
+from netlib import tcp, http, utils, tservers
+from netlib.tutils import raises
from netlib.exceptions import TcpDisconnect
-from netlib.http import Headers
from netlib.http.http2.connections import HTTP2Protocol, TCPHandler
-from netlib.http.http2.frame import *
+
class TestTCPHandlerWrapper:
def test_wrapped(self):
@@ -90,7 +93,8 @@ class TestCheckALPNMismatch(tservers.ServerTestBase):
c.connect()
c.convert_to_ssl(alpn_protos=[b'h2'])
protocol = HTTP2Protocol(c)
- tutils.raises(NotImplementedError, protocol.check_alpn)
+ with raises(NotImplementedError):
+ protocol.check_alpn()
class TestPerformServerConnectionPreface(tservers.ServerTestBase):
@@ -98,24 +102,23 @@ class TestPerformServerConnectionPreface(tservers.ServerTestBase):
def handle(self):
# send magic
- self.wfile.write(
- '505249202a20485454502f322e300d0a0d0a534d0d0a0d0a'.decode('hex'))
+ self.wfile.write(codecs.decode('505249202a20485454502f322e300d0a0d0a534d0d0a0d0a', 'hex_codec'))
self.wfile.flush()
# send empty settings frame
- self.wfile.write('000000040000000000'.decode('hex'))
+ self.wfile.write(codecs.decode('000000040000000000', 'hex_codec'))
self.wfile.flush()
# check empty settings frame
- assert self.rfile.read(9) ==\
- '000000040000000000'.decode('hex')
+ raw = utils.http2_read_raw_frame(self.rfile)
+ assert raw == codecs.decode('00000c040000000000000200000000000300000001', 'hex_codec')
# check settings acknowledgement
- assert self.rfile.read(9) == \
- '000000040100000000'.decode('hex')
+ raw = utils.http2_read_raw_frame(self.rfile)
+ assert raw == codecs.decode('000000040100000000', 'hex_codec')
# send settings acknowledgement
- self.wfile.write('000000040100000000'.decode('hex'))
+ self.wfile.write(codecs.decode('000000040100000000', 'hex_codec'))
self.wfile.flush()
def test_perform_server_connection_preface(self):
@@ -127,7 +130,8 @@ class TestPerformServerConnectionPreface(tservers.ServerTestBase):
protocol.perform_server_connection_preface()
assert protocol.connection_preface_performed
- tutils.raises(TcpDisconnect, protocol.perform_server_connection_preface, force=True)
+ with raises(TcpDisconnect):
+ protocol.perform_server_connection_preface(force=True)
class TestPerformClientConnectionPreface(tservers.ServerTestBase):
@@ -135,23 +139,22 @@ class TestPerformClientConnectionPreface(tservers.ServerTestBase):
def handle(self):
# check magic
- assert self.rfile.read(24) ==\
- '505249202a20485454502f322e300d0a0d0a534d0d0a0d0a'.decode('hex')
+ assert self.rfile.read(24) == HTTP2Protocol.CLIENT_CONNECTION_PREFACE
# check empty settings frame
assert self.rfile.read(9) ==\
- '000000040000000000'.decode('hex')
+ codecs.decode('000000040000000000', 'hex_codec')
# send empty settings frame
- self.wfile.write('000000040000000000'.decode('hex'))
+ self.wfile.write(codecs.decode('000000040000000000', 'hex_codec'))
self.wfile.flush()
# check settings acknowledgement
assert self.rfile.read(9) == \
- '000000040100000000'.decode('hex')
+ codecs.decode('000000040100000000', 'hex_codec')
# send settings acknowledgement
- self.wfile.write('000000040100000000'.decode('hex'))
+ self.wfile.write(codecs.decode('000000040100000000', 'hex_codec'))
self.wfile.flush()
def test_perform_client_connection_preface(self):
@@ -164,7 +167,7 @@ class TestPerformClientConnectionPreface(tservers.ServerTestBase):
assert protocol.connection_preface_performed
-class TestClientStreamIds():
+class TestClientStreamIds(object):
c = tcp.TCPClient(("127.0.0.1", 0))
protocol = HTTP2Protocol(c)
@@ -178,7 +181,7 @@ class TestClientStreamIds():
assert self.protocol.current_stream_id == 5
-class TestServerStreamIds():
+class TestServerStreamIds(object):
c = tcp.TCPClient(("127.0.0.1", 0))
protocol = HTTP2Protocol(c, is_server=True)
@@ -196,7 +199,7 @@ class TestApplySettings(tservers.ServerTestBase):
class handler(tcp.BaseHandler):
def handle(self):
# check settings acknowledgement
- assert self.rfile.read(9) == '000000040100000000'.decode('hex')
+ assert self.rfile.read(9) == codecs.decode('000000040100000000', 'hex_codec')
self.wfile.write("OK")
self.wfile.flush()
self.rfile.safe_read(9) # just to keep the connection alive a bit longer
@@ -210,22 +213,22 @@ class TestApplySettings(tservers.ServerTestBase):
protocol = HTTP2Protocol(c)
protocol._apply_settings({
- SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 'foo',
- SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS: 'bar',
- SettingsFrame.SETTINGS.SETTINGS_INITIAL_WINDOW_SIZE: 'deadbeef',
+ SettingsFrame.ENABLE_PUSH: 'foo',
+ SettingsFrame.MAX_CONCURRENT_STREAMS: 'bar',
+ SettingsFrame.INITIAL_WINDOW_SIZE: 'deadbeef',
})
- assert c.rfile.safe_read(2) == "OK"
+ assert c.rfile.safe_read(2) == b"OK"
assert protocol.http2_settings[
- SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH] == 'foo'
+ SettingsFrame.ENABLE_PUSH] == 'foo'
assert protocol.http2_settings[
- SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS] == 'bar'
+ SettingsFrame.MAX_CONCURRENT_STREAMS] == 'bar'
assert protocol.http2_settings[
- SettingsFrame.SETTINGS.SETTINGS_INITIAL_WINDOW_SIZE] == 'deadbeef'
+ SettingsFrame.INITIAL_WINDOW_SIZE] == 'deadbeef'
-class TestCreateHeaders():
+class TestCreateHeaders(object):
c = tcp.TCPClient(("127.0.0.1", 0))
def test_create_headers(self):
@@ -238,14 +241,12 @@ class TestCreateHeaders():
bytes = HTTP2Protocol(self.c)._create_headers(
headers, 1, end_stream=True)
assert b''.join(bytes) ==\
- '000014010500000001824488355217caf3a69a3f87408294e7838c767f'\
- .decode('hex')
+ codecs.decode('000014010500000001824488355217caf3a69a3f87408294e7838c767f', 'hex_codec')
bytes = HTTP2Protocol(self.c)._create_headers(
headers, 1, end_stream=False)
assert b''.join(bytes) ==\
- '000014010400000001824488355217caf3a69a3f87408294e7838c767f'\
- .decode('hex')
+ codecs.decode('000014010400000001824488355217caf3a69a3f87408294e7838c767f', 'hex_codec')
def test_create_headers_multiple_frames(self):
headers = http.Headers([
@@ -256,44 +257,45 @@ class TestCreateHeaders():
(b'server', b'version')])
protocol = HTTP2Protocol(self.c)
- protocol.http2_settings[SettingsFrame.SETTINGS.SETTINGS_MAX_FRAME_SIZE] = 8
+ protocol.http2_settings[SettingsFrame.MAX_FRAME_SIZE] = 8
bytes = protocol._create_headers(headers, 1, end_stream=True)
assert len(bytes) == 3
- assert bytes[0] == '000008010000000001828487408294e783'.decode('hex')
- assert bytes[1] == '0000080900000000018c767f7685ee5b10'.decode('hex')
- assert bytes[2] == '00000209050000000163d5'.decode('hex')
+ assert bytes[0] == codecs.decode('000008010100000001828487408294e783', 'hex_codec')
+ assert bytes[1] == codecs.decode('0000080900000000018c767f7685ee5b10', 'hex_codec')
+ assert bytes[2] == codecs.decode('00000209040000000163d5', 'hex_codec')
-class TestCreateBody():
+class TestCreateBody(object):
c = tcp.TCPClient(("127.0.0.1", 0))
def test_create_body_empty(self):
protocol = HTTP2Protocol(self.c)
bytes = protocol._create_body(b'', 1)
- assert b''.join(bytes) == ''.decode('hex')
+ assert b''.join(bytes) == b''
def test_create_body_single_frame(self):
protocol = HTTP2Protocol(self.c)
- bytes = protocol._create_body('foobar', 1)
- assert b''.join(bytes) == '000006000100000001666f6f626172'.decode('hex')
+ bytes = protocol._create_body(b'foobar', 1)
+ assert b''.join(bytes) == codecs.decode('000006000100000001666f6f626172', 'hex_codec')
def test_create_body_multiple_frames(self):
protocol = HTTP2Protocol(self.c)
- protocol.http2_settings[SettingsFrame.SETTINGS.SETTINGS_MAX_FRAME_SIZE] = 5
- bytes = protocol._create_body('foobarmehm42', 1)
+ protocol.http2_settings[SettingsFrame.MAX_FRAME_SIZE] = 5
+ bytes = protocol._create_body(b'foobarmehm42', 1)
assert len(bytes) == 3
- assert bytes[0] == '000005000000000001666f6f6261'.decode('hex')
- assert bytes[1] == '000005000000000001726d65686d'.decode('hex')
- assert bytes[2] == '0000020001000000013432'.decode('hex')
+ assert bytes[0] == codecs.decode('000005000000000001666f6f6261', 'hex_codec')
+ assert bytes[1] == codecs.decode('000005000000000001726d65686d', 'hex_codec')
+ assert bytes[2] == codecs.decode('0000020001000000013432', 'hex_codec')
class TestReadRequest(tservers.ServerTestBase):
class handler(tcp.BaseHandler):
+
def handle(self):
self.wfile.write(
- b'000003010400000001828487'.decode('hex'))
+ codecs.decode('000003010400000001828487', 'hex_codec'))
self.wfile.write(
- b'000006000100000001666f6f626172'.decode('hex'))
+ codecs.decode('000006000100000001666f6f626172', 'hex_codec'))
self.wfile.flush()
self.rfile.safe_read(9) # just to keep the connection alive a bit longer
@@ -309,7 +311,7 @@ class TestReadRequest(tservers.ServerTestBase):
req = protocol.read_request(NotImplemented)
assert req.stream_id
- assert req.headers.fields == [[':method', 'GET'], [':path', '/'], [':scheme', 'https']]
+ assert req.headers.fields == [[b':method', b'GET'], [b':path', b'/'], [b':scheme', b'https']]
assert req.content == b'foobar'
@@ -317,7 +319,7 @@ class TestReadRequestRelative(tservers.ServerTestBase):
class handler(tcp.BaseHandler):
def handle(self):
self.wfile.write(
- b'00000c0105000000014287d5af7e4d5a777f4481f9'.decode('hex'))
+ codecs.decode('00000c0105000000014287d5af7e4d5a777f4481f9', 'hex_codec'))
self.wfile.flush()
ssl = True
@@ -340,7 +342,7 @@ class TestReadRequestAbsolute(tservers.ServerTestBase):
class handler(tcp.BaseHandler):
def handle(self):
self.wfile.write(
- b'00001901050000000182448d9d29aee30c0e492c2a1170426366871c92585422e085'.decode('hex'))
+ codecs.decode('00001901050000000182448d9d29aee30c0e492c2a1170426366871c92585422e085', 'hex_codec'))
self.wfile.flush()
ssl = True
@@ -364,9 +366,9 @@ class TestReadRequestConnect(tservers.ServerTestBase):
class handler(tcp.BaseHandler):
def handle(self):
self.wfile.write(
- b'00001b0105000000014287bdab4e9c17b7ff44871c92585422e08541871c92585422e085'.decode('hex'))
+ codecs.decode('00001b0105000000014287bdab4e9c17b7ff44871c92585422e08541871c92585422e085', 'hex_codec'))
self.wfile.write(
- b'00001d0105000000014287bdab4e9c17b7ff44882f91d35d055c87a741882f91d35d055c87a7'.decode('hex'))
+ codecs.decode('00001d0105000000014287bdab4e9c17b7ff44882f91d35d055c87a741882f91d35d055c87a7', 'hex_codec'))
self.wfile.flush()
ssl = True
@@ -395,9 +397,9 @@ class TestReadResponse(tservers.ServerTestBase):
class handler(tcp.BaseHandler):
def handle(self):
self.wfile.write(
- b'00000801040000002a88628594e78c767f'.decode('hex'))
+ codecs.decode('00000801040000002a88628594e78c767f', 'hex_codec'))
self.wfile.write(
- b'00000600010000002a666f6f626172'.decode('hex'))
+ codecs.decode('00000600010000002a666f6f626172', 'hex_codec'))
self.wfile.flush()
self.rfile.safe_read(9) # just to keep the connection alive a bit longer
@@ -412,10 +414,10 @@ class TestReadResponse(tservers.ServerTestBase):
resp = protocol.read_response(NotImplemented, stream_id=42)
- assert resp.http_version == (2, 0)
+ assert resp.http_version == '2.0'
assert resp.status_code == 200
- assert resp.msg == ""
- assert resp.headers.fields == [[':status', '200'], ['etag', 'foobar']]
+ assert resp.msg == ''
+ assert resp.headers.fields == [[b':status', b'200'], [b'etag', b'foobar']]
assert resp.content == b'foobar'
assert resp.timestamp_end
@@ -424,7 +426,7 @@ class TestReadEmptyResponse(tservers.ServerTestBase):
class handler(tcp.BaseHandler):
def handle(self):
self.wfile.write(
- b'00000801050000002a88628594e78c767f'.decode('hex'))
+ codecs.decode('00000801050000002a88628594e78c767f', 'hex_codec'))
self.wfile.flush()
ssl = True
@@ -439,10 +441,10 @@ class TestReadEmptyResponse(tservers.ServerTestBase):
resp = protocol.read_response(NotImplemented, stream_id=42)
assert resp.stream_id == 42
- assert resp.http_version == (2, 0)
+ assert resp.http_version == '2.0'
assert resp.status_code == 200
- assert resp.msg == ""
- assert resp.headers.fields == [[':status', '200'], ['etag', 'foobar']]
+ assert resp.msg == ''
+ assert resp.headers.fields == [[b':status', b'200'], [b'etag', b'foobar']]
assert resp.content == b''
@@ -451,53 +453,53 @@ class TestAssembleRequest(object):
def test_request_simple(self):
bytes = HTTP2Protocol(self.c).assemble_request(http.Request(
- '',
- 'GET',
- 'https',
- '',
- '',
- '/',
- (2, 0),
+ b'',
+ b'GET',
+ b'https',
+ b'',
+ b'',
+ b'/',
+ b'2.0',
None,
None,
))
assert len(bytes) == 1
- assert bytes[0] == '00000d0105000000018284874188089d5c0b8170dc07'.decode('hex')
+ assert bytes[0] == codecs.decode('00000d0105000000018284874188089d5c0b8170dc07', 'hex_codec')
def test_request_with_stream_id(self):
req = http.Request(
- '',
- 'GET',
- 'https',
- '',
- '',
- '/',
- (2, 0),
+ b'',
+ b'GET',
+ b'https',
+ b'',
+ b'',
+ b'/',
+ b'2.0',
None,
None,
)
req.stream_id = 0x42
bytes = HTTP2Protocol(self.c).assemble_request(req)
assert len(bytes) == 1
- assert bytes[0] == '00000d0105000000428284874188089d5c0b8170dc07'.decode('hex')
+ assert bytes[0] == codecs.decode('00000d0105000000428284874188089d5c0b8170dc07', 'hex_codec')
def test_request_with_body(self):
bytes = HTTP2Protocol(self.c).assemble_request(http.Request(
- '',
- 'GET',
- 'https',
- '',
- '',
- '/',
- (2, 0),
- http.Headers([('foo', 'bar')]),
- 'foobar',
+ b'',
+ b'GET',
+ b'https',
+ b'',
+ b'',
+ b'/',
+ b'2.0',
+ http.Headers([(b'foo', b'bar')]),
+ b'foobar',
))
assert len(bytes) == 2
assert bytes[0] ==\
- '0000150104000000018284874188089d5c0b8170dc07408294e7838c767f'.decode('hex')
+ codecs.decode('0000150104000000018284874188089d5c0b8170dc07408294e7838c767f', 'hex_codec')
assert bytes[1] ==\
- '000006000100000001666f6f626172'.decode('hex')
+ codecs.decode('000006000100000001666f6f626172', 'hex_codec')
class TestAssembleResponse(object):
@@ -505,34 +507,34 @@ class TestAssembleResponse(object):
def test_simple(self):
bytes = HTTP2Protocol(self.c, is_server=True).assemble_response(http.Response(
- (2, 0),
+ b'2.0',
200,
))
assert len(bytes) == 1
assert bytes[0] ==\
- '00000101050000000288'.decode('hex')
+ codecs.decode('00000101050000000288', 'hex_codec')
def test_with_stream_id(self):
resp = http.Response(
- (2, 0),
+ b'2.0',
200,
)
resp.stream_id = 0x42
bytes = HTTP2Protocol(self.c, is_server=True).assemble_response(resp)
assert len(bytes) == 1
assert bytes[0] ==\
- '00000101050000004288'.decode('hex')
+ codecs.decode('00000101050000004288', 'hex_codec')
def test_with_body(self):
bytes = HTTP2Protocol(self.c, is_server=True).assemble_response(http.Response(
- (2, 0),
+ b'2.0',
200,
- '',
- Headers(foo="bar"),
- 'foobar'
+ b'',
+ http.Headers(foo=b"bar"),
+ b'foobar'
))
assert len(bytes) == 2
assert bytes[0] ==\
- '00000901040000000288408294e7838c767f'.decode('hex')
+ codecs.decode('00000901040000000288408294e7838c767f', 'hex_codec')
assert bytes[1] ==\
- '000006000100000002666f6f626172'.decode('hex')
+ codecs.decode('000006000100000002666f6f626172', 'hex_codec')
diff --git a/test/http/http2/test_frames.py b/test/http/http2/test_frames.py
deleted file mode 100644
index 9f41c74d..00000000
--- a/test/http/http2/test_frames.py
+++ /dev/null
@@ -1,669 +0,0 @@
-from io import BytesIO
-
-from netlib import tcp, tutils
-from netlib.http.http2.frame import *
-
-
-def hex_to_file(data):
- data = data.decode('hex')
- return tcp.Reader(BytesIO(data))
-
-
-def test_invalid_flags():
- tutils.raises(
- ValueError,
- DataFrame,
- flags=ContinuationFrame.FLAG_END_HEADERS,
- stream_id=0x1234567,
- payload='foobar')
-
-
-def test_frame_equality():
- a = DataFrame(
- length=6,
- flags=Frame.FLAG_END_STREAM,
- stream_id=0x1234567,
- payload='foobar')
- b = DataFrame(
- length=6,
- flags=Frame.FLAG_END_STREAM,
- stream_id=0x1234567,
- payload='foobar')
- assert a == b
-
-
-def test_too_large_frames():
- f = DataFrame(
- length=9000,
- flags=Frame.FLAG_END_STREAM,
- stream_id=0x1234567,
- payload='foobar' * 3000)
- tutils.raises(HttpSyntaxException, f.to_bytes)
-
-
-def test_data_frame_to_bytes():
- f = DataFrame(
- length=6,
- flags=Frame.FLAG_END_STREAM,
- stream_id=0x1234567,
- payload='foobar')
- assert f.to_bytes().encode('hex') == '000006000101234567666f6f626172'
-
- f = DataFrame(
- length=11,
- flags=(Frame.FLAG_END_STREAM | Frame.FLAG_PADDED),
- stream_id=0x1234567,
- payload='foobar',
- pad_length=3)
- assert f.to_bytes().encode('hex') == '00000a00090123456703666f6f626172000000'
-
- f = DataFrame(
- length=6,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- payload='foobar')
- tutils.raises(ValueError, f.to_bytes)
-
-
-def test_data_frame_from_bytes():
- f = Frame.from_file(hex_to_file('000006000101234567666f6f626172'))
- assert isinstance(f, DataFrame)
- assert f.length == 6
- assert f.TYPE == DataFrame.TYPE
- assert f.flags == Frame.FLAG_END_STREAM
- assert f.stream_id == 0x1234567
- assert f.payload == 'foobar'
-
- f = Frame.from_file(hex_to_file('00000a00090123456703666f6f626172000000'))
- assert isinstance(f, DataFrame)
- assert f.length == 10
- assert f.TYPE == DataFrame.TYPE
- assert f.flags == Frame.FLAG_END_STREAM | Frame.FLAG_PADDED
- assert f.stream_id == 0x1234567
- assert f.payload == 'foobar'
-
-
-def test_data_frame_human_readable():
- f = DataFrame(
- length=11,
- flags=(Frame.FLAG_END_STREAM | Frame.FLAG_PADDED),
- stream_id=0x1234567,
- payload='foobar',
- pad_length=3)
- assert f.human_readable()
-
-
-def test_headers_frame_to_bytes():
- f = HeadersFrame(
- length=6,
- flags=(Frame.FLAG_NO_FLAGS),
- stream_id=0x1234567,
- header_block_fragment='668594e75e31d9'.decode('hex'))
- assert f.to_bytes().encode('hex') == '000007010001234567668594e75e31d9'
-
- f = HeadersFrame(
- length=10,
- flags=(HeadersFrame.FLAG_PADDED),
- stream_id=0x1234567,
- header_block_fragment='668594e75e31d9'.decode('hex'),
- pad_length=3)
- assert f.to_bytes().encode('hex') == '00000b01080123456703668594e75e31d9000000'
-
- f = HeadersFrame(
- length=10,
- flags=(HeadersFrame.FLAG_PRIORITY),
- stream_id=0x1234567,
- header_block_fragment='668594e75e31d9'.decode('hex'),
- exclusive=True,
- stream_dependency=0x7654321,
- weight=42)
- assert f.to_bytes().encode('hex') == '00000c012001234567876543212a668594e75e31d9'
-
- f = HeadersFrame(
- length=14,
- flags=(HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY),
- stream_id=0x1234567,
- header_block_fragment='668594e75e31d9'.decode('hex'),
- pad_length=3,
- exclusive=True,
- stream_dependency=0x7654321,
- weight=42)
- assert f.to_bytes().encode('hex') == '00001001280123456703876543212a668594e75e31d9000000'
-
- f = HeadersFrame(
- length=14,
- flags=(HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY),
- stream_id=0x1234567,
- header_block_fragment='668594e75e31d9'.decode('hex'),
- pad_length=3,
- exclusive=False,
- stream_dependency=0x7654321,
- weight=42)
- assert f.to_bytes().encode('hex') == '00001001280123456703076543212a668594e75e31d9000000'
-
- f = HeadersFrame(
- length=6,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- header_block_fragment='668594e75e31d9'.decode('hex'))
- tutils.raises(ValueError, f.to_bytes)
-
-
-def test_headers_frame_from_bytes():
- f = Frame.from_file(hex_to_file(
- '000007010001234567668594e75e31d9'))
- assert isinstance(f, HeadersFrame)
- assert f.length == 7
- assert f.TYPE == HeadersFrame.TYPE
- assert f.flags == Frame.FLAG_NO_FLAGS
- assert f.stream_id == 0x1234567
- assert f.header_block_fragment == '668594e75e31d9'.decode('hex')
-
- f = Frame.from_file(hex_to_file(
- '00000b01080123456703668594e75e31d9000000'))
- assert isinstance(f, HeadersFrame)
- assert f.length == 11
- assert f.TYPE == HeadersFrame.TYPE
- assert f.flags == HeadersFrame.FLAG_PADDED
- assert f.stream_id == 0x1234567
- assert f.header_block_fragment == '668594e75e31d9'.decode('hex')
-
- f = Frame.from_file(hex_to_file(
- '00000c012001234567876543212a668594e75e31d9'))
- assert isinstance(f, HeadersFrame)
- assert f.length == 12
- assert f.TYPE == HeadersFrame.TYPE
- assert f.flags == HeadersFrame.FLAG_PRIORITY
- assert f.stream_id == 0x1234567
- assert f.header_block_fragment == '668594e75e31d9'.decode('hex')
- assert f.exclusive == True
- assert f.stream_dependency == 0x7654321
- assert f.weight == 42
-
- f = Frame.from_file(hex_to_file(
- '00001001280123456703876543212a668594e75e31d9000000'))
- assert isinstance(f, HeadersFrame)
- assert f.length == 16
- assert f.TYPE == HeadersFrame.TYPE
- assert f.flags == HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY
- assert f.stream_id == 0x1234567
- assert f.header_block_fragment == '668594e75e31d9'.decode('hex')
- assert f.exclusive == True
- assert f.stream_dependency == 0x7654321
- assert f.weight == 42
-
- f = Frame.from_file(hex_to_file(
- '00001001280123456703076543212a668594e75e31d9000000'))
- assert isinstance(f, HeadersFrame)
- assert f.length == 16
- assert f.TYPE == HeadersFrame.TYPE
- assert f.flags == HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY
- assert f.stream_id == 0x1234567
- assert f.header_block_fragment == '668594e75e31d9'.decode('hex')
- assert f.exclusive == False
- assert f.stream_dependency == 0x7654321
- assert f.weight == 42
-
-
-def test_headers_frame_human_readable():
- f = HeadersFrame(
- length=7,
- flags=(HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY),
- stream_id=0x1234567,
- header_block_fragment=b'',
- pad_length=3,
- exclusive=False,
- stream_dependency=0x7654321,
- weight=42)
- assert f.human_readable()
-
- f = HeadersFrame(
- length=14,
- flags=(HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY),
- stream_id=0x1234567,
- header_block_fragment='668594e75e31d9'.decode('hex'),
- pad_length=3,
- exclusive=False,
- stream_dependency=0x7654321,
- weight=42)
- assert f.human_readable()
-
-
-def test_priority_frame_to_bytes():
- f = PriorityFrame(
- length=5,
- flags=(Frame.FLAG_NO_FLAGS),
- stream_id=0x1234567,
- exclusive=True,
- stream_dependency=0x0,
- weight=42)
- assert f.to_bytes().encode('hex') == '000005020001234567800000002a'
-
- f = PriorityFrame(
- length=5,
- flags=(Frame.FLAG_NO_FLAGS),
- stream_id=0x1234567,
- exclusive=False,
- stream_dependency=0x7654321,
- weight=21)
- assert f.to_bytes().encode('hex') == '0000050200012345670765432115'
-
- f = PriorityFrame(
- length=5,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- stream_dependency=0x1234567)
- tutils.raises(ValueError, f.to_bytes)
-
-
-def test_priority_frame_from_bytes():
- f = Frame.from_file(hex_to_file('000005020001234567876543212a'))
- assert isinstance(f, PriorityFrame)
- assert f.length == 5
- assert f.TYPE == PriorityFrame.TYPE
- assert f.flags == Frame.FLAG_NO_FLAGS
- assert f.stream_id == 0x1234567
- assert f.exclusive == True
- assert f.stream_dependency == 0x7654321
- assert f.weight == 42
-
- f = Frame.from_file(hex_to_file('0000050200012345670765432115'))
- assert isinstance(f, PriorityFrame)
- assert f.length == 5
- assert f.TYPE == PriorityFrame.TYPE
- assert f.flags == Frame.FLAG_NO_FLAGS
- assert f.stream_id == 0x1234567
- assert f.exclusive == False
- assert f.stream_dependency == 0x7654321
- assert f.weight == 21
-
-
-def test_priority_frame_human_readable():
- f = PriorityFrame(
- length=5,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x1234567,
- exclusive=False,
- stream_dependency=0x7654321,
- weight=21)
- assert f.human_readable()
-
-
-def test_rst_stream_frame_to_bytes():
- f = RstStreamFrame(
- length=4,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x1234567,
- error_code=0x7654321)
- assert f.to_bytes().encode('hex') == '00000403000123456707654321'
-
- f = RstStreamFrame(
- length=4,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0)
- tutils.raises(ValueError, f.to_bytes)
-
-
-def test_rst_stream_frame_from_bytes():
- f = Frame.from_file(hex_to_file('00000403000123456707654321'))
- assert isinstance(f, RstStreamFrame)
- assert f.length == 4
- assert f.TYPE == RstStreamFrame.TYPE
- assert f.flags == Frame.FLAG_NO_FLAGS
- assert f.stream_id == 0x1234567
- assert f.error_code == 0x07654321
-
-
-def test_rst_stream_frame_human_readable():
- f = RstStreamFrame(
- length=4,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x1234567,
- error_code=0x7654321)
- assert f.human_readable()
-
-
-def test_settings_frame_to_bytes():
- f = SettingsFrame(
- length=0,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0)
- assert f.to_bytes().encode('hex') == '000000040000000000'
-
- f = SettingsFrame(
- length=0,
- flags=SettingsFrame.FLAG_ACK,
- stream_id=0x0)
- assert f.to_bytes().encode('hex') == '000000040100000000'
-
- f = SettingsFrame(
- length=6,
- flags=SettingsFrame.FLAG_ACK,
- stream_id=0x0,
- settings={
- SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1})
- assert f.to_bytes().encode('hex') == '000006040100000000000200000001'
-
- f = SettingsFrame(
- length=12,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- settings={
- SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1,
- SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS: 0x12345678})
- assert f.to_bytes().encode('hex') == '00000c040000000000000200000001000312345678'
-
- f = SettingsFrame(
- length=0,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x1234567)
- tutils.raises(ValueError, f.to_bytes)
-
-
-def test_settings_frame_from_bytes():
- f = Frame.from_file(hex_to_file('000000040000000000'))
- assert isinstance(f, SettingsFrame)
- assert f.length == 0
- assert f.TYPE == SettingsFrame.TYPE
- assert f.flags == Frame.FLAG_NO_FLAGS
- assert f.stream_id == 0x0
-
- f = Frame.from_file(hex_to_file('000000040100000000'))
- assert isinstance(f, SettingsFrame)
- assert f.length == 0
- assert f.TYPE == SettingsFrame.TYPE
- assert f.flags == SettingsFrame.FLAG_ACK
- assert f.stream_id == 0x0
-
- f = Frame.from_file(hex_to_file('000006040100000000000200000001'))
- assert isinstance(f, SettingsFrame)
- assert f.length == 6
- assert f.TYPE == SettingsFrame.TYPE
- assert f.flags == SettingsFrame.FLAG_ACK, 0x0
- assert f.stream_id == 0x0
- assert len(f.settings) == 1
- assert f.settings[SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH] == 1
-
- f = Frame.from_file(hex_to_file(
- '00000c040000000000000200000001000312345678'))
- assert isinstance(f, SettingsFrame)
- assert f.length == 12
- assert f.TYPE == SettingsFrame.TYPE
- assert f.flags == Frame.FLAG_NO_FLAGS
- assert f.stream_id == 0x0
- assert len(f.settings) == 2
- assert f.settings[SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH] == 1
- assert f.settings[SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS] == 0x12345678
-
-
-def test_settings_frame_human_readable():
- f = SettingsFrame(
- length=12,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- settings={})
- assert f.human_readable()
-
- f = SettingsFrame(
- length=12,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- settings={
- SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1,
- SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS: 0x12345678})
- assert f.human_readable()
-
-
-def test_push_promise_frame_to_bytes():
- f = PushPromiseFrame(
- length=10,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x1234567,
- promised_stream=0x7654321,
- header_block_fragment='foobar')
- assert f.to_bytes().encode('hex') == '00000a05000123456707654321666f6f626172'
-
- f = PushPromiseFrame(
- length=14,
- flags=HeadersFrame.FLAG_PADDED,
- stream_id=0x1234567,
- promised_stream=0x7654321,
- header_block_fragment='foobar',
- pad_length=3)
- assert f.to_bytes().encode('hex') == '00000e0508012345670307654321666f6f626172000000'
-
- f = PushPromiseFrame(
- length=4,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- promised_stream=0x1234567)
- tutils.raises(ValueError, f.to_bytes)
-
- f = PushPromiseFrame(
- length=4,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x1234567,
- promised_stream=0x0)
- tutils.raises(ValueError, f.to_bytes)
-
-
-def test_push_promise_frame_from_bytes():
- f = Frame.from_file(hex_to_file('00000a05000123456707654321666f6f626172'))
- assert isinstance(f, PushPromiseFrame)
- assert f.length == 10
- assert f.TYPE == PushPromiseFrame.TYPE
- assert f.flags == Frame.FLAG_NO_FLAGS
- assert f.stream_id == 0x1234567
- assert f.header_block_fragment == 'foobar'
-
- f = Frame.from_file(hex_to_file(
- '00000e0508012345670307654321666f6f626172000000'))
- assert isinstance(f, PushPromiseFrame)
- assert f.length == 14
- assert f.TYPE == PushPromiseFrame.TYPE
- assert f.flags == PushPromiseFrame.FLAG_PADDED
- assert f.stream_id == 0x1234567
- assert f.header_block_fragment == 'foobar'
-
-
-def test_push_promise_frame_human_readable():
- f = PushPromiseFrame(
- length=14,
- flags=HeadersFrame.FLAG_PADDED,
- stream_id=0x1234567,
- promised_stream=0x7654321,
- header_block_fragment='foobar',
- pad_length=3)
- assert f.human_readable()
-
-
-def test_ping_frame_to_bytes():
- f = PingFrame(
- length=8,
- flags=PingFrame.FLAG_ACK,
- stream_id=0x0,
- payload=b'foobar')
- assert f.to_bytes().encode('hex') == '000008060100000000666f6f6261720000'
-
- f = PingFrame(
- length=8,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- payload=b'foobardeadbeef')
- assert f.to_bytes().encode('hex') == '000008060000000000666f6f6261726465'
-
- f = PingFrame(
- length=8,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x1234567)
- tutils.raises(ValueError, f.to_bytes)
-
-
-def test_ping_frame_from_bytes():
- f = Frame.from_file(hex_to_file('000008060100000000666f6f6261720000'))
- assert isinstance(f, PingFrame)
- assert f.length == 8
- assert f.TYPE == PingFrame.TYPE
- assert f.flags == PingFrame.FLAG_ACK
- assert f.stream_id == 0x0
- assert f.payload == b'foobar\0\0'
-
- f = Frame.from_file(hex_to_file('000008060000000000666f6f6261726465'))
- assert isinstance(f, PingFrame)
- assert f.length == 8
- assert f.TYPE == PingFrame.TYPE
- assert f.flags == Frame.FLAG_NO_FLAGS
- assert f.stream_id == 0x0
- assert f.payload == b'foobarde'
-
-
-def test_ping_frame_human_readable():
- f = PingFrame(
- length=8,
- flags=PingFrame.FLAG_ACK,
- stream_id=0x0,
- payload=b'foobar')
- assert f.human_readable()
-
-
-def test_goaway_frame_to_bytes():
- f = GoAwayFrame(
- length=8,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- last_stream=0x1234567,
- error_code=0x87654321,
- data=b'')
- assert f.to_bytes().encode('hex') == '0000080700000000000123456787654321'
-
- f = GoAwayFrame(
- length=14,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- last_stream=0x1234567,
- error_code=0x87654321,
- data=b'foobar')
- assert f.to_bytes().encode('hex') == '00000e0700000000000123456787654321666f6f626172'
-
- f = GoAwayFrame(
- length=8,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x1234567,
- last_stream=0x1234567,
- error_code=0x87654321)
- tutils.raises(ValueError, f.to_bytes)
-
-
-def test_goaway_frame_from_bytes():
- f = Frame.from_file(hex_to_file(
- '0000080700000000000123456787654321'))
- assert isinstance(f, GoAwayFrame)
- assert f.length == 8
- assert f.TYPE == GoAwayFrame.TYPE
- assert f.flags == Frame.FLAG_NO_FLAGS
- assert f.stream_id == 0x0
- assert f.last_stream == 0x1234567
- assert f.error_code == 0x87654321
- assert f.data == b''
-
- f = Frame.from_file(hex_to_file(
- '00000e0700000000000123456787654321666f6f626172'))
- assert isinstance(f, GoAwayFrame)
- assert f.length == 14
- assert f.TYPE == GoAwayFrame.TYPE
- assert f.flags == Frame.FLAG_NO_FLAGS
- assert f.stream_id == 0x0
- assert f.last_stream == 0x1234567
- assert f.error_code == 0x87654321
- assert f.data == b'foobar'
-
-
-def test_go_away_frame_human_readable():
- f = GoAwayFrame(
- length=14,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- last_stream=0x1234567,
- error_code=0x87654321,
- data=b'foobar')
- assert f.human_readable()
-
-
-def test_window_update_frame_to_bytes():
- f = WindowUpdateFrame(
- length=4,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- window_size_increment=0x1234567)
- assert f.to_bytes().encode('hex') == '00000408000000000001234567'
-
- f = WindowUpdateFrame(
- length=4,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x1234567,
- window_size_increment=0x7654321)
- assert f.to_bytes().encode('hex') == '00000408000123456707654321'
-
- f = WindowUpdateFrame(
- length=4,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x0,
- window_size_increment=0xdeadbeef)
- tutils.raises(ValueError, f.to_bytes)
-
- f = WindowUpdateFrame(4, Frame.FLAG_NO_FLAGS, 0x0, window_size_increment=0)
- tutils.raises(ValueError, f.to_bytes)
-
-
-def test_window_update_frame_from_bytes():
- f = Frame.from_file(hex_to_file('00000408000000000001234567'))
- assert isinstance(f, WindowUpdateFrame)
- assert f.length == 4
- assert f.TYPE == WindowUpdateFrame.TYPE
- assert f.flags == Frame.FLAG_NO_FLAGS
- assert f.stream_id == 0x0
- assert f.window_size_increment == 0x1234567
-
-
-def test_window_update_frame_human_readable():
- f = WindowUpdateFrame(
- length=4,
- flags=Frame.FLAG_NO_FLAGS,
- stream_id=0x1234567,
- window_size_increment=0x7654321)
- assert f.human_readable()
-
-
-def test_continuation_frame_to_bytes():
- f = ContinuationFrame(
- length=6,
- flags=ContinuationFrame.FLAG_END_HEADERS,
- stream_id=0x1234567,
- header_block_fragment='foobar')
- assert f.to_bytes().encode('hex') == '000006090401234567666f6f626172'
-
- f = ContinuationFrame(
- length=6,
- flags=ContinuationFrame.FLAG_END_HEADERS,
- stream_id=0x0,
- header_block_fragment='foobar')
- tutils.raises(ValueError, f.to_bytes)
-
-
-def test_continuation_frame_from_bytes():
- f = Frame.from_file(hex_to_file('000006090401234567666f6f626172'))
- assert isinstance(f, ContinuationFrame)
- assert f.length == 6
- assert f.TYPE == ContinuationFrame.TYPE
- assert f.flags == ContinuationFrame.FLAG_END_HEADERS
- assert f.stream_id == 0x1234567
- assert f.header_block_fragment == 'foobar'
-
-
-def test_continuation_frame_human_readable():
- f = ContinuationFrame(
- length=6,
- flags=ContinuationFrame.FLAG_END_HEADERS,
- stream_id=0x1234567,
- header_block_fragment='foobar')
- assert f.human_readable()