Using Twisted and Python to create a Router - python

When a client (ie. web browser) points to localhost:8080, its request should be sent to an address defined by SERVER_ADDR such as http://www.yahoo.com. It's more like a router or load balancer, than a proxy.
Problem: When my web browser points to localhost:8080, nothing is returned to it. On the Python console, the HTTP request header can be seen to have reached the Twisted app. Maybe this cannot be used for redirecting the browser's request?
from twisted.internet import protocol, reactor
LISTEN_PORT = 8080
SERVER_PORT = 80
SERVER_ADDR = 'http://www.yahoo.com'
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):
print 'Data received from Client:'
if self.client:
self.client.write(data)
else:
data = data.replace('localhost:8080', SERVER_ADDR)
print data
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):
print 'Data received from Server: '
print 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()

It partially works for me: I get an HTTP error 400 bad request from the Yahoo server in my browser when I visit http://localhost:8080. This is because you're replacing the Host: localhost:8080 section of the GET request with Host: http://www.yahoo.com and the protocol here is invalid. It should just be Host: www.yahoo.com.
That said, it then responds with a HTTP/1.1 301 Moved Permanently redirect and it you run into other problems.
Edit: you should take a look at the proxy example on the twisted website (https://twistedmatrix.com/documents/12.3.0/web/examples/#auto2) and dive into the source to see how it's implemented.
from twisted.web import proxy, http
from twisted.internet import reactor
class ProxyFactory(http.HTTPFactory):
def buildProtocol(self, addr):
return proxy.Proxy()
reactor.listenTCP(8080, ProxyFactory())
reactor.run()

Related

Maintaining another persistent TCP connection with TCPServer

I am connecting to a XMPP server using slixmpp, I need access to this connection while serving a HTTP protocol, I am trying to maintain a persistent connection, rather than connecting connecting to XMPP server for each HTTP request. I am using TCPServer to get the functionality of HTTP. I wrote this code.
import logging
from slixmpp import ClientXMPP
from slixmpp.exceptions import IqError, IqTimeout
import socketserver
from time import sleep
class EchoBot(ClientXMPP):
def __init__(self, jid, password):
ClientXMPP.__init__(self, jid, password)
self.add_event_handler("session_start", self.session_start)
self.add_event_handler("message", self.message)
def session_start(self, event):
self.send_presence()
self.get_roster()
def message(self, msg):
print(msg)
if msg['type'] in ('chat', 'normal'):
msg.reply("Thanks for sending\n%(body)s" % msg).send()
class MyTCPHandler(socketserver.BaseRequestHandler):
xmpp = EchoBot('xxx#fcm.googleapis.com', 'xyz')
def __init__(self,request, client_address,server):
super().__init__(request, client_address,server)
self.xmpp.connect(address=('fcm-xmpp.googleapis.com',5235),use_ssl=True,disable_starttls=True)
self.xmpp.process(forever=True)
def handle(self):
self.data = self.request.recv(1024).strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# just send back the same data, but upper-cased
self.request.sendall(self.data.upper())
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG,format='%(levelname)-8s %(message)s')
HOST, PORT = "localhost", 9999
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
server.serve_forever()
This works for first time. MyTCPHandler handle function works only first time, second time, it doesn't return any response. I am using telnet localhost 9999 to test the connection. What might be going wrong here? Is there a better way to achieve the result I'm looking for?
if I comment these three lines TCPServer works as expected.
# xmpp = EchoBot('xxx#fcm.googleapis.com', 'xyz')
def __init__(self,request, client_address,server):
super().__init__(request, client_address,server)
# self.xmpp.connect(address=('fcm-xmpp.googleapis.com',5235),use_ssl=True,disable_starttls=True)
# self.xmpp.process(forever=True)
I solved the problem using asyncio
import logging
from slixmpp import ClientXMPP
from slixmpp.exceptions import IqError, IqTimeout
import logging
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
log = logging.getLogger(__name__)
import asyncio
import base64
import slixmpp
from aiohttp import web
XMPP = None
class EchoBot(ClientXMPP):
def __init__(self, jid, password):
ClientXMPP.__init__(self, jid, password)
self.connected_future = asyncio.Future()
self.add_event_handler("session_start", self.session_start)
self.add_event_handler("message", self.message)
def session_start(self, event):
self.send_presence()
self.get_roster()
def message(self, msg):
if msg['type'] in ('chat', 'normal'):
msg.reply("Thanks for sending\n%(body)s" % msg).send()
def reset_future(self):
"Reset the future in case of disconnection"
self.connected_future = asyncio.Future()
async def handle(request):
"Handle the HTTP request and block until the vcard is fetched"
err_404 = web.Response(status=404, text='Not found')
print(await request.json())
try:
XMPP.send_raw('<message id="gsgsfssdfds"> <gcm xmlns="google:mobile:data">{ "notification": {"title": "change","body": "body changed","sound":"default"},"to" : "efsfdsf","message_id":"flajlfdjlfdklajflda","priority":"high","delivery_receipt_requested":true}</gcm></message>')
except Exception as e:
print(e)
log.warning("cannot send message")
return err_404
return web.Response(text="yes")
async def init(loop, host: str, port: str, avatar_prefix: str):
"Initialize the HTTP server"
app = web.Application(loop=loop)
app.router.add_route('POST', '/', handle)
srv = await loop.create_server(app.make_handler(), host, port)
log.info("Server started at http://%s:%s", host, port)
return srv
def main(namespace):
"Start the xmpp client and delegate the main loop to asyncio"
loop = asyncio.get_event_loop()
global XMPP
XMPP = EchoBot('xxx#gcm.googleapis.com', 'ysfafdafdsfa')
XMPP.connect(use_ssl=True,disable_starttls=False)
#XMPP.connect()
loop.run_until_complete(init(loop, namespace.host, namespace.port,
namespace.avatar_prefix))
XMPP.reset_future()
loop.run_until_complete(XMPP.connected_future)
try:
loop.run_forever()
except KeyboardInterrupt:
import sys
def parse_args():
"Parse the command-line arguments"
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('--jid', '-j', dest='jid', default=JID,
help='JID to use for fetching the vcards')
parser.add_argument('--password', '-p', dest='password', default=PASSWORD,
help='Password linked to the JID')
parser.add_argument('--host', dest='host', default=HOST,
help='Host on which the HTTP server will listen')
parser.add_argument('--port', dest='port', default=PORT,
help='Port on which the HTTP server will listen')
parser.add_argument('--avatar_prefix', dest='avatar_prefix',
default=AVATAR_PREFIX,
help='Prefix path for the avatar request')
return parser.parse_args()
HOST = '127.0.0.1'
PORT = 8765
JID = 'changeme#example.com'
PASSWORD = 'changemetoo'
AVATAR_PREFIX = 'avatar/'
if __name__ == "__main__":
print(parse_args())
main(parse_args())

