attach application protocol to connection

This commit is contained in:
Thomas Kriechbaumer 2015-07-30 13:52:50 +02:00
parent a5d9e1f44d
commit 4f38c6b90e
5 changed files with 67 additions and 46 deletions

View File

@ -30,7 +30,7 @@ def response(context, flow):
value = flow.response.headers.get_first("Connection", None) value = flow.response.headers.get_first("Connection", None)
if value and value.upper() == "UPGRADE": if value and value.upper() == "UPGRADE":
# We need to send the response manually now... # We need to send the response manually now...
flow.client_conn.send(flow.client_protocol.assemble(flow.response)) flow.client_conn.send(flow.client_conn.protocol.assemble(flow.response))
# ...and then delegate to tcp passthrough. # ...and then delegate to tcp passthrough.
TCPHandler(flow.live.c, log=False).handle_messages() TCPHandler(flow.live.c, log=False).handle_messages()
flow.reply(KILL) flow.reply(KILL)

View File

@ -252,7 +252,7 @@ def copy_flow_format_data(part, scope, flow):
return None, "Request content is missing" return None, "Request content is missing"
with decoded(flow.request): with decoded(flow.request):
if part == "h": if part == "h":
data += flow.client_protocol.assemble(flow.request) data += flow.client_conn.protocol.assemble(flow.request)
elif part == "c": elif part == "c":
data += flow.request.content data += flow.request.content
else: else:
@ -265,7 +265,7 @@ def copy_flow_format_data(part, scope, flow):
return None, "Response content is missing" return None, "Response content is missing"
with decoded(flow.response): with decoded(flow.response):
if part == "h": if part == "h":
data += flow.client_protocol.assemble(flow.response) data += flow.client_conn.protocol.assemble(flow.response)
elif part == "c": elif part == "c":
data += flow.response.content data += flow.response.content
else: else:

View File

