Python socket.io server error 400 (NodeJS server works) - python

I'm trying to make JavaScript client to a Python websocket server through an Apache2 proxy.
The client is dead simple:
const socket = io({
transports: ['websocket']
});
I have a NodeJS websocket server and a working Apache2 reverse proxy setup.
Now I want to replace the NodeJS server with a Python server - but none of the example implementations from socket.io works. With each of the my client reports an "error 400" when setting up the websocket connection.
The Python server examples come from here:
https://github.com/miguelgrinberg/python-socketio/tree/master/examples/server
Error 400 stands for "Bad Request" - but I know that my requests are fine because my NodeJS server understands them.
When not running behind a proxy then all Python examples work fine.
What could be the problem?

I found the solution - all the Python socket.io server examples that I refered to are not configured to run behind a reverse proxy. The reason is, that the socket.io server is managing a list of allowed request origins and the automatic list creation is failing in the reverse proxy situation.
This function creates the automatic list of allowed origins (engineio/asyncio_server.py):
def _cors_allowed_origins(self, environ):
default_origins = []
if 'wsgi.url_scheme' in environ and 'HTTP_HOST' in environ:
default_origins.append('{scheme}://{host}'.format(
scheme=environ['wsgi.url_scheme'], host=environ['HTTP_HOST']))
if 'HTTP_X_FORWARDED_HOST' in environ:
scheme = environ.get(
'HTTP_X_FORWARDED_PROTO',
environ['wsgi.url_scheme']).split(',')[0].strip()
default_origins.append('{scheme}://{host}'.format(
scheme=scheme, host=environ['HTTP_X_FORWARDED_HOST'].split(
',')[0].strip()))
As you can see, it only adds URLs with {scheme} as a protocol. When behind a reverse proxy, {scheme} will always be "http". So if the initial request was HTTPS based, it will not be in the list of allowed origins.
The solution to this problem is very simple: when creating the socket.io server, you have to either tell him to allow all origins or specify your origin:
import socketio
sio = socketio.AsyncServer(cors_allowed_origins="*") # allow all
# or
sio = socketio.AsyncServer(cors_allowed_origins="https://example.com") # allow specific

Related

Create a default vhost to serve http request in uwsgi

I've uwsgi 2.0.19 on Linux running with the python plugin. I serve http(s) traffic with different applications each for a specific record of my managed domain using such kind of configuration to register them to the front uwsgi servers.
subscribe2 = server=x.x.x.x:4443,key=domain.com,sni_key=/etc/ssl/private/domain.com.key,sni_cert=/etc/ssl/certs/domain.com.crt
subscribe2 = server=x.x.x.x:4443,key=domain.com:443,sni_key=/etc/ssl/private/domain.com.key,sni_cert=/etc/ssl/certs/domain.com.crt
subscribe2 = server=y.y.y.y:4443,key=domain.com,sni_key=/etc/ssl/private/domain.com.key,sni_cert=/etc/ssl/certs/domain.com.crt
subscribe2 = server=y.y.y.y:4443,key=domain.com:443,sni_key=/etc/ssl/private/domain.com.key,sni_cert=/etc/ssl/certs/domain.com.crt
Now when I reach one of the front servers to access a not-existing host, I received such error (the TCP connexion is closed I assume)
curl: (52) Empty reply from server
I would like to be able to have a default/catchall key for such case, that permits to return an HTTP status 404 as I would do in Apache using the _default_ vhost. is it possible.
In order to implement this, you need to define a fallback application using the http-subscription-fallback-key on the front uwsgi server
http-subscription-fallback-key=default
default is a standard application registered on the frontal uwsgi like any other application
subscribe2 = server=x.x.x.x:4443,key=default
subscribe2 = server=x.x.x.x:4443,key=default:80

How to configure apache to support websockets

I wrote server in python and now I would like to configure apache web server to support websockets.
My server returns information when a client sends queries to these addresses:
def make_app():
return tornado.web.Application([
(r"/playgame", EmptyGame),
(r"/playgame/", EmptyGame),
(r"/playgame/(.*)", PlayerGameWebsocket)
])
How to configure the server to support regular user traffic but also to enable websockets when the client establishes such a connection?
I user apache2.4 server.
Ok, it turned out that the solution is trivial. If someone ever looked for an answer, just add a simple redirection to the application in the virtual host configuration which listens on localhost:
ProxyPassMatch "/playgame/(.*)" "ws://127.0.0.1:8888/playgame/$1"
ProxyPassReverse "/playgame/(.*)" "ws://127.0.0.1:8888/playgame/$1"
Thanks to such syntax, we can even pass additional data, e.g. "/playgame/123".
We connect from the client without specifying the port:
var adr = "ws://serverip/playgame/" + gameid;
var ws = new WebSocket(adr);

Python HTTP Server - Create without using HTTP modules

