Skip to content
This repository was archived by the owner on May 5, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,13 @@ pip-log.txt

# Mac crap
.DS_Store

pyvenv.cfg
lib
lib64
include
share
.eggs
local
pip-selfcheck.json
man
64 changes: 64 additions & 0 deletions echotest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@

# Use https://github.com/hathawsh/sockjs-protocol
# to run a test against this server.

# Known issues:
#
# - The websocket server does not support the old hixie-76 or
# hybi-10 protocols, so 5 of the tests fail.
#
# - The test_abort_xhr_polling and test_abort_xhr_streaming tests
# apparently expect the server to close the session when the client makes
# a parallel connection. Is that really desired behavior?
# Because we decide to keep sessions alive instead, 2 of the tests fail.

from twisted.internet import reactor
from twisted.internet.protocol import Factory
from twisted.internet.protocol import Protocol
from twisted.web.resource import Resource
from twisted.web.server import Site
from txsockjs.factory import SockJSResource


class EchoProtocol(Protocol):
def dataReceived(self, msg):
self.transport.write(msg)


class ImmediateCloseProtocol(Protocol):
def connectionMade(self):
reactor.callLater(0.001, self.transport.loseConnection)


def main():
root = Resource()

echo_factory = Factory.forProtocol(EchoProtocol)

echo_resource = SockJSResource(echo_factory, options={
'streaming_limit': 4096,
})
root.putChild(b'echo', echo_resource)

disabled_websocket_resource = SockJSResource(echo_factory, options={
'streaming_limit': 4096,
'websocket': False,
})
root.putChild(b'disabled_websocket_echo', disabled_websocket_resource)

cookie_needed_resource = SockJSResource(echo_factory, options={
'streaming_limit': 4096,
'cookie_needed': True,
})
root.putChild(b'cookie_needed_echo', cookie_needed_resource)

close_factory = Factory.forProtocol(ImmediateCloseProtocol)
root.putChild(b'close', SockJSResource(close_factory))

site = Site(root)
reactor.listenTCP(8081, site)
reactor.run()


if __name__ == '__main__':
main()
46 changes: 23 additions & 23 deletions qunit/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from OpenSSL import SSL
from txsockjs.factory import SockJSResource

SECURE = True
SECURE = False

### The website

