Flask - Stream content keeping context - javascript

I am using the folllowing Flask code to stream the output of a command:
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
...
# some logic to get cmd from POST request
...
return redirect_to(url_to(stream, cmd=cmd))
return render_template('index.html')
#app.route('/stream/<cmd>')
def stream(cmd):
print("Executing %s" % cmd)
g = proc.Group()
p = g.run(cmd)
def stream_cmd():
while g.is_pending():
lines = g.readlines()
for proc, line in lines:
print(line)
yield line + '</br>'
return Response(stream_cmd(), mimetype='text/html') # text/html is required for most browsers to show th$
When I post my form, it redirects to a blank page where I see the output of my stream but I loose all my layout / css / html / etc ...
How can I keep the current layout in place while still seeing a streamed output ?
Ideally, I'd like to be able to update a <div> element in the current page (instead of redirect) with the stream output dynamically (Jquery), but I'm not sure that's even possible.

Following #reptilicus recommendation, I rewrote the code to use websockets.
Here is the working code:
Python
#socketio.on('message', namespace='/stream')
def stream(cmd):
# Streams output of a command
from shelljob import proc
g = proc.Group()
p = g.run(cmd)
while g.is_pending():
lines = g.readlines()
for proc, line in lines:
send(line, namespace='/stream')
eventlet.sleep(0) # THIS IS MANDATORY
The corresponding JavaScript that receives those messages sent by send calls is as follow:
JavaScript (JQuery)
var socket = io.connect('http://' + document.domain + ':' + location.port + '/stream')
socket.on('message', function(msg){
$('#streaming_text').append(msg);
})
...
jqxhr.done(function(cmd){ # This is executed after an AJAX post, but you can change this to whatever event you like
socket.send(cmd)
return false;
})

Related

Why does client.recv(1024) return an empty byte literal in this bare-bones WebSocket Server implementation?