Multiple call/response messages between Twisted server/client

I have a basic server and client implemented in Twisted. My goal is to have the client process some data, report its progress back to the server, and repeat until all the data is processed. The client is able to send an initial message to the server but it is not receiving the server's response letting it know it is ok to start processing that data. Here is the code I have.
Server:
from twisted.internet import reactor, protocol
PORT = 9000
progress = 0
class MyServer(protocol.Protocol):
def dataReceived(self, data):
global progress
print(data)
if data != "Ready?":
progress = int(data)
self.transport.write("Got it.")
self.transport.loseConnection()
def connectionLost(self, reason):
global progress
if progress == 10:
print("Completed.")
reactor.stop()
class MyServerFactory(protocol.Factory):
protocol = MyServer
factory = MyServerFactory()
reactor.listenTCP(PORT, factory)
reactor.run()
Client:
from twisted.internet import reactor, protocol
import time
HOST = 'localhost'
PORT = 9000
progress = 0
class MyClient(protocol.Protocol):
def connectionMade(self):
self.transport.write("Ready?")
self.transport.loseConnection()
def dataReceived(self, data):
global progress
progress += 1
print(progress)
self.transport.write(str(progress))
self.loseConnection()
def connectionLost(self, reason):
global progress
if progress == 10:
print("Completed.")
reactor.stop()
class MyClientFactory(protocol.ClientFactory):
protocol = MyClient
factory = MyClientFactory()
reactor.connectTCP(HOST, PORT, factory)
reactor.run()
I figured out that my issue was that I was prematurely closing the connection. In some earlier testing I was trying to send multiple messages within the dataReceived function which were getting concatenated into a single message. This led me to believe that you must lose the connection for each message to go through. Rather, you simply need to let the function finish before sending another message. Here is the updated code that is working as intended.
Server:
from twisted.internet import reactor, protocol
PORT = 9000
progress = 0
class MyServer(protocol.Protocol):
def dataReceived(self, data):
global progress
print(data)
if data != "Ready?":
progress = int(data)
self.transport.write("Got it")
if progress == 10:
self.transport.loseConnection()
def connectionLost(self, reason):
print("Completed.")
reactor.stop()
class MyServerFactory(protocol.Factory):
protocol = MyServer
factory = MyServerFactory()
reactor.listenTCP(PORT, factory)
reactor.run()
Client:
from twisted.internet import reactor, protocol
import time
HOST = 'localhost'
PORT = 9000
progress = 0
class MyClient(protocol.Protocol):
def connectionMade(self):
self.transport.write("Ready?")
def dataReceived(self, data):
global progress
progress += 1
print(progress)
self.transport.write(str(progress))
if progress == 10:
self.transport.loseConnection()
def connectionLost(self, reason):
print("Completed.")
reactor.stop()
class MyClientFactory(protocol.ClientFactory):
protocol = MyClient
factory = MyClientFactory()
reactor.connectTCP(HOST, PORT, factory)
reactor.run()

