I came across a nice example of a Twisted “man-in-the-middle” style proxy on Stack Overflow. This style of proxy is great for logging traffic between two endpoints, as well as modifying the requests and responses that travel between them.
The original was posted here, and I reproduce the vast majority of the code below with some modifications. My real motivation for posting this is to “get the code out there”, because I had a hard time finding it originally. A big thanks to the original author for posting his code on Stack Overflow.
All you need to do is change the three constants at the top, and add whatever validation/modification logic you want in the
write methods. Those four methods are labeled so you know which “hop” the data is taking. A request is going to take the following path: client => proxy => server => proxy => client. For example, the first
dataReceived method handles data travelling from the client to your proxy.
#!/usr/bin/env python LISTEN_PORT = 8000 SERVER_PORT = 1234 SERVER_ADDR = "server address" from twisted.internet import protocol, reactor # Adapted from http://stackoverflow.com/a/15645169/221061 class ServerProtocol(protocol.Protocol): def __init__(self): self.buffer = None self.client = None def connectionMade(self): factory = protocol.ClientFactory() factory.protocol = ClientProtocol factory.server = self reactor.connectTCP(SERVER_ADDR, SERVER_PORT, factory) # Client => Proxy def dataReceived(self, data): if self.client: self.client.write(data) else: self.buffer = data # Proxy => Client def write(self, data): self.transport.write(data) class ClientProtocol(protocol.Protocol): def connectionMade(self): self.factory.server.client = self self.write(self.factory.server.buffer) self.factory.server.buffer = '' # Server => Proxy def dataReceived(self, data): self.factory.server.write(data) # Proxy => Server def write(self, data): if data: self.transport.write(data) def main(): factory = protocol.ServerFactory() factory.protocol = ServerProtocol reactor.listenTCP(LISTEN_PORT, factory) reactor.run() if __name__ == '__main__': main()