I need a web socket client server exchange between Python and JavaScript on an air-gapped network, so I'm limited to what I can read and type up (believe me I'd love to be able to run pip install websockets). Here's a bare-bones RFC 6455 WebSocket client-server relationship between Python and JavaScript. Below the code, I'll pinpoint a specific issue with client.recv(1024) returning an empty byte literal, causing the WebSocket Server implementation to abort the connection.
Client:
<script>
const message = {
name: "ping",
data: 0
}
const socket = new WebSocket("ws://localhost:8000")
socket.addEventListener("open", (event) => {
console.log("socket connected to server")
socket.send(JSON.stringify(message))
})
socket.addEventListener("message", (event) => {
console.log("message from socket server:", JSON.parse(event))
})
</script>
Server, found here (minimal implementation of RFC 6455):
import array
import time
import socket
import hashlib
import sys
from select import select
import re
import logging
from threading import Thread
import signal
from base64 import b64encode
class WebSocket(object):
handshake = (
"HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
"Upgrade: WebSocket\r\n"
"Connection: Upgrade\r\n"
"WebSocket-Origin: %(origin)s\r\n"
"WebSocket-Location: ws://%(bind)s:%(port)s/\r\n"
"Sec-Websocket-Accept: %(accept)s\r\n"
"Sec-Websocket-Origin: %(origin)s\r\n"
"Sec-Websocket-Location: ws://%(bind)s:%(port)s/\r\n"
"\r\n"
)
def __init__(self, client, server):
self.client = client
self.server = server
self.handshaken = False
self.header = ""
self.data = ""
def feed(self, data):
if not self.handshaken:
self.header += str(data)
if self.header.find('\\r\\n\\r\\n') != -1:
parts = self.header.split('\\r\\n\\r\\n', 1)
self.header = parts[0]
if self.dohandshake(self.header, parts[1]):
logging.info("Handshake successful")
self.handshaken = True
else:
self.data += data.decode("utf-8", "ignore")
playloadData = data[6:]
mask = data[2:6]
unmasked = array.array("B", playloadData)
for i in range(len(playloadData)):
unmasked[i] = unmasked[i] ^ mask[i % 4]
self.onmessage(bytes(unmasked).decode("utf-8", "ignore"))
def dohandshake(self, header, key=None):
logging.debug("Begin handshake: %s" % header)
digitRe = re.compile(r'[^0-9]')
spacesRe = re.compile(r'\s')
part = part_1 = part_2 = origin = None
for line in header.split('\\r\\n')[1:]:
name, value = line.split(': ', 1)
if name.lower() == "sec-websocket-key1":
key_number_1 = int(digitRe.sub('', value))
spaces_1 = len(spacesRe.findall(value))
if spaces_1 == 0:
return False
if key_number_1 % spaces_1 != 0:
return False
part_1 = key_number_1 / spaces_1
elif name.lower() == "sec-websocket-key2":
key_number_2 = int(digitRe.sub('', value))
spaces_2 = len(spacesRe.findall(value))
if spaces_2 == 0:
return False
if key_number_2 % spaces_2 != 0:
return False
part_2 = key_number_2 / spaces_2
elif name.lower() == "sec-websocket-key":
part = bytes(value, 'UTF-8')
elif name.lower() == "origin":
origin = value
if part:
sha1 = hashlib.sha1()
sha1.update(part)
sha1.update("258EAFA5-E914-47DA-95CA-C5AB0DC85B11".encode('utf-8'))
accept = (b64encode(sha1.digest())).decode("utf-8", "ignore")
handshake = WebSocket.handshake % {
'accept': accept,
'origin': origin,
'port': self.server.port,
'bind': self.server.bind
}
#handshake += response
else:
logging.warning("Not using challenge + response")
handshake = WebSocket.handshake % {
'origin': origin,
'port': self.server.port,
'bind': self.server.bind
}
logging.debug("Sending handshake %s" % handshake)
self.client.send(bytes(handshake, 'UTF-8'))
return True
def onmessage(self, data):
logging.info("Got message: %s" % data)
def send(self, data):
logging.info("Sent message: %s" % data)
self.client.send("\x00%s\xff" % data)
def close(self):
self.client.close()
class WebSocketServer(object):
def __init__(self, bind, port, cls):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((bind, port))
self.bind = bind
self.port = port
self.cls = cls
self.connections = {}
self.listeners = [self.socket]
def listen(self, backlog=5):
self.socket.listen(backlog)
logging.info("Listening on %s" % self.port)
self.running = True
while self.running:
# upon first connection rList = [784] and the other two are empty
rList, wList, xList = select(self.listeners, [], self.listeners, 1)
for ready in rList:
if ready == self.socket:
logging.debug("New client connection")
client, address = self.socket.accept()
fileno = client.fileno()
self.listeners.append(fileno)
self.connections[fileno] = self.cls(client, self)
else:
logging.debug("Client ready for reading %s" % ready)
client = self.connections[ready].client
data = client.recv(1024) # currently, this results in: b''
fileno = client.fileno()
if data: # data = b''
self.connections[fileno].feed(data)
else:
logging.debug("Closing client %s" % ready)
self.connections[fileno].close()
del self.connections[fileno]
self.listeners.remove(ready)
for failed in xList:
if failed == self.socket:
logging.error("Socket broke")
for fileno, conn in self.connections:
conn.close()
self.running = False
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG,
format="%(asctime)s - %(levelname)s - %(message)s")
server = WebSocketServer("localhost", 8000, WebSocket)
server_thread = Thread(target=server.listen, args=[5])
server_thread.start()
# Add SIGINT handler for killing the threads
def signal_handler(signal, frame):
logging.info("Caught Ctrl+C, shutting down...")
server.running = False
sys.exit()
signal.signal(signal.SIGINT, signal_handler)
while True:
time.sleep(100)
server side logs:
INFO - Hanshake successful
DEBUG - Client ready for reading 664
DEBUG - Closing client 664
and on the client side I get
WebSocket connection to 'ws://localhost:8000' failed: Unknown Reason
The problem is traced here:
if data:
self.connections[fileno].feed(data)
else: # this is being triggered on the server side
logging.debug("Closing client %s" % ready)
So researching this I found a potential problem in the Python documentation for select used to retrieve rlist, wlist, xlist
select.select(rlist, wlist, xlist[, timeout]) This is a
straightforward interface to the Unix select() system call. The first
three arguments are iterables of ‘waitable objects’: either integers
representing file descriptors or objects with a parameterless method
named fileno() returning such an integer:
rlist: wait until ready for reading
wlist: wait until ready for writing
xlist: wait for an “exceptional condition” (see the manual page for
what your system considers such a condition)
Seeing that the feature is based on the Unix system call, I realized this code might not support Windows, which is my environment. I checked the values of rlist, wlist, xlist and found they're all empty lists on the first iteration rList = [784] (or another number, such as 664) and the other two are empty, after which the connection is closed.
The documentation goes on to note:
Note: File objects on Windows are not acceptable, but sockets are. On Windows, the underlying select() function is provided by the
WinSock library, and does not handle file descriptors that don’t
originate from WinSock.
But I'm not clear on the exact meaning of this.
So in the code logic, I did some logging and traced the issue here:
rList, wList, xList = select(self.listeners, [], self.listeners, 1)
for ready in rList: # rList = [836] or some other number
# and then we check if ready (so the 836 int) == self.socket
# but if we log self.socket we get this:
# <socket.socket fd=772, family=AddressFamily.AF_INET,
# type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8000)>
# so of course an integer isn't going to be equivalent to that
if ready == self.socket:
logging.debug("New client connection")
#so lets skip this code and see what the other condition does
else:
logging.debug("Client ready for reading %s" % ready)
client = self.connections[ready].client
data = client.recv(1024) # currently, this results in: b''
fileno = client.fileno()
if data: # data = b'', so this is handled as falsy
self.connections[fileno].feed(data)
else:
logging.debug("Closing client %s" % ready)
And as to why client.recv(1024) returns an empty binary string, I have no idea. I don't know if rList was supposed to contain more than an integer, or if the protocol is working as intended up until recv
Can anyone explain what's causing the broken .recv call here? Is the client side JavaScript WebSocket protocol not sending whatever data should be expected? Or is the WebSocket Server at fault, and what's wrong with it?
I tried running your example and it seem to be working as expected. At least server logs end with the following line:
INFO - Got message: {"name":"ping","data":0}
My environment:
OS: Arch Linux;
WebSocket client: Chromium/85.0.4183.121 running the JS-code you provided;
WebSocket server: Python/3.8.5 running the Python code you provided;
select.select docstring indeed states that
On Windows, only sockets are supported
but most likely the OS is irrelevant since the server code uses only sockets as select.select arguments.
recv returns an empty byte string when the reading end of a socket is closed. From recv(3) man:
If no messages are available to be received and the peer has performed an orderly shutdown, recv() shall return 0.
An interesting thing is a message about a successful handshake in server logs you got:
INFO - Hanshake successful
It means that in your case the connection between the client and the server has been established and some data has flown in both directions. After that the socket got closed. Looking at the server code I see no reason for the server to stop the connection. So I assume that the client you are using is to blame.
To find out exactly what is going wrong, try intercepting the network traffic using tcpdump or wireshark and running the following Python WebSocket client script that reproduces the actions my browser did when I was testing:
import socket
SERVER = ("localhost", 8000)
HANDSHAKE = (
b"GET /chat HTTP/1.1\r\n"
b"Host: server.example.com\r\n"
b"Upgrade: websocket\r\n"
b"Connection: Upgrade\r\n"
b"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
b"Sec-WebSocket-Protocol: chat, superchat\r\n"
b"Sec-WebSocket-Version: 13\r\n"
b"Origin: http://example.com\r\n"
b"\r\n\r\n"
)
# a frame with `{"name":"ping","data":0}` payload
MESSAGE = b"\x81\x983\x81\xde\x04H\xa3\xb0e^\xe4\xfc>\x11\xf1\xb7jT\xa3\xf2&W\xe0\xaae\x11\xbb\xeey"
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(SERVER)
n = s.send(HANDSHAKE)
assert n != 0
data = s.recv(1024)
print(data.decode())
n = s.send(MESSAGE)
assert n != 0

