Load test websocket server? - python

IDEA
I have a websocket server I'd like to determine:
max concurrent connections
any errors
Testing
As far as I know, to get this job done, I need a decent websocket test suite. I've so far only attempted to use Thor. But it doesn't allow me to send extra data.
Problem
The websocket server requires a token for a connection, hence why Thor doesn't work out of the box, I need a way to send a token with the test software. I'd like to keep this part in the test, as it has to authenticate to another machine, and hence would not be a true load test if I remove that part.
code for the tornado server.py file:
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def check_origin(self, origin):
return True
def open(self, *args):
self.id = self.get_argument("id")
accessToken = self.get_argument("accessToken")
#print "socket opened id: %s
self.stream.set_nodelay(True)
addActive(accessToken)
clients[self.id] = {"id": self.id, "accessToken":accessToken, "object": self}
Any thoughts on a test suite or method to get Thor working?

When I had to perform load testing of a WebSocket application I used Apache JMeter for it. It doesn't support WebSocket out of box, however it is possible via plugin or some Java coding.
See WebSocket Testing With Apache JMeter guide for approaches and instructions on how to do it with JMeter

Related

Is it possible to write a Python (iOS) program to allow/execute commands to a RaspberryPi?

I am currently underway with my Senior Capstone project, in which I am to write a somewhat basic program which allows a custom interface on my iPhone6 device to remotely control or issue critical commands to a NIDS (Suricata) established at my home RaspberryPi(3B+) VPN. My question, however, is whether it's feasible to write said program which can allow remote access control of basic functions/response options on the Pi's IDS, given that I am utilizing it as a device within the VPN network. The main issue would be establish remote signaling to the iOS device whenever there is an anomaly and allowing it to respond back and execute root-level commands on the NIDS.
If it is of any good use, I am currently using Pythonista as a runtime environment on my mobile device and have set my VPN's connection methods to UDP, but I'm not sure if enabling SSH would assist me. I have a rather basic understanding of how to operate programming in regards to network connectivity. I very much appreciate any and all the help given!
from tkinter import *
window=Tk()
window.geometry("450x450")
window.title("IDS Response Manager")
label1=Label(window,text="Intrusion Response Options",fg= 'black',bg ='white',relief="solid",font=("times new roman",12,"bold"))
label1.pack()
button1=Button(window,text="Terminate Session",fg='white', bg='brown',relief=RIDGE,font=("arial",12,"bold"))
button1.place(x=50,y=110) #GROOVE ,RIDGE ,SUNKEN ,RAISED
button2=Button(window,text="Packet Dump",fg='white', bg='brown',relief=RIDGE,font=("arial",12,"bold"))
button2.place(x=220,y=110) #GROOVE ,RIDGE ,SUNKEN ,RAISED
button3=Button(window,text="Block Port",fg='white', bg='brown',relief=RIDGE,font=("arial",12,"bold"))
button3.place(x=110,y=170) #GROOVE ,RIDGE ,SUNKEN ,RAISED
Very basic options as are shown here.
You can use a flask server with an API, which you can send post requests to. You can then send get requests to receive the commands. To host your API, look at Heroku (free tier available, and very much functional, with already configured app_name.herokuapp.com).
Search up to send a post request with the technologies you are using to build your app. Send keyword command with the command to the /send_commands along with the password, "password_here" (changeable to anything you want).
Python:
Modules: Flask (server), request (client)
Server Code:
from flask import Flask
app = Flask(__name__)
commands = []
#app.route('/get_commands', methods=['GET'])
def get_commands():
tmp_commands = commands[::]
commands = []
return {'commands': tmp_commands}
#app.route('/send_commands', methods=['POST'])
def send_commands():
if request.json['password'] == "password_here":
commands.append(request.json['command'])
return {'worked': True}
else:
return {'worked': False}
if __name__ == '__main__':
app.run(debug=True)
Client Code:
import requests
URL = "url_here/get_commands"
commands = requests.get(url = URL)
for command in commands:
os.system(command)

Using gevent and Flask to implement websocket, how to achieve concurrency?

