send a variable to a TCPHandler in python - python

I'm confused as to how to send a variable to a TCPHandler using SocketServer.TCPServer in python..
HOST, PORT = hosts[0], args.port
server = SocketServer.TCPServer((HOST, PORT), METCPHandler)
server.serve_forever()
Which calls:
class METCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
print "{} wrote:".format(self.client_address[0])
r = MExpressHandler(self.data, False)
But I want to pass a debug boolean to MExpressHandler.. so
HOST, PORT = hosts[0], args.port
server = SocketServer.TCPServer((HOST, PORT), METCPHandler(debug))
server.serve_forever()
Fails. Whats the correct way of doing this? Do I have to recreate a whole TCPHandler over-ridding __init__?

Trust your instincts, the correct way is indeed to subclass TCPServer and override the __init__ method, but Python makes this very easy!
import SocketServer
class DebugTCPServer(SocketServer.TCPServer):
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True, debug=True):
self.debug = debug
SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass, bind_and_activate=True)
class DebugMETCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
# self.server is an instance of the DebugTCPServer
DEBUG = self.server.debug
self.data = self.request.recv(1024).strip()
if DEBUG:
print "{} wrote:".format(self.client_address[0])
r = MExpressHandler(self.data, False)
server = DebugTCPServer((HOST, PORT), DebugMETCPHandler, debug=True)
or since we specified debug=True as the default:
server = DebugTCPServer((HOST, PORT), DebugMETCPHandler)

As I proposed in this post already, it is possible to do it without sub-classing the TCPServer. It is in fact more concise and generic.
You can give parameters to your handler this way:
class METCPHandler(SocketServer.BaseRequestHandler):
def __init__(self, debug):
self.debug = debug
def __call__(self, request, client_address, server):
h = METCPHandler(self.debug)
SocketServer.StreamRequestHandler.__init__(h, request, client_address, server)
You can now give an instance of your handler to the TCPServer:
SocketServer.TCPServer((HOST, PORT), METCPHandler(True))
The TCPServer normally creates a new instance of METCPHandler per request but in this case, the __call__ method will be called instead of the constructor (it is already an instance.)
In the call method, I explicitly make a copy of the current METCPHandler and pass it to the super constructor to conform to the original logic of "one handler instance per request".
It is worth having a look at the SocketServer module to understand what happens here: https://github.com/python/cpython/blob/2.7/Lib/SocketServer.py

Related

How to modify handle method in socketserver

