Can not get cookie from localhost websocket fast api - python

I'm testing websockets to work with cookies and trying to get them in fast api. I manually installed them in chrome but I get an empty dictionary inside the application. I used the fast api documentation templates and slightly redesigned it
My html
html = """
<!DOCTYPE html>
<html>
<head>
<title>Chat</title>
</head>
<body>
<h1>WebSocket Chat</h1>
<form action="" onsubmit="sendMessage(event)">
<label>Item ID: <input type="text" id="itemId" autocomplete="off" value="foo"/></label>
<label>Token: <input type="text" id="token" autocomplete="off" value="some-key-token"/></label>
<button onclick="connect(event)">Connect</button>
<hr>
<label>Message: <input type="text" id="messageText" autocomplete="off"/></label>
<button>Send</button>
</form>
<ul id='messages'>
</ul>
<script>
var ws = null;
function connect(event) {
var itemId = "1000"
var token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI2MjlmYmU3ZGNjMzQxMGFiNWE2MmZkOWYiLCJ1c2VyIjoiQmk4eVVhOG5TS1dFRm8weEJjYWkwRUtDU2E3TyJ9.7hE3qIcFoLLoDqQSliaHXhSPs4FW75fNafumPdKHPmI"
ws = new WebSocket("ws://localhost:8000/ws/" + itemId + "/?token=" + token);
ws.onmessage = function(event) {
var messages = document.getElementById('messages')
var message = document.createElement('li')
var content = document.createTextNode(event.data)
message.appendChild(content)
messages.appendChild(message)
};
event.preventDefault()
}
function sendMessage(event) {
var input = document.getElementById("messageText")
ws.send(input.value)
input.value = ''
event.preventDefault()
}
</script>
</body>
</html>
"""
My websocket
class ConnectionManager:
def __init__(self):
self.active_connections: list = []
async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket)
def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket)
async def send_personal_message(self, message: str, websocket: WebSocket):
await websocket.send_text(message)
async def broadcast(self, message: str):
for connection in self.active_connections:
await connection.send_text(message)
manager = ConnectionManager()
#app.websocket("/ws/{project_id}/")
async def test_websocket(websocket: WebSocket,
project_id: int,
token: str = Depends(authorization.get_user_websocket_token)
):
print(websocket.cookies)
await manager.connect(websocket)
try:
while True:
project = await db["storages"].find_one({"project_id": project_id})
if token in project["users"]:
print(True)
data = await websocket.receive_text()
await manager.send_personal_message(f"You wrote: {data}", websocket)
await manager.broadcast(f"Client says: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket)
await manager.broadcast(f"Client left the chat")
print(websocket.cookies) returns an empty dictionary {}

Cookies are domain-defined, so you should point at localhost:8000 and there define that Cookie, not 127.0.0.1:8000.
Maybe it's easier to check them in Postman.
With such crafted request:
They will be available at websocket.cookies for sure.
websocket.cookies under the hood checks Cookie header:
#property
def cookies(self) -> typing.Dict[str, str]:
if not hasattr(self, "_cookies"):
cookies: typing.Dict[str, str] = {}
cookie_header = self.headers.get("cookie")
if cookie_header:
cookies = cookie_parser(cookie_header)
self._cookies = cookies
return self._cookies

Related

Unable to make websocket connection in django

I am trying to make a websocket connection
I am using the django's channels API.
For some reason the handshaking is not taking place. Maybe it has something to do with the url?
This is my main routing.py file
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter,URLRouter
import chat.routing
application = ProtocolTypeRouter({
'websocket':AuthMiddlewareStack(
URLRouter(
chat.routing.websocket_urlpatterns
)
),
})
This is my chat app routing.py
from django.urls import re_path,path
# from django.conf.urls import url
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/room/(?P<room_name>\w+)/(?P<username>\w+)/$',consumers.ChatRoomConsumer.as_asgi()),
]
This is my chat app consumers.py file
# import pytest
from accounts.models import Account
from .models import ChatRoom, Message
from channels.generic.websocket import AsyncWebsocketConsumer
import json
from channels.db import database_sync_to_async
import string
import random
# from asgiref.sync import sync_to_async
class ChatRoomConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name= 'chat_%s'%self.room_name
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self,close_code):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
username = text_data_json['username']
room_name = text_data_json['room_name']
# print("[RECIEVED] "+room_name)
print("[receiving....]"+message)
await self.channel_layer.group_send(
self.room_group_name,
{
'type':'chatroom_message',
'message':message,
'username':username,
'room_name':room_name,
}
)
async def chatroom_message(self,event):
message = event['message']
username = event['username']
room_name = event['room_name']
print("[SENDING....]"+message)
room_obj = ChatRoom.objects.get(room_name=room_name)
sender = Account.objects.get(username=username)
await self.save_message(message=message,sender=sender,room_name=room_obj)
await self.send(text_data=json.dumps({
'message':message,
'username':username,
}))
#database_sync_to_async
def save_message(self,message,sender,room_name):
chat_id = ''.join(random.choices(string.ascii_uppercase+string.digits,k=10))
new_message = Message(message_id=chat_id,message=message,room_name=room_name,message_sender=sender)
new_message.save()
This is my JavaScript
{{room_name|json_script:"room-name"}}
{{username|json_script:"user_username"}}
{% if room_user_1_username %}
{{room_user_1_username|json_script:"room_user_1"}}
{% endif %}
{% if room_user_2_username %}
{{room_user_2_username|json_script:"room_user_2"}}
{% endif %}
<script>
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const username = JSON.parse(document.getElementById('user_username').textContent);
const room_user_1 = JSON.parse(document.getElementById('room_user_1').textContent);
const room_user_2 = JSON.parse(document.getElementById('room_user_2').textContent);
var input_box = document.getElementById("input");
if(input_box.innerHTML==""){
document.getElementById("submit").style.visibility=false;
}
else{
document.getElementById("submit").style.visibility=true;
}
//for sending the message to websocket
document.getElementById("submit").onclick=function(e){
const messageInputDom = document.querySelector("#input");
const message = messageInputDom.value;
console.log("message: "+message);
console.log("username:"+username);
current_user = "{{request.user.username}}";
console.log("current_user: "+current_user);
if(username==room_user_1 || username==room_user_2){
if(messageInputDom.value!=""){
chatSocket.send(JSON.stringify({
'message':message,
'username':username,
'room_name':roomName,
}));
}
messageInputDom.value = '';
}
else{alert("Unauthorised access to a chat room");console.log("Message could not be sent!");}
messageInputDom.value='';
}
//setting up the websocket
const chatSocket = new WebSocket(
'ws://'+
window.location.host+
'/ws/chat/room/'+
roomName+
'/'+
username+
'/'
);
//getting messages from the websocket
chatSocket.onmessage = function(e){
const data = JSON.parse(e.data);
location.reload();//ON GETTING NEW MESSAGES
console.log("data: message->"+data.message+"\nusername->"+data.username);
$("#text-msg").val('\n'+data.username+": "+data.message+' (just now)');
alert(data.message);
}
</script>
It should show something like "CONNECT handshaking" on the cmd