So I'm using Flask_Socket to try to implement a websocket on Flask. Using this I hope to notify all connected clients whenever a piece of data has changed. Here's a simplification of my routes/index.py. The issue that I have is that when a websocket connection is opened, it will stay in the notify_change loop until the socket is closed, and in the meantime, other routes like /users can't be accessed.
from flask_sockets import Sockets
sockets = Sockets(app)
#app.route('/users',methods=['GET'])
def users():
return json.dumps(get_users())
data = "Some value" # the piece of data to push
is_dirty = False # A flag which is set by the code that changes the data
#sockets.route("/notifyChange")
def notify_change(ws):
global is_dirty, data
while not ws.closed:
if is_dirty:
is_dirty = False
ws.send(data)
This seems a normal consequence of what is essentially a while True: however, I've been looking online for a way to get around this while still using flask_sockets and haven't had any luck. I'm running the server on GUnicorn
flask/bin/gunicorn -b '0.0.0.0:8000' -k flask_sockets.worker app:app
I tried adding threads by doing --threads 12 but no luck.
Note: I'm only serving up to 4 users at a time, so scaling is not a requirement, and the solution doesn't need to be ultra-optimized.

Pyro4 RPC blocking

I am currently doing development on a high performance numeric calculations system that posts it results to a web server. We use flask on a seperate process to serve the web page to keep everything interactive and use websockets to send the data to a JS plotter. The calculations are also split using multiprocessing for performance reasons.
We are currently using Pyro4 to get parameters updated in the calculations from the server. However, if the number of updates per second gets high on our Pyro4 settings object it starts blocking and makes it impossible to update any parameters until we restart the server. The proxy calls are made inside of an async websocket callback.
We are currently not getting any tracebacks, nor exceptions which makes debugging this all tricky. Are there any other people that have a lot of experience with Pyro in this context?
Pyro daemon init code:
Pyro4.config.SERVERTYPE = "multiplex"
daemon = Pyro4.Daemon()
ns = Pyro4.locateNS()
settings = cg.Settings(web_opt, src_opt, rec_opt, det_opt)
uri = daemon.register(settings)
ns.register("cg.settings", uri)
daemon.requestLoop()
Settings class:
class Settings(object):
def __init__(self, web_opt, src_opt, rec_opt, det_opt):
self.pipes = [web_opt, src_opt, rec_opt, det_opt]
def update(self, update):
[pipe.send(update) for pipe in self.pipes]
Proxy call:
def onMessage(self, payload, isBinary):
data = json.loads(payload)
self.factory.content.set_by_uuid(data['id'], data['value'], self.client_address)
settings = Pyro4.Proxy("PYRONAME:cg.settings")
values = self.factory.content.values
settings.update(values)
settings._pyroRelease()

How to force Tornado websockets to generate error page on handshake

I am writing a small app where I use Tornado websockets. I use cookies for authentication, and I therefore want to check this when establishing the web socket.
So here is the challenge (building on the official websockets example):
class EchoWebSocket(tornado.websocket.WebSocketHandler):
def open(self):
userx = self.get_secure_cookie("user")
if not userx:
self.send_error(403) #I want to redirect or otherwise stop the user
print "WebSocket opened"
def on_message(self, message):
userx = self.get_secure_cookie("user")
self.write_message(u"%s said: %s" % (userx, message))
def on_close(self):
print "WebSocket closed"
Turns out that this is not very popular with Tornado, throwing this error:
ERROR:root:Exception in callback <tornado.stack_context._StackContextWrapper object at 0x10fa10db8>
Traceback (most recent call last):
....
File "/Library/Python/2.7/site-packages/tornado-2.4-py2.7.egg/tornado/iostream.py", line 565, in _check_closed
raise IOError("Stream is closed")
IOError: Stream is closed
Is it even possible to "authenticate or cancel" during websockets handshake? Is this a problem with my code, or is there a issue/missing feature with Tornado? Is Tornado capable of sending some error condition over websockets to trigger the onerror handler at the client side in this situation (can not find any way to force an error condition in the manual)?
The sequence of events I am trying to achieve is:
the user logs in using regular web requests, and a session token is stored as a secure cookie
the user loads a page that uses websockets, javascript attempts to establish the socket
before the socket is established I want to check the presence of a valid session token
if valid token, the socket is established and my app works as intended
if invalid token, there is no socket, and the user gets an error (alternatively, an websocket error condition is raised by the server)
You can track your users, you can do it easily, as each user connects to the server is created an instance, check this SO link.
Regarding that you want to close the connection if he is not authenticated, you a can do it by calling close() method inside WebSWebSocketHandler instance like this:
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
if not condition:
self.close()
Check this in Tornado: tornado.websocket.WebSocketHandler.WebSocketHandler.close()
I didn't try it by myself. You can try it and tell me if it works for you.
Is this?
http://www.tornadoweb.org/documentation/web.html#tornado.web.RequestHandler.write_error
You can overwrite handle.write_error method, But I do not know whether WebSocketHandler includes this method
It looks like the answer to this question is as Nikolay Fominyh suggests (although I can not accept that answer since its a comment and not an answer) to send message to user and close connection. This currently seems to be the best way to solve this.