I am learning the socketserver module and I am following the example but I modified the handle function a bit
class CustomServer(socketserver.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
print(f">{self.client_address[0]}: {self.data}")
def send(self, targets=[]):
if not targets:
return
if __name__ == "__main__":
HOST, PORT = "localhost", 6666
with socketserver.TCPServer((HOST, PORT), CustomServer) as server:
server.serve_forever()
Now when I try to use netcat and send sth to the server I don't see anything being outputted to the console
nc -v 10.0.0.112 6666
How do you properly edit the handle method so that it will print the address of the client each time
It is really important to understand the OOP concept and how to use it
Looking at the source code for socketserver I realized that I can create a class that inherits the BaseRequestHandler than I modified the handler method and passed my class to the TCPServer
class CustomHandler(BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
print(f">{self.client_address[0]}: {self.data}")
if __name__ == "__main__":
HOST, PORT = "0.0.0.0", 6666
server = TCPServer(((HOST, PORT)), CustomHandler)
server.serve_forever()

Assigning an object as an attribute to another object in python

I'm trying to set a custom object as an attribute to a TCP handler class documented here. In my case both the server and client handlers need a custom object:
import SocketServer
class MyTCPHandler(SocketServer.BaseRequestHandler):
"""
The request handler 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 set_custom_object(self, custom_object):
self.customObject = custom_object
def handle(self):
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
# Call a custom method of the custom object over the data
self.data = self.customObject.custom_method(self.data)
# Send back the processed data
self.request.sendall(self.data)
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
# 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()
How may I bind the customObject to the handler class?
The BaseRequestHandler offers a set of overridable methods but can't find where they're invoked.
class BaseRequestHandler:
"""Base class for request handler classes.
This class is instantiated for each request to be handled. The
constructor sets the instance variables request, client_address
and server, and then calls the handle() method. To implement a
specific service, all you need to do is to derive a class which
defines a handle() method.
The handle() method can find the request as self.request, the
client address as self.client_address, and the server (in case it
needs access to per-server information) as self.server. Since a
separate instance is created for each request, the handle() method
can define arbitrary other instance variariables.
"""
def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
finally:
self.finish()
def setup(self):
pass
def handle(self):
pass
def finish(self):
pass

Is there a way for BaseRequestHandler classes to be stateful?

Short Question
Using my examples below, is there a Pythonic way to share my_object's actual instance with with the BaseRequestHandler class?
Background
By definition, the BaseRequestHandler class creates a new instance for each request. Because of this, I am struggling to try find a solution on how to get data from the handle() function back to the ProtocolInterface instance. Note that this might be the wrong approach if I am needing to do something in handle() other than print to stdout.
At this point in time, I do not believe that global variables will work because my_object is passed in and is expected to change often (this is why handle() needs to see it. To see an example client (sending bogus data) see my other SO question. I think the biggest issue I am facing is the the socketservers are running in a background thread.
Example of what I would like to do
class ProtocolHandler(SocketServer.BaseRequestHandler):
def handle(self):
while(1):
self.data = self.request.recv(1024)
if self.data == '':
break
self.request.send("Success")
self.my_object.success = True# <--- How can I share my_object's instance?
class ProtocolInterface():
def __init__(self, obj, host='127.0.0.1', port=8000, single_connection=False):
self.my_object = obj # <--- This ideally is the same instance seen in ProtocolHandler
self.host = host
self.port = port
# Create the socket server to process in coming traffic
if(single_connection):
self.server = SocketServer.TCPServer((self.host, self.port), ProtocolHandler)
else:
self.server = SocketServer.ThreadingTCPServer((self.host, self.port), ProtocolHandler)
def start(self):
print "Server Starting on HOST: " + self.host
server_thread = threading.Thread(target=self.server.serve_forever)
server_thread.daemon = True
server_thread.start()
You could pass the object through the server instance:
self.server = SocketServer.TCPServer((self.host, self.port), ProtocolHandler)
self.server.my_object = self.my_object
The documentation indicates that you can have access to the server instance in handle() as self.server.

Sending message from one server to another in Twisted

I'm a complete Twisted AND Python noob, so my apologies if any of my terminology is wrong or anything I've done is silly. Nonetheless....
I've implemented my servers in the following way:
def makeServer(application, port):
factory = protocol.ServerFactory()
factory.protocol = MyChat
factory.clients = []
tempServer = internet.TCPServer(port, factory)
tempServer.setServiceParent(application)
return tempServer
application = service.Application("chatserver")
server1 = makeServer(application, port=1025)
server2 = makeServer(application, port=1026)
server3 = makeServer(application, port=1027)
Note that MyChat is an event handling class that has a "receiveMessage" action:
def lineReceived(self, line):
print "received", repr(line)
for c in self.factory.clients:
c.transport.write(message + '\n')
I want server1 to be able to pass messages to server2. Rather, I want server1 to be treated as a client of server2. If server1 receives the message "hi" then I want it to send that same exact message to server2. The only thing server1 needs to be able to do is to send the message it received from its client to server2.
How can I accomplish this?
NOTE: You can totally change the way I'm implementing my server if it helps.
Different parts of your application can interact with each other using method calls.
Send a message to server2 really just means Call a method on one of the objects related to server2.
For example, in MyChat, you might have:
def lineReceived(self, line):
print "received", repr(line)
for c in self.factory.clients:
c.transport.write(message + '\n')
for server in self.factory.otherServers:
server.otherServerMessage(self, line)
This supposes a couple things:
You add a new otherServers attribute to your factory. Its contents are objects related to the other listening servers you have set up. These might be factory objects or protocol objects. It depends on what's most convenient based on what you intend to do with the message.
You give those related objects a new method, otherServerMessage, to handle messages delivered this way. If you were to deliver the messages directly to MyChat.lineReceived (which you easily could, if you wanted) then I would expect you to end up with infinite recursion; having a different method lets you differentiate between messages received from a client and messages received from another server.
You will probably need to implement a separate client. It is possible that an object can be both a client and a server, but I doubt it will be worth it and you are likely to run into trouble.
I suggest that the server instantiates a client object, which you connect to the 'next' server. The client can for example be an instance variable on the server.
Example:
class MyChat(LineReceiver):
def connectionMade(self):
print "Proxy: connected"
factory = protocol.ClientFactory()
class Proxy(protocol.Protocol):
def relayMessage(self, msg):
self.transport.write(msg)
factory.protocol = Proxy
point = TCP4ClientEndpoint(reactor, "localhost", 1025)
conn = point.connect(factory)
conn.addCallback(self.hasConnection)
def hasConnection(self, client):
print "Proxy: Connected to relay", client
self.client = client
def lineReceived(self, line):
print "Proxy: received", repr(line)
self.client.transport.write(line+"\n")
class MyEcho(LineReceiver):
def lineReceived(self, line):
print "Echo: received", repr(line)
factory = protocol.ServerFactory()
factory.protocol = MyChat
reactor.listenTCP(1024, factory)
factory = protocol.ServerFactory()
factory.protocol = MyEcho
reactor.listenTCP(1025, factory)
You need just declare clients inside your server, like this:
factory = SomeClientFactory('ws://127.0.0.1')
connectWS(factory)
and in your Client Class:
class SomeClient(WebSocketClientProtocol):
def __init__(self):
pass
def sendCommand(self):
self.sendMessage('A message to another server')
def onOpen(self):
self.sendCommand()
def onClose(self, wasClean, code, reason):
print(reason)
def onMessage(self, payload, isBinary):
print('A answer from another server')
class SomeClientFactory(WebSocketClientFactory):
def __init__(self, url):
WebSocketClientFactory.__init__(self,url)
self.proto = DeltaClient()
self.proto.factory = self
def buildProtocol(self, addr):
return self.proto
Tip: use a "Controller" class to manage that instances of clients inside your servers.

With python socketserver how can I pass a variable to the constructor of the handler class

I would like to pass my database connection to the EchoHandler class, however I can't figure out how to do that or access the EchoHandler class at all.
class EchoHandler(SocketServer.StreamRequestHandler):
def handle(self):
print self.client_address, 'connected'
if __name__ == '__main__':
conn = MySQLdb.connect (host = "10.0.0.5", user = "user", passwd = "pass", db = "database")
SocketServer.ForkingTCPServer.allow_reuse_address = 1
server = SocketServer.ForkingTCPServer(('10.0.0.6', 4242), EchoHandler)
print "Server listening on localhost:4242..."
try:
server.allow_reuse_address
server.serve_forever()
except KeyboardInterrupt:
print "\nbailing..."
Unfortunately, there really isn't an easy way to access the handlers directly from outside the server.
You have two options to get the information to the EchoHandler instances:
Store the connection as a property of the server (add server.conn = conn before calling server_forever()) and then access that property in EchoHandler.handler through self.server.conn.
You can overwrite the server's finish_request and assign the value there (you would have to pass it to the constructor of EchoHandler and overwrite EchoHandler.__init__). That is a far messier solution and it pretty much requires you to store the connection on the server anyway.
My optionon of your best bet:
class EchoHandler(SocketServer.StreamRequestHandler):
def handle(self):
# I have no idea why you would print this but this is an example
print( self.server.conn );
print self.client_address, 'connected'
if __name__ == '__main__':
SocketServer.ForkingTCPServer.allow_reuse_address = 1
server = SocketServer.ForkingTCPServer(('10.0.0.6', 4242), EchoHandler)
server.conn = MySQLdb.connect (host = "10.0.0.5",
user = "user", passwd = "pass", db = "database")
# continue as normal
Mark T has the following to say on the python list archive
In the handler class, self.server refers to the server object, so subclass
the server and override init to take any additional server parameters
and store them as instance variables.
import SocketServer
class MyServer(SocketServer.ThreadingTCPServer):
def __init__(self, server_address, RequestHandlerClass, arg1, arg2):
SocketServer.ThreadingTCPServer.__init__(self,
server_address,
RequestHandlerClass)
self.arg1 = arg1
self.arg2 = arg2
class MyHandler(SocketServer.StreamRequestHandler):
def handle(self):
print self.server.arg1
print self.server.arg2
Another way, that I believe more pythonic, is to do the following:
class EchoHandler(SocketServer.StreamRequestHandler):
def __init__(self, a, b):
self.a = a
self.b = b
def __call__(self, request, client_address, server):
h = EchoHandler(self.a, self.b)
SocketServer.StreamRequestHandler.__init__(h, request, client_address, server)
You can now give an instance of your handler to the TCPServer:
SocketServer.ForkingTCPServer(('10.0.0.6', 4242), EchoHandler("aaa", "bbb"))
The TCPServer normally creates a new instance of EchoHandler per request but in this case, the __call__ method will be called instead of the constructor (it is already an instance.)
In the call method, I explicitly make a copy of the current EchoHandler and pass it to the super constructor to conform to the original logic of "one handler instance per request".
It is worth having a look at the SocketServer module to understand what happens here: https://github.com/python/cpython/blob/2.7/Lib/SocketServer.py
I was currently solving same problem, but I used slightly different solution, I feel it's slightly nicer and more general (inspired by #aramaki).
In the EchoHandler you just need to overwrite __init__ and specify custom Creator method.
class EchoHandler(SocketServer.StreamRequestHandler):
def __init__(self, request, client_address, server, a, b):
self.a = a
self.b = b
# super().__init__() must be called at the end
# because it's immediately calling handle method
super().__init__(request, client_address, server)
#classmethod
def Creator(cls, *args, **kwargs):
def _HandlerCreator(request, client_address, server):
cls(request, client_address, server, *args, **kwargs)
return _HandlerCreator
Then you can just call the Creator method and pass anything you need.
SocketServer.ForkingTCPServer(('10.0.0.6', 4242), EchoHandler.Creator(0, "foo"))
Main benefit is, that this way you are not creating any more instances than necessary and you are extending the class in more manageable way - you don't need to change the Creator method ever again.
It seems that you can't use ForkingServer to share variables because Copy-on-Write happens when a process tries to modify a shared variable.
Change it to ThreadingServer and you'll be able to share global variables.

Categories

Resources