X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=hippotat;h=6aa3121c7ad68a72367930015817a9d91955e460;hb=HEAD;hp=43faf28715e1ab2a27f71b496cf1fc6226f1696a;hpb=a571ef2d5e5929225f8874a9e3f819c43d21b534;p=hippotat.git diff --git a/hippotat b/hippotat deleted file mode 100755 index 43faf28..0000000 --- a/hippotat +++ /dev/null @@ -1,279 +0,0 @@ -#!/usr/bin/python3 -# -# Hippotat - Asinine IP Over HTTP program -# ./hippotat - client main program -# -# Copyright 2017 Ian Jackson -# -# GPLv3+ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program, in the file GPLv3. If not, -# see . - -#@ sys.path.append('@PYBUILD_INSTALL_DIR@') -from hippotatlib import * - -import twisted.web -import twisted.web.client - -import io - -class GeneralResponseConsumer(twisted.internet.protocol.Protocol): - def __init__(self, cl, req, desc): - self._cl = cl - self._req = req - self._desc = desc - - def _log(self, dflag, msg, **kwargs): - self._cl.log(dflag, '%s: %s' % (self._desc, msg), idof=self._req, **kwargs) - - def connectionMade(self): - self._log(DBG.HTTP_CTRL, 'connectionMade') - -class ResponseConsumer(GeneralResponseConsumer): - def __init__(self, cl, req): - super().__init__(cl, req, 'RC') - ssddesc = '[%s] %s' % (id(req), self._desc) - self._ssd = SlipStreamDecoder(ssddesc, partial(queue_inbound, cl.ipif)) - self._log(DBG.HTTP_CTRL, '__init__') - - def dataReceived(self, data): - self._log(DBG.HTTP, 'dataReceived', d=data) - try: - self._ssd.inputdata(data) - except Exception as e: - self._handleexception() - - def connectionLost(self, reason): - self._log(DBG.HTTP_CTRL, 'connectionLost ' + str(reason)) - if not reason.check(twisted.web.client.ResponseDone): - self.latefailure() - return - try: - self._log(DBG.HTTP, 'ResponseDone') - self._ssd.flush() - self._cl.req_fin(self._req) - except Exception as e: - self._handleexception() - self._cl.report_running() - - def _handleexception(self): - self._latefailure(traceback.format_exc()) - - def _latefailure(self, reason): - self._log(DBG.HTTP_CTRL, '_latefailure ' + str(reason)) - self._cl.req_err(self._req, reason) - -class ErrorResponseConsumer(GeneralResponseConsumer): - def __init__(self, cl, req, resp): - super().__init__(cl, req, 'ERROR-RC') - self._resp = resp - self._m = b'' - try: - self._phrase = resp.phrase.decode('utf-8') - except Exception: - self._phrase = repr(resp.phrase) - self._log(DBG.HTTP_CTRL, '__init__ %d %s' % (resp.code, self._phrase)) - - def dataReceived(self, data): - self._log(DBG.HTTP_CTRL, 'dataReceived ' + repr(data)) - self._m += data - - def connectionLost(self, reason): - try: - mbody = self._m.decode('utf-8') - except Exception: - mbody = repr(self._m) - if not reason.check(twisted.web.client.ResponseDone): - mbody += ' || ' + str(reason) - self._cl.req_err(self._req, - "FAILED %d %s | %s" - % (self._resp.code, self._phrase, mbody)) - -class Client(): - def __init__(cl, c,ss,cs): - cl.c = c - cl.outstanding = { } - cl.desc = '[%s %s] ' % (ss,cs) - cl.running_reported = False - cl.log_info('setting up') - - def log_info(cl, msg): - log.info(cl.desc + msg, dflag=False) - - def report_running(cl): - if not cl.running_reported: - cl.log_info('running OK') - cl.running_reported = True - - def log(cl, dflag, msg, **kwargs): - log_debug(dflag, cl.desc + msg, **kwargs) - - def log_outstanding(cl): - cl.log(DBG.CTRL_DUMP, 'OS %s' % cl.outstanding) - - def start(cl): - cl.queue = PacketQueue('up', cl.c.max_queue_time) - cl.agent = twisted.web.client.Agent( - reactor, connectTimeout = cl.c.http_timeout) - - def outbound(cl, packet, saddr, daddr): - #print('OUT ', saddr, daddr, repr(packet)) - cl.queue.append(packet) - cl.check_outbound() - - def req_ok(cl, req, resp): - cl.log(DBG.HTTP_CTRL, - 'req_ok %d %s %s' % (resp.code, repr(resp.phrase), str(resp)), - idof=req) - if resp.code == 200: - rc = ResponseConsumer(cl, req) - else: - rc = ErrorResponseConsumer(cl, req, resp) - - resp.deliverBody(rc) - # now rc is responsible for calling req_fin - - def req_err(cl, req, err): - # called when the Deferred fails, or (if it completes), - # later, by ResponsConsumer or ErrorResponsConsumer - try: - cl.log(DBG.HTTP_CTRL, 'req_err ' + str(err), idof=req) - if isinstance(err, twisted.python.failure.Failure): - err = err.getTraceback() - print('[%#x] %s' % (id(req), err), file=sys.stderr) - if not isinstance(cl.outstanding[req], int): - raise RuntimeError('[%#x] previously %s' % - (id(req), cl.outstanding[req])) - cl.outstanding[req] = err - cl.log_outstanding() - reactor.callLater(cl.c.http_retry, partial(cl.req_fin, req)) - except Exception as e: - crash(traceback.format_exc() + '\n----- handling -----\n' + err) - - def req_fin(cl, req): - del cl.outstanding[req] - cl.log(DBG.HTTP_CTRL, 'req_fin OS=%d' % len(cl.outstanding), idof=req) - cl.check_outbound() - - def check_outbound(cl): - while True: - if len(cl.outstanding) >= cl.c.max_outstanding: - break - - if (not cl.queue.nonempty() and - len(cl.outstanding) >= cl.c.target_requests_outstanding): - break - - d = b'' - def moredata(s): nonlocal d; d += s - cl.queue.process((lambda: len(d)), - moredata, - cl.c.max_batch_up) - - d = mime_translate(d) - - crlf = b'\r\n' - lf = b'\n' - mime = (b'--b' + crlf + - b'Content-Type: text/plain; charset="utf-8"' + crlf + - b'Content-Disposition: form-data; name="m"' + crlf + crlf + - str(cl.c.client) .encode('ascii') + crlf + - cl.c.password + crlf + - str(cl.c.target_requests_outstanding) - .encode('ascii') + crlf + - str(cl.c.http_timeout) .encode('ascii') + crlf + - (( - b'--b' + crlf + - b'Content-Type: application/octet-stream' + crlf + - b'Content-Disposition: form-data; name="d"' + crlf + crlf + - d + crlf - ) if len(d) else b'') + - b'--b--' + crlf) - - #df = open('data.dump.dbg', mode='wb') - #df.write(mime) - #df.close() - # POST -use -c 'multipart/form-data; boundary="b"' http://localhost:8099/