Merge pull request #65 from Kriechi/h2-client

HTTP/2 protocol definition
This commit is contained in:
Aldo Cortesi 2015-06-06 11:54:35 +12:00
commit fcaabeb455
11 changed files with 795 additions and 407 deletions

View File

@ -1 +0,0 @@
from __future__ import (absolute_import, print_function, division)

View File

@ -1,89 +0,0 @@
from .. import utils, odict, tcp
from frame import *
# "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
CLIENT_CONNECTION_PREFACE = '505249202a20485454502f322e300d0a0d0a534d0d0a0d0a'
ERROR_CODES = utils.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
)
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."

182
netlib/http2/__init__.py Normal file
View File

@ -0,0 +1,182 @@
from __future__ import (absolute_import, print_function, division)
import itertools
import logging
from .frame import *
from .. import utils
log = logging.getLogger(__name__)
class HTTP2Protocol(object):
ERROR_CODES = utils.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
)
# "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
CLIENT_CONNECTION_PREFACE = '505249202a20485454502f322e300d0a0d0a534d0d0a0d0a'
ALPN_PROTO_H2 = 'h2'
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,
}
def __init__(self, tcp_client):
self.tcp_client = tcp_client
self.http2_settings = self.HTTP2_DEFAULT_SETTINGS.copy()
self.current_stream_id = None
self.encoder = Encoder()
self.decoder = Decoder()
def check_alpn(self):
alp = self.tcp_client.get_alpn_proto_negotiated()
if alp != self.ALPN_PROTO_H2:
raise NotImplementedError(
"HTTP2Protocol can not handle unknown ALP: %s" % alp)
log.debug("ALP 'h2' successfully negotiated.")
return True
def perform_connection_preface(self):
self.tcp_client.wfile.write(
bytes(self.CLIENT_CONNECTION_PREFACE.decode('hex')))
self.send_frame(SettingsFrame(state=self))
# read server settings frame
frame = Frame.from_file(self.tcp_client.rfile, self)
assert isinstance(frame, SettingsFrame)
self._apply_settings(frame.settings)
# read setting ACK frame
settings_ack_frame = self.read_frame()
assert isinstance(settings_ack_frame, SettingsFrame)
assert settings_ack_frame.flags & Frame.FLAG_ACK
assert len(settings_ack_frame.settings) == 0
log.debug("Connection Preface completed.")
def next_stream_id(self):
if self.current_stream_id is None:
self.current_stream_id = 1
else:
self.current_stream_id += 2
return self.current_stream_id
def send_frame(self, frame):
raw_bytes = frame.to_bytes()
self.tcp_client.wfile.write(raw_bytes)
self.tcp_client.wfile.flush()
def read_frame(self):
frame = Frame.from_file(self.tcp_client.rfile, self)
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.http2_settings[setting]
if not old_value:
old_value = '-'
self.http2_settings[setting] = value
log.debug("Setting changed: %s to %s (was %s)" % (
SettingsFrame.SETTINGS.get_name(setting),
str(value),
str(old_value)))
self.send_frame(SettingsFrame(state=self, flags=Frame.FLAG_ACK))
log.debug("New settings acknowledged.")
def _create_headers(self, headers, stream_id, end_stream=True):
# TODO: implement max frame size checks and sending in chunks
flags = Frame.FLAG_END_HEADERS
if end_stream:
flags |= Frame.FLAG_END_STREAM
header_block_fragment = self.encoder.encode(headers)
bytes = HeadersFrame(
state=self,
flags=flags,
stream_id=stream_id,
header_block_fragment=header_block_fragment).to_bytes()
return [bytes]
def _create_body(self, body, stream_id):
if body is None or len(body) == 0:
return b''
# TODO: implement max frame size checks and sending in chunks
# TODO: implement flow-control window
bytes = DataFrame(
state=self,
flags=Frame.FLAG_END_STREAM,
stream_id=stream_id,
payload=body).to_bytes()
return [bytes]
def create_request(self, method, path, headers=None, body=None):
if headers is None:
headers = []
headers = [
(b':method', bytes(method)),
(b':path', bytes(path)),
(b':scheme', b'https')] + headers
stream_id = self.next_stream_id()
return list(itertools.chain(
self._create_headers(headers, stream_id, end_stream=(body is None)),
self._create_body(body, stream_id)))
def read_response(self):
header_block_fragment = b''
body = b''
while True:
frame = self.read_frame()
if isinstance(frame, HeadersFrame):
header_block_fragment += frame.header_block_fragment
if frame.flags | Frame.FLAG_END_HEADERS:
break
while True:
frame = self.read_frame()
if isinstance(frame, DataFrame):
body += frame.payload
if frame.flags | Frame.FLAG_END_STREAM:
break
headers = {}
for header, value in self.decoder.decode(header_block_fragment):
headers[header] = value
for header, value in headers.items():
log.debug("%s: %s" % (header, value))
return headers[':status'], headers, body

View File

