2011-03-26 20:27:08 +00:00
|
|
|
#!/usr/bin/env python
|
2011-01-13 00:09:54 +00:00
|
|
|
|
|
|
|
'''
|
|
|
|
WebSocket server-side load test program. Sends and receives traffic
|
|
|
|
that has a random payload (length and content) that is checksummed and
|
|
|
|
given a sequence number. Any errors are reported and counted.
|
|
|
|
'''
|
|
|
|
|
2014-01-30 22:08:50 +00:00
|
|
|
import sys, os, select, random, time, optparse, logging
|
2013-10-29 14:37:02 +00:00
|
|
|
sys.path.insert(0,os.path.join(os.path.dirname(__file__), ".."))
|
2016-09-16 17:50:55 +01:00
|
|
|
from websockify.websockifyserver import WebSockifyServer, WebSockifyRequestHandler
|
2011-01-13 00:09:54 +00:00
|
|
|
|
2016-09-16 17:50:55 +01:00
|
|
|
class WebSocketLoadServer(WebSockifyServer):
|
2011-01-13 00:09:54 +00:00
|
|
|
|
|
|
|
recv_cnt = 0
|
|
|
|
send_cnt = 0
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.delay = kwargs.pop('delay')
|
|
|
|
|
2016-09-16 17:50:55 +01:00
|
|
|
WebSockifyServer.__init__(self, *args, **kwargs)
|
Make echo.py and load.py work again, after the refactoring of
websocket.py:
* With echo.py, which doesn't need any server configuration, we can
just switch over our application class to inherit from
WebSocketRequestHandler instead of WebSocketServer. Also, need to
use the new method name new_websocket_client.
* With load.py, since we have the "delay" configuration, we need both
a server class and a request handler.
Note that for both tests, I've removed the raising of
self.EClose(closed). This is incorrect. First of all, it's described
as "An exception before the WebSocket connection was established", so
not suitable for our case. Second, it will cause send_close to be
called twice. Finally, self.EClose is now in the WebSocketServer
class, and not a member of the request handler.
2013-03-20 09:03:04 +00:00
|
|
|
|
|
|
|
|
2016-09-16 17:50:55 +01:00
|
|
|
class WebSocketLoad(WebSockifyRequestHandler):
|
Make echo.py and load.py work again, after the refactoring of
websocket.py:
* With echo.py, which doesn't need any server configuration, we can
just switch over our application class to inherit from
WebSocketRequestHandler instead of WebSocketServer. Also, need to
use the new method name new_websocket_client.
* With load.py, since we have the "delay" configuration, we need both
a server class and a request handler.
Note that for both tests, I've removed the raising of
self.EClose(closed). This is incorrect. First of all, it's described
as "An exception before the WebSocket connection was established", so
not suitable for our case. Second, it will cause send_close to be
called twice. Finally, self.EClose is now in the WebSocketServer
class, and not a member of the request handler.
2013-03-20 09:03:04 +00:00
|
|
|
|
|
|
|
max_packet_size = 10000
|
|
|
|
|
|
|
|
def new_websocket_client(self):
|
2011-01-13 00:09:54 +00:00
|
|
|
print "Prepopulating random array"
|
|
|
|
self.rand_array = []
|
|
|
|
for i in range(0, self.max_packet_size):
|
|
|
|
self.rand_array.append(random.randint(0, 9))
|
|
|
|
|
Make echo.py and load.py work again, after the refactoring of
websocket.py:
* With echo.py, which doesn't need any server configuration, we can
just switch over our application class to inherit from
WebSocketRequestHandler instead of WebSocketServer. Also, need to
use the new method name new_websocket_client.
* With load.py, since we have the "delay" configuration, we need both
a server class and a request handler.
Note that for both tests, I've removed the raising of
self.EClose(closed). This is incorrect. First of all, it's described
as "An exception before the WebSocket connection was established", so
not suitable for our case. Second, it will cause send_close to be
called twice. Finally, self.EClose is now in the WebSocketServer
class, and not a member of the request handler.
2013-03-20 09:03:04 +00:00
|
|
|
self.errors = 0
|
2011-01-13 00:09:54 +00:00
|
|
|
self.send_cnt = 0
|
|
|
|
self.recv_cnt = 0
|
|
|
|
|
2016-09-15 18:51:26 +01:00
|
|
|
self.responder(self.request)
|
|
|
|
|
|
|
|
print "accumulated errors:", self.errors
|
|
|
|
self.errors = 0
|
2011-01-13 00:09:54 +00:00
|
|
|
|
|
|
|
def responder(self, client):
|
2011-05-02 04:17:04 +01:00
|
|
|
c_pend = 0
|
2011-01-13 00:09:54 +00:00
|
|
|
cqueue = []
|
|
|
|
cpartial = ""
|
|
|
|
socks = [client]
|
|
|
|
last_send = time.time() * 1000
|
|
|
|
|
|
|
|
while True:
|
2011-05-02 04:17:04 +01:00
|
|
|
ins, outs, excepts = select.select(socks, socks, socks, 1)
|
2011-01-13 00:09:54 +00:00
|
|
|
if excepts: raise Exception("Socket exception")
|
|
|
|
|
|
|
|
if client in ins:
|
2011-05-02 04:17:04 +01:00
|
|
|
frames, closed = self.recv_frames()
|
|
|
|
|
|
|
|
err = self.check(frames)
|
|
|
|
if err:
|
|
|
|
self.errors = self.errors + 1
|
|
|
|
print err
|
|
|
|
|
|
|
|
if closed:
|
2016-09-15 18:51:26 +01:00
|
|
|
break
|
2011-01-13 00:09:54 +00:00
|
|
|
|
|
|
|
now = time.time() * 1000
|
2011-05-02 04:17:04 +01:00
|
|
|
if client in outs:
|
|
|
|
if c_pend:
|
|
|
|
last_send = now
|
|
|
|
c_pend = self.send_frames()
|
Make echo.py and load.py work again, after the refactoring of
websocket.py:
* With echo.py, which doesn't need any server configuration, we can
just switch over our application class to inherit from
WebSocketRequestHandler instead of WebSocketServer. Also, need to
use the new method name new_websocket_client.
* With load.py, since we have the "delay" configuration, we need both
a server class and a request handler.
Note that for both tests, I've removed the raising of
self.EClose(closed). This is incorrect. First of all, it's described
as "An exception before the WebSocket connection was established", so
not suitable for our case. Second, it will cause send_close to be
called twice. Finally, self.EClose is now in the WebSocketServer
class, and not a member of the request handler.
2013-03-20 09:03:04 +00:00
|
|
|
elif now > (last_send + self.server.delay):
|
2011-05-02 04:17:04 +01:00
|
|
|
last_send = now
|
|
|
|
c_pend = self.send_frames([self.generate()])
|
2011-01-13 00:09:54 +00:00
|
|
|
|
|
|
|
def generate(self):
|
|
|
|
length = random.randint(10, self.max_packet_size)
|
|
|
|
numlist = self.rand_array[self.max_packet_size-length:]
|
|
|
|
# Error in length
|
|
|
|
#numlist.append(5)
|
|
|
|
chksum = sum(numlist)
|
|
|
|
# Error in checksum
|
|
|
|
#numlist[0] = 5
|
|
|
|
nums = "".join( [str(n) for n in numlist] )
|
|
|
|
data = "^%d:%d:%d:%s$" % (self.send_cnt, length, chksum, nums)
|
|
|
|
self.send_cnt += 1
|
|
|
|
|
2011-05-02 04:17:04 +01:00
|
|
|
return data
|
2011-01-13 00:09:54 +00:00
|
|
|
|
|
|
|
|
2011-05-02 04:17:04 +01:00
|
|
|
def check(self, frames):
|
2011-01-13 00:09:54 +00:00
|
|
|
|
|
|
|
err = ""
|
2011-05-02 04:17:04 +01:00
|
|
|
for data in frames:
|
2011-01-13 00:09:54 +00:00
|
|
|
if data.count('$') > 1:
|
|
|
|
raise Exception("Multiple parts within single packet")
|
|
|
|
if len(data) == 0:
|
|
|
|
self.traffic("_")
|
|
|
|
continue
|
|
|
|
|
|
|
|
if data[0] != "^":
|
|
|
|
err += "buf did not start with '^'\n"
|
|
|
|
continue
|
|
|
|
|
|
|
|
try:
|
|
|
|
cnt, length, chksum, nums = data[1:-1].split(':')
|
|
|
|
cnt = int(cnt)
|
|
|
|
length = int(length)
|
|
|
|
chksum = int(chksum)
|
2017-11-10 10:18:32 +00:00
|
|
|
except ValueError:
|
2011-01-13 00:09:54 +00:00
|
|
|
print "\n<BOF>" + repr(data) + "<EOF>"
|
|
|
|
err += "Invalid data format\n"
|
|
|
|
continue
|
|
|
|
|
|
|
|
if self.recv_cnt != cnt:
|
|
|
|
err += "Expected count %d but got %d\n" % (self.recv_cnt, cnt)
|
|
|
|
self.recv_cnt = cnt + 1
|
|
|
|
continue
|
|
|
|
|
|
|
|
self.recv_cnt += 1
|
|
|
|
|
|
|
|
if len(nums) != length:
|
|
|
|
err += "Expected length %d but got %d\n" % (length, len(nums))
|
|
|
|
continue
|
|
|
|
|
|
|
|
inv = nums.translate(None, "0123456789")
|
|
|
|
if inv:
|
|
|
|
err += "Invalid characters found: %s\n" % inv
|
|
|
|
continue
|
|
|
|
|
|
|
|
real_chksum = 0
|
|
|
|
for num in nums:
|
|
|
|
real_chksum += int(num)
|
|
|
|
|
|
|
|
if real_chksum != chksum:
|
|
|
|
err += "Expected checksum %d but real chksum is %d\n" % (chksum, real_chksum)
|
|
|
|
return err
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2011-05-02 04:17:04 +01:00
|
|
|
parser = optparse.OptionParser(usage="%prog [options] listen_port")
|
|
|
|
parser.add_option("--verbose", "-v", action="store_true",
|
|
|
|
help="verbose messages and per frame traffic")
|
|
|
|
parser.add_option("--cert", default="self.pem",
|
|
|
|
help="SSL certificate file")
|
|
|
|
parser.add_option("--key", default=None,
|
|
|
|
help="SSL key file (if separate from cert)")
|
|
|
|
parser.add_option("--ssl-only", action="store_true",
|
|
|
|
help="disallow non-encrypted connections")
|
|
|
|
(opts, args) = parser.parse_args()
|
|
|
|
|
2011-01-13 00:09:54 +00:00
|
|
|
try:
|
2017-11-10 10:18:32 +00:00
|
|
|
if len(args) != 1: raise ValueError
|
2011-05-02 04:17:04 +01:00
|
|
|
opts.listen_port = int(args[0])
|
|
|
|
|
2017-11-10 10:18:32 +00:00
|
|
|
if len(args) not in [1,2]: raise ValueError
|
2011-05-02 04:17:04 +01:00
|
|
|
opts.listen_port = int(args[0])
|
|
|
|
if len(args) == 2:
|
|
|
|
opts.delay = int(args[1])
|
2011-01-13 00:09:54 +00:00
|
|
|
else:
|
2011-05-02 04:17:04 +01:00
|
|
|
opts.delay = 10
|
2017-11-10 10:18:32 +00:00
|
|
|
except ValueError:
|
2011-05-02 04:17:04 +01:00
|
|
|
parser.error("Invalid arguments")
|
|
|
|
|
2014-01-30 22:08:50 +00:00
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
|
2011-05-02 04:17:04 +01:00
|
|
|
opts.web = "."
|
Make echo.py and load.py work again, after the refactoring of
websocket.py:
* With echo.py, which doesn't need any server configuration, we can
just switch over our application class to inherit from
WebSocketRequestHandler instead of WebSocketServer. Also, need to
use the new method name new_websocket_client.
* With load.py, since we have the "delay" configuration, we need both
a server class and a request handler.
Note that for both tests, I've removed the raising of
self.EClose(closed). This is incorrect. First of all, it's described
as "An exception before the WebSocket connection was established", so
not suitable for our case. Second, it will cause send_close to be
called twice. Finally, self.EClose is now in the WebSocketServer
class, and not a member of the request handler.
2013-03-20 09:03:04 +00:00
|
|
|
server = WebSocketLoadServer(WebSocketLoad, **opts.__dict__)
|
2011-01-13 00:09:54 +00:00
|
|
|
server.start_server()
|
2011-05-02 04:17:04 +01:00
|
|
|
|