Rudimentary support for reflected websocket frames.

This commit is contained in:
Aldo Cortesi 2015-06-04 23:57:23 +12:00
parent 9311d60596
commit 0a7da6a9b1
4 changed files with 55 additions and 18 deletions

View File

@ -57,6 +57,16 @@ def parse_pathoc(s):
return expanded return expanded
def parse_websocket_frame(s):
try:
return websockets.WebsocketFrame.expr().parseString(
s,
parseAll = True
)[0]
except pp.ParseException as v:
raise exceptions.ParseException(v.msg, v.line, v.col)
def serve(msg, fp, settings): def serve(msg, fp, settings):
""" """
fp: The file pointer to write to. fp: The file pointer to write to.

View File

@ -3,6 +3,7 @@ import netlib.websockets
import pyparsing as pp import pyparsing as pp
from . import base, generators, actions, message from . import base, generators, actions, message
NESTED_LEADER = "pathod!"
class WF(base.CaselessLiteral): class WF(base.CaselessLiteral):
TOK = "wf" TOK = "wf"
@ -160,6 +161,10 @@ class WebsocketFrame(message.Message):
resp = resp.setParseAction(klass) resp = resp.setParseAction(klass)
return resp return resp
@property
def nested_frame(self):
return self.tok(NestedFrame)
def resolve(self, settings, msg=None): def resolve(self, settings, msg=None):
tokens = self.tokens[:] tokens = self.tokens[:]
if not self.mask and settings.is_client: if not self.mask and settings.is_client:
@ -181,6 +186,9 @@ class WebsocketFrame(message.Message):
elif self.rawbody: elif self.rawbody:
bodygen = self.rawbody.value.get_generator(settings) bodygen = self.rawbody.value.get_generator(settings)
length = len(self.rawbody.value.get_generator(settings)) length = len(self.rawbody.value.get_generator(settings))
elif self.nested_frame:
bodygen = NESTED_LEADER + self.nested_frame.parsed.spec()
length = len(bodygen)
else: else:
bodygen = None bodygen = None
length = 0 length = 0
@ -228,7 +236,3 @@ class WebsocketClientFrame(WebsocketFrame):
components = COMPONENTS + ( components = COMPONENTS + (
NestedFrame, NestedFrame,
) )
@property
def nested_frame(self):
return self.tok(NestedFrame)

View File

@ -11,6 +11,8 @@ from netlib import tcp, http, wsgi, certutils, websockets
from . import version, app, language, utils, log from . import version, app, language, utils, log
import language.http import language.http
import language.actions import language.actions
import language.exceptions
import language.websockets
DEFAULT_CERT_DOMAIN = "pathod.net" DEFAULT_CERT_DOMAIN = "pathod.net"
@ -102,21 +104,37 @@ class PathodHandler(tcp.BaseHandler):
def handle_websocket(self): def handle_websocket(self):
lr = self.rfile if self.server.logreq else None lr = self.rfile if self.server.logreq else None
lw = self.wfile if self.server.logresp else None lw = self.wfile if self.server.logresp else None
with log.Log(self.logfp, self.server.hexdump, lr, lw) as lg: while True:
while True: with log.Log(self.logfp, self.server.hexdump, lr, lw) as lg:
try: try:
frm = websockets.Frame.from_file(self.rfile) frm = websockets.Frame.from_file(self.rfile)
retlog = dict( except tcp.NetLibIncomplete, e:
type="wsframe", lg("Error reading websocket frame: %s"%e)
frame=dict(
),
cipher=None,
)
self.addlog(retlog)
break break
except tcp.NetLibTimeout: # pragma: no cover lg(frm.human_readable())
pass retlog = dict(
lg(frm.human_readable()) type="wsframe",
frame=dict(
),
cipher=None,
)
ld = language.websockets.NESTED_LEADER
if frm.payload.startswith(ld):
nest = frm.payload[len(ld):]
try:
wf = language.parse_websocket_frame(nest)
except language.exceptions.ParseException, v:
lg(
"Parse error in reflected frame specifcation:"
" %s" % v.msg
)
break
frame_log = language.serve(
wf,
self.wfile,
self.settings
)
self.addlog(retlog)
return self.handle_websocket, None return self.handle_websocket, None
def handle_http_connect(self, connect, lg): def handle_http_connect(self, connect, lg):

View File

@ -2,6 +2,7 @@ import cStringIO
from libpathod import pathod, version from libpathod import pathod, version
from netlib import tcp, http from netlib import tcp, http
import time import time
import sys
import tutils import tutils
@ -211,10 +212,14 @@ class CommonTests(tutils.DaemonTests):
def test_websocket_frame(self): def test_websocket_frame(self):
r = self.pathoc(["ws:/p/", "wf:b@10"], ws_read_limit=0) r = self.pathoc(["ws:/p/", "wf:b@10"], ws_read_limit=0)
print r print >> sys.stderr, r
print self.d.log() print >> sys.stderr, self.d.log()
assert self.d.last_log()["type"] == "wsframe" assert self.d.last_log()["type"] == "wsframe"
def test_websocket_reflected_frame(self):
r = self.pathoc(["ws:/p/", "wf:f'wf'"], ws_read_limit=0)
assert r
class TestDaemon(CommonTests): class TestDaemon(CommonTests):
ssl = False ssl = False