Twisted man in the middle proxy headers

Trying to read headers in twisted middle proxy server.
Here you can see simple twisted server (stolen from stackoverflow, yeah...). It nicely works, but I need to switch proxies based on request headers. Can't understand where I can get headers here. It workd with hard coded proxie server but the idea is to switch proxies based on requests.
Any ideas please? Thanks for your time.
Here is the code:
#!/usr/bin/env python
LISTEN_PORT = 8080
SERVER_PORT = 3128
SERVER_ADDR = "89.40.127.96"
from twisted.internet import protocol, reactor
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)
print "server"
reactor.run()
if __name__ == '__main__':
main()

SocketServer Python

So I found the following sample code which allows for a basic python HTTP server to be established at a given url and port. I am quite inexperienced with web servers and am trying to create handlers for certain GET requests to this server. However, I cannot figure out how to actually create handlers for a GET request made by another computer when accessing this URL remotely. Any suggestions?
import SocketServer
class MyTCPHandler(SocketServer.BaseRequestHandler):
"""
The RequestHandler class for our server.
It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""
def handle(self):
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
print "{} wrote:".format(self.client_address[0])
print self.data
# just send back the same data, but upper-cased
self.request.sendall(self.data.upper())
if __name__ == "__main__":
HOST, PORT = "url" , PORT
# Create the server, binding to localhost on port 9999
server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
Here is very simple example of how it could work. You would start this and call it with this, for example:
curl -i 'http://localhost:5001/foo/bar?foo=bar' -X POST -d '{"Foo":"Bar"}'
HTTP/1.1 200 OK
Some response%
It is missing tons of things, but this should at least give you some sort of idea.
import SocketServer
class MyTCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
print self.data
self.parse_request(self.data)
func, args = self.path.split("/", 1)
args = args.split("/")
resp = getattr(self, func)(*args)
self.request.sendall("HTTP/1.1 200 OK\n")
self.request.sendall("\n")
self.request.sendall(resp)
def parse_request(self, req):
headers = {}
lines = req.splitlines()
inbody = False
body = ''
for line in lines[1:]:
if line.strip() == "":
inbody = True
if inbody:
body += line
else:
k, v = line.split(":", 1)
headers[k.strip()] = v.strip()
method, path, _ = lines[0].split()
self.path = path.lstrip("/")
self.method = method
self.headers = headers
self.body = body
self.path, self.query_string = self.path.split("?")
def foo(self, *args):
print self.path
print self.query_string
print self.body
print self.headers
print self.method
return "Some response"
if __name__ == "__main__":
server = SocketServer.TCPServer(("localhost", 5001), MyTCPHandler)
server.serve_forever()

Python Proxy with Twisted

Hello! I have this code:
from twisted.web import proxy, http
from twisted.internet import reactor
class akaProxy(proxy.Proxy):
"""
Local proxy = bridge between browser and web application
"""
def dataReceived(self, data):
print "Received data..."
headers = data.split("\n")
request = headers[0].split(" ")
method = request[0].lower()
action = request[1]
print action
print "ended content manipulation"
return proxy.Proxy.dataReceived(self, data)
class ProxyFactory(http.HTTPFactory):
protocol = akaProxy
def intercept(port):
print "Intercept"
try:
factory = ProxyFactory()
reactor.listenTCP(port, factory)
reactor.run()
except Exception as excp:
print str(excp)
intercept(1337)
I use above code to intercept everything between browser and web site. When using above, I configure my browser settings: to IP: 127.0.0.1 and Port: 1337. I put this script in remote server to act my remote server as proxy server. But when I change browser proxy IP settings to my server's it does not work. What I do wrong? What else I need to configure?
Presumably your dataReceived is raising an exception during its attempts to parse the data passed to it. Try enabling logging so you can see more of what's going on:
from twisted.python.log import startLogging
from sys import stdout
startLogging(stdout)
The reason your parser is likely to raise exceptions is that dataReceived is not called only with a complete request. It is called with whatever bytes are read from the TCP connection. This may be a complete request, a partial request, or even two requests (if pipelining is in use).
dataReceived in the Proxy context is handling "translation of rawData into lines", so it may be too early for trying your manipulation code. You can try overriding allContentReceived instead and you will have access to the complete headers and content. Here is an example that I believe does what you are after:
#!/usr/bin/env python
from twisted.web import proxy, http
class SnifferProxy(proxy.Proxy):
"""
Local proxy = bridge between browser and web application
"""
def allContentReceived(self):
print "Received data..."
print "method = %s" % self._command
print "action = %s" % self._path
print "ended content manipulation\n\n"
return proxy.Proxy.allContentReceived(self)
class ProxyFactory(http.HTTPFactory):
protocol = SnifferProxy
if __name__ == "__main__":
from twisted.internet import reactor
reactor.listenTCP(8080, ProxyFactory())
reactor.run()

Categories

Resources