@ -1,8 +1,15 @@
import struct import struct
import logging
from functools import reduce
from hpack.hpack import Encoder, Decoder from hpack.hpack import Encoder, Decoder
from .. import utils from .. import utils
from functools import reduce
log = logging.getLogger(__name__)
class FrameSizeError(Exception):
pass
class Frame(object): class Frame(object):
@ -20,18 +27,53 @@ class Frame(object):
FLAG_PADDED = 0x8 FLAG_PADDED = 0x8
FLAG_PRIORITY = 0x20 FLAG_PRIORITY = 0x20
def __init__(self, length, flags, stream_id): def __init__(
self,
state=None,
length=0,
flags=FLAG_NO_FLAGS,
stream_id=0x0):
valid_flags = reduce(lambda x, y: x | y, self.VALID_FLAGS, 0x0) valid_flags = reduce(lambda x, y: x | y, self.VALID_FLAGS, 0x0)
if flags | valid_flags != valid_flags: if flags | valid_flags != valid_flags:
raise ValueError('invalid flags detected.') raise ValueError('invalid flags detected.')
if state is None:
from . import HTTP2Protocol
class State(object):
pass
state = State()
state.http2_settings = HTTP2Protocol.HTTP2_DEFAULT_SETTINGS.copy()
state.encoder = Encoder()
state.decoder = Decoder()
self.state = state
self.length = length self.length = length
self.type = self.TYPE self.type = self.TYPE
self.flags = flags self.flags = flags
self.stream_id = stream_id self.stream_id = stream_id
@classmethod @classmethod
def from_file(self, fp): def _check_frame_size(self, length, state):
from . import HTTP2Protocol
if state:
settings = state.http2_settings
else:
settings = HTTP2Protocol.HTTP2_DEFAULT_SETTINGS
max_frame_size = settings[
SettingsFrame.SETTINGS.SETTINGS_MAX_FRAME_SIZE]
if length > max_frame_size:
raise FrameSizeError(
"Frame size exceeded: %d, but only %d allowed." % (
length, max_frame_size))
@classmethod
def from_file(self, fp, state=None):
""" """
read a HTTP/2 frame sent by a server or client read a HTTP/2 frame sent by a server or client
fp is a "file like" object that could be backed by a network fp is a "file like" object that could be backed by a network
@ -44,22 +86,22 @@ class Frame(object):
flags = fields[3] flags = fields[3]
stream_id = fields[4] stream_id = fields[4]
payload = fp.safe_read(length) self._check_frame_size(length, state)
return FRAMES[fields[2]].from_bytes(length, flags, stream_id, payload)
@classmethod payload = fp.safe_read(length)
def from_bytes(self, data): return FRAMES[fields[2]].from_bytes(
fields = struct.unpack("!HBBBL", data[:9]) state,
length = (fields[0] << 8) + fields[1] length,
# type is already deducted from class flags,
flags = fields[3] stream_id,
stream_id = fields[4] payload)
return FRAMES[fields[2]].from_bytes(length, flags, stream_id, data[9:])
def to_bytes(self): def to_bytes(self):
payload = self.payload_bytes() payload = self.payload_bytes()
self.length = len(payload) self.length = len(payload)
self._check_frame_size(self.length, self.state)
b = struct.pack('!HB', self.length & 0xFFFF00, self.length & 0x0000FF) b = struct.pack('!HB', self.length & 0xFFFF00, self.length & 0x0000FF)
b += struct.pack('!B', self.TYPE) b += struct.pack('!B', self.TYPE)
b += struct.pack('!B', self.flags) b += struct.pack('!B', self.flags)
@ -96,18 +138,19 @@ class DataFrame(Frame):
def __init__( def __init__(
self, self,
state=None,
length=0, length=0,
flags=Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0, stream_id=0x0,
payload=b'', payload=b'',
pad_length=0): pad_length=0):
super(DataFrame, self).__init__(length, flags, stream_id) super(DataFrame, self).__init__(state, length, flags, stream_id)
self.payload = payload self.payload = payload
self.pad_length = pad_length self.pad_length = pad_length
@classmethod @classmethod
def from_bytes(self, length, flags, stream_id, payload): def from_bytes(self, state, length, flags, stream_id, payload):
f = self(length=length, flags=flags, stream_id=stream_id) f = self(state=state, length=length, flags=flags, stream_id=stream_id)
if f.flags & self.FLAG_PADDED: if f.flags & self.FLAG_PADDED:
f.pad_length = struct.unpack('!B', payload[0])[0] f.pad_length = struct.unpack('!B', payload[0])[0]
@ -146,45 +189,39 @@ class HeadersFrame(Frame):
def __init__( def __init__(
self, self,
state=None,
length=0, length=0,
flags=Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0, stream_id=0x0,
headers=None, header_block_fragment=b'',
pad_length=0, pad_length=0,
exclusive=False, exclusive=False,
stream_dependency=0x0, stream_dependency=0x0,
weight=0): weight=0):
super(HeadersFrame, self).__init__(length, flags, stream_id) super(HeadersFrame, self).__init__(state, length, flags, stream_id)
if headers is None: self.header_block_fragment = header_block_fragment
headers = []
self.headers = headers
self.pad_length = pad_length self.pad_length = pad_length
self.exclusive = exclusive self.exclusive = exclusive
self.stream_dependency = stream_dependency self.stream_dependency = stream_dependency
self.weight = weight self.weight = weight
@classmethod @classmethod
def from_bytes(self, length, flags, stream_id, payload): def from_bytes(self, state, length, flags, stream_id, payload):
f = self(length=length, flags=flags, stream_id=stream_id) f = self(state=state, length=length, flags=flags, stream_id=stream_id)
if f.flags & self.FLAG_PADDED: if f.flags & self.FLAG_PADDED:
f.pad_length = struct.unpack('!B', payload[0])[0] f.pad_length = struct.unpack('!B', payload[0])[0]
header_block_fragment = payload[1:-f.pad_length] f.header_block_fragment = payload[1:-f.pad_length]
else: else:
header_block_fragment = payload[0:] f.header_block_fragment = payload[0:]
if f.flags & self.FLAG_PRIORITY: if f.flags & self.FLAG_PRIORITY:
f.stream_dependency, f.weight = struct.unpack( f.stream_dependency, f.weight = struct.unpack(
'!LB', header_block_fragment[ '!LB', f.header_block_fragment[:5])
:5])
f.exclusive = bool(f.stream_dependency >> 31) f.exclusive = bool(f.stream_dependency >> 31)
f.stream_dependency &= 0x7FFFFFFF f.stream_dependency &= 0x7FFFFFFF
header_block_fragment = header_block_fragment[5:] f.header_block_fragment = f.header_block_fragment[5:]
for header, value in Decoder().decode(header_block_fragment):
f.headers.append((header, value))
return f return f
@ -201,7 +238,7 @@ class HeadersFrame(Frame):
(int(self.exclusive) << 31) | self.stream_dependency, (int(self.exclusive) << 31) | self.stream_dependency,
self.weight) self.weight)
b += Encoder().encode(self.headers) b += self.header_block_fragment
if self.flags & self.FLAG_PADDED: if self.flags & self.FLAG_PADDED:
b += b'\0' * self.pad_length b += b'\0' * self.pad_length
@ -219,11 +256,9 @@ class HeadersFrame(Frame):
if self.flags & self.FLAG_PADDED: if self.flags & self.FLAG_PADDED:
s.append("padding: %d" % self.pad_length) s.append("padding: %d" % self.pad_length)
if not self.headers: s.append(
s.append("headers: None") "header_block_fragment: %s" %
else: self.header_block_fragment.encode('hex'))
for header, value in self.headers:
s.append("%s: %s" % (header, value))
return "\n".join(s) return "\n".join(s)
@ -234,20 +269,21 @@ class PriorityFrame(Frame):
def __init__( def __init__(
self, self,
state=None,
length=0, length=0,
flags=Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0, stream_id=0x0,
exclusive=False, exclusive=False,
stream_dependency=0x0, stream_dependency=0x0,
weight=0): weight=0):
super(PriorityFrame, self).__init__(length, flags, stream_id) super(PriorityFrame, self).__init__(state, length, flags, stream_id)
self.exclusive = exclusive self.exclusive = exclusive
self.stream_dependency = stream_dependency self.stream_dependency = stream_dependency
self.weight = weight self.weight = weight
@classmethod @classmethod
def from_bytes(self, length, flags, stream_id, payload): def from_bytes(self, state, length, flags, stream_id, payload):
f = self(length=length, flags=flags, stream_id=stream_id) f = self(state=state, length=length, flags=flags, stream_id=stream_id)
f.stream_dependency, f.weight = struct.unpack('!LB', payload) f.stream_dependency, f.weight = struct.unpack('!LB', payload)
f.exclusive = bool(f.stream_dependency >> 31) f.exclusive = bool(f.stream_dependency >> 31)
@ -283,16 +319,17 @@ class RstStreamFrame(Frame):
def __init__( def __init__(
self, self,
state=None,
length=0, length=0,
flags=Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0, stream_id=0x0,
error_code=0x0): error_code=0x0):
super(RstStreamFrame, self).__init__(length, flags, stream_id) super(RstStreamFrame, self).__init__(state, length, flags, stream_id)
self.error_code = error_code self.error_code = error_code
@classmethod @classmethod
def from_bytes(self, length, flags, stream_id, payload): def from_bytes(self, state, length, flags, stream_id, payload):
f = self(length=length, flags=flags, stream_id=stream_id) f = self(state=state, length=length, flags=flags, stream_id=stream_id)
f.error_code = struct.unpack('!L', payload)[0] f.error_code = struct.unpack('!L', payload)[0]
return f return f
@ -322,11 +359,12 @@ class SettingsFrame(Frame):
def __init__( def __init__(
self, self,
state=None,
length=0, length=0,
flags=Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0, stream_id=0x0,
settings=None): settings=None):
super(SettingsFrame, self).__init__(length, flags, stream_id) super(SettingsFrame, self).__init__(state, length, flags, stream_id)
if settings is None: if settings is None:
settings = {} settings = {}
@ -334,8 +372,8 @@ class SettingsFrame(Frame):
self.settings = settings self.settings = settings
@classmethod @classmethod
def from_bytes(self, length, flags, stream_id, payload): def from_bytes(self, state, length, flags, stream_id, payload):
f = self(length=length, flags=flags, stream_id=stream_id) f = self(state=state, length=length, flags=flags, stream_id=stream_id)
for i in xrange(0, len(payload), 6): for i in xrange(0, len(payload), 6):
identifier, value = struct.unpack("!HL", payload[i:i + 6]) identifier, value = struct.unpack("!HL", payload[i:i + 6])
@ -372,20 +410,21 @@ class PushPromiseFrame(Frame):
def __init__( def __init__(
self, self,
state=None,
length=0, length=0,
flags=Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0, stream_id=0x0,
promised_stream=0x0, promised_stream=0x0,
header_block_fragment=b'', header_block_fragment=b'',
pad_length=0): pad_length=0):
super(PushPromiseFrame, self).__init__(length, flags, stream_id) super(PushPromiseFrame, self).__init__(state, length, flags, stream_id)
self.pad_length = pad_length self.pad_length = pad_length
self.promised_stream = promised_stream self.promised_stream = promised_stream
self.header_block_fragment = header_block_fragment self.header_block_fragment = header_block_fragment
@classmethod @classmethod
def from_bytes(self, length, flags, stream_id, payload): def from_bytes(self, state, length, flags, stream_id, payload):
f = self(length=length, flags=flags, stream_id=stream_id) f = self(state=state, length=length, flags=flags, stream_id=stream_id)
if f.flags & self.FLAG_PADDED: if f.flags & self.FLAG_PADDED:
f.pad_length, f.promised_stream = struct.unpack('!BL', payload[:5]) f.pad_length, f.promised_stream = struct.unpack('!BL', payload[:5])
@ -435,16 +474,17 @@ class PingFrame(Frame):
def __init__( def __init__(
self, self,
state=None,
length=0, length=0,
flags=Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0, stream_id=0x0,
payload=b''): payload=b''):
super(PingFrame, self).__init__(length, flags, stream_id) super(PingFrame, self).__init__(state, length, flags, stream_id)
self.payload = payload self.payload = payload
@classmethod @classmethod
def from_bytes(self, length, flags, stream_id, payload): def from_bytes(self, state, length, flags, stream_id, payload):
f = self(length=length, flags=flags, stream_id=stream_id) f = self(state=state, length=length, flags=flags, stream_id=stream_id)
f.payload = payload f.payload = payload
return f return f
@ -467,20 +507,21 @@ class GoAwayFrame(Frame):
def __init__( def __init__(
self, self,
state=None,
length=0, length=0,
flags=Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0, stream_id=0x0,
last_stream=0x0, last_stream=0x0,
error_code=0x0, error_code=0x0,
data=b''): data=b''):
super(GoAwayFrame, self).__init__(length, flags, stream_id) super(GoAwayFrame, self).__init__(state, length, flags, stream_id)
self.last_stream = last_stream self.last_stream = last_stream
self.error_code = error_code self.error_code = error_code
self.data = data self.data = data
@classmethod @classmethod
def from_bytes(self, length, flags, stream_id, payload): def from_bytes(self, state, length, flags, stream_id, payload):
f = self(length=length, flags=flags, stream_id=stream_id) f = self(state=state, length=length, flags=flags, stream_id=stream_id)
f.last_stream, f.error_code = struct.unpack("!LL", payload[:8]) f.last_stream, f.error_code = struct.unpack("!LL", payload[:8])
f.last_stream &= 0x7FFFFFFF f.last_stream &= 0x7FFFFFFF
@ -511,16 +552,17 @@ class WindowUpdateFrame(Frame):
def __init__( def __init__(
self, self,
state=None,
length=0, length=0,
flags=Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0, stream_id=0x0,
window_size_increment=0x0): window_size_increment=0x0):
super(WindowUpdateFrame, self).__init__(length, flags, stream_id) super(WindowUpdateFrame, self).__init__(state, length, flags, stream_id)
self.window_size_increment = window_size_increment self.window_size_increment = window_size_increment
@classmethod @classmethod
def from_bytes(self, length, flags, stream_id, payload): def from_bytes(self, state, length, flags, stream_id, payload):
f = self(length=length, flags=flags, stream_id=stream_id) f = self(state=state, length=length, flags=flags, stream_id=stream_id)
f.window_size_increment = struct.unpack("!L", payload)[0] f.window_size_increment = struct.unpack("!L", payload)[0]
f.window_size_increment &= 0x7FFFFFFF f.window_size_increment &= 0x7FFFFFFF
@ -544,16 +586,17 @@ class ContinuationFrame(Frame):
def __init__( def __init__(
self, self,
state=None,
length=0, length=0,
flags=Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0, stream_id=0x0,
header_block_fragment=b''): header_block_fragment=b''):
super(ContinuationFrame, self).__init__(length, flags, stream_id) super(ContinuationFrame, self).__init__(state, length, flags, stream_id)
self.header_block_fragment = header_block_fragment self.header_block_fragment = header_block_fragment
@classmethod @classmethod
def from_bytes(self, length, flags, stream_id, payload): def from_bytes(self, state, length, flags, stream_id, payload):
f = self(length=length, flags=flags, stream_id=stream_id) f = self(state=state, length=length, flags=flags, stream_id=stream_id)
f.header_block_fragment = payload f.header_block_fragment = payload
return f return f