Video is freezed while video streaming

I find the following code for streaming video over a socket in python2.7. When I run it, the video will be freeze at the beginning in the server-side (It shows the video in a web browser). I debugged the code and understood that in the streamer.py, the third while loop condition creates an infinite loop because of the condition while len(data) < msg_size: is always satisfied. In other words, len(data) is always less than msg_size.So, the streamer.py does not return the image to the server.py. Could anyone help me to solve this issue?
The server.py is:
from flask import Flask, render_template, Response
from streamer import Streamer
app = Flask(__name__)
def gen():
streamer = Streamer('localhost', 8089)
streamer.start()
while True:
if streamer.client_connected():
yield (b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' +
streamer.get_jpeg() + b'\r\n\r\n')
#app.route('/')
def index():
return render_template('index.html')
#app.route('/video_feed')
def video_feed():
return Response(gen(), mimetype='multipart/x-mixed-replace;
boundary=frame')
if __name__ == '__main__':
app.run(host='localhost', threaded=True)
The streamer.py is:
import threading
import socket
import struct
import StringIO
import json
import numpy
class Streamer (threading.Thread):
def __init__(self, hostname, port):
threading.Thread.__init__(self)
self.hostname = hostname
self.port = port
self.connected = False
self.jpeg = None
def run(self):
self.isRunning = True
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
s.bind((self.hostname, self.port))
print 'Socket bind complete'
data = ""
payload_size = struct.calcsize("L")
s.listen(10)
print 'Socket now listening'
while self.isRunning:
conn, addr = s.accept()
print 'while 1...'
while True:
data = conn.recv(4096)
print 'while 2...'
if data:
packed_msg_size = data[:payload_size]
data = data[payload_size:]
msg_size = struct.unpack("L", packed_msg_size)[0]
while len(data) < msg_size:# the infinite loop is here(my problem)!
data += conn.recv(4096)
print ("lenght of data is " , len(data) )
print ("message size is " , msg_size )
frame_data = data[:msg_size]
#frame_data = data[:len(data)]
memfile = StringIO.StringIO()
memfile.write(json.loads(frame_data).encode('latin-1'))
memfile.seek(0)
frame = numpy.load(memfile)
ret, jpeg = cv2.imencode('.jpg', frame)
self.jpeg = jpeg
self.connected = True
print 'recieving...'
else:
conn.close()
self.connected = False
print 'connected=false...'
break
self.connected = False
def stop(self):
self.isRunning = False
def client_connected(self):
return self.connected
def get_jpeg(self):
return self.jpeg.tobytes()
Client.py is:
import socket
import sys
import pickle
import struct
import StringIO
import json
import time
cap=cv2.VideoCapture(0)
clientsocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
clientsocket.connect(('localhost',8089))
while(cap.isOpened()):
ret,frame=cap.read()
memfile = StringIO.StringIO()
np.save(memfile, fravidme)
memfile.seek(0)
data = json.dumps(memfile.read().decode('latin-1'))
clientsocket.sendall(struct.pack("L", len(data))+data)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
I want to show the video captured by my laptop's camera on a client machine in the same network. I expect video stream but in the browser, I just watch an image and it does not update continuously.
As I analyzed this code I noticed that the default implementation for sending OpenCV frames over the network was not working. I decided to replace it with ZeroMQ implementation I have used before. You can check out the linked question for a deeper explanation of how the streaming works. I have neatly packaged it into classes, with unit tests and documentation as SmoothStream check it out too.
Coming back to the question, here is the working code.
client.py
import base64
import cv2
import zmq
context = zmq.Context()
footage_socket = context.socket(zmq.PUB)
footage_socket.connect('tcp://localhost:5555')
camera = cv2.VideoCapture(0) # init the camera
while True:
try:
grabbed, frame = camera.read() # grab the current frame
frame = cv2.resize(frame, (640, 480)) # resize the frame
encoded, buffer = cv2.imencode('.jpg', frame)
jpg_as_text = base64.b64encode(buffer)
footage_socket.send(jpg_as_text)
except KeyboardInterrupt:
camera.release()
cv2.destroyAllWindows()
break
server.py
from flask import Flask, render_template, Response
from streamer import Streamer
app = Flask(__name__)
def gen():
streamer = Streamer('*', 5555)
streamer.start()
while True:
if streamer.client_connected():
yield (b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + streamer.get_jpeg() + b'\r\n\r\n')
#app.route('/')
def index():
return render_template('index.html')
#app.route('/video_feed')
def video_feed():
return Response(gen(), mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
app.run(host='localhost', threaded=True)
streamer.py
import base64
import threading
import cv2
import numpy as np
import zmq
class Streamer(threading.Thread):
def __init__(self, hostname, port):
threading.Thread.__init__(self)
self.hostname = hostname
self.port = port
self.connected = False
self.jpeg = None
def run(self):
self.isRunning = True
context = zmq.Context()
footage_socket = context.socket(zmq.SUB)
footage_socket.bind('tcp://{}:{}'.format(self.hostname, self.port))
footage_socket.setsockopt_string(zmq.SUBSCRIBE, np.unicode(''))
while self.isRunning:
frame = footage_socket.recv_string()
img = base64.b64decode(frame)
npimg = np.fromstring(img, dtype=np.uint8)
source = cv2.imdecode(npimg, 1)
ret, jpeg = cv2.imencode('.jpg', source)
self.jpeg = jpeg
self.connected = True
self.connected = False
def stop(self):
self.isRunning = False
def client_connected(self):
return self.connected
def get_jpeg(self):
return self.jpeg.tobytes()
I understand that copy-pasting entire .py files are probably not the best way to post an answer here, but this is a complex question with a lot of moving parts and I honestly could not think of a better way to help the OP.

OSError: [Errno 98] Address already in use (Python program hosted by public server)

I am sorry. I have done python before, but hosting a python program in a webserver is a new thing I am learning. Your help will be much appreciated.
My aim is to host a pyhton program in a webpage such that user can give a set of inputs. Based on the inputs python program does some calculation and provides an output to the user. This whole thing is running fine on windows. In windows what I am doing is: I run cmd and
I type C:\Python34\python.exe -m http.server 8020
Then I run the python program (most important part is provided below) and it starts:
"serving at port
Server Starts - localhost:8020"
Then from chrome I type: http://127.0.0.1:8000, provide the input and get the output.
I have the same directory structure in the webserver (based on centos). However, I cannot run both http.server and python program as it gives the OSerror when I run the second one from the same port. How can I tackle this?
I am posting the javascript code first and then the python program.
When I run in the webpage, I get 404 error from inspect element of chrome.
<script>
function submitReq() {
// alert("calling post");
var singleValues = $( "#single" ).val();
var payload = {};
payload["Seq"] = $("#seq").val();
payload["Mut_Seq"] = $("#mut_seq").val();
payload["Ct"] = parseInt($("#ct").val());
payload["Sodium_con"] = parseInt($("#sodium_con").val());
// alert(JSON.stringify(payload));
$.post("/getDNAsequence",
JSON.stringify(payload),
function(data,status){
var res = data.replace(new RegExp("\n", 'g'), " <br/> ");
$("#output").html(res);
});
};
$("#submit").click(submitReq);
</script>
class MyServer(BaseHTTPRequestHandler):
def do_GET(self) :
if (self.path == "/"):
with open ("dna.html", "r") as myfile:
data=myfile.read().replace('\n', '\n')
self.wfile.write(bytes(data, "utf-8"))
def do_POST(self):
if(self.path=="/getDNAsequence"):
content_len = int(self.headers.get('content-length', 0))
post_body = self.rfile.read(content_len)
parsed_json = json.loads(post_body.decode("utf-8"))
Seq = parsed_json['Seq']
Mut_Seq = parsed_json['Mut_Seq']
Ct = parsed_json['Ct']
Sodium_con = parsed_json['Sodium_con']
baal = Forward_mamapcr_primer(Seq, Mut_Seq, Ct, Sodium_con)
self.wfile.write(bytes(baal, "utf-8"))
self.send_response(200)
self.send_header("Access-Control-Allow-Origin", "*")
def main():
hostName = "localhost"
hostPort = 8020
print ("serving at port")
myServer = HTTPServer((hostName, hostPort), MyServer)
print( "Server Starts - %s:%s" % (hostName, hostPort))
try:
myServer.serve_forever()
except KeyboardInterrupt:
pass
myServer.server_close()
if __name__=='__main__':
main()
Use cgi-bin of apache server to run python script with user provided parameters.

how to call python script from javascript?

I have a backend python script where it retrieves the data from the sqlalchemy engine. And I would like to show the data in a search box where you can scroll down the list of data and select it. I read some answers to the similar questions like mine, (use ajax to call python script). But I'm still not clear about this. Here is my python script.
# models.py
from sqlalchemy import create_engine
from sqlalchemy.engine.url import URL
from sqlalchemy.ext.declarative import declarative_base
import pandas as pd
aURL = URL(drivername='mysql', username='chlee021690', database = 'recommender')
engine = create_engine(aURL, echo=True)
sql_command = 'SELECT product_id FROM bestbuy_data'
results = pd.read_sql(sql = sql_command, con = engine)
Can anybody tell me how to create javscript code to retrieve that results and render it in my form? Thanks.
Step 1: make your script available as a web service. You can use CGI, or you can use one of the cool server frameworks that will run standalone or WSGI like CherryPy, web.py or Flask.
Step 2: make an AJAX call to the URL served by step 1, either manually (look for XmlHttpRequest examples), or easily using jQuery or another framework (jQuery.ajax(), jQuery.get()).
These are two separate tasks, both are well documented on the web. If you have a more specific question, I suggest you ask again, showing what you are stuck on.
There are also many examples for the complete package available ("python ajax example"), for example this.
Your Python server needs to do 2 things:
Serve up the AJAX javascript file itself (via GET)
respond to calls from the web client (via POST).
Also it should be threaded to support multiple simultaneous connections.
Below is an example showing how to do all of the above with the built-in BaseHTTPServer.
JS (put in static/hello.html to serve via Python):
<html><head><meta charset="utf-8"/></head><body>
Hello.
<script>
var xhr = new XMLHttpRequest();
xhr.open("POST", "/postman", true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
value: 'value'
}));
xhr.onload = function() {
console.log("HELLO")
console.log(this.responseText);
var data = JSON.parse(this.responseText);
console.log(data);
}
</script></body></html>
Python server (for testing):
import time, threading, socket, SocketServer, BaseHTTPServer
import os, traceback, sys, json
log_lock = threading.Lock()
log_next_thread_id = 0
# Local log functiondef
def Log(module, msg):
with log_lock:
thread = threading.current_thread().__name__
msg = "%s %s: %s" % (module, thread, msg)
sys.stderr.write(msg + '\n')
def Log_Traceback():
t = traceback.format_exc().strip('\n').split('\n')
if ', in ' in t[-3]:
t[-3] = t[-3].replace(', in','\n***\n*** In') + '(...):'
t[-2] += '\n***'
err = '\n*** '.join(t[-3:]).replace('"','').replace(' File ', '')
err = err.replace(', line',':')
Log("Traceback", '\n'.join(t[:-3]) + '\n\n\n***\n*** ' + err + '\n***\n\n')
os._exit(4)
def Set_Thread_Label(s):
global log_next_thread_id
with log_lock:
threading.current_thread().__name__ = "%d%s" \
% (log_next_thread_id, s)
log_next_thread_id += 1
class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
Set_Thread_Label(self.path + "[get]")
try:
Log("HTTP", "PATH='%s'" % self.path)
with open('static' + self.path) as f:
data = f.read()
Log("Static", "DATA='%s'" % data)
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(data)
except:
Log_Traceback()
def do_POST(self):
Set_Thread_Label(self.path + "[post]")
try:
length = int(self.headers.getheader('content-length'))
req = self.rfile.read(length)
Log("HTTP", "PATH='%s'" % self.path)
Log("URL", "request data = %s" % req)
req = json.loads(req)
response = {'req': req}
response = json.dumps(response)
Log("URL", "response data = %s" % response)
self.send_response(200)
self.send_header("Content-type", "application/json")
self.send_header("content-length", str(len(response)))
self.end_headers()
self.wfile.write(response)
except:
Log_Traceback()
# Create ONE socket.
addr = ('', 8000)
sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(addr)
sock.listen(5)
# Launch 10 listener threads.
class Thread(threading.Thread):
def __init__(self, i):
threading.Thread.__init__(self)
self.i = i
self.daemon = True
self.start()
def run(self):
httpd = BaseHTTPServer.HTTPServer(addr, Handler, False)
# Prevent the HTTP server from re-binding every handler.
# https://stackoverflow.com/questions/46210672/
httpd.socket = sock
httpd.server_bind = self.server_close = lambda self: None
httpd.serve_forever()
[Thread(i) for i in range(10)]
time.sleep(9e9)
Console log (chrome):
HELLO
hello.html:14 {"req": {"value": "value"}}
hello.html:16
{req: {…}}
req
:
{value: "value"}
__proto__
:
Object
Console log (firefox):
GET
http://XXXXX:8000/hello.html [HTTP/1.0 200 OK 0ms]
POST
XHR
http://XXXXX:8000/postman [HTTP/1.0 200 OK 0ms]
HELLO hello.html:13:3
{"req": {"value": "value"}} hello.html:14:3
Object { req: Object }
Console log (Edge):
HTML1300: Navigation occurred.
hello.html
HTML1527: DOCTYPE expected. Consider adding a valid HTML5 doctype: "<!DOCTYPE html>".
hello.html (1,1)
Current window: XXXXX/hello.html
HELLO
hello.html (13,3)
{"req": {"value": "value"}}
hello.html (14,3)
[object Object]
hello.html (16,3)
{
[functions]: ,
__proto__: { },
req: {
[functions]: ,
__proto__: { },
value: "value"
}
}
Python log:
HTTP 8/postman[post]: PATH='/postman'
URL 8/postman[post]: request data = {"value":"value"}
URL 8/postman[post]: response data = {"req": {"value": "value"}}
Also you can easily add SSL by wrapping the socket before passing it to BaseHTTPServer.