Can I create a HTTP server without using
python -m http.server [port number]
Using an old school style with sockets and such.
Latest code and errors...
import socketserver
response = """HTTP/1.0 500 Internal Server Error
Content-type: text/html
Invalid Server Error"""
class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The RequestHandler class for our server.
It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""
def handle(self):
# self.request is the TCP socket connected to the client
self.data = self.request.recv(1024).strip()
self.request.sendall(response)
if __name__ == "__main__":
HOST, PORT = "localhost", 8000
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
server.serve_forever()
TypeError: 'str' does not support the buffer interface
Yes, you can, but it's a terrible idea -- in fact, even http.server is at best a toy implementation.
You're better off writing whatever webapp you want as a standard WSGI application (most Python web frameworks do that -- Django, Pyramid, Flask...), and serving it with one of the dozens of production-grade HTTP servers that exist for Python.
uWSGI (https://uwsgi-docs.readthedocs.org/en/latest/) is my personal favorite, with Gevent a close second.
If you want more info about how it's done, I recommend that you read the source code to the CherryPy server (http://www.cherrypy.org/). While not as powerful as the aforementioned uWSGI, it's a good reference implementation written in pure Python, that serves WSGI apps through a thread pool.
Sure you can, and servers like Tornado already do it this way.
For simple test servers which can do only HTTP/1.0 GET requests and handle only a single request at a time it should not be that hard once you understood the basics of the HTTP protocol. But if you care even a bit about performance it gets complex fast.

Web2py client server not working

I'm trying to run a project which holds data in web2py server and web2py based client shows the visualization. When running both server and client , the chrome console on clinet side shows:
XMLHttpRequest cannot load http://127.0.0.1:8075/?format=json.
No 'Access-Control- Allow-Origin' header is present on the requested resource.
Origin 'http://127.0.0.1:8080' is therefore not allowed access. (index):1
[ERROR] Cannot connect to data server: http://127.0.0.1:8075?format=json
I'm running above with web2py2.9.5 on linux.
It looks like your web2py client page is served on port 8080 but is then making an Ajax request to port 8075, which violates the same origin policy enforced by web browsers.
If you can't serve both from the same origin, you can get around this by using JSONP or by setting up CORS.

Combining websockets and WSGI in a python app

I'm working on a scientific experiment where about two dozen test persons play a turn-based game with/against each other. Right now, it's a Python web app with a WSGI interface. I'd like to augment the usability with websockets: When all players have finished their turns, I'd like to notify all clients to update their status. Right now, everyone has to either wait for the turn timeout, or continually reload and wait for the "turn is still in progress" error message not to appear again (busy waiting, effectively).
I read through multiple websocket libraries' documentation and I understand how websockets work, but I'm not sure about the architecture for mixing WSGI and websockets: Can I have a websockets and a WSGI server in the same process (and if so, how, using really any websockets library) and just call my_websocket.send_message() from a WSGI handler, or should I have a separate websockets server and do some IPC? Or should I not mix them at all?
edit, 6 months later: I ended up starting a separate websockets server process (using Autobahn), instead of integrating it with the WSGI server. The reason was that it's much easier and cleaner to separate the two of them, and talking to the websockets server from the WSGI process (server to server communication) was straight forward and worked on the first attempt using websocket-client.
Here is an example that does what you want:
https://github.com/tavendo/AutobahnPython/tree/master/examples/twisted/websocket/echo_wsgi
It runs a WSGI web app (Flask-based in this case, but can be anything WSGI conforming) plus a WebSocket server under 1 server and 1 port.
You can send WS messages from within Web handlers. Autobahn also provides PubSub on top of WebSocket, which greatly simplifies the sending of notifications (via WampServerProtocol.dispatch) like in your case.
http://autobahn.ws/python
Disclosure: I am author of Autobahn and work for Tavendo.
but I'm not sure about the architecture for mixing WSGI and websockets
I made it
use WSocket
Simple WSGI HTTP + Websocket Server, Framework, Middleware And App.
Includes
Server(WSGI) included - works with any WSGI framework
Middleware - adds Websocket support for any WSGI framework
Framework - simple Websocket WSGI web application framework
App - Event based app for Websocket communication
When external server used, some clients like Firefox requires http 1.1 Server. for Middleware, Framework, App
Handler - adds Websocket support to wsgiref(python builtin WSGI server)
Client -Coming soon...
Common Features
only single file less than 1000 lines
websocket sub protocol supported
websocket message compression supported (works if client asks)
receive and send pong and ping messages(with automatic pong sender)
receive and send binary or text messages
works for messages with or without mask
closing messages supported
auto and manual close
example using bottle web framework and WSocket middleware
from bottle import request, Bottle
from wsocket import WSocketApp, WebSocketError, logger, run
from time import sleep
logger.setLevel(10) # for debugging
bottle = Bottle()
app = WSocketApp(bottle)
# app = WSocketApp(bottle, "WAMP")
#bottle.route("/")
def handle_websocket():
wsock = request.environ.get("wsgi.websocket")
if not wsock:
return "Hello World!"
while True:
try:
message = wsock.receive()
if message != None:
print("participator : " + message)
wsock.send("you : "+message)
sleep(2)
wsock.send("you : "+message)
except WebSocketError:
break
run(app)

Categories

Resources