diff --git a/libpathod/pathoc.py b/libpathod/pathoc.py index 7af5a2886..793135e77 100644 --- a/libpathod/pathoc.py +++ b/libpathod/pathoc.py @@ -40,23 +40,23 @@ class Pathoc(tcp.TCPClient): r = rparse.parse_request({}, i) req = r.serve(self.wfile) if reqdump: - print >> fp, ">>", req["method"], req["path"] + print >> fp, "\n>>", req["method"], req["path"] for a in req["actions"]: print >> fp, "\t", for x in a: - print x, - print + print >> fp, x, + print >> fp self.wfile.flush() - resp = self.request(i) + resp = http.read_response(self.rfile, r.method, None) except rparse.ParseException, v: print >> fp, "Error parsing request spec: %s"%v.msg print >> fp, v.marked() return except http.HttpError, v: - print >> fp, v.msg + print >> fp, "<<", v.msg return except tcp.NetLibTimeout: - print >> fp, "Timeout" + print >> fp, "<<", "Timeout" else: if respdump: print_full(fp, *resp) diff --git a/libpathod/pathod.py b/libpathod/pathod.py index 44bcadb71..026986bb3 100644 --- a/libpathod/pathod.py +++ b/libpathod/pathod.py @@ -18,6 +18,82 @@ class PathodHandler(tcp.BaseHandler): def handle_sni(self, connection): self.sni = connection.get_servername() + def handle_request(self): + """ + Returns True if handling should continue. + """ + line = self.rfile.readline() + if line == "\r\n" or line == "\n": # Possible leftover from previous message + line = self.rfile.readline() + if line == "": + return + + parts = http.parse_init_http(line) + if not parts: + s = "Invalid first line: %s"%line.rstrip() + self.info(s) + self.server.add_log( + dict( + type = "error", + msg = s + ) + ) + return + method, path, httpversion = parts + + headers = http.read_headers(self.rfile) + content = http.read_http_body_request( + self.rfile, self.wfile, headers, httpversion, None + ) + + crafted = None + for i in self.server.anchors: + if i[0].match(path): + crafted = i[1] + + if not crafted and path.startswith(self.server.prefix): + spec = urllib.unquote(path)[len(self.server.prefix):] + try: + crafted = rparse.parse_response(self.server.request_settings, spec) + except rparse.ParseException, v: + crafted = rparse.InternalResponse( + 800, + "Error parsing response spec: %s\n"%v.msg + v.marked() + ) + + request_log = dict( + path = path, + method = method, + headers = headers.lst, + sni = self.sni, + remote_address = self.client_address, + httpversion = httpversion, + ) + if crafted: + response_log = crafted.serve(self.wfile) + if response_log["disconnect"]: + return + self.server.add_log( + dict( + type = "crafted", + request=request_log, + response=response_log + ) + ) + else: + cc = wsgi.ClientConn(self.client_address) + req = wsgi.Request(cc, "http", method, path, headers, content) + sn = self.connection.getsockname() + app = wsgi.WSGIAdaptor( + self.server.app, + sn[0], + self.server.port, + version.NAMEVERSION + ) + app.serve(req, self.wfile) + self.debug("%s %s"%(method, path)) + return True + def handle(self): if self.server.ssloptions: try: @@ -34,79 +110,21 @@ class PathodHandler(tcp.BaseHandler): ) ) self.info(s) - self.finish() + return while not self.finished: - line = self.rfile.readline() - if line == "\r\n" or line == "\n": # Possible leftover from previous message - line = self.rfile.readline() - if line == "": - return None - - parts = http.parse_init_http(line) - if not parts: - s = "Invalid first line: %s"%line.rstrip() - self.info(s) + try: + if not self.handle_request(): + return + except tcp.NetLibDisconnect: + self.info("Disconnect") self.server.add_log( dict( type = "error", - msg = s + msg = "Disconnect" ) ) - return None - method, path, httpversion = parts - - headers = http.read_headers(self.rfile) - content = http.read_http_body_request( - self.rfile, self.wfile, headers, httpversion, None - ) - - crafted = None - for i in self.server.anchors: - if i[0].match(path): - crafted = i[1] - - if not crafted and path.startswith(self.server.prefix): - spec = urllib.unquote(path)[len(self.server.prefix):] - try: - crafted = rparse.parse_response(self.server.request_settings, spec) - except rparse.ParseException, v: - crafted = rparse.InternalResponse( - 800, - "Error parsing response spec: %s\n"%v.msg + v.marked() - ) - - request_log = dict( - path = path, - method = method, - headers = headers.lst, - sni = self.sni, - remote_address = self.client_address, - httpversion = httpversion, - ) - if crafted: - response_log = crafted.serve(self.wfile) - if response_log["disconnect"]: - self.finish() - self.server.add_log( - dict( - type = "crafted", - request=request_log, - response=response_log - ) - ) - else: - cc = wsgi.ClientConn(self.client_address) - req = wsgi.Request(cc, "http", method, path, headers, content) - sn = self.connection.getsockname() - app = wsgi.WSGIAdaptor( - self.server.app, - sn[0], - self.server.port, - version.NAMEVERSION - ) - app.serve(req, self.wfile) - self.debug("%s %s"%(method, path)) + return class Pathod(tcp.TCPServer): diff --git a/test/test_pathod.py b/test/test_pathod.py index a23b7c711..1484efcd4 100644 --- a/test/test_pathod.py +++ b/test/test_pathod.py @@ -64,11 +64,13 @@ class _DaemonTests: scheme = "https" if self.SSL else "http" return requests.get("%s://localhost:%s/p/%s"%(scheme, self.d.port, spec), verify=False) - def pathoc(self, spec): + def pathoc(self, spec, timeout=None): c = pathoc.Pathoc("localhost", self.d.port) c.connect() if self.SSL: c.convert_to_ssl() + if timeout: + c.settimeout(timeout) return c.request(spec) def test_preline(self): @@ -114,6 +116,7 @@ class _DaemonTests: assert "foo" in l["msg"] + class TestDaemon(_DaemonTests): SSL = False