@ -9,7 +9,7 @@ from email.utils import parsedate_tz, formatdate, mktime_tz
import netlib import netlib
from netlib import http, tcp, odict, utils from netlib import http, tcp, odict, utils
from netlib.http import cookies, http1 from netlib.http import cookies, http1, http2
from netlib.http.semantics import CONTENT_MISSING from netlib.http.semantics import CONTENT_MISSING
from .tcp import TCPHandler from .tcp import TCPHandler
@ -39,7 +39,7 @@ def send_connect_request(conn, host, port, update_state=True):
odict.ODictCaseless(), odict.ODictCaseless(),
"" ""
) )
protocol = http.http1.HTTP1Protocol(conn) protocol = http1.HTTP1Protocol(conn)
conn.send(protocol.assemble(upstream_request)) conn.send(protocol.assemble(upstream_request))
resp = HTTPResponse.from_protocol(protocol, upstream_request.method) resp = HTTPResponse.from_protocol(protocol, upstream_request.method)
if resp.status_code != 200: if resp.status_code != 200:
@ -177,12 +177,16 @@ class HTTPHandler(ProtocolHandler):
for attempt in (0, 1): for attempt in (0, 1):
try: try:
flow.server_protocol = http.http1.HTTP1Protocol(self.c.server_conn) if not self.c.server_conn.protocol:
self.c.server_conn.send(flow.server_protocol.assemble(flow.request)) # instantiate new protocol if connection does not have one yet
self.c.server_conn.protocol = http2.HTTP2Protocol(self.c.server_conn)
self.c.server_conn.protocol.perform_connection_preface()
self.c.server_conn.send(self.c.server_conn.protocol.assemble(flow.request))
# Only get the headers at first... # Only get the headers at first...
flow.response = HTTPResponse.from_protocol( flow.response = HTTPResponse.from_protocol(
flow.server_protocol, flow.server_conn.protocol,
flow.request.method, flow.request.method,
body_size_limit=self.c.config.body_size_limit, body_size_limit=self.c.config.body_size_limit,
include_body=False include_body=False
@ -220,23 +224,27 @@ class HTTPHandler(ProtocolHandler):
if flow.response.stream: if flow.response.stream:
flow.response.content = CONTENT_MISSING flow.response.content = CONTENT_MISSING
else: else:
flow.server_protocol = http1.HTTP1Protocol(self.c.server_conn) if isinstance(flow.server_conn.protocol, http1.HTTP1Protocol):
flow.response.content = flow.server_protocol.read_http_body( flow.response.content = flow.server_conn.protocol.read_http_body(
flow.response.headers, flow.response.headers,
self.c.config.body_size_limit, self.c.config.body_size_limit,
flow.request.method, flow.request.method,
flow.response.code, flow.response.code,
False False
) )
flow.response.timestamp_end = utils.timestamp() flow.response.timestamp_end = utils.timestamp()
def handle_flow(self): def handle_flow(self):
flow = HTTPFlow(self.c.client_conn, self.c.server_conn, self.live) flow = HTTPFlow(self.c.client_conn, self.c.server_conn, self.live)
try: try:
try: try:
flow.client_protocol = http.http1.HTTP1Protocol(self.c.client_conn) if not flow.client_conn.protocol:
# instantiate new protocol if connection does not have one yet
flow.client_conn.protocol = http1.HTTP1Protocol(self.c.client_conn)
req = HTTPRequest.from_protocol( req = HTTPRequest.from_protocol(
flow.client_protocol, flow.client_conn.protocol,
body_size_limit=self.c.config.body_size_limit body_size_limit=self.c.config.body_size_limit
) )
except tcp.NetLibError: except tcp.NetLibError:
@ -249,9 +257,15 @@ class HTTPHandler(ProtocolHandler):
[repr(req)] [repr(req)]
) )
ret = self.process_request(flow, req) ret = self.process_request(flow, req)
if ret:
# CONNECT successful - upgrade to HTTP/2
# instantiate new protocol if connection does not have one yet
flow.client_conn.protocol = http2.HTTP2Protocol(self.c.client_conn, is_server=True)
if ret is not None: if ret is not None:
return ret return ret
print("still here: %s" % flow.client_conn.protocol.__class__)
# Be careful NOT to assign the request to the flow before # Be careful NOT to assign the request to the flow before
# process_request completes. This is because the call can raise an # process_request completes. This is because the call can raise an
# exception. If the request object is already attached, this results # exception. If the request object is already attached, this results
@ -375,30 +389,31 @@ class HTTPHandler(ProtocolHandler):
pass pass
def send_error(self, code, message, headers): def send_error(self, code, message, headers):
response = http.status_codes.RESPONSES.get(code, "Unknown") raise NotImplementedError("todo - adapt for HTTP/2 - make use of make_error_reponse from pathod")
html_content = """ # response = http.status_codes.RESPONSES.get(code, "Unknown")
<html> # html_content = """
<head> # <html>
<title>%d %s</title> # <head>
</head> # <title>%d %s</title>
<body>%s</body> # </head>
</html> # <body>%s</body>
""" % (code, response, message) # </html>
self.c.client_conn.wfile.write("HTTP/1.1 %s %s\r\n" % (code, response)) # """ % (code, response, message)
self.c.client_conn.wfile.write( # self.c.client_conn.wfile.write("HTTP/1.1 %s %s\r\n" % (code, response))
"Server: %s\r\n" % self.c.config.server_version # self.c.client_conn.wfile.write(
) # "Server: %s\r\n" % self.c.config.server_version
self.c.client_conn.wfile.write("Content-type: text/html\r\n") # )
self.c.client_conn.wfile.write( # self.c.client_conn.wfile.write("Content-type: text/html\r\n")
"Content-Length: %d\r\n" % len(html_content) # self.c.client_conn.wfile.write(
) # "Content-Length: %d\r\n" % len(html_content)
if headers: # )
for key, value in headers.items(): # if headers:
self.c.client_conn.wfile.write("%s: %s\r\n" % (key, value)) # for key, value in headers.items():
self.c.client_conn.wfile.write("Connection: close\r\n") # self.c.client_conn.wfile.write("%s: %s\r\n" % (key, value))
self.c.client_conn.wfile.write("\r\n") # self.c.client_conn.wfile.write("Connection: close\r\n")
self.c.client_conn.wfile.write(html_content) # self.c.client_conn.wfile.write("\r\n")
self.c.client_conn.wfile.flush() # self.c.client_conn.wfile.write(html_content)
# self.c.client_conn.wfile.flush()
def process_request(self, flow, request): def process_request(self, flow, request):
""" """
@ -554,7 +569,7 @@ class HTTPHandler(ProtocolHandler):
# no streaming: # no streaming:
# we already received the full response from the server and can # we already received the full response from the server and can
# send it to the client straight away. # send it to the client straight away.
self.c.client_conn.send(flow.client_protocol.assemble(flow.response)) self.c.client_conn.send(self.c.client_conn.protocol.assemble(flow.response))
else: else:
raise NotImplementedError("HTTP streaming is currently not supported.") raise NotImplementedError("HTTP streaming is currently not supported.")
# TODO: implement it according to new protocols and messages # TODO: implement it according to new protocols and messages
@ -731,12 +746,11 @@ class RequestReplayThread(threading.Thread):
) )
r.form_out = "relative" r.form_out = "relative"
server.send(self.flow.server_protocol.assemble(r)) server.send(self.flow.server_conn.protocol.assemble(r))
self.flow.server_conn = server self.flow.server_conn = server
self.flow.server_protocol = http.http1.HTTP1Protocol(self.flow.server_conn)
self.flow.response = HTTPResponse.from_protocol( self.flow.response = HTTPResponse.from_protocol(
self.flow.server_protocol, self.flow.server_conn.protocol,
r.method, r.method,
body_size_limit=self.config.body_size_limit, body_size_limit=self.config.body_size_limit,
) )