View File

@ -4,6 +4,7 @@ import Queue
import cStringIO import cStringIO
import OpenSSL import OpenSSL
from . import tcp, certutils from . import tcp, certutils
from test import tutils
class ServerThread(threading.Thread): class ServerThread(threading.Thread):
@ -55,22 +56,33 @@ class TServer(tcp.TCPServer):
dhparams, v3_only dhparams, v3_only
""" """
tcp.TCPServer.__init__(self, addr) tcp.TCPServer.__init__(self, addr)
self.ssl, self.q = ssl, q
if ssl is True:
self.ssl = dict()
elif isinstance(ssl, dict):
self.ssl = ssl
else:
self.ssl = None
self.q = q
self.handler_klass = handler_klass self.handler_klass = handler_klass
self.last_handler = None self.last_handler = None
def handle_client_connection(self, request, client_address): def handle_client_connection(self, request, client_address):
h = self.handler_klass(request, client_address, self) h = self.handler_klass(request, client_address, self)
self.last_handler = h self.last_handler = h
if self.ssl: if self.ssl is not None:
cert = certutils.SSLCert.from_pem( raw_cert = self.ssl.get(
file(self.ssl["cert"], "rb").read() "cert",
) tutils.test_data.path("data/server.crt"))
raw = file(self.ssl["key"], "rb").read() cert = certutils.SSLCert.from_pem(open(raw_cert, "rb").read())
raw_key = self.ssl.get(
"key",
tutils.test_data.path("data/server.key"))
key = OpenSSL.crypto.load_privatekey( key = OpenSSL.crypto.load_privatekey(
OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_PEM,
raw) open(raw_key, "rb").read())
if self.ssl["v3_only"]: if self.ssl.get("v3_only", False):
method = tcp.SSLv3_METHOD method = tcp.SSLv3_METHOD
options = OpenSSL.SSL.OP_NO_SSLv2 | OpenSSL.SSL.OP_NO_TLSv1 options = OpenSSL.SSL.OP_NO_SSLv2 | OpenSSL.SSL.OP_NO_TLSv1
else: else:
@ -81,7 +93,7 @@ class TServer(tcp.TCPServer):
method=method, method=method,
options=options, options=options,
handle_sni=getattr(h, "handle_sni", None), handle_sni=getattr(h, "handle_sni", None),
request_client_cert=self.ssl["request_client_cert"], request_client_cert=self.ssl.get("request_client_cert", None),
cipher_list=self.ssl.get("cipher_list", None), cipher_list=self.ssl.get("cipher_list", None),
dhparams=self.ssl.get("dhparams", None), dhparams=self.ssl.get("dhparams", None),
chain_file=self.ssl.get("chain_file", None), chain_file=self.ssl.get("chain_file", None),

View File

@ -1,18 +0,0 @@
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()

0
test/http2/__init__.py Normal file
View File

View File

