Connecting flask-socketio with user_loader - flask

Warning: Apologies for the long post
We are currently running a flask server with a couchdb backend. We have a few API endpoints that provide user information. We've used flask-login for user management. THe user_loader checks the user database on each request:
#login_manager.user_loader
def load_user(id):
mapping = g.couch.get('_design/authentication')
mappingDD = ViewDefinition('_design/authentication','auth2',mapping['views']['userIDMapping']['map'])
for row in mappingDD[id]:
return userModel.User(row.value)
We also have a segment that has a websocket to enable chat between the server and the client. The code below I took after seeing the authentication section of the flask-socketio documentation:
def authenticated_only(f):
#functools.wraps(f)
def wrapped(*args, **kwargs):
if not current_user.is_authenticated:
disconnect()
else:
return f(*args, **kwargs)
return wrapped
I have the following code for my web socket:
#app.route('/sampleEndPoint/')
def chatRoom():
return render_template('randomIndex.html', async_mode=socketio.async_mode)
#socketio.on('connect', namespace='/test')
#authenticated_only
def test_connect():
app.logger.debug(session)
emit('my_response', {'data': 'Connected'})
#socketio.on('disconnect_request', namespace='/test')
#authenticated_only
def disconnect_request():
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response',{'data': 'Disconnected!', 'count': session['receive_count']})
disconnect()
#socketio.on('disconnect', namespace='/test')
#authenticated_only
def test_disconnect():
print('Client disconnected', request.sid)
Everything works well in the other routes. However, I get the following error when I connect to the websocket:
File "/home/sunilgopikrishna/insurance_brokerage/perilback/main.py",
line 144, in load_user
mapping = g.couch.get('_design/authentication') File "/home/sunilgopikrishna/.local/lib/python3.5/site-packages/werkzeug/local.py",
line 343, in getattr
return getattr(self._get_current_object(), name) AttributeError: '_AppCtxGlobals' object has no attribute 'couch'
I read in the flask-socketio documentation that login_required does not work with socketio.
Is there a workaround for this? Any help is appreciated.
Regards,
Galeej

I read in the flask-socketio documentation that login_required does not work with socketio
Sure, but you are not using login_required in your socketio events, so that is not the problem here.
The problem is that you probably have a before_request handler that sets g.couch. The "before" and "after" handlers only run for HTTP requests, they do not run for Socket.IO, because in this protocol there is no concept of requests, there is just a single long-term connection.
So basically, you need to find another way to access your database connection in your user loader handler that does not rely on a before_request handler.

Related

Tornado on pika consumer can't run

I want to build monitoring system using RabbitMQ and Tornado. I can run the producer and my consumer can consume the data on queue but the data cant be show on website.
This just my experiment before I using the sensor
import pika
import tornado.ioloop
import tornado.web
import tornado.websocket
import logging
from threading import Thread
logging.basicConfig(lvl=logging.INFO)
clients=[]
credentials = pika.credentials.PlainCredentials('ayub','ayub')
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.43.101',
5672,
'/',
credentials))
channel = connection.channel()
def threaded_rmq():
channel.basic_consume('Queue',
on_message_callback= consumer_callback,
auto_ack=True,
exclusive=False,
consumer_tag=None,
arguments=None)
channel.start_consuming()
def disconect_rmq():
channel.stop_consuming()
Connection.close()
logging.info('Disconnected from broker')
def consumer_callback(ch,method,properties,body):
for itm in clients:
itm.write_message(body)
class SocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
logging.info('websocket open')
clients.remove(self)
def close(self):
logging.info('websocket closed')
clients.remove(self)
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("websocket.html")
application = tornado.web.Application([
(r'/ws',SocketHandler),
(r"/", MainHandler),
])
def startTornado():
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
def stopTornado():
tornado.ioloop.IOLoop.instance().stop()
if __name__ == "__main__":
logging.info('starting thread RMQ')
threadRMQ = Thread(target=threaded_rmq)
threadRMQ.start()
logging.info('starting thread tornado')
threadTornado = Thread(target=startTornado)
threadTornado.start()
try:
raw_input("server ready")
except SyntaxError:
pass
try:
logging.info('disconnected')
disconnect_rmq()
except Exception, e:
pass
stopTornado()
but I got this error
WARNING:tornado.access:404 GET /favicon.ico (192.168.43.10) 0.98ms
please help me
In your SocketHandler.open function you need to add the client not remove it.
Also consider using a set for clients instead of a list because the remove operation will be faster:
clients = set()
...
class SocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
logging.info('websocket open')
clients.add(self)
def close(self):
logging.info('websocket closed')
clients.remove(self)
The message you get regarding favicon.ico is actually a warning and it's harmless (the browser is requesting an icon to show for web application but won't complain if none is available).
You might also run into threading issues because Tornado and Pika are running in different threads so you will have to synchronize them; you can use Tornado's IOLoop.add_callback method for that.

How to send socket messages via Django views when socket server and views.py are split into two files?