Text not updating on Asyncronous Webpage using flask-websockets

I am trying to get this webpage to update asynchronously using a python flask server and MQTT values coming in from Arduino devices. All values are coming through and getting saved correctly however the values do not show up on the webpage.
I think it might have something to do with trying to write to the id tag using the $('#humidity').text
Here's the relevant HTML:
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
var socket = io.connect('http://' + document.domain + ':' + location.port);
socket.on('connect', function() {
socket.emit('my event', {data: 'I\'m connected!'});
});
socket.on('dht_temperature', function(msg) {
var nDate = new Date();
$('#readingsUpdated').text(nDate.getHours() + 'h:' + nDate.getMinutes() +
'm:' + nDate.getSeconds() + 's').html();
$('#temperature').text(msg.data).html();
});
socket.on('dht_humidity', function(msg) {
$('#humidity').text(msg.data).html();
});
});
</script>
<body>
<h1>RPi Web Server - ESP8266 MQTT</h1>
{% for pin in pins %}
<h2>{{ pins[pin].name }}
{% if pins[pin].state == 'True' %}
is currently <strong>on</strong></h2><div class="row"><div class="col-md-2">
Turn off</div></div>
{% else %}
is currently <strong>off</strong></h2><div class="row"><div class="col-md-2">
Turn on</div></div>
{% endif %}
{% endfor %}
<h3>DHT Readings (updated <span id="readingsUpdated"></span>)</h3>
<h3>Temperature: <span id="temperature"></span>ÂșC</h3>
<h3>Humidity: <span id="humidity"></span>%</h3>
</body>
</html>
And here is the Python Script
import paho.mqtt.client as mqtt
from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("/esp8266/temperature")
client.subscribe("/esp8266/humidity")
# The callback for when a PUBLISH message is received from the ESP8266.
def on_message(client, userdata, message):
#socketio.emit('my variable')
print("Received message '" + str(message.payload) + "' on topic '"
+ message.topic + "' with QoS " + str(message.qos))
if message.topic == "/esp8266/temperature":
print("temperature update")
socketio.emit('dht_temperature', {'data': message.payload})
if message.topic == "/esp8266/humidity":
print("humidity update")
socketio.emit('dht_humidity', {'data': message.payload})
mqttc=mqtt.Client()
mqttc.on_connect = on_connect
mqttc.on_message = on_message
mqttc.connect("localhost",1883,60)
mqttc.loop_start()
# Create a dictionary called pins to store the pin number, name, and pin state:
pins = {
4 : {'name' : 'GPIO 4', 'board' : 'esp8266', 'topic' : 'esp8266/4', 'state' : 'False'},
5 : {'name' : 'GPIO 5', 'board' : 'esp8266', 'topic' : 'esp8266/5', 'state' : 'False'}
}
# Put the pin dictionary into the template data dictionary:
templateData = {
'pins' : pins
}
#app.route("/")
def main():
# Pass the template data into the template main.html and return it to the user
return render_template('main.html', async_mode=socketio.async_mode, **templateData)
# The function below is executed when someone requests a URL with the pin number and action in it:
#app.route("/<board>/<changePin>/<action>")
def action(board, changePin, action):
# Convert the pin from the URL into an integer:
changePin = int(changePin)
# Get the device name for the pin being changed:
devicePin = pins[changePin]['name']
# If the action part of the URL is "1" execute the code indented below:
if action == "1" and board == 'esp8266':
mqttc.publish(pins[changePin]['topic'],"1")
pins[changePin]['state'] = 'True'
if action == "0" and board == 'esp8266':
mqttc.publish(pins[changePin]['topic'],"0")
pins[changePin]['state'] = 'False'
# Along with the pin dictionary, put the message into the template data dictionary:
templateData = {
'pins' : pins
}
return render_template('main.html', **templateData)
#socketio.on('my event')
def handle_my_custom_event(json):
print('received json data here: ' + str(json))
if __name__ == "__main__":
socketio.run(app, host='0.0.0.0', port=8181, debug=True)

