Reputation: 379
I have some websocket protocols implemented with Twisted, they work fine when I connect using "ws", but when I enable secure websockets, the __init__
method is called twice. To more specific, it is called once, then the connection apparently fails, with connectionLost being called, then it the __init__
is called again, and this time the connection stays open.
The code bellow exemplifies it. When I connect with wss, the log line in the __init__
of the websocket protocol is called twice, but this doesn't happen with plain websockets.
import socket from datetime import datetime from twisted.internet import reactor
from twisted.internet.ssl import DefaultOpenSSLContextFactory
from autobahn.twisted.websocket import WebSocketServerProtocol, WebSocketServerFactory, listenWS
import txaio
txaio.use_twisted()
CERT_KEY = "certificate.key"
CERT_PATH = "certificate.crt"
def log(msg):
print("{}: {}".format(str(datetime.now()), msg))
class TestProtocol(WebSocketServerProtocol):
def __init__(self):
super(TestProtocol, self).__init__()
log("Test protocol init")
def connectionLost(self, reason):
WebSocketServerProtocol.connectionLost(self, reason)
log("Connection closed: Reason is {}".format(reason))
class TestProtocolFactory(WebSocketServerFactory):
protocol = TestProtocol
def init_websocket_protocol(factory_cls, port):
try:
key, crt = CERT_KEY, CERT_PATH
context_factory = DefaultOpenSSLContextFactory(key, crt)
connection_string = "wss://localhost:{}".format(str(port))
factory = factory_cls(connection_string)
listenWS(factory, contextFactory=context_factory)
log("Port {} bound to test websocket server".format(str(port)))
except socket.error as e:
log("Server was unable to bind to a new port: ".format(str(e)))
def main():
init_websocket_protocol(TestProtocolFactory, 9000)
reactor.run()
if __name__ == '__main__':
main()
Upvotes: 1
Views: 676
Reputation: 316
The recommend API these days is to use endpoints. Also, twisted.internet.ssl.CertificateOptions
is the preferred API for TLS connections. So with those changes your code above would look like this:
from datetime import datetime
from autobahn.twisted.websocket import WebSocketServerProtocol, WebSocketServerFactory
from twisted.internet.ssl import CertificateOptions, PrivateCertificate, Certificate, KeyPair
from twisted.internet.endpoints import SSL4ServerEndpoint
from twisted.internet.task import react
from OpenSSL import crypto
CERT_KEY = "certificate.key"
CERT_PATH = "certificate.crt"
def log(msg):
print("{}: {}".format(str(datetime.now()), msg))
class TestProtocol(WebSocketServerProtocol):
def __init__(self):
super(TestProtocol, self).__init__()
log("Test protocol init")
def connectionLost(self, reason):
WebSocketServerProtocol.connectionLost(self, reason)
log("Connection closed: Reason is {}".format(reason))
class TestProtocolFactory(WebSocketServerFactory):
protocol = TestProtocol
def init_websocket_protocol(reactor, port):
with open(CERT_KEY) as key_file, open(CERT_PATH) as cert_file:
key = KeyPair.load(key_file.read(), crypto.FILETYPE_PEM).original
cert = Certificate.loadPEM(cert_file.read()).original
ctx = CertificateOptions(
privateKey=key,
certificate=cert,
)
return SSL4ServerEndpoint(reactor, port, ctx)
def main(reactor):
ep = init_websocket_protocol(reactor, 9000)
ep.listen(TestProtocolFactory())
reactor.run()
if __name__ == '__main__':
react(main)
When I run this code and point Firefox at it, it connects once. What does the browser-side code you're using look like?
Upvotes: 2