Merge pull request #62 from Kriechi/h2-client

H2 client
This commit is contained in:
Aldo Cortesi 2015-05-30 12:01:19 +12:00
commit 5e4850d3b3
8 changed files with 417 additions and 56 deletions

View File

@ -1,4 +1,5 @@
import struct
from hpack.hpack import Encoder, Decoder
from .. import utils
from functools import reduce
@ -25,9 +26,27 @@ class Frame(object):
raise ValueError('invalid flags detected.')
self.length = length
self.type = self.TYPE
self.flags = flags
self.stream_id = stream_id
@classmethod
def from_file(self, fp):
"""
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]
payload = fp.safe_read(length)
return FRAMES[fields[2]].from_bytes(length, flags, stream_id, payload)
@classmethod
def from_bytes(self, data):
fields = struct.unpack("!HBBBL", data[:9])
@ -49,6 +68,24 @@ class Frame(object):
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):
return "\n".join([
"============================================================",
"length: %d bytes" % self.length,
"type: %s (%#x)" % (self.__class__.__name__, self.TYPE),
"flags: %#x" % self.flags,
"stream_id: %#x" % self.stream_id,
"------------------------------------------------------------",
self.payload_human_readable(),
"============================================================",
])
def __eq__(self, other):
return self.to_bytes() == other.to_bytes()
@ -89,15 +126,21 @@ class DataFrame(Frame):
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, length=0, flags=Frame.FLAG_NO_FLAGS, stream_id=0x0, header_block_fragment=b'',
pad_length=0, exclusive=False, stream_dependency=0x0, weight=0):
def __init__(self, length=0, flags=Frame.FLAG_NO_FLAGS, stream_id=0x0, headers=None, pad_length=0, exclusive=False, stream_dependency=0x0, weight=0):
super(HeadersFrame, self).__init__(length, flags, stream_id)
self.header_block_fragment = header_block_fragment
if headers is None:
headers = []
self.headers = headers
self.pad_length = pad_length
self.exclusive = exclusive
self.stream_dependency = stream_dependency
@ -109,15 +152,18 @@ class HeadersFrame(Frame):
if f.flags & self.FLAG_PADDED:
f.pad_length = struct.unpack('!B', payload[0])[0]
f.header_block_fragment = payload[1:-f.pad_length]
header_block_fragment = payload[1:-f.pad_length]
else:
f.header_block_fragment = payload[0:]
header_block_fragment = payload[0:]
if f.flags & self.FLAG_PRIORITY:
f.stream_dependency, f.weight = struct.unpack('!LB', f.header_block_fragment[:5])
f.stream_dependency, f.weight = struct.unpack('!LB', header_block_fragment[:5])
f.exclusive = bool(f.stream_dependency >> 31)
f.stream_dependency &= 0x7FFFFFFF
f.header_block_fragment = f.header_block_fragment[5:]
header_block_fragment = header_block_fragment[5:]
for header, value in Decoder().decode(header_block_fragment):
f.headers.append((header, value))
return f
@ -132,13 +178,32 @@ class HeadersFrame(Frame):
if self.flags & self.FLAG_PRIORITY:
b += struct.pack('!LB', (int(self.exclusive) << 31) | self.stream_dependency, self.weight)
b += bytes(self.header_block_fragment)
b += Encoder().encode(self.headers)
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)
if not self.headers:
s.append("headers: None")
else:
for header, value in self.headers:
s.append("%s: %s" % (header, value))
return "\n".join(s)
class PriorityFrame(Frame):
TYPE = 0x2
@ -169,6 +234,13 @@ class PriorityFrame(Frame):
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
@ -190,6 +262,9 @@ class RstStreamFrame(Frame):
return struct.pack('!L', self.error_code)
def payload_human_readable(self):
return "error code: %#x" % self.error_code
class SettingsFrame(Frame):
TYPE = 0x4
@ -204,8 +279,12 @@ class SettingsFrame(Frame):
SETTINGS_MAX_HEADER_LIST_SIZE=0x6,
)
def __init__(self, length=0, flags=Frame.FLAG_NO_FLAGS, stream_id=0x0, settings={}):
def __init__(self, length=0, flags=Frame.FLAG_NO_FLAGS, stream_id=0x0, settings=None):
super(SettingsFrame, self).__init__(length, flags, stream_id)
if settings is None:
settings = {}
self.settings = settings
@classmethod
@ -228,6 +307,17 @@ class SettingsFrame(Frame):
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
@ -273,6 +363,16 @@ class PushPromiseFrame(Frame):
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" % str(self.header_block_fragment))
return "\n".join(s)
class PingFrame(Frame):
TYPE = 0x6
@ -296,6 +396,9 @@ class PingFrame(Frame):
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
@ -325,6 +428,13 @@ class GoAwayFrame(Frame):
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
@ -349,6 +459,9 @@ class WindowUpdateFrame(Frame):
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
@ -370,6 +483,9 @@ class ContinuationFrame(Frame):
return self.header_block_fragment
def payload_human_readable(self):
return "header_block_fragment: %s" % str(self.header_block_fragment)
_FRAME_CLASSES = [
DataFrame,
HeadersFrame,

View File

@ -1,3 +1,5 @@
from .. import utils, odict, tcp
from frame import *
# "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
CLIENT_CONNECTION_PREFACE = '505249202a20485454502f322e300d0a0d0a534d0d0a0d0a'
@ -18,3 +20,66 @@ ERROR_CODES = utils.BiDi(
INADEQUATE_SECURITY=0xc,
HTTP_1_1_REQUIRED=0xd
)
class H2Client(tcp.TCPClient):
ALPN_PROTO_H2 = b'h2'
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,
}
def __init__(self, address, source_address=None):
super(H2Client, self).__init__(address, source_address)
self.settings = self.DEFAULT_SETTINGS.copy()
def connect(self, send_preface=True):
super(H2Client, self).connect()
self.convert_to_ssl(alpn_protos=[self.ALPN_PROTO_H2])
alp = self.get_alpn_proto_negotiated()
if alp != b'h2':
raise NotImplementedError("H2Client can not handle unknown protocol: %s" % alp)
print "-> Successfully negotiated 'h2' application layer protocol."
if send_preface:
self.wfile.write(bytes(CLIENT_CONNECTION_PREFACE.decode('hex')))
self.send_frame(SettingsFrame())
frame = Frame.from_file(self.rfile)
print frame.human_readable()
assert isinstance(frame, SettingsFrame)
self.apply_settings(frame.settings)
print "-> Connection Preface completed."
print "-> H2Client is ready..."
def send_frame(self, frame):
self.wfile.write(frame.to_bytes())
self.wfile.flush()
def read_frame(self):
frame = Frame.from_file(self.rfile)
if isinstance(frame, SettingsFrame):
self.apply_settings(frame.settings)
return frame
def apply_settings(self, settings):
for setting, value in settings.items():
old_value = self.settings[setting]
if not old_value:
old_value = '-'
self.settings[setting] = value
print "-> Setting changed: %s to %d (was %s)" %
(SettingsFrame.SETTINGS.get_name(setting), value, str(old_value))
self.send_frame(SettingsFrame(flags=Frame.FLAG_ACK))
print "-> New settings acknowledged."

View File

@ -360,7 +360,9 @@ class _Connection(object):
def _create_ssl_context(self,
method=SSLv23_METHOD,
options=(OP_NO_SSLv2 | OP_NO_SSLv3),
cipher_list=None
cipher_list=None,
alpn_protos=None,
alpn_select=None,
):
"""
:param method: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, TLSv1_METHOD or TLSv1_1_METHOD
@ -389,6 +391,17 @@ class _Connection(object):
if log_ssl_key:
context.set_info_callback(log_ssl_key)
# advertise application layer protocols
if alpn_protos is not None:
context.set_alpn_protos(alpn_protos)
# select application layer protocol
if alpn_select is not None:
def alpn_select_f(conn, options):
return bytes(alpn_select)
context.set_alpn_select_callback(alpn_select_f)
return context
@ -413,8 +426,8 @@ class TCPClient(_Connection):
self.ssl_established = False
self.sni = None
def create_ssl_context(self, cert=None, **sslctx_kwargs):
context = self._create_ssl_context(**sslctx_kwargs)
def create_ssl_context(self, cert=None, alpn_protos=None, **sslctx_kwargs):
context = self._create_ssl_context(alpn_protos=alpn_protos, **sslctx_kwargs)
# Client Certs
if cert:
try:
@ -424,13 +437,13 @@ class TCPClient(_Connection):
raise NetLibError("SSL client certificate error: %s" % str(v))
return context
def convert_to_ssl(self, sni=None, **sslctx_kwargs):
def convert_to_ssl(self, sni=None, alpn_protos=None, **sslctx_kwargs):
"""
cert: Path to a file containing both client cert and private key.
options: A bit field consisting of OpenSSL.SSL.OP_* values
"""
context = self.create_ssl_context(**sslctx_kwargs)
context = self.create_ssl_context(alpn_protos=alpn_protos, **sslctx_kwargs)
self.connection = SSL.Connection(context, self.connection)
if sni:
self.sni = sni
@ -465,6 +478,9 @@ class TCPClient(_Connection):
def gettimeout(self):
return self.connection.gettimeout()
def get_alpn_proto_negotiated(self):
return self.connection.get_alpn_proto_negotiated()
class BaseHandler(_Connection):
@ -492,6 +508,7 @@ class BaseHandler(_Connection):
request_client_cert=None,
chain_file=None,
dhparams=None,
alpn_select=None,
**sslctx_kwargs):
"""
cert: A certutils.SSLCert object.
@ -517,7 +534,8 @@ class BaseHandler(_Connection):
we may be able to make the proper behaviour the default again, but
until then we're conservative.
"""
context = self._create_ssl_context(**sslctx_kwargs)
context = self._create_ssl_context(alpn_select=alpn_select, **sslctx_kwargs)
context.use_privatekey(key)
context.use_certificate(cert.x509)
@ -542,12 +560,13 @@ class BaseHandler(_Connection):
return context
def convert_to_ssl(self, cert, key, **sslctx_kwargs):
def convert_to_ssl(self, cert, key, alpn_select=None, **sslctx_kwargs):
"""
Convert connection to SSL.
For a list of parameters, see BaseHandler._create_ssl_context(...)
"""
context = self.create_ssl_context(cert, key, **sslctx_kwargs)
context = self.create_ssl_context(cert, key, alpn_select=alpn_select, **sslctx_kwargs)
self.connection = SSL.Connection(context, self.connection)
self.connection.set_accept_state()
try:

View File

@ -82,7 +82,8 @@ class TServer(tcp.TCPServer):
request_client_cert=self.ssl["request_client_cert"],
cipher_list=self.ssl.get("cipher_list", None),
dhparams=self.ssl.get("dhparams", None),
chain_file=self.ssl.get("chain_file", None)
chain_file=self.ssl.get("chain_file", None),
alpn_select=self.ssl.get("alpn_select", None)
)
h.handle()
h.finish()

View File

@ -42,7 +42,8 @@ setup(
"pyasn1>=0.1.7",
"pyOpenSSL>=0.15.1",
"cryptography>=0.9",
"passlib>=1.6.2"
"passlib>=1.6.2",
"hpack>=1.0.1"
],
extras_require={
'dev': [

18
test/h2/example.py Normal file
View File

@ -0,0 +1,18 @@
from netlib.h2.frame import *
from netlib.h2.h2 import *
c = H2Client(("127.0.0.1", 443))
c.connect()
c.send_frame(HeadersFrame(
flags=(Frame.FLAG_END_HEADERS | Frame.FLAG_END_STREAM),
stream_id=0x1,
headers=[
(b':method', 'GET'),
(b':path', b'/index.html'),
(b':scheme', b'https'),
(b':authority', b'localhost'),
]))
while True:
print c.read_frame().human_readable()

View File

@ -3,9 +3,9 @@ import tutils
from nose.tools import assert_equal
# TODO test stream association if valid or not
def test_invalid_flags():
tutils.raises(ValueError, DataFrame, ContinuationFrame.FLAG_END_HEADERS, 0x1234567, 'foobar')
@ -49,79 +49,138 @@ def test_data_frame_from_bytes():
assert_equal(f.payload, 'foobar')
def test_data_frame_human_readable():
f = DataFrame(11, Frame.FLAG_END_STREAM | Frame.FLAG_PADDED, 0x1234567, 'foobar', pad_length=3)
assert f.human_readable()
def test_headers_frame_to_bytes():
f = HeadersFrame(6, Frame.FLAG_NO_FLAGS, 0x1234567, 'foobar')
assert_equal(f.to_bytes().encode('hex'), '000006010001234567666f6f626172')
f = HeadersFrame(
6,
Frame.FLAG_NO_FLAGS,
0x1234567,
headers=[('host', 'foo.bar')])
assert_equal(f.to_bytes().encode('hex'), '000007010001234567668594e75e31d9')
f = HeadersFrame(10, HeadersFrame.FLAG_PADDED, 0x1234567, 'foobar', pad_length=3)
assert_equal(f.to_bytes().encode('hex'), '00000a01080123456703666f6f626172000000')
f = HeadersFrame(
10,
HeadersFrame.FLAG_PADDED,
0x1234567,
headers=[('host', 'foo.bar')],
pad_length=3)
assert_equal(f.to_bytes().encode('hex'), '00000b01080123456703668594e75e31d9000000')
f = HeadersFrame(10, HeadersFrame.FLAG_PRIORITY, 0x1234567, 'foobar', exclusive=True, stream_dependency=0x7654321, weight=42)
assert_equal(f.to_bytes().encode('hex'), '00000b012001234567876543212a666f6f626172')
f = HeadersFrame(
10,
HeadersFrame.FLAG_PRIORITY,
0x1234567,
headers=[('host', 'foo.bar')],
exclusive=True,
stream_dependency=0x7654321,
weight=42)
assert_equal(f.to_bytes().encode('hex'), '00000c012001234567876543212a668594e75e31d9')
f = HeadersFrame(14, HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY, 0x1234567,
'foobar', pad_length=3, exclusive=True, stream_dependency=0x7654321, weight=42)
assert_equal(f.to_bytes().encode('hex'), '00000f01280123456703876543212a666f6f626172000000')
f = HeadersFrame(
14,
HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY,
0x1234567,
headers=[('host', 'foo.bar')],
pad_length=3,
exclusive=True,
stream_dependency=0x7654321,
weight=42)
assert_equal(f.to_bytes().encode('hex'), '00001001280123456703876543212a668594e75e31d9000000')
f = HeadersFrame(14, HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY, 0x1234567, 'foobar',
pad_length=3, exclusive=False, stream_dependency=0x7654321, weight=42)
assert_equal(f.to_bytes().encode('hex'), '00000f01280123456703076543212a666f6f626172000000')
f = HeadersFrame(
14,
HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY,
0x1234567,
headers=[('host', 'foo.bar')],
pad_length=3,
exclusive=False,
stream_dependency=0x7654321,
weight=42)
assert_equal(f.to_bytes().encode('hex'), '00001001280123456703076543212a668594e75e31d9000000')
f = HeadersFrame(6, Frame.FLAG_NO_FLAGS, 0x0, 'foobar')
tutils.raises(ValueError, f.to_bytes)
def test_headers_frame_from_bytes():
f = Frame.from_bytes('000006010001234567666f6f626172'.decode('hex'))
f = Frame.from_bytes('000007010001234567668594e75e31d9'.decode('hex'))
assert isinstance(f, HeadersFrame)
assert_equal(f.length, 6)
assert_equal(f.length, 7)
assert_equal(f.TYPE, HeadersFrame.TYPE)
assert_equal(f.flags, Frame.FLAG_NO_FLAGS)
assert_equal(f.stream_id, 0x1234567)
assert_equal(f.header_block_fragment, 'foobar')
assert_equal(f.headers, [('host', 'foo.bar')])
f = Frame.from_bytes('00000a01080123456703666f6f626172000000'.decode('hex'))
assert isinstance(f, HeadersFrame)
assert_equal(f.length, 10)
assert_equal(f.TYPE, HeadersFrame.TYPE)
assert_equal(f.flags, HeadersFrame.FLAG_PADDED)
assert_equal(f.stream_id, 0x1234567)
assert_equal(f.header_block_fragment, 'foobar')
f = Frame.from_bytes('00000b012001234567876543212a666f6f626172'.decode('hex'))
f = Frame.from_bytes('00000b01080123456703668594e75e31d9000000'.decode('hex'))
assert isinstance(f, HeadersFrame)
assert_equal(f.length, 11)
assert_equal(f.TYPE, HeadersFrame.TYPE)
assert_equal(f.flags, HeadersFrame.FLAG_PADDED)
assert_equal(f.stream_id, 0x1234567)
assert_equal(f.headers, [('host', 'foo.bar')])
f = Frame.from_bytes('00000c012001234567876543212a668594e75e31d9'.decode('hex'))
assert isinstance(f, HeadersFrame)
assert_equal(f.length, 12)
assert_equal(f.TYPE, HeadersFrame.TYPE)
assert_equal(f.flags, HeadersFrame.FLAG_PRIORITY)
assert_equal(f.stream_id, 0x1234567)
assert_equal(f.header_block_fragment, 'foobar')
assert_equal(f.headers, [('host', 'foo.bar')])
assert_equal(f.exclusive, True)
assert_equal(f.stream_dependency, 0x7654321)
assert_equal(f.weight, 42)
f = Frame.from_bytes('00000f01280123456703876543212a666f6f626172000000'.decode('hex'))
f = Frame.from_bytes('00001001280123456703876543212a668594e75e31d9000000'.decode('hex'))
assert isinstance(f, HeadersFrame)
assert_equal(f.length, 15)
assert_equal(f.length, 16)
assert_equal(f.TYPE, HeadersFrame.TYPE)
assert_equal(f.flags, HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY)
assert_equal(f.stream_id, 0x1234567)
assert_equal(f.header_block_fragment, 'foobar')
assert_equal(f.headers, [('host', 'foo.bar')])
assert_equal(f.exclusive, True)
assert_equal(f.stream_dependency, 0x7654321)
assert_equal(f.weight, 42)
f = Frame.from_bytes('00000f01280123456703076543212a666f6f626172000000'.decode('hex'))
f = Frame.from_bytes('00001001280123456703076543212a668594e75e31d9000000'.decode('hex'))
assert isinstance(f, HeadersFrame)
assert_equal(f.length, 15)
assert_equal(f.length, 16)
assert_equal(f.TYPE, HeadersFrame.TYPE)
assert_equal(f.flags, HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY)
assert_equal(f.stream_id, 0x1234567)
assert_equal(f.header_block_fragment, 'foobar')
assert_equal(f.headers, [('host', 'foo.bar')])
assert_equal(f.exclusive, False)
assert_equal(f.stream_dependency, 0x7654321)
assert_equal(f.weight, 42)
def test_headers_frame_human_readable():
f = HeadersFrame(
7,
HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY,
0x1234567,
headers=[],
pad_length=3,
exclusive=False,
stream_dependency=0x7654321,
weight=42)
assert f.human_readable()
f = HeadersFrame(
14,
HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY,
0x1234567,
headers=[('host', 'foo.bar')],
pad_length=3,
exclusive=False,
stream_dependency=0x7654321,
weight=42)
assert f.human_readable()
def test_priority_frame_to_bytes():
f = PriorityFrame(5, Frame.FLAG_NO_FLAGS, 0x1234567, exclusive=True, stream_dependency=0x7654321, weight=42)
assert_equal(f.to_bytes().encode('hex'), '000005020001234567876543212a')
@ -158,6 +217,11 @@ def test_priority_frame_from_bytes():
assert_equal(f.weight, 21)
def test_priority_frame_human_readable():
f = PriorityFrame(5, Frame.FLAG_NO_FLAGS, 0x1234567, exclusive=False, stream_dependency=0x7654321, weight=21)
assert f.human_readable()
def test_rst_stream_frame_to_bytes():
f = RstStreamFrame(4, Frame.FLAG_NO_FLAGS, 0x1234567, error_code=0x7654321)
assert_equal(f.to_bytes().encode('hex'), '00000403000123456707654321')
@ -176,6 +240,11 @@ def test_rst_stream_frame_from_bytes():
assert_equal(f.error_code, 0x07654321)
def test_rst_stream_frame_human_readable():
f = RstStreamFrame(4, Frame.FLAG_NO_FLAGS, 0x1234567, error_code=0x7654321)
assert f.human_readable()
def test_settings_frame_to_bytes():
f = SettingsFrame(0, Frame.FLAG_NO_FLAGS, 0x0)
assert_equal(f.to_bytes().encode('hex'), '000000040000000000')
@ -183,11 +252,20 @@ def test_settings_frame_to_bytes():
f = SettingsFrame(0, SettingsFrame.FLAG_ACK, 0x0)
assert_equal(f.to_bytes().encode('hex'), '000000040100000000')
f = SettingsFrame(6, SettingsFrame.FLAG_ACK, 0x0, settings={SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1})
f = SettingsFrame(
6,
SettingsFrame.FLAG_ACK, 0x0,
settings={
SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1})
assert_equal(f.to_bytes().encode('hex'), '000006040100000000000200000001')
f = SettingsFrame(12, Frame.FLAG_NO_FLAGS, 0x0, settings={
SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1, SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS: 0x12345678})
f = SettingsFrame(
12,
Frame.FLAG_NO_FLAGS,
0x0,
settings={
SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1,
SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS: 0x12345678})
assert_equal(f.to_bytes().encode('hex'), '00000c040000000000000200000001000312345678')
f = SettingsFrame(0, Frame.FLAG_NO_FLAGS, 0x1234567)
@ -229,6 +307,20 @@ def test_settings_frame_from_bytes():
assert_equal(f.settings[SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS], 0x12345678)
def test_settings_frame_human_readable():
f = SettingsFrame(12, Frame.FLAG_NO_FLAGS, 0x0, settings={})
assert f.human_readable()
f = SettingsFrame(
12,
Frame.FLAG_NO_FLAGS,
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(10, Frame.FLAG_NO_FLAGS, 0x1234567, 0x7654321, 'foobar')
assert_equal(f.to_bytes().encode('hex'), '00000a05000123456707654321666f6f626172')
@ -261,6 +353,11 @@ def test_push_promise_frame_from_bytes():
assert_equal(f.header_block_fragment, 'foobar')
def test_push_promise_frame_human_readable():
f = PushPromiseFrame(14, HeadersFrame.FLAG_PADDED, 0x1234567, 0x7654321, 'foobar', pad_length=3)
assert f.human_readable()
def test_ping_frame_to_bytes():
f = PingFrame(8, PingFrame.FLAG_ACK, 0x0, payload=b'foobar')
assert_equal(f.to_bytes().encode('hex'), '000008060100000000666f6f6261720000')
@ -290,6 +387,11 @@ def test_ping_frame_from_bytes():
assert_equal(f.payload, b'foobarde')
def test_ping_frame_human_readable():
f = PingFrame(8, PingFrame.FLAG_ACK, 0x0, payload=b'foobar')
assert f.human_readable()
def test_goaway_frame_to_bytes():
f = GoAwayFrame(8, Frame.FLAG_NO_FLAGS, 0x0, last_stream=0x1234567, error_code=0x87654321, data=b'')
assert_equal(f.to_bytes().encode('hex'), '0000080700000000000123456787654321')
@ -323,6 +425,11 @@ def test_goaway_frame_from_bytes():
assert_equal(f.data, b'foobar')
def test_go_away_frame_human_readable():
f = GoAwayFrame(14, Frame.FLAG_NO_FLAGS, 0x0, last_stream=0x1234567, error_code=0x87654321, data=b'foobar')
assert f.human_readable()
def test_window_update_frame_to_bytes():
f = WindowUpdateFrame(4, Frame.FLAG_NO_FLAGS, 0x0, window_size_increment=0x1234567)
assert_equal(f.to_bytes().encode('hex'), '00000408000000000001234567')
@ -347,6 +454,11 @@ def test_window_update_frame_from_bytes():
assert_equal(f.window_size_increment, 0x1234567)
def test_window_update_frame_human_readable():
f = WindowUpdateFrame(4, Frame.FLAG_NO_FLAGS, 0x1234567, window_size_increment=0x7654321)
assert f.human_readable()
def test_continuation_frame_to_bytes():
f = ContinuationFrame(6, ContinuationFrame.FLAG_END_HEADERS, 0x1234567, 'foobar')
assert_equal(f.to_bytes().encode('hex'), '000006090401234567666f6f626172')
@ -363,3 +475,8 @@ def test_continuation_frame_from_bytes():
assert_equal(f.flags, ContinuationFrame.FLAG_END_HEADERS)
assert_equal(f.stream_id, 0x1234567)
assert_equal(f.header_block_fragment, 'foobar')
def test_continuation_frame_human_readable():
f = ContinuationFrame(6, ContinuationFrame.FLAG_END_HEADERS, 0x1234567, 'foobar')
assert f.human_readable()

View File

@ -4,11 +4,14 @@ import time
import socket
import random
import os
from netlib import tcp, certutils, test, certffi
import threading
import mock
import tutils
from OpenSSL import SSL
import OpenSSL
from netlib import tcp, certutils, test, certffi
import tutils
class EchoHandler(tcp.BaseHandler):
@ -389,6 +392,26 @@ class TestTimeOut(test.ServerTestBase):
tutils.raises(tcp.NetLibTimeout, c.rfile.read, 10)
class TestALPN(test.ServerTestBase):
handler = HangHandler
ssl = dict(
cert=tutils.test_data.path("data/server.crt"),
key=tutils.test_data.path("data/server.key"),
request_client_cert=False,
v3_only=False,
alpn_select="h2"
)
if OpenSSL._util.lib.Cryptography_HAS_ALPN:
def test_alpn(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
c.connect()
c.convert_to_ssl(alpn_protos=["h2"])
print "ALPN: %s" % c.get_alpn_proto_negotiated()
assert c.get_alpn_proto_negotiated() == "h2"
class TestSSLTimeOut(test.ServerTestBase):
handler = HangHandler
ssl = dict(
@ -603,6 +626,7 @@ class TestAddress:
assert a.use_ipv6
b = tcp.Address("foo.com", True)
assert not a == b
assert str(b) == str(tuple("foo.com"))
c = tcp.Address("localhost", True)
assert a == c
assert not a != c