Why is my websocket request "unauthorized" only when written in OOP, when the same works perfectly when written with just functions?

I'm writing a Python program that does some trading automation. The API I work with is from Deribit, whose preferred transport mechanism is Websocket. I'm a complete newbie to Python's websockets and asyncio modules.
Here's the code I first wrote for authenticating my client and then sending a separate private message to get an order position from the account, written only with functions and no classes:
import asyncio
import websockets
import json
CL_ID = 'qxv0EeAu'
CL_SECRET = 't24F49ocH1_qFawiKnEyqlWF5D-haABb31O8xCQhySg'
REQ_URL = 'wss://test.deribit.com/ws/api/v2'
acc_token = ''
msg = {
"jsonrpc": "2.0",
"id": 1,
"params": {}
}
async def auth_api():
global msg
global acc_token
msg["method"] = "public/auth"
msg["params"] = {
"grant_type": "client_credentials",
"client_id": CL_ID,
"client_secret": CL_SECRET,
"scope": "session:test"
}
async with websockets.connect(REQ_URL) as websocket:
await websocket.send(json.dumps(msg))
while websocket.open:
response = await websocket.recv()
response_json = json.loads(response)
acc_token = response_json["result"]["access_token"]
return
async def get_position(websocket, instrument):
global msg
global acc_token
msg["id"] += 1
msg["method"] = "private/get_position"
msg["params"] = {
"access_token": acc_token,
"instrument_name": instrument
}
await websocket.send(json.dumps(msg))
while websocket.open:
response = await websocket.recv()
return response
async def main():
global msg
await auth_api()
async with websockets.connect(REQ_URL) as websocket:
response = await get_position(websocket, "BTC-PERPETUAL")
print(response)
asyncio.get_event_loop().run_until_complete(main())
It works perfectly fine. Here's my result:
{"jsonrpc":"2.0","id":2,"result":{"total_profit_loss":0.000209124,"size_currency":-0.017402402,"size":-150.0,"settlement_price":8649.9,"realized_profit_loss":2.67e-7,"open_orders_margin":0.0,"mark_price":8619.5,"maintenance_margin":0.000100079,"leverage":100,"kind":"future","instrument_name":"BTC-PERPETUAL","initial_margin":0.000174039,"index_price":8619.45,"floating_profit_loss":0.000061161,"estimated_liquidation_price":-14.95,"direction":"sell","delta":-0.017402402,"average_price":8724.34},"usIn":1573756522511975,"usOut":1573756522512240,"usDiff":265,"testnet":true}
I decided to rewrite it the OOP way, and here's the class I created (the file is named "Call_Deribit"):
import asyncio, websockets, json
class WSClient():
def __init__(self, key=None, secret=None, url=None):
self.api_key = key
self.api_secret = secret
self.msg = {
"jsonrpc": "2.0",
"id": 0
}
if url:
self.host = url
else:
self.host = 'wss://test.deribit.com/ws/api/v2'
async def call_api(self, msg):
async with websockets.connect(self.host) as websocket:
print("Connected to URL:", self.host)
try:
await websocket.send(msg)
while websocket.open:
response = await websocket.recv()
response_json = json.loads(response)
return response_json
except Exception as e:
return e
def request(self, method, params, session=None):
msg = self.msg
msg["id"] += 1
msg["method"] = method
msg["params"] = params
if session != None:
msg["params"]["scope": "session:{}".format(session)]
return asyncio.get_event_loop().run_until_complete(self.call_api(json.dumps(msg)))
def get_order_book(self, instrument):
method = "public/get_order_book"
params = {
"instrument_name": instrument
}
return self.request(method, params)
And here's the main file I'm accessing the class from and where I make all the requests:
import json, asyncio, websockets
from Call_Deribit import WSClient
CL_ID = 'qxv0EeAu'
CL_SECRET = 't24F49ocH1_qFawiKnEyqlWF5D-haABb31O8xCQhySg'
REQ_URL = 'wss://test.deribit.com/ws/api/v2'
method_auth = "public/auth"
params_auth = {
"grant_type": "client_credentials",
"client_id": CL_ID,
"client_secret": CL_SECRET
}
main_client = WSClient(key=CL_ID, secret=CL_SECRET, url=REQ_URL)
auth_response = main_client.request(method_auth, params_auth)
acc_token = auth_response["result"]["access_token"]
method_pos = "private/get_position"
params_pos = {
"access_token": acc_token,
"instrument_name": "BTC-PERPETUAL"
}
position = main_client.request(method_pos, params_pos)
print(position)
The first request for authentication is working this time, and I'm able to extract the access token as well, but the second private/get_position message is, for whatever reason, returning an unauthorized error.
{'jsonrpc': '2.0', 'id': 1, 'error': {'message': 'unauthorized', 'code': 13009}, 'testnet': True, 'usIn': 1573756936534405, 'usOut': 1573756936534629, 'usDiff': 224}
I've spent hours on it, and I seem to be doing exactly the same thing in the OOP version as I did on the original one. My familiarity with OOP and its concepts (such as inheritance) is limited, so I'd like to know what I'm missing here, and why my code isn't working in the OOP version, despite following the same exact workflow as in the original version.
Here's the documentation for the Deribit API: https://docs.deribit.com/v2/?python#json-rpc
Any help would be greatly appreciated.
Adding the scope under params_auth in the main file works:
params_auth = {
"grant_type": "client_credentials",
"client_id": CL_ID,
"client_secret": CL_SECRET,
"scope": "session:test"
}