Env: Python 3.6, and Django 2.1
I have created a Django website and a socket server, and files are organized like this:
web
...
user (a Django app)
__init__.py
views.py
...
server.py
Actually I want to build a umbrella rental system by using django, and server connects to umbrella shelf via multi-thread socket (sending some messages). Like I press the borrow button, and views.py can call the server test_function and send some messages to the connected umbrella shelf.
I can import server variables or functions in views.py, but I cannot get the right answer while server.py is running. So I want to ask you if you could give me some advice. Thanks a lot!
By the way, I tried to import the global variable clients directly in views.py, but still got [].
server.py defines a multi-thread server, which is basically as below:
clients = []
class StuckThread(threading.Thread):
def __init__(self, **kwargs):
self.name = kwargs.get('name', '')
def run(self):
while True:
# do something
def func1(self):
# do something
def test_function(thread_name):
# if the function is called by `views.py`, then `clients = []` and return 'nothing', but if I call this function in `server.py`, then I can get a wanted result, which is `got the thread`
for client in clients:
if client['thread'].name == thread_name:
return 'got the thread'
return 'nothing'
if __name__ == '__main__':
ip_port = ('0.0.0.0', 65432)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ip_port)
server.listen(max_listen_num)
while True:
client, address = socket.accept()
param = {'name': 'test name'}
stuck_thread = StuckThread(**param)
clients.append({"client": client, "address": address, "thread": stuck_thread})
stuck_thread.start()
and I have a Django views.py like this
def view_function(request):
from server import clients
print(clients) # got []
form server import test_function
print(test_function('test name')) # got 'nothing'
return render(request, 'something.html')
I have solve this problem by socket communication between django views.py and server.py. I open another port to receive messages from views.py. Once the borrow button is pressed, a socket client in views.py will build up and send arguments and other messages to the server.

Django system check stuck on unreachable url

In my project I have requests library that sends POST request. Url for that request is hardcoded in function, which is accessed from views.py.
The problem is that when I dont have internet connection, or host, on which url is pointing, is down, I cant launch developer server, it gets stuck on Performing system check. However, if I comment the line with url, or change it to guarantee working host, check is going well.
What is good workaround here ?
views.py
def index(request):
s = Sync()
s.do()
return HttpResponse("Hello, world. You're at the polls index.")
sync.py
class Sync:
def do(self):
reservations = Reservation.objects.filter(is_synced=False)
for reservation in reservations:
serializer = ReservationPKSerializer(reservation)
dictionary = {'url': 'url', 'hash': 'hash', 'json': serializer.data}
encoded_data = json.dumps(dictionary)
r = requests.post('http://gservice.ca29983.tmweb.ru/gdocs/do.php', headers={'Content-Type': 'application/json'}, data=encoded_data)
if r.status_code == 200:
reservation.is_synced = True
reservation.save()
It might appear to be stuck because requests automatically retries the connection a few times. Try reducing the retry count to 0 or 1 with:
Can I set max_retries for requests.request?

Flask: refer to 'g' in gevent spawn in a request

I use db pool in my flask-restful project, I register a before request hook so that every request will get a db connection and store in the thread local variable g:
# acquire db connection from pool
#app.before_request
def get_connection():
setattr(g, '__con__', MysqlHandler())
My module layer will then get the db connection from g for CURD:
#classmethod
def get(cls, **kwargs):
res = g.__con__.simple_query(cls.__table__, query_cond=kwargs)
return cls(**res[0]) if res else None
Finally after the request, the connection will be committed and released back to the pool in after_request hook:
# commit db update after the request, if no exception
#app.after_request
def commit(response):
if getattr(g, '__con__', None):
g.__con__.commit()
return response
This framework works fine until I introduce gevent to handle some long term async task in a request:
#copy_current_request_context
def my_async_task():
time.sleep(5)
print 'I am g', g.__con__
class TeamListView(Resource):
# http GET handler, return all team members
def get(self):
gevent.spawn(my_async_task)
all_groups = Team.all()
return return_json(data=all_groups)
The above code will return JSON data to front-end immediately, which means the request context will be destroyed after the request, so that the g.__con__ could not be accessed after 5 seconds sleep in my async task.
My async task has to handle database operation via g.__con__, so is there any solution to keep the g, event after the request complete ?
Thanks in advance for your help.

Django send_mail results in error 61 refused on Mac OSX

Running into a very stange error. I'm running Django on my Mac OSX and when I tried to send an email from my application it hangs and gives me this error: "Error 61 Connection Refused"
Any ideas? I don't have my firewall turned on. I can upload an image of the error if needed.
Have you actually configured the EMAIL_* settings in settings.py? Error 61 is the error you get if you leave it on the default values and you don't have a local SMTP server running.
Alternatively, as Peter suggests, if you have set it up then you might need to use authentication with your SMTP server.
Being totally Max OS X ignorant, my first guess would be that your SMTP server requires authentication.
Its simple, if sendmail works via command-line, copy the code from http://djangosnippets.org/snippets/1864/ into a file called sendmail.py
"""sendmail email backend class."""
import threading
from django.conf import settings
from django.core.mail.backends.base import BaseEmailBackend
from subprocess import Popen,PIPE
class EmailBackend(BaseEmailBackend):
def __init__(self, fail_silently=False, **kwargs):
super(EmailBackend, self).__init__(fail_silently=fail_silently)
self._lock = threading.RLock()
def open(self):
return True
def close(self):
pass
def send_messages(self, email_messages):
"""
Sends one or more EmailMessage objects and returns the number of email
messages sent.
"""
if not email_messages:
return
self._lock.acquire()
try:
num_sent = 0
for message in email_messages:
sent = self._send(message)
if sent:
num_sent += 1
finally:
self._lock.release()
return num_sent
def _send(self, email_message):
"""A helper method that does the actual sending."""
if not email_message.recipients():
return False
try:
ps = Popen(["sendmail"]+list(email_message.recipients()), \
stdin=PIPE)
ps.stdin.write(email_message.message().as_string())
ps.stdin.flush()
ps.stdin.close()
return not ps.wait()
except:
if not self.fail_silently:
raise
return False
return True
Inside of settings.py, set the variable:
EMAIL_BACKEND = 'path.to.sendmail.EmailBackend'
I had a similar problem and found this link: http://dashasalo.com/2011/05/29/django-send_mail-connection-refused-on-macos-x/
Basically, you need to have a mail server running in order to send mail from it. If there is no mail server running you will get a 61.