View File

@ -167,6 +167,7 @@ class Flow(stateobject.StateObject):
master.handle_accept_intercept(self) master.handle_accept_intercept(self)
class ProtocolHandler(object): class ProtocolHandler(object):
""" """
A ProtocolHandler implements an application-layer protocol, e.g. HTTP. A ProtocolHandler implements an application-layer protocol, e.g. HTTP.

View File

@ -23,6 +23,7 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
self.timestamp_start = utils.timestamp() self.timestamp_start = utils.timestamp()
self.timestamp_end = None self.timestamp_end = None
self.timestamp_ssl_setup = None self.timestamp_ssl_setup = None
self.protocol = None
def __repr__(self): def __repr__(self):
return "<ClientConnection: {ssl}{host}:{port}>".format( return "<ClientConnection: {ssl}{host}:{port}>".format(
@ -58,6 +59,8 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
return copy.copy(self) return copy.copy(self)
def send(self, message): def send(self, message):
if isinstance(message, list):
message = b''.join(message)
self.wfile.write(message) self.wfile.write(message)
self.wfile.flush() self.wfile.flush()
@ -93,6 +96,7 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
self.timestamp_end = None self.timestamp_end = None
self.timestamp_tcp_setup = None self.timestamp_tcp_setup = None
self.timestamp_ssl_setup = None self.timestamp_ssl_setup = None
self.protocol = None
def __repr__(self): def __repr__(self):
if self.ssl_established and self.sni: if self.ssl_established and self.sni:
@ -157,6 +161,8 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
self.timestamp_tcp_setup = utils.timestamp() self.timestamp_tcp_setup = utils.timestamp()
def send(self, message): def send(self, message):
if isinstance(message, list):
message = b''.join(message)
self.wfile.write(message) self.wfile.write(message)
self.wfile.flush() self.wfile.flush()