flask socketio session query string

this code is supposed to work, but it does not work
As soon as a client connects, the session 'keyo' is assigned to the value 'example'
I tried to make an equivalent of socket.myvariable with session['myvariable']
#app.route("/a")
def helljo():
return render_template('index.html')
#socketio.on('connect')
def handleMessagae():
session['keyo'] = request.args.get('session') # /a?session=example
emit('connected', session.get('keyo'))
#socketio.on('message')
def handleMessage(message):
emit('message', {'pseudo':session.get('keyo'),'message':message} , broadcast=True, include_self=False)
The correct way to do what you want is:
#app.route("/a")
def helljo():
session['keyo'] = request.args.get('session') # /a?session=example
return render_template('index.html')
#socketio.on('connect')
def handleMessagae():
emit('connected', session.get('keyo'))
#socketio.on('message')
def handleMessage(message):
emit('message', {'pseudo':session.get('keyo'),'message':message} , broadcast=True, include_self=False)
The problem is that you are mixing up the HTTP request with the Socket.IO requests. If you invoke the /a endpoint with some query string args, you can only access those arguments in the handler for that endpoint. But saving them in the session makes them accessible later by the Socket.IO event handlers.
#app.route("/a", methods=['GET'])
def helljo():
session['keyo'] = request.args.get('session')
socketio.emit('EVENT', {'var': session.get('keyo')})
return render_template('index.html')
#socketio.on('connect')
def handleMessagae():
emit('connected', session.get('keyo'))
#socketio.on('message')
def handleMessage(message):
emit('message', {'pseudo':session.get('keyo'),'message':message} , broadcast=True, include_self=False)
The problem here is, that you're trying to get request arguments within a socketio event, this isn't going to work as request arguments are only accessible inside of an #app.route() function.
the fourth line of this code sends to all clients except to sender of the request. I would like to send it only to sender
#app.route("/a", methods=['GET'])
def helljo():
session['keyo'] = request.args.get('session')
socketio.emit('alert_sender', {'resp': session.get('keyo')})
return render_template('index.html')
#socketio.on('connect')
def handleMessagae():
emit('connected', 'hi')
#socketio.on('message')
def handleMessage(message):
emit('message', {'pseudo':session.get('keyo'),'message':message} , broadcast=True, include_self=False)
on index.html (when I go on /a?session=example)
...
socket.on('alert_sender', function(data) {
alert(data.resp);
// others receive alert : example sender receive : nothing
});
...
The Alternative Solution :
#app.route("/a", methods=['GET'])
def index():
resp = request.args.get('session')
return render_template( 'index.html' , resp=resp )
#socketio.on('My_Event')
def message(msg):
print(msg)
on index.html
<script>
// ...
{% if resp %}
variable = '{{ resp }}';
{% else %}
variable = 'null';
{% endif %}
socket.emit( 'My_Event' , variable );
// ...
</script>

Flask streaming SSEs closes connection after every event

I am using the following code to implement the backend for a javascript EventSource
from flask import Flask, Response
from time import sleep
import time
class ServerSentEvent(object):
def __init__(self, data):
self.data = data
self.event = None
self.id = None
self.desc_map = {
self.data : "data",
self.event : "event",
self.id : "id",
self.retry: 500
}
def encode(self):
if not self.data:
return ""
lines = ["%s: %s" % (v, k)
for k, v in self.desc_map.items() if k]
return "%s\n\n" % "\n".join(lines)
def stream():
while True:
ev = ServerSentEvent('hi ' + str(int(round(time.time()))))
yield ev
sleep(0.1)
app = Flask(__name__)
#app.route("/events")
def streamSessionEvents():
return Response(stream(), mimetype="text/event-stream")
#app.route("/")
def index():
template = """
<!doctype html>
<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
var source = new EventSource('/events')
source.onmessage = function (event) {
$('#log').append(event.data + '</br>')
}
</script>
</head>
<body>
<div id="log"></div>
</body>
</html>
"""
return(template)
app.run(threaded=True)
However the EventSource keeps reconnecting every 3 seconds (which is the default) because the connection is closed by the server after every event. How can I establish a continuous connection?

Categories