connecting python to javascript for two-direction communication

I would like to serve queries from a javascript code by python. But I am not experienced in this field at all. What I would like to build is something like this:
1. request.js:
open_connection('server.py');
for (var i=0; i<10; i++)
document.write(request_next_number());
close_connection('server.py')
2. server.py
x = 0
while connected:
if request:
send(x)
x = x + 1
I heard about JSON, but don't know if I should use it. (?)
Could you please give me some code examples or guides how to implement the two files above?
What you need is a socket server on the python end and a client/request server on the javascript end.
For the python server side, refer to SocketServer, (example taken from there as well), one thing you have to make sure is to have the socket go past NAT (possibly port forwarding). One other alternative is Twisted which is a very powerful framework, i believe it has functionality to send data through NAT.
import SocketServer
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()
print "{} wrote:".format(self.client_address[0])
print self.data
# just send back the same data, but upper-cased
self.request.sendall(self.data.upper())
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
# Create the server, binding to localhost on port 9999
server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
On the JavaScript there are many frameworks that allow socket connections, here are a few
Socket IO
Example:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
You can even use HTML5 Web Sockets
Example:
var connection = new WebSocket('ws://IPAddress:Port');
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};
Also, take a look at a part of this book , Chapter 22 of Javascript: The Definitive Guide , https://www.inkling.com/read/javascript-definitive-guide-david-flanagan-6th/chapter-22/web-sockets
Finally, take a look at jssockets
Example:
_jssocket.setCallBack(event, callback);
_jssocket.connect(ip,port);
_jssocket.write(message);
_jssocket.disconnect();
Hope this help!
An example with Web Socket that i have used to transfer image to a web server and stream my screen.
stream.html
<!DOCTYPE HTML>
<meta charset = utf-8>
<html>
<header>
<title>Stream</title>
<script type="text/javascript" src="js/request.js"></script>
</header>
<body onload="doLoad()">
<div id="canvasWrapper">
<canvas id="display"></canvas>
</div>
</body>
</html>
request.js
var disp;
var dispCtx;
var im;
var ws;
function doLoad() {
disp = document.getElementById("display");
dispCtx = disp.getContext("2d");
im = new Image();
im.onload = function() {
disp.setAttribute("width", im.width);
disp.setAttribute("height", im.height);
dispCtx.drawImage(this, 0, 0);
};
im.src = "img/img_not_found.png";
ws = new WebSocket("ws://127.0.0.1:50007");
ws.onmessage = function (evt) {
im.src = "data:image/png;base64," + evt.data;
}
}
server.py
from autobahn.twisted.websocket import WebSocketServerProtocol, WebSocketServerFactory
import base64
import sys
from twisted.python import log
from twisted.internet import reactor
class MyServerProtocol(WebSocketServerProtocol):
def onConnect(self, request):
print("Client connecting: {}".format(request.peer))
def onOpen(self):
print("WebSocket connection open.")
def hello():
with open("/var/www/html/img/image.png", "rb") as image_file:
encoded_string = base64.b64encode(image_file.read())
self.sendMessage(encoded_string.encode('utf8'))
self.factory.reactor.callLater(0.2, hello)
# start sending messages every 20ms ..
hello()
def onMessage(self, payload, isBinary):
if isBinary:
print("Binary message received: {} bytes".format(len(payload)))
else:
print("Text message received: {}".format(payload.decode('utf8')))
# echo back message verbatim
self.sendMessage(payload, isBinary)
def onClose(self, wasClean, code, reason):
print("WebSocket connection closed: {}".format(reason))
if __name__ == '__main__':
log.startLogging(sys.stdout)
factory = WebSocketServerFactory(u"ws://127.0.0.1:50007")
factory.protocol = MyServerProtocol
# factory.setProtocolOptions(maxConnections=2)
# note to self: if using putChild, the child must be bytes...
reactor.listenTCP(50007, factory)
reactor.run()
You will need autobahn (you can install it with pip install autobahn)

Categories

Resources