@ -1,50 +1,75 @@
from netlib.h2.frame import * import cStringIO
import tutils from test import tutils
from nose.tools import assert_equal from nose.tools import assert_equal
from netlib import tcp
from netlib.http2.frame import *
# TODO test stream association if valid or not
def hex_to_file(data):
data = data.decode('hex')
return tcp.Reader(cStringIO.StringIO(data))
def test_invalid_flags(): def test_invalid_flags():
tutils.raises( tutils.raises(
ValueError, ValueError,
DataFrame, DataFrame,
ContinuationFrame.FLAG_END_HEADERS, flags=ContinuationFrame.FLAG_END_HEADERS,
0x1234567, stream_id=0x1234567,
'foobar') payload='foobar')
def test_frame_equality(): def test_frame_equality():
a = DataFrame(6, Frame.FLAG_END_STREAM, 0x1234567, 'foobar') a = DataFrame(
b = DataFrame(6, Frame.FLAG_END_STREAM, 0x1234567, 'foobar') 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_equal(a, b) assert_equal(a, b)
def test_too_large_frames(): def test_too_large_frames():
DataFrame(6, Frame.FLAG_END_STREAM, 0x1234567) f = DataFrame(
length=9000,
flags=Frame.FLAG_END_STREAM,
stream_id=0x1234567,
payload='foobar' * 3000)
tutils.raises(FrameSizeError, f.to_bytes)
def test_data_frame_to_bytes(): def test_data_frame_to_bytes():
f = DataFrame(6, Frame.FLAG_END_STREAM, 0x1234567, 'foobar') f = DataFrame(
length=6,
flags=Frame.FLAG_END_STREAM,
stream_id=0x1234567,
payload='foobar')
assert_equal(f.to_bytes().encode('hex'), '000006000101234567666f6f626172') assert_equal(f.to_bytes().encode('hex'), '000006000101234567666f6f626172')
f = DataFrame( f = DataFrame(
11, length=11,
Frame.FLAG_END_STREAM | Frame.FLAG_PADDED, flags=(Frame.FLAG_END_STREAM | Frame.FLAG_PADDED),
0x1234567, stream_id=0x1234567,
'foobar', payload='foobar',
pad_length=3) pad_length=3)
assert_equal( assert_equal(
f.to_bytes().encode('hex'), f.to_bytes().encode('hex'),
'00000a00090123456703666f6f626172000000') '00000a00090123456703666f6f626172000000')
f = DataFrame(6, Frame.FLAG_NO_FLAGS, 0x0, 'foobar') f = DataFrame(
length=6,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0,
payload='foobar')
tutils.raises(ValueError, f.to_bytes) tutils.raises(ValueError, f.to_bytes)
def test_data_frame_from_bytes(): def test_data_frame_from_bytes():
f = Frame.from_bytes('000006000101234567666f6f626172'.decode('hex')) f = Frame.from_file(hex_to_file('000006000101234567666f6f626172'))
assert isinstance(f, DataFrame) assert isinstance(f, DataFrame)
assert_equal(f.length, 6) assert_equal(f.length, 6)
assert_equal(f.TYPE, DataFrame.TYPE) assert_equal(f.TYPE, DataFrame.TYPE)
@ -52,7 +77,7 @@ def test_data_frame_from_bytes():
assert_equal(f.stream_id, 0x1234567) assert_equal(f.stream_id, 0x1234567)
assert_equal(f.payload, 'foobar') assert_equal(f.payload, 'foobar')
f = Frame.from_bytes('00000a00090123456703666f6f626172000000'.decode('hex')) f = Frame.from_file(hex_to_file('00000a00090123456703666f6f626172000000'))
assert isinstance(f, DataFrame) assert isinstance(f, DataFrame)
assert_equal(f.length, 10) assert_equal(f.length, 10)
assert_equal(f.TYPE, DataFrame.TYPE) assert_equal(f.TYPE, DataFrame.TYPE)
@ -63,37 +88,37 @@ def test_data_frame_from_bytes():
def test_data_frame_human_readable(): def test_data_frame_human_readable():
f = DataFrame( f = DataFrame(
11, length=11,
Frame.FLAG_END_STREAM | Frame.FLAG_PADDED, flags=(Frame.FLAG_END_STREAM | Frame.FLAG_PADDED),
0x1234567, stream_id=0x1234567,
'foobar', payload='foobar',
pad_length=3) pad_length=3)
assert f.human_readable() assert f.human_readable()
def test_headers_frame_to_bytes(): def test_headers_frame_to_bytes():
f = HeadersFrame( f = HeadersFrame(
6, length=6,
Frame.FLAG_NO_FLAGS, flags=(Frame.FLAG_NO_FLAGS),
0x1234567, stream_id=0x1234567,
headers=[('host', 'foo.bar')]) header_block_fragment='668594e75e31d9'.decode('hex'))
assert_equal(f.to_bytes().encode('hex'), '000007010001234567668594e75e31d9') assert_equal(f.to_bytes().encode('hex'), '000007010001234567668594e75e31d9')
f = HeadersFrame( f = HeadersFrame(
10, length=10,
HeadersFrame.FLAG_PADDED, flags=(HeadersFrame.FLAG_PADDED),
0x1234567, stream_id=0x1234567,
headers=[('host', 'foo.bar')], header_block_fragment='668594e75e31d9'.decode('hex'),
pad_length=3) pad_length=3)
assert_equal( assert_equal(
f.to_bytes().encode('hex'), f.to_bytes().encode('hex'),
'00000b01080123456703668594e75e31d9000000') '00000b01080123456703668594e75e31d9000000')
f = HeadersFrame( f = HeadersFrame(
10, length=10,
HeadersFrame.FLAG_PRIORITY, flags=(HeadersFrame.FLAG_PRIORITY),
0x1234567, stream_id=0x1234567,
headers=[('host', 'foo.bar')], header_block_fragment='668594e75e31d9'.decode('hex'),
exclusive=True, exclusive=True,
stream_dependency=0x7654321, stream_dependency=0x7654321,
weight=42) weight=42)
@ -102,10 +127,10 @@ def test_headers_frame_to_bytes():
'00000c012001234567876543212a668594e75e31d9') '00000c012001234567876543212a668594e75e31d9')
f = HeadersFrame( f = HeadersFrame(
14, length=14,
HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY, flags=(HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY),
0x1234567, stream_id=0x1234567,
headers=[('host', 'foo.bar')], header_block_fragment='668594e75e31d9'.decode('hex'),
pad_length=3, pad_length=3,
exclusive=True, exclusive=True,
stream_dependency=0x7654321, stream_dependency=0x7654321,
@ -115,10 +140,10 @@ def test_headers_frame_to_bytes():
'00001001280123456703876543212a668594e75e31d9000000') '00001001280123456703876543212a668594e75e31d9000000')
f = HeadersFrame( f = HeadersFrame(
14, length=14,
HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY, flags=(HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY),
0x1234567, stream_id=0x1234567,
headers=[('host', 'foo.bar')], header_block_fragment='668594e75e31d9'.decode('hex'),
pad_length=3, pad_length=3,
exclusive=False, exclusive=False,
stream_dependency=0x7654321, stream_dependency=0x7654321,
@ -127,60 +152,65 @@ def test_headers_frame_to_bytes():
f.to_bytes().encode('hex'), f.to_bytes().encode('hex'),
'00001001280123456703076543212a668594e75e31d9000000') '00001001280123456703076543212a668594e75e31d9000000')
f = HeadersFrame(6, Frame.FLAG_NO_FLAGS, 0x0, 'foobar') f = HeadersFrame(
length=6,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0,
header_block_fragment='668594e75e31d9'.decode('hex'))
tutils.raises(ValueError, f.to_bytes) tutils.raises(ValueError, f.to_bytes)
def test_headers_frame_from_bytes(): def test_headers_frame_from_bytes():
f = Frame.from_bytes('000007010001234567668594e75e31d9'.decode('hex')) f = Frame.from_file(hex_to_file(
'000007010001234567668594e75e31d9'))
assert isinstance(f, HeadersFrame) assert isinstance(f, HeadersFrame)
assert_equal(f.length, 7) assert_equal(f.length, 7)
assert_equal(f.TYPE, HeadersFrame.TYPE) assert_equal(f.TYPE, HeadersFrame.TYPE)
assert_equal(f.flags, Frame.FLAG_NO_FLAGS) assert_equal(f.flags, Frame.FLAG_NO_FLAGS)
assert_equal(f.stream_id, 0x1234567) assert_equal(f.stream_id, 0x1234567)
assert_equal(f.headers, [('host', 'foo.bar')]) assert_equal(f.header_block_fragment, '668594e75e31d9'.decode('hex'))
f = Frame.from_bytes( f = Frame.from_file(hex_to_file(
'00000b01080123456703668594e75e31d9000000'.decode('hex')) '00000b01080123456703668594e75e31d9000000'))
assert isinstance(f, HeadersFrame) assert isinstance(f, HeadersFrame)
assert_equal(f.length, 11) assert_equal(f.length, 11)
assert_equal(f.TYPE, HeadersFrame.TYPE) assert_equal(f.TYPE, HeadersFrame.TYPE)
assert_equal(f.flags, HeadersFrame.FLAG_PADDED) assert_equal(f.flags, HeadersFrame.FLAG_PADDED)
assert_equal(f.stream_id, 0x1234567) assert_equal(f.stream_id, 0x1234567)
assert_equal(f.headers, [('host', 'foo.bar')]) assert_equal(f.header_block_fragment, '668594e75e31d9'.decode('hex'))
f = Frame.from_bytes( f = Frame.from_file(hex_to_file(
'00000c012001234567876543212a668594e75e31d9'.decode('hex')) '00000c012001234567876543212a668594e75e31d9'))
assert isinstance(f, HeadersFrame) assert isinstance(f, HeadersFrame)
assert_equal(f.length, 12) assert_equal(f.length, 12)
assert_equal(f.TYPE, HeadersFrame.TYPE) assert_equal(f.TYPE, HeadersFrame.TYPE)
assert_equal(f.flags, HeadersFrame.FLAG_PRIORITY) assert_equal(f.flags, HeadersFrame.FLAG_PRIORITY)
assert_equal(f.stream_id, 0x1234567) assert_equal(f.stream_id, 0x1234567)
assert_equal(f.headers, [('host', 'foo.bar')]) assert_equal(f.header_block_fragment, '668594e75e31d9'.decode('hex'))
assert_equal(f.exclusive, True) assert_equal(f.exclusive, True)
assert_equal(f.stream_dependency, 0x7654321) assert_equal(f.stream_dependency, 0x7654321)
assert_equal(f.weight, 42) assert_equal(f.weight, 42)
f = Frame.from_bytes( f = Frame.from_file(hex_to_file(
'00001001280123456703876543212a668594e75e31d9000000'.decode('hex')) '00001001280123456703876543212a668594e75e31d9000000'))
assert isinstance(f, HeadersFrame) assert isinstance(f, HeadersFrame)
assert_equal(f.length, 16) assert_equal(f.length, 16)
assert_equal(f.TYPE, HeadersFrame.TYPE) assert_equal(f.TYPE, HeadersFrame.TYPE)
assert_equal(f.flags, HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY) assert_equal(f.flags, HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY)
assert_equal(f.stream_id, 0x1234567) assert_equal(f.stream_id, 0x1234567)
assert_equal(f.headers, [('host', 'foo.bar')]) assert_equal(f.header_block_fragment, '668594e75e31d9'.decode('hex'))
assert_equal(f.exclusive, True) assert_equal(f.exclusive, True)
assert_equal(f.stream_dependency, 0x7654321) assert_equal(f.stream_dependency, 0x7654321)
assert_equal(f.weight, 42) assert_equal(f.weight, 42)
f = Frame.from_bytes( f = Frame.from_file(hex_to_file(
'00001001280123456703076543212a668594e75e31d9000000'.decode('hex')) '00001001280123456703076543212a668594e75e31d9000000'))
assert isinstance(f, HeadersFrame) assert isinstance(f, HeadersFrame)
assert_equal(f.length, 16) assert_equal(f.length, 16)
assert_equal(f.TYPE, HeadersFrame.TYPE) assert_equal(f.TYPE, HeadersFrame.TYPE)
assert_equal(f.flags, HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY) assert_equal(f.flags, HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY)
assert_equal(f.stream_id, 0x1234567) assert_equal(f.stream_id, 0x1234567)
assert_equal(f.headers, [('host', 'foo.bar')]) assert_equal(f.header_block_fragment, '668594e75e31d9'.decode('hex'))
assert_equal(f.exclusive, False) assert_equal(f.exclusive, False)
assert_equal(f.stream_dependency, 0x7654321) assert_equal(f.stream_dependency, 0x7654321)
assert_equal(f.weight, 42) assert_equal(f.weight, 42)
@ -188,10 +218,10 @@ def test_headers_frame_from_bytes():
def test_headers_frame_human_readable(): def test_headers_frame_human_readable():
f = HeadersFrame( f = HeadersFrame(
7, length=7,
HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY, flags=(HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY),
0x1234567, stream_id=0x1234567,
headers=[], header_block_fragment=b'',
pad_length=3, pad_length=3,
exclusive=False, exclusive=False,
stream_dependency=0x7654321, stream_dependency=0x7654321,
@ -199,10 +229,10 @@ def test_headers_frame_human_readable():
assert f.human_readable() assert f.human_readable()
f = HeadersFrame( f = HeadersFrame(
14, length=14,
HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY, flags=(HeadersFrame.FLAG_PADDED | HeadersFrame.FLAG_PRIORITY),
0x1234567, stream_id=0x1234567,
headers=[('host', 'foo.bar')], header_block_fragment='668594e75e31d9'.decode('hex'),
pad_length=3, pad_length=3,
exclusive=False, exclusive=False,
stream_dependency=0x7654321, stream_dependency=0x7654321,
@ -212,32 +242,40 @@ def test_headers_frame_human_readable():
def test_priority_frame_to_bytes(): def test_priority_frame_to_bytes():
f = PriorityFrame( f = PriorityFrame(
5, length=5,
Frame.FLAG_NO_FLAGS, flags=(Frame.FLAG_NO_FLAGS),
0x1234567, stream_id=0x1234567,
exclusive=True, exclusive=True,
stream_dependency=0x7654321, stream_dependency=0x7654321,
weight=42) weight=42)
assert_equal(f.to_bytes().encode('hex'), '000005020001234567876543212a') assert_equal(f.to_bytes().encode('hex'), '000005020001234567876543212a')
f = PriorityFrame( f = PriorityFrame(
5, length=5,
Frame.FLAG_NO_FLAGS, flags=(Frame.FLAG_NO_FLAGS),
0x1234567, stream_id=0x1234567,
exclusive=False, exclusive=False,
stream_dependency=0x7654321, stream_dependency=0x7654321,
weight=21) weight=21)
assert_equal(f.to_bytes().encode('hex'), '0000050200012345670765432115') assert_equal(f.to_bytes().encode('hex'), '0000050200012345670765432115')
f = PriorityFrame(5, Frame.FLAG_NO_FLAGS, 0x0, stream_dependency=0x1234567) f = PriorityFrame(
length=5,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0,
stream_dependency=0x1234567)
tutils.raises(ValueError, f.to_bytes) tutils.raises(ValueError, f.to_bytes)
f = PriorityFrame(5, Frame.FLAG_NO_FLAGS, 0x1234567, stream_dependency=0x0) f = PriorityFrame(
length=5,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x1234567,
stream_dependency=0x0)
tutils.raises(ValueError, f.to_bytes) tutils.raises(ValueError, f.to_bytes)
def test_priority_frame_from_bytes(): def test_priority_frame_from_bytes():
f = Frame.from_bytes('000005020001234567876543212a'.decode('hex')) f = Frame.from_file(hex_to_file('000005020001234567876543212a'))
assert isinstance(f, PriorityFrame) assert isinstance(f, PriorityFrame)
assert_equal(f.length, 5) assert_equal(f.length, 5)
assert_equal(f.TYPE, PriorityFrame.TYPE) assert_equal(f.TYPE, PriorityFrame.TYPE)
@ -247,7 +285,7 @@ def test_priority_frame_from_bytes():
assert_equal(f.stream_dependency, 0x7654321) assert_equal(f.stream_dependency, 0x7654321)
assert_equal(f.weight, 42) assert_equal(f.weight, 42)
f = Frame.from_bytes('0000050200012345670765432115'.decode('hex')) f = Frame.from_file(hex_to_file('0000050200012345670765432115'))
assert isinstance(f, PriorityFrame) assert isinstance(f, PriorityFrame)
assert_equal(f.length, 5) assert_equal(f.length, 5)
assert_equal(f.TYPE, PriorityFrame.TYPE) assert_equal(f.TYPE, PriorityFrame.TYPE)
@ -260,9 +298,9 @@ def test_priority_frame_from_bytes():
def test_priority_frame_human_readable(): def test_priority_frame_human_readable():
f = PriorityFrame( f = PriorityFrame(
5, length=5,
Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
0x1234567, stream_id=0x1234567,
exclusive=False, exclusive=False,
stream_dependency=0x7654321, stream_dependency=0x7654321,
weight=21) weight=21)
@ -270,15 +308,22 @@ def test_priority_frame_human_readable():
def test_rst_stream_frame_to_bytes(): def test_rst_stream_frame_to_bytes():
f = RstStreamFrame(4, Frame.FLAG_NO_FLAGS, 0x1234567, error_code=0x7654321) f = RstStreamFrame(
length=4,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x1234567,
error_code=0x7654321)
assert_equal(f.to_bytes().encode('hex'), '00000403000123456707654321') assert_equal(f.to_bytes().encode('hex'), '00000403000123456707654321')
f = RstStreamFrame(4, Frame.FLAG_NO_FLAGS, 0x0) f = RstStreamFrame(
length=4,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0)
tutils.raises(ValueError, f.to_bytes) tutils.raises(ValueError, f.to_bytes)
def test_rst_stream_frame_from_bytes(): def test_rst_stream_frame_from_bytes():
f = Frame.from_bytes('00000403000123456707654321'.decode('hex')) f = Frame.from_file(hex_to_file('00000403000123456707654321'))
assert isinstance(f, RstStreamFrame) assert isinstance(f, RstStreamFrame)
assert_equal(f.length, 4) assert_equal(f.length, 4)
assert_equal(f.TYPE, RstStreamFrame.TYPE) assert_equal(f.TYPE, RstStreamFrame.TYPE)
@ -288,28 +333,39 @@ def test_rst_stream_frame_from_bytes():
def test_rst_stream_frame_human_readable(): def test_rst_stream_frame_human_readable():
f = RstStreamFrame(4, Frame.FLAG_NO_FLAGS, 0x1234567, error_code=0x7654321) f = RstStreamFrame(
length=4,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x1234567,
error_code=0x7654321)
assert f.human_readable() assert f.human_readable()
def test_settings_frame_to_bytes(): def test_settings_frame_to_bytes():
f = SettingsFrame(0, Frame.FLAG_NO_FLAGS, 0x0) f = SettingsFrame(
length=0,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0)
assert_equal(f.to_bytes().encode('hex'), '000000040000000000') assert_equal(f.to_bytes().encode('hex'), '000000040000000000')
f = SettingsFrame(0, SettingsFrame.FLAG_ACK, 0x0) f = SettingsFrame(
length=0,
flags=SettingsFrame.FLAG_ACK,
stream_id=0x0)
assert_equal(f.to_bytes().encode('hex'), '000000040100000000') assert_equal(f.to_bytes().encode('hex'), '000000040100000000')
f = SettingsFrame( f = SettingsFrame(
6, length=6,
SettingsFrame.FLAG_ACK, 0x0, flags=SettingsFrame.FLAG_ACK,
stream_id=0x0,
settings={ settings={
SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1}) SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1})
assert_equal(f.to_bytes().encode('hex'), '000006040100000000000200000001') assert_equal(f.to_bytes().encode('hex'), '000006040100000000000200000001')
f = SettingsFrame( f = SettingsFrame(
12, length=12,
Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
0x0, stream_id=0x0,
settings={ settings={
SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1, SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1,
SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS: 0x12345678}) SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS: 0x12345678})
@ -317,26 +373,29 @@ def test_settings_frame_to_bytes():
f.to_bytes().encode('hex'), f.to_bytes().encode('hex'),
'00000c040000000000000200000001000312345678') '00000c040000000000000200000001000312345678')
f = SettingsFrame(0, Frame.FLAG_NO_FLAGS, 0x1234567) f = SettingsFrame(
length=0,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x1234567)
tutils.raises(ValueError, f.to_bytes) tutils.raises(ValueError, f.to_bytes)
def test_settings_frame_from_bytes(): def test_settings_frame_from_bytes():
f = Frame.from_bytes('000000040000000000'.decode('hex')) f = Frame.from_file(hex_to_file('000000040000000000'))
assert isinstance(f, SettingsFrame) assert isinstance(f, SettingsFrame)
assert_equal(f.length, 0) assert_equal(f.length, 0)
assert_equal(f.TYPE, SettingsFrame.TYPE) assert_equal(f.TYPE, SettingsFrame.TYPE)
assert_equal(f.flags, Frame.FLAG_NO_FLAGS) assert_equal(f.flags, Frame.FLAG_NO_FLAGS)
assert_equal(f.stream_id, 0x0) assert_equal(f.stream_id, 0x0)
f = Frame.from_bytes('000000040100000000'.decode('hex')) f = Frame.from_file(hex_to_file('000000040100000000'))
assert isinstance(f, SettingsFrame) assert isinstance(f, SettingsFrame)
assert_equal(f.length, 0) assert_equal(f.length, 0)
assert_equal(f.TYPE, SettingsFrame.TYPE) assert_equal(f.TYPE, SettingsFrame.TYPE)
assert_equal(f.flags, SettingsFrame.FLAG_ACK) assert_equal(f.flags, SettingsFrame.FLAG_ACK)
assert_equal(f.stream_id, 0x0) assert_equal(f.stream_id, 0x0)
f = Frame.from_bytes('000006040100000000000200000001'.decode('hex')) f = Frame.from_file(hex_to_file('000006040100000000000200000001'))
assert isinstance(f, SettingsFrame) assert isinstance(f, SettingsFrame)
assert_equal(f.length, 6) assert_equal(f.length, 6)
assert_equal(f.TYPE, SettingsFrame.TYPE) assert_equal(f.TYPE, SettingsFrame.TYPE)
@ -345,8 +404,8 @@ def test_settings_frame_from_bytes():
assert_equal(len(f.settings), 1) assert_equal(len(f.settings), 1)
assert_equal(f.settings[SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH], 1) assert_equal(f.settings[SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH], 1)
f = Frame.from_bytes( f = Frame.from_file(hex_to_file(
'00000c040000000000000200000001000312345678'.decode('hex')) '00000c040000000000000200000001000312345678'))
assert isinstance(f, SettingsFrame) assert isinstance(f, SettingsFrame)
assert_equal(f.length, 12) assert_equal(f.length, 12)
assert_equal(f.TYPE, SettingsFrame.TYPE) assert_equal(f.TYPE, SettingsFrame.TYPE)
@ -361,13 +420,17 @@ def test_settings_frame_from_bytes():
def test_settings_frame_human_readable(): def test_settings_frame_human_readable():
f = SettingsFrame(12, Frame.FLAG_NO_FLAGS, 0x0, settings={}) f = SettingsFrame(
length=12,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0,
settings={})
assert f.human_readable() assert f.human_readable()
f = SettingsFrame( f = SettingsFrame(
12, length=12,
Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
0x0, stream_id=0x0,
settings={ settings={
SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1, SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH: 1,
SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS: 0x12345678}) SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS: 0x12345678})
@ -376,35 +439,43 @@ def test_settings_frame_human_readable():
def test_push_promise_frame_to_bytes(): def test_push_promise_frame_to_bytes():
f = PushPromiseFrame( f = PushPromiseFrame(
10, length=10,
Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
0x1234567, stream_id=0x1234567,
0x7654321, promised_stream=0x7654321,
'foobar') header_block_fragment='foobar')
assert_equal( assert_equal(
f.to_bytes().encode('hex'), f.to_bytes().encode('hex'),
'00000a05000123456707654321666f6f626172') '00000a05000123456707654321666f6f626172')
f = PushPromiseFrame( f = PushPromiseFrame(
14, length=14,
HeadersFrame.FLAG_PADDED, flags=HeadersFrame.FLAG_PADDED,
0x1234567, stream_id=0x1234567,
0x7654321, promised_stream=0x7654321,
'foobar', header_block_fragment='foobar',
pad_length=3) pad_length=3)
assert_equal( assert_equal(
f.to_bytes().encode('hex'), f.to_bytes().encode('hex'),
'00000e0508012345670307654321666f6f626172000000') '00000e0508012345670307654321666f6f626172000000')
f = PushPromiseFrame(4, Frame.FLAG_NO_FLAGS, 0x0, 0x1234567) f = PushPromiseFrame(
length=4,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0,
promised_stream=0x1234567)
tutils.raises(ValueError, f.to_bytes) tutils.raises(ValueError, f.to_bytes)
f = PushPromiseFrame(4, Frame.FLAG_NO_FLAGS, 0x1234567, 0x0) f = PushPromiseFrame(
length=4,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x1234567,
promised_stream=0x0)
tutils.raises(ValueError, f.to_bytes) tutils.raises(ValueError, f.to_bytes)
def test_push_promise_frame_from_bytes(): def test_push_promise_frame_from_bytes():
f = Frame.from_bytes('00000a05000123456707654321666f6f626172'.decode('hex')) f = Frame.from_file(hex_to_file('00000a05000123456707654321666f6f626172'))
assert isinstance(f, PushPromiseFrame) assert isinstance(f, PushPromiseFrame)
assert_equal(f.length, 10) assert_equal(f.length, 10)
assert_equal(f.TYPE, PushPromiseFrame.TYPE) assert_equal(f.TYPE, PushPromiseFrame.TYPE)
@ -412,8 +483,8 @@ def test_push_promise_frame_from_bytes():
assert_equal(f.stream_id, 0x1234567) assert_equal(f.stream_id, 0x1234567)
assert_equal(f.header_block_fragment, 'foobar') assert_equal(f.header_block_fragment, 'foobar')
f = Frame.from_bytes( f = Frame.from_file(hex_to_file(
'00000e0508012345670307654321666f6f626172000000'.decode('hex')) '00000e0508012345670307654321666f6f626172000000'))
assert isinstance(f, PushPromiseFrame) assert isinstance(f, PushPromiseFrame)
assert_equal(f.length, 14) assert_equal(f.length, 14)
assert_equal(f.TYPE, PushPromiseFrame.TYPE) assert_equal(f.TYPE, PushPromiseFrame.TYPE)
@ -424,32 +495,43 @@ def test_push_promise_frame_from_bytes():
def test_push_promise_frame_human_readable(): def test_push_promise_frame_human_readable():
f = PushPromiseFrame( f = PushPromiseFrame(
14, length=14,
HeadersFrame.FLAG_PADDED, flags=HeadersFrame.FLAG_PADDED,
0x1234567, stream_id=0x1234567,
0x7654321, promised_stream=0x7654321,
'foobar', header_block_fragment='foobar',
pad_length=3) pad_length=3)
assert f.human_readable() assert f.human_readable()
def test_ping_frame_to_bytes(): def test_ping_frame_to_bytes():
f = PingFrame(8, PingFrame.FLAG_ACK, 0x0, payload=b'foobar') f = PingFrame(
length=8,
flags=PingFrame.FLAG_ACK,
stream_id=0x0,
payload=b'foobar')
assert_equal( assert_equal(
f.to_bytes().encode('hex'), f.to_bytes().encode('hex'),
'000008060100000000666f6f6261720000') '000008060100000000666f6f6261720000')
f = PingFrame(8, Frame.FLAG_NO_FLAGS, 0x0, payload=b'foobardeadbeef') f = PingFrame(
length=8,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x0,
payload=b'foobardeadbeef')
assert_equal( assert_equal(
f.to_bytes().encode('hex'), f.to_bytes().encode('hex'),
'000008060000000000666f6f6261726465') '000008060000000000666f6f6261726465')
f = PingFrame(8, Frame.FLAG_NO_FLAGS, 0x1234567) f = PingFrame(
length=8,
flags=Frame.FLAG_NO_FLAGS,
stream_id=0x1234567)
tutils.raises(ValueError, f.to_bytes) tutils.raises(ValueError, f.to_bytes)
def test_ping_frame_from_bytes(): def test_ping_frame_from_bytes():
f = Frame.from_bytes('000008060100000000666f6f6261720000'.decode('hex')) f = Frame.from_file(hex_to_file('000008060100000000666f6f6261720000'))
assert isinstance(f, PingFrame) assert isinstance(f, PingFrame)
assert_equal(f.length, 8) assert_equal(f.length, 8)
assert_equal(f.TYPE, PingFrame.TYPE) assert_equal(f.TYPE, PingFrame.TYPE)
@ -457,7 +539,7 @@ def test_ping_frame_from_bytes():
assert_equal(f.stream_id, 0x0) assert_equal(f.stream_id, 0x0)
assert_equal(f.payload, b'foobar\0\0') assert_equal(f.payload, b'foobar\0\0')
f = Frame.from_bytes('000008060000000000666f6f6261726465'.decode('hex')) f = Frame.from_file(hex_to_file('000008060000000000666f6f6261726465'))
assert isinstance(f, PingFrame) assert isinstance(f, PingFrame)
assert_equal(f.length, 8) assert_equal(f.length, 8)
assert_equal(f.TYPE, PingFrame.TYPE) assert_equal(f.TYPE, PingFrame.TYPE)
@ -467,15 +549,19 @@ def test_ping_frame_from_bytes():
def test_ping_frame_human_readable(): def test_ping_frame_human_readable():
f = PingFrame(8, PingFrame.FLAG_ACK, 0x0, payload=b'foobar') f = PingFrame(
length=8,
flags=PingFrame.FLAG_ACK,
stream_id=0x0,
payload=b'foobar')
assert f.human_readable() assert f.human_readable()
def test_goaway_frame_to_bytes(): def test_goaway_frame_to_bytes():
f = GoAwayFrame( f = GoAwayFrame(
8, length=8,
Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
0x0, stream_id=0x0,
last_stream=0x1234567, last_stream=0x1234567,
error_code=0x87654321, error_code=0x87654321,
data=b'') data=b'')
@ -484,9 +570,9 @@ def test_goaway_frame_to_bytes():
'0000080700000000000123456787654321') '0000080700000000000123456787654321')
f = GoAwayFrame( f = GoAwayFrame(
14, length=14,
Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
0x0, stream_id=0x0,
last_stream=0x1234567, last_stream=0x1234567,
error_code=0x87654321, error_code=0x87654321,
data=b'foobar') data=b'foobar')
@ -495,16 +581,17 @@ def test_goaway_frame_to_bytes():
'00000e0700000000000123456787654321666f6f626172') '00000e0700000000000123456787654321666f6f626172')
f = GoAwayFrame( f = GoAwayFrame(
8, length=8,
Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
0x1234567, stream_id=0x1234567,
last_stream=0x1234567, last_stream=0x1234567,
error_code=0x87654321) error_code=0x87654321)
tutils.raises(ValueError, f.to_bytes) tutils.raises(ValueError, f.to_bytes)
def test_goaway_frame_from_bytes(): def test_goaway_frame_from_bytes():
f = Frame.from_bytes('0000080700000000000123456787654321'.decode('hex')) f = Frame.from_file(hex_to_file(
'0000080700000000000123456787654321'))
assert isinstance(f, GoAwayFrame) assert isinstance(f, GoAwayFrame)
assert_equal(f.length, 8) assert_equal(f.length, 8)
assert_equal(f.TYPE, GoAwayFrame.TYPE) assert_equal(f.TYPE, GoAwayFrame.TYPE)
@ -514,8 +601,8 @@ def test_goaway_frame_from_bytes():
assert_equal(f.error_code, 0x87654321) assert_equal(f.error_code, 0x87654321)
assert_equal(f.data, b'') assert_equal(f.data, b'')
f = Frame.from_bytes( f = Frame.from_file(hex_to_file(
'00000e0700000000000123456787654321666f6f626172'.decode('hex')) '00000e0700000000000123456787654321666f6f626172'))
assert isinstance(f, GoAwayFrame) assert isinstance(f, GoAwayFrame)
assert_equal(f.length, 14) assert_equal(f.length, 14)
assert_equal(f.TYPE, GoAwayFrame.TYPE) assert_equal(f.TYPE, GoAwayFrame.TYPE)
@ -528,9 +615,9 @@ def test_goaway_frame_from_bytes():
def test_go_away_frame_human_readable(): def test_go_away_frame_human_readable():
f = GoAwayFrame( f = GoAwayFrame(
14, length=14,
Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
0x0, stream_id=0x0,
last_stream=0x1234567, last_stream=0x1234567,
error_code=0x87654321, error_code=0x87654321,
data=b'foobar') data=b'foobar')
@ -539,23 +626,23 @@ def test_go_away_frame_human_readable():
def test_window_update_frame_to_bytes(): def test_window_update_frame_to_bytes():
f = WindowUpdateFrame( f = WindowUpdateFrame(
4, length=4,
Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
0x0, stream_id=0x0,
window_size_increment=0x1234567) window_size_increment=0x1234567)
assert_equal(f.to_bytes().encode('hex'), '00000408000000000001234567') assert_equal(f.to_bytes().encode('hex'), '00000408000000000001234567')
f = WindowUpdateFrame( f = WindowUpdateFrame(
4, length=4,
Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
0x1234567, stream_id=0x1234567,
window_size_increment=0x7654321) window_size_increment=0x7654321)
assert_equal(f.to_bytes().encode('hex'), '00000408000123456707654321') assert_equal(f.to_bytes().encode('hex'), '00000408000123456707654321')
f = WindowUpdateFrame( f = WindowUpdateFrame(
4, length=4,
Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
0x0, stream_id=0x0,
window_size_increment=0xdeadbeef) window_size_increment=0xdeadbeef)
tutils.raises(ValueError, f.to_bytes) tutils.raises(ValueError, f.to_bytes)
@ -564,7 +651,7 @@ def test_window_update_frame_to_bytes():
def test_window_update_frame_from_bytes(): def test_window_update_frame_from_bytes():
f = Frame.from_bytes('00000408000000000001234567'.decode('hex')) f = Frame.from_file(hex_to_file('00000408000000000001234567'))
assert isinstance(f, WindowUpdateFrame) assert isinstance(f, WindowUpdateFrame)
assert_equal(f.length, 4) assert_equal(f.length, 4)
assert_equal(f.TYPE, WindowUpdateFrame.TYPE) assert_equal(f.TYPE, WindowUpdateFrame.TYPE)
@ -575,27 +662,31 @@ def test_window_update_frame_from_bytes():
def test_window_update_frame_human_readable(): def test_window_update_frame_human_readable():
f = WindowUpdateFrame( f = WindowUpdateFrame(
4, length=4,
Frame.FLAG_NO_FLAGS, flags=Frame.FLAG_NO_FLAGS,
0x1234567, stream_id=0x1234567,
window_size_increment=0x7654321) window_size_increment=0x7654321)
assert f.human_readable() assert f.human_readable()
def test_continuation_frame_to_bytes(): def test_continuation_frame_to_bytes():
f = ContinuationFrame( f = ContinuationFrame(
6, length=6,
ContinuationFrame.FLAG_END_HEADERS, flags=ContinuationFrame.FLAG_END_HEADERS,
0x1234567, stream_id=0x1234567,
'foobar') header_block_fragment='foobar')
assert_equal(f.to_bytes().encode('hex'), '000006090401234567666f6f626172') assert_equal(f.to_bytes().encode('hex'), '000006090401234567666f6f626172')
f = ContinuationFrame(6, ContinuationFrame.FLAG_END_HEADERS, 0x0, 'foobar') f = ContinuationFrame(
length=6,
flags=ContinuationFrame.FLAG_END_HEADERS,
stream_id=0x0,
header_block_fragment='foobar')
tutils.raises(ValueError, f.to_bytes) tutils.raises(ValueError, f.to_bytes)
def test_continuation_frame_from_bytes(): def test_continuation_frame_from_bytes():
f = Frame.from_bytes('000006090401234567666f6f626172'.decode('hex')) f = Frame.from_file(hex_to_file('000006090401234567666f6f626172'))
assert isinstance(f, ContinuationFrame) assert isinstance(f, ContinuationFrame)
assert_equal(f.length, 6) assert_equal(f.length, 6)
assert_equal(f.TYPE, ContinuationFrame.TYPE) assert_equal(f.TYPE, ContinuationFrame.TYPE)
@ -606,8 +697,8 @@ def test_continuation_frame_from_bytes():
def test_continuation_frame_human_readable(): def test_continuation_frame_human_readable():
f = ContinuationFrame( f = ContinuationFrame(
6, length=6,
ContinuationFrame.FLAG_END_HEADERS, flags=ContinuationFrame.FLAG_END_HEADERS,
0x1234567, stream_id=0x1234567,
'foobar') header_block_fragment='foobar')
assert f.human_readable() assert f.human_readable()

