reverse logging proxy

There are a number of transparent/reverse proxy applications available, but I’ve thus far been unable to find one suitable for my needs. They’re either too low level (sniffing TCP packets for example), or intended for other purposes; and as a consequence either don’t present the right info in the right format, or just too gosh-dratted [sic] complicated for me to bother figuring out. Thus, in true lazy-programmer fashion, I’ve written my own quick-and-dirty tool for the job (actually when I say written, I really mean thrown together a few lines of code with some python modules which are doing the real work).

logproxy.py takes a hostname as its argument and redirects all requests to that host, in the meantime logging the headers and content body (if content has been sent). The response is also logged, and then sent back to the client accordingly.

#! /usr/bin/python

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer

import httplib
import logging
import os
import socket
import sys
import time

global redirecthost
redirecthost = ''

log = logging.getLogger('log')
hdlr = logging.FileHandler('dav.log')
formatter = logging.Formatter('%(message)s')
hdlr.setFormatter(formatter)
log.addHandler(hdlr)
log.setLevel(logging.DEBUG)

#
# a logging http-method handler
#
def do_HTTP(self):
    try:
        log.info('%s %s' % (self.command, self.path))

        headers = {}
        for header in self.headers.headers:
            s = header.strip().split(': ')
            if len(s) == 2:
                headers[s[0]] = s[1]
                log.info('%s: %s' % (s[0], s[1]))

        if len(headers) == 0:
            log.info('no headers')

        if headers.has_key('Content-Length'):
            cl = int(headers['Content-Length'])
            body = self.rfile.read(cl)
            log.info(body)
        else:
            body = ''

        log.info('n----------------------------------------------------------------n')

        conn = httplib.HTTPConnection(redirecthost)

        conn.request(self.command, self.path, body, headers)
        resp = conn.getresponse()

        status = resp.status
        reason = resp.reason

        if resp.msg.has_key('Content-Length'):
            body = resp.read(int(resp.msg['Content-Length']))
        elif resp.msg.has_key('Transfer-Encoding') and resp.msg['Transfer-Encoding'] == 'chunked':
            body = resp.read()
        elif not resp.msg.has_key('Content-Length'):
            body = resp.read()
        else:
            body = ''

        self.send_response(status, reason)

        for header in resp.msg.headers:
            s = header.strip().split(': ')
            if s[0] == 'Transfer-Encoding':
                self.send_header('Content-Length', len(body))
            else:
                self.send_header(s[0], s[1])
            log.info('%s: %s' % (s[0], s[1]))

        log.info('')
        self.end_headers()

        log.info(body)
        self.wfile.write(body)
        self.wfile.flush()

        log.info('n================================================================n')

    except IOError, e:
        self.send_error(500, e)        

class MyHandler(BaseHTTPRequestHandler): pass

#
# http methods we'll handle
#
MyHandler.do_COPY = do_HTTP
MyHandler.do_DELETE = do_HTTP
MyHandler.do_GET = do_HTTP
MyHandler.do_LOCK = do_HTTP
MyHandler.do_MKCOL = do_HTTP
MyHandler.do_MOVE = do_HTTP
MyHandler.do_OPTIONS = do_HTTP
MyHandler.do_POST = do_HTTP
MyHandler.do_PROPFIND = do_HTTP
MyHandler.do_PROPPATCH = do_HTTP
MyHandler.do_PUT = do_HTTP
MyHandler.do_TRACE = do_HTTP
MyHandler.do_UNLOCK = do_HTTP

def main():
    global redirecthost
    redirecthost = sys.argv[1]

    rtn = os.fork()

    if rtn == 0:
        try:
            server = HTTPServer(('', 19080), MyHandler)
            server.serve_forever()
        except KeyboardInterrupt:
            log.info('^C received, shutting down server')
            server.socket.close()
    else:
        try:
            while 1:
                line = sys.stdin.readline()
                log.info('************** %s' % line)
        except KeyboardInterrupt:
            pass

if __name__ == '__main__':
    main()

RSS feed | Trackback URI

2 Comments »

Comment by KK
2007-05-30 07:59:51

thank you, this program is just what i’m looking for

 
Comment by Proxy
2008-10-03 13:42:52

Thank you for this source code.

Kindest Regards,
Proxy

 
Name (required)
E-mail (required - never shown publicly)
URI
Your Comment (smaller size | larger size)
You may use <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> in your comment.

Trackback responses to this post