Counting the number of requests per second in Tornado

I am new to Python and Tornado WebServer.
I am trying to figure out the number of request and number of requests/second in my server side code. I am using Tornadio2 to implement websockets.
Kindly take a look at the following code and let me know, what modification can be done to it.
I am using the RequestHandler.prepare() to bottleneck all the requests and using a list as it is immutable to store the count.
Consider all modules are included
count=[0]
class IndexHandler(tornado.web.RequestHandler):
"""Regular HTTP handler to serve the chatroom page"""
def prepare(self):
count[0]=count[0]+1
def get(self):
self.render('index1.html')
class SocketIOHandler(tornado.web.RequestHandler):
def get(self):
self.render('../socket.io.js')
partQue=Queue.Queue()
class ChatConnection(tornadio2.conn.SocketConnection):
participants = set()
def on_open(self, info):
self.send("Welcome from the server.")
self.participants.add(self)
def on_message(self, message):
partQue.put(message)
time.sleep(10)
self.qmes=partQue.get()
for p in self.participants:
p.send(self.qmes+" "+str(count[0]))
partQue.task_done()
def on_close(self):
self.participants.remove(self)
partQue.join()
# Create tornadio server
ChatRouter = tornadio2.router.TornadioRouter(ChatConnection)
# Create socket application
sock_app = tornado.web.Application(
ChatRouter.urls,
flash_policy_port = 843,
flash_policy_file = op.join(ROOT, 'flashpolicy.xml'),
socket_io_port = 8002)
# Create HTTP application
http_app = tornado.web.Application(
[(r"/", IndexHandler), (r"/socket.io.js", SocketIOHandler)])
if __name__ == "__main__":
import logging
logging.getLogger().setLevel(logging.DEBUG)
# Create http server on port 8001
http_server = tornado.httpserver.HTTPServer(http_app)
http_server.listen(8001)
# Create tornadio server on port 8002, but don't start it yet
tornadio2.server.SocketServer(sock_app, auto_start=False)
# Start both servers
tornado.ioloop.IOLoop.instance().start()
Also, I am confused about every Websocket messages. Does each Websocket event got to server in the form of an HTTP request? or a Socket.IO request?
I use Siege - excellent tool for testing requests if your running on linux. Example
siege http://localhost:8000/?q=yourquery -c10 -t10s
-c10 = 10 concurrent users
-t10s = 10 seconds
Tornadio2 has built-in statistics module, which includes incoming connections/s and other counters.
Check following example: https://github.com/MrJoes/tornadio2/tree/master/examples/stats
When testing applications, always approach performance testing with a healthy appreciation for the uncertainty principle..
If you want to test a server, hook up two PCs to a HUB where you can monitor traffic from one going to the other. Then bang the hell out of the server. There are a variety of tools for doing this, just look for web load testing tools.
Normal HTTP requests in Tornado create a new RequestHandler instance, which persists until the connection is terminated.
WebSockets use persistent connections. One WebSocketHandler instance is created, and each message sent by the browser to the server calls the on_message method.
From what I understand, Socket.IO/Tornad.IO will use WebSockets if supported by the browser, falling back to long polling.

Categories

Resources