View File

@ -0,0 +1,217 @@
import OpenSSL
from netlib import http2
from netlib import tcp
from netlib import test
from netlib.http2.frame import *
from test import tutils
class EchoHandler(tcp.BaseHandler):
sni = None
def handle(self):
while True:
v = self.rfile.safe_read(1)
self.wfile.write(v)
self.wfile.flush()
class TestCheckALPNMatch(test.ServerTestBase):
handler = EchoHandler
ssl = dict(
alpn_select=http2.HTTP2Protocol.ALPN_PROTO_H2,
)
if OpenSSL._util.lib.Cryptography_HAS_ALPN:
def test_check_alpn(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
c.connect()
c.convert_to_ssl(alpn_protos=[http2.HTTP2Protocol.ALPN_PROTO_H2])
protocol = http2.HTTP2Protocol(c)
assert protocol.check_alpn()
class TestCheckALPNMismatch(test.ServerTestBase):
handler = EchoHandler
ssl = dict(
alpn_select=None,
)
if OpenSSL._util.lib.Cryptography_HAS_ALPN:
def test_check_alpn(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
c.connect()
c.convert_to_ssl(alpn_protos=[http2.HTTP2Protocol.ALPN_PROTO_H2])
protocol = http2.HTTP2Protocol(c)
tutils.raises(NotImplementedError, protocol.check_alpn)
class TestPerformConnectionPreface(test.ServerTestBase):
class handler(tcp.BaseHandler):
def handle(self):
# check magic
assert self.rfile.read(24) ==\
'505249202a20485454502f322e300d0a0d0a534d0d0a0d0a'.decode('hex')
# check empty settings frame
assert self.rfile.read(9) ==\
'000000040000000000'.decode('hex')
# send empty settings frame
self.wfile.write('000000040000000000'.decode('hex'))
self.wfile.flush()
# check settings acknowledgement
assert self.rfile.read(9) == \
'000000040100000000'.decode('hex')
# send settings acknowledgement
self.wfile.write('000000040100000000'.decode('hex'))
self.wfile.flush()
ssl = True
def test_perform_connection_preface(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
c.connect()
c.convert_to_ssl()
protocol = http2.HTTP2Protocol(c)
protocol.perform_connection_preface()
class TestStreamIds():
c = tcp.TCPClient(("127.0.0.1", 0))
protocol = http2.HTTP2Protocol(c)
def test_stream_ids(self):
assert self.protocol.current_stream_id is None
assert self.protocol.next_stream_id() == 1
assert self.protocol.current_stream_id == 1
assert self.protocol.next_stream_id() == 3
assert self.protocol.current_stream_id == 3
assert self.protocol.next_stream_id() == 5
assert self.protocol.current_stream_id == 5
class TestApplySettings(test.ServerTestBase):
class handler(tcp.BaseHandler):
def handle(self):
# check settings acknowledgement
assert self.rfile.read(9) == '000000040100000000'.decode('hex')
self.wfile.write("OK")
self.wfile.flush()
ssl = True
def test_apply_settings(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
c.connect()
c.convert_to_ssl()
protocol = http2.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',
})
assert c.rfile.safe_read(2) == "OK"
assert protocol.http2_settings[
SettingsFrame.SETTINGS.SETTINGS_ENABLE_PUSH] == 'foo'
assert protocol.http2_settings[
SettingsFrame.SETTINGS.SETTINGS_MAX_CONCURRENT_STREAMS] == 'bar'
assert protocol.http2_settings[
SettingsFrame.SETTINGS.SETTINGS_INITIAL_WINDOW_SIZE] == 'deadbeef'
class TestCreateHeaders():
c = tcp.TCPClient(("127.0.0.1", 0))
def test_create_headers(self):
headers = [
(b':method', b'GET'),
(b':path', b'index.html'),
(b':scheme', b'https'),
(b'foo', b'bar')]
bytes = http2.HTTP2Protocol(self.c)._create_headers(
headers, 1, end_stream=True)
assert b''.join(bytes) ==\
'000014010500000001824488355217caf3a69a3f87408294e7838c767f'\
.decode('hex')
bytes = http2.HTTP2Protocol(self.c)._create_headers(
headers, 1, end_stream=False)
assert b''.join(bytes) ==\
'000014010400000001824488355217caf3a69a3f87408294e7838c767f'\
.decode('hex')
# TODO: add test for too large header_block_fragments
class TestCreateBody():
c = tcp.TCPClient(("127.0.0.1", 0))
protocol = http2.HTTP2Protocol(c)
def test_create_body_empty(self):
bytes = self.protocol._create_body(b'', 1)
assert b''.join(bytes) == ''.decode('hex')
def test_create_body_single_frame(self):
bytes = self.protocol._create_body('foobar', 1)
assert b''.join(bytes) == '000006000100000001666f6f626172'.decode('hex')
def test_create_body_multiple_frames(self):
pass
# bytes = self.protocol._create_body('foobar' * 3000, 1)
# TODO: add test for too large frames
class TestCreateRequest():
c = tcp.TCPClient(("127.0.0.1", 0))
def test_create_request_simple(self):
bytes = http2.HTTP2Protocol(self.c).create_request('GET', '/')
assert len(bytes) == 1
assert bytes[0] == '000003010500000001828487'.decode('hex')
def test_create_request_with_body(self):
bytes = http2.HTTP2Protocol(self.c).create_request(
'GET', '/', [(b'foo', b'bar')], 'foobar')
assert len(bytes) == 2
assert bytes[0] ==\
'00000b010400000001828487408294e7838c767f'.decode('hex')
assert bytes[1] ==\
'000006000100000001666f6f626172'.decode('hex')
class TestReadResponse(test.ServerTestBase):
class handler(tcp.BaseHandler):
def handle(self):
self.wfile.write(
b'00000801040000000188628594e78c767f'.decode('hex'))
self.wfile.write(
b'000006000100000001666f6f626172'.decode('hex'))
self.wfile.flush()
ssl = True
def test_read_response(self):
c = tcp.TCPClient(("127.0.0.1", self.port))
c.connect()
c.convert_to_ssl()
protocol = http2.HTTP2Protocol(c)
status, headers, body = protocol.read_response()
assert headers == {':status': '200', 'etag': 'foobar'}
assert status == '200'
assert body == b'foobar'

View File

@ -135,10 +135,6 @@ class TestFinishFail(test.ServerTestBase):
class TestServerSSL(test.ServerTestBase): class TestServerSSL(test.ServerTestBase):
handler = EchoHandler handler = EchoHandler
ssl = dict( 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,
cipher_list="AES256-SHA", cipher_list="AES256-SHA",
chain_file=tutils.test_data.path("data/server.crt") chain_file=tutils.test_data.path("data/server.crt")
) )
@ -165,8 +161,6 @@ class TestServerSSL(test.ServerTestBase):
class TestSSLv3Only(test.ServerTestBase): class TestSSLv3Only(test.ServerTestBase):
handler = EchoHandler handler = EchoHandler
ssl = dict( ssl = dict(
cert=tutils.test_data.path("data/server.crt"),
key=tutils.test_data.path("data/server.key"),
request_client_cert=False, request_client_cert=False,
v3_only=True v3_only=True
) )
@ -188,9 +182,8 @@ class TestSSLClientCert(test.ServerTestBase):
def handle(self): def handle(self):
self.wfile.write("%s\n" % self.clientcert.serial) self.wfile.write("%s\n" % self.clientcert.serial)
self.wfile.flush() self.wfile.flush()
ssl = dict( ssl = dict(
cert=tutils.test_data.path("data/server.crt"),
key=tutils.test_data.path("data/server.key"),
request_client_cert=True, request_client_cert=True,
v3_only=False v3_only=False
) )
@ -224,12 +217,7 @@ class TestSNI(test.ServerTestBase):
self.wfile.write(self.sni) self.wfile.write(self.sni)
self.wfile.flush() self.wfile.flush()
ssl = dict( ssl = True
cert=tutils.test_data.path("data/server.crt"),
key=tutils.test_data.path("data/server.key"),
request_client_cert=False,
v3_only=False
)
def test_echo(self): def test_echo(self):
c = tcp.TCPClient(("127.0.0.1", self.port)) c = tcp.TCPClient(("127.0.0.1", self.port))
@ -242,10 +230,6 @@ class TestSNI(test.ServerTestBase):
class TestServerCipherList(test.ServerTestBase): class TestServerCipherList(test.ServerTestBase):
handler = ClientCipherListHandler handler = ClientCipherListHandler
ssl = dict( 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,
cipher_list='RC4-SHA' cipher_list='RC4-SHA'
) )
@ -264,11 +248,8 @@ class TestServerCurrentCipher(test.ServerTestBase):
def handle(self): def handle(self):
self.wfile.write("%s" % str(self.get_current_cipher())) self.wfile.write("%s" % str(self.get_current_cipher()))
self.wfile.flush() self.wfile.flush()
ssl = dict( 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,
cipher_list='RC4-SHA' cipher_list='RC4-SHA'
) )
@ -282,10 +263,6 @@ class TestServerCurrentCipher(test.ServerTestBase):
class TestServerCipherListError(test.ServerTestBase): class TestServerCipherListError(test.ServerTestBase):
handler = ClientCipherListHandler handler = ClientCipherListHandler
ssl = dict( 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,
cipher_list='bogus' cipher_list='bogus'
) )
@ -298,10 +275,6 @@ class TestServerCipherListError(test.ServerTestBase):
class TestClientCipherListError(test.ServerTestBase): class TestClientCipherListError(test.ServerTestBase):
handler = ClientCipherListHandler handler = ClientCipherListHandler
ssl = dict( 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,
cipher_list='RC4-SHA' cipher_list='RC4-SHA'
) )
@ -321,12 +294,8 @@ class TestSSLDisconnect(test.ServerTestBase):
def handle(self): def handle(self):
self.finish() self.finish()
ssl = dict(
cert=tutils.test_data.path("data/server.crt"), ssl = True
key=tutils.test_data.path("data/server.key"),
request_client_cert=False,
v3_only=False
)
def test_echo(self): def test_echo(self):
c = tcp.TCPClient(("127.0.0.1", self.port)) c = tcp.TCPClient(("127.0.0.1", self.port))
@ -341,12 +310,7 @@ class TestSSLDisconnect(test.ServerTestBase):
class TestSSLHardDisconnect(test.ServerTestBase): class TestSSLHardDisconnect(test.ServerTestBase):
handler = HardDisconnectHandler handler = HardDisconnectHandler
ssl = dict( ssl = True
cert=tutils.test_data.path("data/server.crt"),
key=tutils.test_data.path("data/server.key"),
request_client_cert=False,
v3_only=False
)
def test_echo(self): def test_echo(self):
c = tcp.TCPClient(("127.0.0.1", self.port)) c = tcp.TCPClient(("127.0.0.1", self.port))
@ -400,13 +364,9 @@ class TestTimeOut(test.ServerTestBase):
class TestALPN(test.ServerTestBase): class TestALPN(test.ServerTestBase):
handler = HangHandler handler = EchoHandler
ssl = dict( ssl = dict(
cert=tutils.test_data.path("data/server.crt"), alpn_select="foobar"
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: if OpenSSL._util.lib.Cryptography_HAS_ALPN:
@ -414,19 +374,13 @@ class TestALPN(test.ServerTestBase):
def test_alpn(self): def test_alpn(self):
c = tcp.TCPClient(("127.0.0.1", self.port)) c = tcp.TCPClient(("127.0.0.1", self.port))
c.connect() c.connect()
c.convert_to_ssl(alpn_protos=["h2"]) c.convert_to_ssl(alpn_protos=["foobar"])
print "ALPN: %s" % c.get_alpn_proto_negotiated() assert c.get_alpn_proto_negotiated() == "foobar"
assert c.get_alpn_proto_negotiated() == "h2"
class TestSSLTimeOut(test.ServerTestBase): class TestSSLTimeOut(test.ServerTestBase):
handler = HangHandler handler = HangHandler
ssl = dict( ssl = True
cert=tutils.test_data.path("data/server.crt"),
key=tutils.test_data.path("data/server.key"),
request_client_cert=False,
v3_only=False
)
def test_timeout_client(self): def test_timeout_client(self):
c = tcp.TCPClient(("127.0.0.1", self.port)) c = tcp.TCPClient(("127.0.0.1", self.port))
@ -439,10 +393,6 @@ class TestSSLTimeOut(test.ServerTestBase):
class TestDHParams(test.ServerTestBase): class TestDHParams(test.ServerTestBase):
handler = HangHandler handler = HangHandler
ssl = dict( 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,
dhparams=certutils.CertStore.load_dhparam( dhparams=certutils.CertStore.load_dhparam(
tutils.test_data.path("data/dhparam.pem"), tutils.test_data.path("data/dhparam.pem"),
), ),
@ -625,6 +575,11 @@ class TestFileLike:
s = tcp.Reader(o) s = tcp.Reader(o)
tutils.raises(tcp.NetLibDisconnect, s.readline, 10) tutils.raises(tcp.NetLibDisconnect, s.readline, 10)
def test_reader_incomplete_error(self):
s = cStringIO.StringIO("foobar")
s = tcp.Reader(s)
tutils.raises(tcp.NetLibIncomplete, s.safe_read, 10)
class TestAddress: class TestAddress:
@ -643,10 +598,6 @@ class TestAddress:
class TestSSLKeyLogger(test.ServerTestBase): class TestSSLKeyLogger(test.ServerTestBase):
handler = EchoHandler handler = EchoHandler
ssl = dict( 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,
cipher_list="AES256-SHA" cipher_list="AES256-SHA"
) )