Expand All @@ -13,58 +13,58 @@ def render_GET(self, request):
request.setHeader('content-type', 'application/javascript; charset=UTF-8')
return """var client_opts = {{
// Address of a sockjs test server.
url: 'http{}://irc.fugiman.com:8081',
url: 'http{}://localhost:8081',
sockjs_opts: {{
devel: true,
debug: true,
info: {{cookie_needed:false}}
}}
}};""".format("s" if SECURE else "")
}};""".format("s" if SECURE else "").encode('ascii')

class SlowScript(resource.Resource):
isLeaf = True
def render_GET(self, request):
request.setHeader('content-type', 'application/javascript; charset=UTF-8')
request.write("")
request.write(b"")
reactor.callLater(0.500, self.done, request)
return server.NOT_DONE_YET

def done(self, request):
request.write("var a = 1;\n")
request.write(b"var a = 1;\n")
request.finish()

class Streaming(resource.Resource):
isLeaf = True
def render_GET(self, request):
request.setHeader('content-type', 'text/plain; charset=UTF-8')
request.setHeader('Access-Control-Allow-Origin', '*')
request.write("a"*2048+"\n")
request.write(b"a"*2048+b"\n")
reactor.callLater(0.250, self.done, request)
return server.NOT_DONE_YET

def done(self, request):
request.write("b\n")
request.write(b"b\n")
request.finish()

class Simple(resource.Resource):
isLeaf = True
def render_GET(self, request):
request.setHeader('content-type', 'text/plain; charset=UTF-8')
request.setHeader('Access-Control-Allow-Origin', '*')
return "a"*2048+"\nb\n"
return b"a"*2048+b"\nb\n"

class WrongURL(resource.Resource):
isLeaf = True
def render_GET(self, request):
request.setResponseCode(404)
return ""
return b""

website_root = static.File("qunit/html")
website_root.putChild("slow-script.js", SlowScript())
website_root.putChild("streaming.txt", Streaming())
website_root.putChild("simple.txt", Simple())
website_root.putChild("wrong_url_indeed.txt", WrongURL())
website_root.putChild("config.js", Config())
website_root.putChild(b"slow-script.js", SlowScript())
website_root.putChild(b"streaming.txt", Streaming())
website_root.putChild(b"simple.txt", Simple())
website_root.putChild(b"wrong_url_indeed.txt", WrongURL())
website_root.putChild(b"config.js", Config())
website = server.Site(website_root)
reactor.listenTCP(8082, website)

Expand All @@ -90,7 +90,7 @@ def connectionMade(self):
self.ticker = reactor.callLater(1, self.tick)

def tick(self):
self.transport.write("tick!")
self.transport.write(b"tick!")
self.ticker = reactor.callLater(1, self.tick)

def connectionLost(self, reason=None):
Expand All @@ -104,7 +104,7 @@ class Amplify(protocol.Protocol):
def dataReceived(self, data):
length = int(data)
length = length if length > 0 and length < 19 else 1
self.transport.write("x" * 2**length)
self.transport.write(b"x" * 2**length)

class AmplifyFactory(protocol.Factory):
protocol = Amplify
Expand All @@ -131,13 +131,13 @@ class BroadcastFactory(protocol.Factory):
broadcast = BroadcastFactory()

sockjs_root = resource.Resource()
sockjs_root.putChild("echo", SockJSResource(echo, {'streaming_limit': 4 * 1024}))
sockjs_root.putChild("disabled_websocket_echo", SockJSResource(echo, {'websocket': False}))
sockjs_root.putChild("cookie_needed_echo", SockJSResource(echo, {'cookie_needed': True}))
sockjs_root.putChild("close", SockJSResource(close))
sockjs_root.putChild("ticker", SockJSResource(ticker))
sockjs_root.putChild("amplify", SockJSResource(amplify))
sockjs_root.putChild("broadcast", SockJSResource(broadcast))
sockjs_root.putChild(b"echo", SockJSResource(echo, {'streaming_limit': 4 * 1024}))
sockjs_root.putChild(b"disabled_websocket_echo", SockJSResource(echo, {'websocket': False}))
sockjs_root.putChild(b"cookie_needed_echo", SockJSResource(echo, {'cookie_needed': True}))
sockjs_root.putChild(b"close", SockJSResource(close))
sockjs_root.putChild(b"ticker", SockJSResource(ticker))
sockjs_root.putChild(b"amplify", SockJSResource(amplify))
sockjs_root.putChild(b"broadcast", SockJSResource(broadcast))
sockjs = server.Site(sockjs_root)


Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def run(self):
from twisted.plugin import IPlugin, getPlugins
list(getPlugins(IPlugin))
log.info("Twisted plugin cache updated successfully.")
except Exception, e:
except Exception as e:
log.warn("*** Failed to update Twisted plugin cache. ***")
log.warn(str(e))

Expand Down Expand Up @@ -70,6 +70,7 @@ def _hacked_write_toplevel_names(cmd, basename, filename):
platforms=['OS Independent'],
packages=["txsockjs","txsockjs.protocols","twisted.plugins"],
install_requires=[
"six",
"Twisted",
],
classifiers=[
Expand Down
2 changes: 1 addition & 1 deletion txsockjs/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.2.2"
__version__ = "1.2.99.2"
73 changes: 38 additions & 35 deletions txsockjs/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,80 +54,83 @@ def __init__(self, factory, options = None):
'heartbeat': 25,
'timeout': 5,
'streaming_limit': 128 * 1024,
'encoding': 'cp1252', #Latin1
'encoding': 'latin-1',
'sockjs_url': 'https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.js',
'proxy_header': None
}
if options is not None:
self._options.update(options)
# Just in case somebody wants to mess with these
self._methods = {
'xhr': XHR,
'xhr_send': XHRSend,
'xhr_streaming': XHRStream,
'eventsource': EventSource,
'htmlfile': HTMLFile,
'jsonp': JSONP,
'jsonp_send': JSONPSend,
b'xhr': XHR,
b'xhr_send': XHRSend,
b'xhr_streaming': XHRStream,
b'eventsource': EventSource,
b'htmlfile': HTMLFile,
b'jsonp': JSONP,
b'jsonp_send': JSONPSend,
}
self._writeMethods = ('xhr_send','jsonp_send')
self._writeMethods = (b'xhr_send', b'jsonp_send')
# Static Resources
self.putChild("info",Info())
self.putChild("iframe.html",IFrame())
self.putChild("websocket",RawWebSocket())
# Since it's constant, we can declare the websocket handler up here
self._websocket = WebSocket()
self._websocket.parent = self
self.putChild(b"info", Info())
self.putChild(b"iframe.html", IFrame())
if self._options['websocket']:
self.putChild(b"websocket", RawWebSocket())
# Since it's constant, we can declare the websocket handler up here
self._websocket = WebSocket()
self._websocket.parent = self

def getChild(self, name, request):
# Check if it is the greeting url
if not name and not request.postpath:
return self
# Hacks to resove the iframe even when people are dumb
if len(name) > 10 and name[:6] == "iframe" and name[-5:] == ".html":
return self.children["iframe.html"]
if len(name) > 10 and name[:6] == b"iframe" and name[-5:] == b".html":
return self.children[b"iframe.html"]
# Sessions must have 3 parts, name is already the first. Also, no periods in the loadbalancer
if len(request.postpath) != 2 or "." in name or not name:
if len(request.postpath) != 2 or b"." in name or not name:
return resource.NoResource("No such child resource.")
# Extract session & request type. Discard load balancer
session, name = request.postpath
# No periods in the session
if "." in session or not session:
if b"." in session or not session:
return resource.NoResource("No such child resource.")
# Websockets are a special case
if name == "websocket":
if name == b"websocket" and self._options['websocket']:
return self._websocket
# Reject invalid methods
if name not in self._methods:
return resource.NoResource("No such child resource.")
# Reject writes to invalid sessions, unless just checking options
if name in self._writeMethods and session not in self._sessions and request.method != "OPTIONS":
if name in self._writeMethods and session not in self._sessions and request.method != b"OPTIONS":
return resource.NoResource("No such child resource.")
# Generate session if doesn't exist, unless just checking options
if session not in self._sessions and request.method != "OPTIONS":
if session not in self._sessions and request.method != b"OPTIONS":
self._sessions[session] = Stub(self, session)
# Delegate request to appropriate handler
return self._methods[name](self, self._sessions[session] if request.method != "OPTIONS" else None)
return self._methods[name](self, self._sessions[session] if request.method != b"OPTIONS" else None)

def putChild(self, path, child):
child.parent = self
resource.Resource.putChild(self, path, child)

def setBaseHeaders(self, request, cookie=True):
origin = request.getHeader("Origin")
headers = request.getHeader('Access-Control-Request-Headers')
if origin is None or origin == "null":
origin = "*"
request.setHeader('access-control-allow-origin', origin)
request.setHeader('access-control-allow-credentials', 'true')
request.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
origin = request.getHeader(b"Origin")
headers = request.getHeader(b'Access-Control-Request-Headers')
if origin is None:
origin = b'null'
request.setHeader(b'access-control-allow-origin', origin)
request.setHeader(b'access-control-allow-credentials', b'true')
request.setHeader(
b'Cache-Control',
b'no-store, no-cache, no-transform, must-revalidate, max-age=0')
if headers is not None:
request.setHeader('Access-Control-Allow-Headers', headers)
request.setHeader(b'Access-Control-Allow-Headers', headers)
if self._options["cookie_needed"] and cookie:
cookie = request.getCookie("JSESSIONID") if request.getCookie("JSESSIONID") else "dummy"
request.addCookie("JSESSIONID", cookie, path="/")
cookie = request.getCookie(b"JSESSIONID") if request.getCookie(b"JSESSIONID") else b"dummy"
request.addCookie(b"JSESSIONID", cookie, path=b"/")

def render_GET(self, request):
self.setBaseHeaders(request,False)
request.setHeader('content-type', 'text/plain; charset=UTF-8')
return "Welcome to SockJS!\n"
request.setHeader(b'content-type', b'text/plain; charset=UTF-8')
return b"Welcome to SockJS!\n"
10 changes: 5 additions & 5 deletions txsockjs/multiplex.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ def connectionMade(self):
self.factory._connections[self] = {}

def dataReceived(self, message):
type, chaff, topic = message.partition(",")
if "," in topic:
type, chaff, topic = message.partition(b",")
if b"," in topic:
topic, chaff, payload = topic.partition(",")
if type == "sub":
if type == b"sub":
self.factory.subscribe(self, topic)
elif type == "msg":
elif type == b"msg":
self.factory.handleMessage(self, topic, payload)
elif type == "uns":
elif type == b"uns":
self.factory.unsubscribe(self, topic)

def connectionLost(self, reason=None):
Expand Down
Loading