I run a twisted server and It works fine with chrome but in firefox I can't push anything to client. I can post data but when I call request.write nothing happen in firefox!
the code is:
from twisted.web import resource, server, http
from twisted.internet import reactor
import random
# iframe closing tags are apparently necessary.
tophtml = """<html><head><title>Chat channel</title>
<style>
iframe { border: 0 }
</style>
</head><body>
<h1>HTML chat demo</h1>
<p>Inspired by Ka-Ping Yee's awesome <a href="http://zesty.ca/chat/";
>libdynim GIF chat app</a>.</p>
<!-- Like it, this uses no Java, DHTML, or reloading; unlike it,
this uses frames and no JavaScript (Ping's demo works with or without
JavaScript, but without JavaScript, it reloads the page when you speak.) -->
<iframe width="100%%" height="50" src="?frame=form;sess_id=%(sess_id)s">
</iframe>
<iframe width="100%%" height="300" src="?frame=output;sess_id=%(sess_id)s">
</iframe>
</body></html>
"""
formhtml = """<html><head><title>Chat post form</title></head><body>
<form method="POST">
<input name="sess_id" type="hidden" value="%(sess_id)s" />
to: <input name="destination" />
say: <input name="chatline" size="80" />
<input type="submit" value="send" />
</form>
</html>
"""
class ChatSession:
"A persistent connection to a user's browser."
def __init__(self, channel, request, sess_id):
(self.channel, self.request, self.sess_id) = (channel, request, sess_id)
self.deferred = request.notifyFinish()
self.deferred.addCallback(self.stop)
self.deferred.addErrback(self.stop)
def stop(self, reason):
"Called when the request finishes to close the channel."
print "%s stopping: %s" % (self.sess_id, reason)
self.channel.delete_session(self.sess_id)
def sendmsg(self, origin, msg):
"Display a chat message to the user."
self.request.write("""<div>
<%(origin)s> %(msg)s </div>
""" % {'origin': origin, 'msg': msg})
class ChatChannel(resource.Resource):
"A resource representing a chat room, plus private messages."
isLeaf = True
def __init__(self):
resource.Resource.__init__(self) # ??? necessary??
self.sessions = {}
def render_GET(self, request):
"Handle HTTP GET requests by dispatching on 'frame' arg."
if request.args.has_key('frame'):
frame = request.args['frame'][0]
if frame == 'form': return self.render_form(request)
elif frame == 'output': return self.do_chat_output(request)
sess_id = random.randrange(1000) # not secure, like everything here
return tophtml % {'sess_id': sess_id}
def render_form(self, request):
"The form used for posting."
return formhtml % {'sess_id': request.args['sess_id'][0]}
def do_chat_output(self, request):
"Open a persistent ChatSession."
sess_id = request.args['sess_id'][0]
# Note that this may remove a previous ChatSession from
# self.sessions:
self.sessions[sess_id] = ChatSession(self, request, sess_id)
# The next line is a hack due to Donovan Preston: increases
# browsers' per-server connection limit, which is normally 2
# if the server seems to support HTTP 1.1 connection
# keepalive, to 8.
request.setHeader('connection', 'close')
request.write("<html><head><title>session %s</title><body>\n" % sess_id)
return server.NOT_DONE_YET
def render_POST(self, request):
"Send back 204 No Content to an utterance of a chat line."
def arg(name):
return request.args[name][0]
self.handle_chatline(arg('destination'), arg('sess_id'),
arg('chatline'))
request.setResponseCode(http.NO_CONTENT)
return ""
def handle_chatline(self, dest, sess_id, chatline):
"Send a chat line from a source to a destination, '' meaning 'all'."
try:
if dest:
self.sessions[dest].sendmsg(sess_id, '(private) ' + chatline)
self.sessions[sess_id].sendmsg('server', 'private message sent')
else:
for session in self.sessions.values():
session.sendmsg(sess_id, chatline)
except Exception, e:
self.sessions[sess_id].sendmsg('error', str(e))
def delete_session(self, sess_id):
"Delete a session by ID --- if it's there."
try: del self.sessions[sess_id]
except KeyError: pass
if __name__ == '__main__':
port = 8086
reactor.listenTCP(port, server.Site(ChatChannel()))
print "ok, running on port", port
reactor.run()
what is the problem?
Related
I want to make a python code that browsers can connect to get a video stream.
Problem is that my python code handles only one time use, meaning if you open the browser it will connect to that websocket correctly, but if you refresh the page, or another client want to access a stream from in parallel it doesn't work
My code:
import asyncio
import websockets
import cv2
import os
import signal
async def time1(websocket, path):
while True:
vid = cv2.VideoCapture('V_DRONE_097.mp4')
vid.set(cv2.CAP_PROP_FPS,24)
try:
#v = VideoStreamWidget()
#v.ops()
while(vid.isOpened()):
img, frame = vid.read()
#frame = cv2.resize(frame, (640, 480))
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 65]
man = cv2.imencode('.jpg', frame, encode_param)[1]
#sender(man)
#print(len(man.tobytes()))
#cv2.imshow('img',man)
await websocket.send(man.tobytes())
except :
pass
async def main():
loop = asyncio.get_running_loop()
stop = loop.create_future()
#loop.add_signal_handler(signal.SIGTERM, stop.set_result, None)
port = int(os.environ.get("PORT", "8585"))
global stop1
async with websockets.serve(time1, "", port):
await stop
stop1 = False
'''
async def main():
async with websockets.serve(time1, "localhost", 8585):
await asyncio.Future() # run forever
asyncio.run(main())
'''
if __name__ == "__main__":
while True:
asyncio.run(main())
I want the server to keep working even if the client (browser) refreshes the page or close it and comeback later.
This is the code of javascript running in the browser if that will help:
openSocket = () => {
socket = new WebSocket("ws://127.0.0.1:8585/");
let msg = document.getElementById("msg");
socket.addEventListener('open', (e) => {
document.getElementById("status").innerHTML = "Opened";
});
socket.addEventListener('message', (e) => {
let ctx = msg.getContext("2d");
let image = new Image();
image.src = URL.createObjectURL(e.data);
image.addEventListener("load", (e) => {
ctx.drawImage(image, 0, 0, msg.width, msg.height);
});
});
}
and this is the HTML code:
<!DOCTYPE html>
<html>
<head>
<title>Python_Websocket_Live_Streaming</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="script.js" ></script>
</head>
<body onload="openSocket()">
<div id="status">
Connection failed. Somebody may be using the socket.
</div>
<div style="text-align: center">
<canvas id="msg" width="960" height="720" style="display:inline-block" />
</div>
</body>
</html>
I've searched and tried many solutions but non worked.
My final goal is to integrate this with flask API
Update 1:
yes it is stuck in the time1 function
I've known from wrapping the "send" function in a try catch:
try:
await websocket.send(man.tobytes())
except websockets.exceptions.ConnectionClosedError:
print("clean up")
break
when the browser closes/refresh the page, clean up is printed infinitely
Edit 2
It was stuck in the outer loop, that while true loop. Solved now Thanks
There are a few culprits, but ultimately I think that while(vid.isOpened()) prevents the async from ever being free
edit
or stuck in the outer while loop
hi i need to display total number of connected clients on my flask app i write this code for checking connected and disconnected connections.
app = Flask(__name__)
socketio = SocketIO(app)
clients = []
#socketio.on('connect', namespace='/')
def connect():
clients.append(request.namespace)
#socketio.on('disconnect', namespace='/')
def disconnect():
clients.remove(request.namespace)
then i render template like this
return render_template_string(TABLE_TEMPLATE, data=data, clients=len(clients))
In html part i call like this
<h1>{{ clients }} </h1>
but on webpage it keep showing 0 even client is connect i get output from client and it is connected it should print 1 2 depends how many clients are connected. even if i print this print(len(clients)) it return 0. even my client is connect and i get output.
this is my updated code
from flask import Flask, request, render_template_string
from flask_socketio import SocketIO, emit
app = Flask(__name__)
socketio = SocketIO(app, logge=True)
clients = 0
#socketio.on("connect", namespace="/")
def connect():
# global variable as it needs to be shared
global clients
clients += 1
# emits a message with the user count anytime someone connects
emit("users", {"user_count": clients}, broadcast=True)
#socketio.on("disconnect", namespace="/")
def disconnect():
global clients
clients -= 1
emit("users", {"user_count": clients}, broadcast=True)
TABLE_TEMPLATE = """
<script
src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.3/socket.io.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
$(document).ready(function(){
var namespace = '/';
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
// Update the counter when a new user connects
socket.on('users', function(users) {
userCount = document.getElementById('user_counter');
userCount.innerHTML = users.user_count;
});
});
</script>
<h1 id='user_counter'></h1>
<style>
table, th, td {
border: 1px solid black;
}
</style>
<table style="width: 100%">
<thead>
<th>Client</th>
<th>IP</th>
<th>Status</th>
</thead>
<tbody>
{% for row in data %}
<tr>
<td><center>{{ row.client }}</td></center>
<td><center>{{ row.ip }}</td></center>
<td><center>{{ row.status }}</td></center>
</tr>
{% endfor %}
</tbody>
</table>
"""
#app.route("/device_add", methods=['POST'])
def device_add():
name = request.args.get('name')
with open('logs.log', 'a') as f:
f.write(f'{name} Connected USB from IP: {request.remote_addr} \n')
return 'ok'
#app.route("/device_remove", methods=['POST'])
def device_remove():
name = request.args.get('name')
with open('logs.log', 'a') as f:
f.write(f'{name} Disconnected USB from IP: {request.remote_addr}\n')
return 'ok'
#app.route("/", methods=['GET'])
def device_list():
keys = ['client', 'ip', 'status']
data = []
with open('logs.log', 'r') as f:
for line in f:
row = line.split()
data.append(dict(zip(keys, [row[0], row[-1], row[1]])))
return render_template_string(TABLE_TEMPLATE, data=data)
if __name__ == "__main__":
socketio.run(app)
Client Side :
import requests
import subprocess, string, time
import os
url = 'http://127.0.0.1:5000/'
name = os.uname()[1]
def on_device_add():
requests.post(f'{url}/device_add?name={name}')
def on_device_remove():
requests.post(f'{url}/device_remove?name={name}')
def detect_device(previous):
total = subprocess.run('lsblk | grep disk | wc -l', shell=True, stdout=subprocess.PIPE).stdout
time.sleep(3)
# if condition if new device add
if total > previous:
on_device_add()
# if no new device add or remove
elif total == previous:
detect_device(previous)
# if device remove
else:
on_device_remove()
# Infinite loop to keep client running.
while True:
detect_device(subprocess.run(' lsblk | grep disk | wc -l', shell=True , stdout=subprocess.PIPE).stdout)
After reading a bit of socket.io documentation I've managed to spot the problems in your code.
Not a problem per-se, but incrementing/decrementing an int counter is more than enough for this use case. Secondly, you don't have to pass that counter to the render_template call as you're basically passing the user count before the conenct event has had the opportunity to fire. You should emit a message (in this example with a users topic) that will inform your page that something has changed:
from flask import Flask, request, render_template_string
from flask_socketio import SocketIO, emit
app = Flask(__name__)
socketio = SocketIO(app, logge=True)
clients = 0
#socketio.on("connect", namespace="/")
def connect():
# global variable as it needs to be shared
global clients
clients += 1
# emits a message with the user count anytime someone connects
emit("users", {"user_count": clients}, broadcast=True)
#socketio.on("disconnect", namespace="/")
def disconnect():
global clients
clients -= 1
emit("users", {"user_count": clients}, broadcast=True)
Moreover, you didn't open a connection to the socket in your template, this allows you to listen to the messages emitted by your socketio decorators and update all connected clients. You will also need to write a bit of javascript to specify that the counter needs to be updated anytime a user connects/disconnects.
<!-- Remember to import socketio library -->
<script
src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.3/socket.io.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
$(document).ready(function(){
var namespace = '/';
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
// Update the counter when a new user connects
socket.on('users', function(users) {
userCount = document.getElementById('user_counter');
userCount.innerHTML = users.user_count;
});
});
</script>
<h1 id='user_counter'></h1>
<!-- the rest of your template -->
This being said, you don't need to pass the counter value in your render_template call.
Also, from flask-socketio docs, it seems to be good practice to start your app in the following way:
if __name__ == "__main__":
socketio.run(app)
Here a link to an edited version of your example.
To give you an idea of what I am trying to accomplish with Twisted Web and Autobahn websockets: my UI currently sends an initial HTTP GET request with an upgrade to a websocket in the header. Upon reading that in Twisted Web, the connection needs to switch from HTTP to a websocket protocol to pass data back and forth. Note that this websocket upgrade happens on the same port, port 8000.
Does anyone know how I can implement what I am trying to do? Thank you so much.
EDIT: updated code for working example. You can find it here: Payload from POST Request is Cutoff (Twisted Web)
Here is my code using Twisted Web:
class HttpResource(resource.Resource):
isLeaf = 1
def __init__(self):
self.children = {}
self.ws_port = None
print 'resource invoked'
def render_GET(self, request):
print 'render invoked'
if request.getHeader('Sec-WebSocket-Key'):
# Processing the Key as per RFC 6455
key = request.getHeader('Sec-WebSocket-Key')
h = hashlib.sha1(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
request.setHeader('Sec-WebSocket-Accept', base64.b64encode(h.digest()))
# setting response headers
request.setHeader('Upgrade', 'websocket')
request.setHeader('Connection', 'Upgrade')
request.setResponseCode(101)
return ''
else:
log("Regular HTTP GET request.")
return "<html><body style='margin: 0; overflow: hidden;'><iframe style='width: 100%; height: 100%; border: none;' src='http://tsa-graphiql.herokuapp.com/'></iframe></body></html>"
def render_POST(self,request):
log("POST request")
request.setResponseCode(200)
def handle_single_query(self, queryData):
log("Handle single query data.")
return
class HttpWsChannel(http.HTTPChannel):
def dataReceived(self, data):
log('Data received:\n{}'.format(data))
if data.startswith('GET'):
# This will invoke the render method of resource provided
http.HTTPChannel.dataReceived(self, data)
if data.startswith('POST'):
http.HTTPChannel.dataReceived(self, data)
else:
"""
Pass binary data to websocket class.
"""
ws_protocol = self.site.ws_factory.protocol(self.site.ws_factory.connection_subscriptions)
log(ws_protocol)
#just echo for now
# self.transport.write(data)
class HttpFactory(Site):
"""
Factory which takes care of tracking which protocol
instances or request instances are responsible for which
named response channels, so incoming messages can be
routed appropriately.
"""
def __init__(self, resource):
http.HTTPFactory.__init__(self)
self.resource = resource
self.ws_factory = WsProtocolFactory("ws://127.0.0.1:8000")
self.ws_factory.protocol = WsProtocol
def buildProtocol(self, addr):
try:
channel = HttpWsChannel()
channel.requestFactory = self.requestFactory
channel.site = self
return channel
except Exception as e:
log("Could not build protocol: {}".format(e))
site = HttpFactory(HttpResource())
if __name__ == '__main__':
reactor.listenTCP(8000, site)
reactor.run()
EDIT 7/8/2017: Here is the new code I am trying below. The websocket messages are received successfully via the onMessage method. However the HTTP requests are not working. The current error I am getting on a GET request is:
<html>
<head><title>404 - No Such Resource</title></head>
<body>
<h1>No Such Resource</h1>
<p>No such child resource.</p>
</body>
</html>
Python code
from twisted.web.server import (
Site,
)
from twisted.internet import reactor
from twisted.web.resource import (
Resource,
)
from autobahn.twisted.websocket import (
WebSocketServerProtocol,
WebSocketServerFactory,
)
from autobahn.twisted.resource import (
WebSocketResource,
)
class WebSocketProtocol(WebSocketServerProtocol):
def onConnect(self, request):
print("WebSocket connection request: {}".format(request))
def onMessage(self, payload, isBinary):
print("onMessage: {}".format(payload))
if __name__ == '__main__':
factory = WebSocketServerFactory()
factory.protocol = WebSocketProtocol
resource = WebSocketResource(factory)
root = Resource()
root.putChild(b"ws", resource)
site = Site(root)
reactor.listenTCP(8000, site)
reactor.run()
Use WebSocketResource to expose a WebSocketServerFactory as part of a Site.
from twisted.web.server import (
Site,
)
from twisted.web.resource import (
Resource,
)
from autobahn.twisted.websocket import (
WebSocketServerProtocol,
WebSocketServerFactory,
)
from autobahn.twisted.resource import (
WebSocketResource,
)
class YourAppProtocol(WebSocketServerProtocol):
def onConnect(self, request):
...
...
def main():
factory = WebSocketRendezvousFactory()
factory.protocol = YourAppProtocol
resource = WebSocketResource(factory)
root = Resource()
root.putChild(b"some-path-segment", resource)
root.putChild(...)
site = Site(root)
reactor.listenTCP(8080, site)
reactor.run()
The problems with truncated request bodies is probably because your upgrade protocol implementation is buggy. There is no framing applied at the level of dataReceived so you can't expect checks like startswith("GET") to be reliable.
Using WebSocketResource and Site gives you the correct HTTP parsing code from Twisted Web and Autobahn while also allowing you to speak WebSocket to a particular URL and regular HTTP to others.
So after reading a little bit on Google, I found this website that explains how to upgrade the HTTP connection to a websocket connection via Autobahn Twisted: Read and Set request headers via Autobahn Twisted.
The code that I was able to get to work is shown below!
from twisted.web.server import (
Site,
)
from twisted.internet import reactor
from twisted.web.resource import (
Resource,
)
from autobahn.twisted.websocket import (
WebSocketServerProtocol,
WebSocketServerFactory,
)
from autobahn.twisted.resource import (
WebSocketResource,
)
class HttpResource(Resource):
isLeaf = True
def render_GET(self, request):
return "<html><body style='margin: 0; overflow: hidden;'><iframe style='width: 100%; height: 100%; border: none;' src='http://tsa-graphiql.herokuapp.com/'></iframe></body></html>"
class WebSocketProtocol(WebSocketServerProtocol):
def onConnect(self, request):
custom_header = {}
if request.headers['sec-websocket-key']:
custom_header['sec-websocket-protocol'] = 'graphql-ws'
return (None, custom_header)
def onMessage(self, payload, isBinary):
print("onMessage: {}".format(payload))
if __name__ == '__main__':
factory = WebSocketServerFactory()
factory.protocol = WebSocketProtocol
resource = WebSocketResource(factory)
root = Resource()
root.putChild("", HttpResource())
root.putChild(b"ws", ws_resource)
site = Site(root)
reactor.listenTCP(8000, site)
I'm creating a web application in python using tornado web server. I'm using Motor for mongo db connection with tornado web server.
i'm referring to tornado motor docs.
in my application i'm uploading data from csv or other data sources and doing some machine learning operations. all the meta data and other required details have to be stored in mongo DB. Web page is a little heavy(as it has a lot of visualizations other operations).
Some examples i tried for learning motor
Example of call back:
import tornado.web, tornado.ioloop
import motor
class NewMessageHandler(tornado.web.RequestHandler):
def get(self):
"""Show a 'compose message' form."""
self.write('''
<form method="post">
<input type="text" name="msg">
<input type="submit">
</form>''')
# Method exits before the HTTP request completes, thus "asynchronous"
#tornado.web.asynchronous
def post(self):
"""Insert a message."""
msg = self.get_argument('msg')
# Async insert; callback is executed when insert completes
self.settings['db'].messages.insert(
{'msg': msg},
callback=self._on_response)
def _on_response(self, result, error):
if error:
raise tornado.web.HTTPError(500, error)
else:
self.redirect('/')
class MessagesHandler(tornado.web.RequestHandler):
#tornado.web.asynchronous
def get(self):
"""Display all messages."""
self.write('Compose a message<br>')
self.write('<ul>')
db = self.settings['db']
db.messages.find().sort([('_id', -1)]).each(self._got_message)
def _got_message(self, message, error):
if error:
raise tornado.web.HTTPError(500, error)
elif message:
self.write('<li>%s</li>' % message['msg'])
else:
# Iteration complete
self.write('</ul>')
self.finish()
db = motor.MotorClient().test
application = tornado.web.Application(
[
(r'/compose', NewMessageHandler),
(r'/', MessagesHandler)
],
db=db
)
#print 'Listening on http://localhost:8888'
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Example of coroutines:
from tornado import gen
import tornado.web, tornado.ioloop
import motor
class NewMessageHandler(tornado.web.RequestHandler):
def get(self):
"""Show a 'compose message' form."""
self.write('''
<form method="post">
<input type="text" name="msg">
<input type="submit">
</form>''')
#tornado.web.asynchronous
#gen.coroutine
def post(self):
"""Insert a message."""
msg = self.get_argument('msg')
db = self.settings['db']
# insert() returns a Future. Yield the Future to get the result.
result = yield db.messages.insert({'msg': msg})
# Success
self.redirect('/')
class MessagesHandler(tornado.web.RequestHandler):
#tornado.web.asynchronous
#gen.coroutine
def get(self):
"""Display all messages."""
self.write('Compose a message<br>')
self.write('<ul>')
db = self.settings['db']
cursor = db.messages.find().sort([('_id', -1)])
while (yield cursor.fetch_next):
message = cursor.next_object()
self.write('<li>%s</li>' % message['msg'])
# Iteration complete
self.write('</ul>')
self.finish()
db = motor.MotorClient().test
application = tornado.web.Application(
[
(r'/compose', NewMessageHandler),
(r'/', MessagesHandler)
],
db=db
)
print 'Listening on http://localhost:8881'
application.listen(8881)
tornado.ioloop.IOLoop.instance().start()
How do i compare the benefits or performance of each example ? Both example do the same functionality.
What should i use ? callbacks, co-routines, generators ? I need good performance and flexibility in my application. there are some examples in this link also https://motor.readthedocs.org/en/stable/tutorial.html
I tried this:
#!/usr/bin/python
from wsgiref.simple_server import make_server
from cgi import parse_qs, escape
import logging
import os
import sys
html = """
<html>
<body>
<form method="post" action="parsing_post.wsgi">
<p>
Age: <input type="text" name="age">
</p>
<p>
Hobbies:
<input name="hobbies" type="checkbox" value="software"> Software
<input name="hobbies" type="checkbox" value="tunning"> Auto Tunning
</p>
<p>
<input type="submit" value="Submit">
</p>
</form>
<p>
Age: %s<br>
Hobbies: %s
</p>
</body>
</html>
"""
def application(environ, start_response):
# the environment variable CONTENT_LENGTH may be empty or missing
try:
request_body_size = int(environ.get('CONTENT_LENGTH', 0))
except (ValueError):
request_body_size = 0
# When the method is POST the query string will be sent
# in the HTTP request body which is passed by the WSGI server
# in the file like wsgi.input environment variable.
logger = logging.getLogger(__name__)
request_body = environ['wsgi.input'].read(request_body_size)
d = parse_qs(request_body)
age = d.get('age', [''])[0] # Returns the first age value.
hobbies = d.get('hobbies', []) # Returns a list of hobbies.
# Always escape user input to avoid script injection
age = escape(age)
hobbies = [escape(hobby) for hobby in hobbies]
response_body = html % (age or 'Empty',
', '.join(hobbies or ['No Hobbies']))
status = '200 OK'
response_headers = [('Content-Type', 'text/html'),
('Content-Length', str(len(response_body)))]
start_response(status, response_headers)
return [response_body]
But i don't know where it logs. I'm trying to display/log the value on webpage or in a file /var/log/apache2/myapp.log
What's the best way to do this?
Any answer will be highly appreciated.
Note that the above code won't actually produce any log whatsoever since your not calling any of the logger.log() variants - but I guess that's not the point.
If you're running your code with apache/mod_wsgi, the simplest solution is to configure your logger(s) to log to sys.stderr using a StreamHandler (cf http://docs.python.org/howto/logging.html#configuring-logging), and define the error log path, name and level in your apache conf (beware, the default apache behaviour is to only log "error level" message).