From f7da58ca9b8b3dc33f5a9b57999b07f99db5bc63 Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Wed, 17 Sep 2014 09:40:25 +1200 Subject: Basic websocket connection, code cleanup. --- libmproxy/flow.py | 24 ++++++++++++---------- libmproxy/protocol/primitives.py | 43 ++++++++++++++++++++++++---------------- libmproxy/web/__init__.py | 6 +++--- libmproxy/web/app.py | 22 +++++++++++++++++++- libmproxy/web/static/js/app.js | 25 ++++++++++------------- 5 files changed, 74 insertions(+), 46 deletions(-) (limited to 'libmproxy') diff --git a/libmproxy/flow.py b/libmproxy/flow.py index c246894c..b9095a02 100644 --- a/libmproxy/flow.py +++ b/libmproxy/flow.py @@ -2,11 +2,10 @@ This module provides more sophisticated flow tracking and provides filtering and interception facilities. """ from __future__ import absolute_import -import base64 -import hashlib, Cookie, cookielib, re, threading -import os -import flask -import requests +import hashlib +import Cookie +import cookielib +import re from netlib import odict, wsgi import netlib.http from . import controller, protocol, tnetstring, filt, script, version @@ -151,10 +150,13 @@ class StreamLargeBodies(object): def run(self, flow, is_request): r = flow.request if is_request else flow.response code = flow.response.code if flow.response else None - expected_size = netlib.http.expected_http_body_size(r.headers, is_request, flow.request.method, code) + expected_size = netlib.http.expected_http_body_size( + r.headers, is_request, flow.request.method, code + ) if not (0 <= expected_size <= self.max_size): r.stream = True + class ClientPlaybackState: def __init__(self, flows, exit): self.flows, self.exit = flows, exit @@ -645,11 +647,11 @@ class FlowMaster(controller.Master): f.error = None self.process_new_request(f) rt = http.RequestReplayThread( - self.server.config, - f, - self.masterq, - self.should_exit - ) + self.server.config, + f, + self.masterq, + self.should_exit + ) rt.start() # pragma: no cover if block: rt.join() diff --git a/libmproxy/protocol/primitives.py b/libmproxy/protocol/primitives.py index 160c50c7..ecf24fd7 100644 --- a/libmproxy/protocol/primitives.py +++ b/libmproxy/protocol/primitives.py @@ -12,10 +12,10 @@ class Error(stateobject.SimpleStateObject): """ An Error. - This is distinct from an protocol error response (say, a HTTP code 500), which - is represented by a normal HTTPResponse object. This class is responsible - for indicating errors that fall outside of normal protocol communications, - like interrupted connections, timeouts, protocol errors. + This is distinct from an protocol error response (say, a HTTP code 500), + which is represented by a normal HTTPResponse object. This class is + responsible for indicating errors that fall outside of normal protocol + communications, like interrupted connections, timeouts, protocol errors. Exposes the following attributes: @@ -42,7 +42,9 @@ class Error(stateobject.SimpleStateObject): @classmethod def _from_state(cls, state): - f = cls(None) # the default implementation assumes an empty constructor. Override accordingly. + # the default implementation assumes an empty constructor. Override + # accordingly. + f = cls(None) f._load_state(state) return f @@ -133,31 +135,35 @@ class ProtocolHandler(object): def handle_messages(self): """ - This method gets called if a client connection has been made. Depending on the proxy settings, - a server connection might already exist as well. + This method gets called if a client connection has been made. Depending + on the proxy settings, a server connection might already exist as well. """ raise NotImplementedError # pragma: nocover def handle_server_reconnect(self, state): """ - This method gets called if a server connection needs to reconnect and there's a state associated - with the server connection (e.g. a previously-sent CONNECT request or a SOCKS proxy request). - This method gets called after the connection has been restablished but before SSL is established. + This method gets called if a server connection needs to reconnect and + there's a state associated with the server connection (e.g. a + previously-sent CONNECT request or a SOCKS proxy request). This method + gets called after the connection has been restablished but before SSL is + established. """ raise NotImplementedError # pragma: nocover def handle_error(self, error): """ - This method gets called should there be an uncaught exception during the connection. - This might happen outside of handle_messages, e.g. if the initial SSL handshake fails in transparent mode. + This method gets called should there be an uncaught exception during the + connection. This might happen outside of handle_messages, e.g. if the + initial SSL handshake fails in transparent mode. """ raise error # pragma: nocover class LiveConnection(object): """ - This facade allows interested parties (FlowMaster, inline scripts) to interface with a live connection, - without requiring to expose the internals of the ConnectionHandler. + This facade allows interested parties (FlowMaster, inline scripts) to + interface with a live connection, without requiring to expose the internals + of the ConnectionHandler. """ def __init__(self, c): self.c = c @@ -193,7 +199,9 @@ class LiveConnection(object): if not self._backup_server_conn and not persistent_change: self._backup_server_conn = self.c.server_conn self.c.server_conn = None - else: # This is at least the second temporary change. We can kill the current connection. + else: + # This is at least the second temporary change. We can kill the + # current connection. self.c.del_server_connection() self.c.set_server_address(address) @@ -204,8 +212,9 @@ class LiveConnection(object): return False def restore_server(self): - # TODO: Similar to _backup_server_conn, introduce _cache_server_conn, which keeps the changed connection open - # This may be beneficial if a user is rewriting all requests from http to https or similar. + # TODO: Similar to _backup_server_conn, introduce _cache_server_conn, + # which keeps the changed connection open This may be beneficial if a + # user is rewriting all requests from http to https or similar. if not self._backup_server_conn: return diff --git a/libmproxy/web/__init__.py b/libmproxy/web/__init__.py index 8f98060c..044cb0cd 100644 --- a/libmproxy/web/__init__.py +++ b/libmproxy/web/__init__.py @@ -58,6 +58,7 @@ class Options(object): class WebMaster(flow.FlowMaster): def __init__(self, server, options): self.options = options + self.app = app.Application(self.options.wdebug) flow.FlowMaster.__init__(self, server, WebState()) def tick(self): @@ -70,9 +71,7 @@ class WebMaster(flow.FlowMaster): ) iol = tornado.ioloop.IOLoop.instance() - http_server = tornado.httpserver.HTTPServer( - app.Application(self.options.wdebug) - ) + http_server = tornado.httpserver.HTTPServer(self.app) http_server.listen(self.options.wport) tornado.ioloop.PeriodicCallback(self.tick, 5).start() @@ -82,6 +81,7 @@ class WebMaster(flow.FlowMaster): self.shutdown() def handle_request(self, f): + print f flow.FlowMaster.handle_request(self, f) if f: f.reply() diff --git a/libmproxy/web/app.py b/libmproxy/web/app.py index 31b299a3..e9bcc526 100644 --- a/libmproxy/web/app.py +++ b/libmproxy/web/app.py @@ -1,6 +1,7 @@ - import os.path import tornado.web +import tornado.websocket +import logging class IndexHandler(tornado.web.RequestHandler): @@ -8,10 +9,29 @@ class IndexHandler(tornado.web.RequestHandler): self.render("index.html") +class ClientConnection(tornado.websocket.WebSocketHandler): + connections = set() + + def open(self): + ClientConnection.connections.add(self) + + def on_close(self): + ClientConnection.connections.remove(self) + + @classmethod + def broadcast(cls, type, data): + for conn in cls.connections: + try: + conn.write_message(type, data) + except: + logging.error("Error sending message", exc_info=True) + + class Application(tornado.web.Application): def __init__(self, debug): handlers = [ (r"/", IndexHandler), + (r"/updates", ClientConnection), ] settings = dict( template_path=os.path.join(os.path.dirname(__file__), "templates"), diff --git a/libmproxy/web/static/js/app.js b/libmproxy/web/static/js/app.js index c095fd44..6a157b24 100644 --- a/libmproxy/web/static/js/app.js +++ b/libmproxy/web/static/js/app.js @@ -198,17 +198,14 @@ _.extend(_EventLogStore.prototype, EventEmitter.prototype, { var EventLogStore = new _EventLogStore(); AppDispatcher.register(EventLogStore.handle.bind(EventLogStore)); -function _Connection(root) {"use strict"; - if (!root) { - root = location.origin + "/api/v1"; - } - this.root = root; - } -_Connection.prototype.init=function() {"use strict"; +function _Connection(url) { + this.url = url; +} +_Connection.prototype.init=function() { this.openWebSocketConnection(); }; -_Connection.prototype.openWebSocketConnection=function() {"use strict"; - this.ws = new WebSocket(this.root.replace("http", "ws") + "/ws"); +_Connection.prototype.openWebSocketConnection=function() { + this.ws = new WebSocket(this.url.replace("http", "ws")); var ws = this.ws; ws.onopen = this.onopen.bind(this); @@ -216,21 +213,21 @@ _Connection.prototype.openWebSocketConnection=function() {"use strict"; ws.onerror = this.onerror.bind(this); ws.onclose = this.onclose.bind(this); }; -_Connection.prototype.onopen=function(open) {"use strict"; +_Connection.prototype.onopen=function(open) { console.log("onopen", this, arguments); }; -_Connection.prototype.onmessage=function(message) {"use strict"; +_Connection.prototype.onmessage=function(message) { //AppDispatcher.dispatchServerAction(...); console.log("onmessage", this, arguments); }; -_Connection.prototype.onerror=function(error) {"use strict"; +_Connection.prototype.onerror=function(error) { console.log("onerror", this, arguments); }; -_Connection.prototype.onclose=function(close) {"use strict"; +_Connection.prototype.onclose=function(close) { console.log("onclose", this, arguments); }; -var Connection = new _Connection(); +var Connection = new _Connection(location.origin + "/updates"); /** @jsx React.DOM */ -- cgit v1.2.3