I'm building a flask app utilizing flask socketio. I'm running it locally on Ubuntu 20.04 with gunicorn and eventlet on localhost:8000.
My server side test code looks like this:
$(document).ready(function() {
// const socketGame = io(window.location.hostname + "/coin")
var socket = io.connect('http://127.0.0.1:8000');
socket.on('connect', function() {
console.log("ok");
socket.send('User has connected!');
});
socket.on('message', function(msg) {
console.log('Received message');
});
});
and in my server I have the following listener:
import re
from flask import render_template, request, redirect, url_for, jsonify, current_app
from flask_mail import Mail, Message
from flask_socketio import emit, join_room, leave_room, rooms
from flask_login import LoginManager, UserMixin, login_user, login_required, current_user, logout_user
from werkzeug.security import generate_password_hash, check_password_hash
from .. import socketio
from . import games_blueprint
from app.models import *
from app.utilities import sanitizeHtml
#socketio chat server
#need to create event for admin broadcasting messages
@socketio.on('message', namespace='/')
def handleMessage(msg):
print('Message: ' + msg)
send(msg, broadcast=True)
It should simply print the msg from the client in the terminal and broadcast back a message. But, from the gunicorn logs, it looks like the server is receiving the event from the client and not passing it to the listener:
(vegazbetenv) jveiga@LAPTOP-C6PJKMM6:~/vegazbet$ gunicorn --worker-class eventlet -w 1 wsgi:application
[2021-11-24 21:00:59 -0300] [134633] [INFO] Starting gunicorn 20.1.0
[2021-11-24 21:00:59 -0300] [134633] [INFO] Listening at: http://127.0.0.1:8000 (134633)
[2021-11-24 21:00:59 -0300] [134633] [INFO] Using worker: eventlet
[2021-11-24 21:00:59 -0300] [134635] [INFO] Booting worker with pid: 134635
Server initialized for eventlet.
Server initialized for eventlet.
Server initialized for eventlet.
HYYx0QNgrNr7YodDAAAA: Sending packet OPEN data {'sid': 'HYYx0QNgrNr7YodDAAAA', 'upgrades': ['websocket'], 'pingTimeout': 20000, 'pingInterval': 25000}
HYYx0QNgrNr7YodDAAAA: Received packet MESSAGE data 0
HYYx0QNgrNr7YodDAAAA: Sending packet MESSAGE data 0{"sid":"q1NjSbTlPRYmT_TYAAAB"}
HYYx0QNgrNr7YodDAAAA: Received request to upgrade to websocket
HYYx0QNgrNr7YodDAAAA: Received packet MESSAGE data 2["message","User has connected!"]
received event "message" from q1NjSbTlPRYmT_TYAAAB [/]
HYYx0QNgrNr7YodDAAAA: Upgrade to websocket successful
HYYx0QNgrNr7YodDAAAA: Sending packet PING data None
HYYx0QNgrNr7YodDAAAA: Received packet PONG data
As can be seen in the logs, the websocket connection is successfully upgraded and the socket-io server receives the message from the client, it just doesn't pass it to the function that was supposed to listen to the event.
Just to clarify, I'm running flask using the app factory pattern, the listener code is inside a blueprint views.py file, follows my code for app factory, and blueprint:
app/__init__.py
import os
from flask import Flask, jsonify, render_template, request, redirect, url_for
from flask_login import LoginManager, UserMixin, login_user, login_required, current_user, logout_user
from flask_mail import Mail
from flask_socketio import SocketIO
from flask_mongoengine import MongoEngine
from flask_wtf.csrf import CSRFProtect
from celery import Celery
from config import Config
from app.models import *
from config import *
### Flask extension objects instantiation ###
mail = Mail()
csrf = CSRFProtect()
login_manager = LoginManager()
# socketio = SocketIO(engineio_logger=True, logger=True, message_queue=os.getenv('CELERY_BROKER_URL'), cors_allowed_origins="*")
socketio = SocketIO(engineio_logger=True, logger=True, cors_allowed_origins="*")
### Celery instantiation ###
celery = Celery(__name__, broker=Config.CELERY_BROKER_URL, result_backend=Config.RESULT_BACKEND)
### Application Factory ###
def create_app():
app = Flask(__name__)
# Configure the flask app instance
CONFIG_TYPE = os.getenv('CONFIG_TYPE', default='config.DevelopmentConfig')
app.config.from_object(CONFIG_TYPE)
#Configure celery
celery.conf.update(app.config)
# Initialize flask extension objects
initialize_extensions(app)
# Register blueprints
register_blueprints(app)
#setup db integration
configure_db(app)
# Configure logging
configure_logging(app)
# Register error handlers
register_error_handlers(app)
#setup db for flask-login
@login_manager.user_loader
def load_user(user_id):
user = User.objects(id=user_id)[0]
return user
return app
### Helper Functions ###
def register_blueprints(app):
from app.auth import auth_blueprint
from app.main import main_blueprint
from app.games import games_blueprint
from app.admin import admin_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/users')
app.register_blueprint(main_blueprint)
app.register_blueprint(games_blueprint, url_prefix='/games')
app.register_blueprint(admin_blueprint, url_prefix='/admin')
def initialize_extensions(app):
mail.init_app(app)
login_manager.init_app(app)
csrf.init_app(app)
socketio.init_app(app)
def register_error_handlers(app):
# 400 - Bad Request
@app.errorhandler(400)
def bad_request(e):
return render_template('400.html'), 400
# 401 - Unauthorized
@app.errorhandler(401)
def bad_request(e):
return render_template('401.html'), 401
# 403 - Forbidden
@app.errorhandler(403)
def forbidden(e):
return render_template('403.html'), 403
# 404 - Page Not Found
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
# 405 - Method Not Allowed
@app.errorhandler(405)
def method_not_allowed(e):
return render_template('405.html'), 405
# 500 - Internal Server Error
@app.errorhandler(500)
def server_error(e):
return render_template('500.html'), 500
def configure_logging(app):
import logging
from flask.logging import default_handler
from logging.handlers import RotatingFileHandler
# Deactivate the default flask logger so that log messages don't get duplicated
app.logger.removeHandler(default_handler)
# Create a file handler object
file_handler = RotatingFileHandler('flaskapp.log', maxBytes=16384, backupCount=20)
# Set the logging level of the file handler object so that it logs INFO and up
file_handler.setLevel(logging.INFO)
# Create a file formatter object
file_formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(filename)s: %(lineno)d]')
# Apply the file formatter object to the file handler object
file_handler.setFormatter(file_formatter)
# Add file handler object to the logger
app.logger.addHandler(file_handler)
def configure_db(app):
db = MongoEngine(app)
app/games/__init__.py
#This bluepint will deal with all site functionality
from flask import Blueprint
games_blueprint = Blueprint('games', __name__, template_folder='templates')
from . import views
Any thoughts on why this is happening and how to solve it?