I wanted to know how WebSocket servers are written so I tried making my own.
I used this as my reference and wrote a simple ThreadedTCPServer from the socket server module
this is my code for it
import base64
import hashlib
import re
import socketserver
def make_headers(headers: dict):
result = ""
result += "HTTP/1.1 101 Switching Protocols\r\n"
for header, value in headers.items():
result += f"{header}: {value}\r\n"
return result
def get_key(handshake_string):
key = re.findall(r"Sec-WebSocket-Key: .*", handshake_string)[0]
key = key.split(":")[1].strip()
return key
class Handler(socketserver.BaseRequestHandler):
MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
def setup(self) -> None:
handshake_text = self.request.recv(1024).decode('utf-8')
print(handshake_text)
key = get_key(handshake_text)
key = key + self.MAGIC
print(key)
key = hashlib.sha1(key.encode()).digest()
key = base64.b64encode(key).decode()
print(key)
headers = {"Upgrade": "websocket", "Connection": "Upgrade",
"Sec-WebSocket-Accept": key}
headers = make_headers(headers)
self.request.send(headers.encode())
def handle(self) -> None:
self.request.send(b"Connection Finally works ^_^")
print("conn sent?")
def finish(self) -> None:
print("Connection Over :(")
if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(("127.0.0.1", 2449), Handler)
server.serve_forever()
As mentioned in the referenced site, I have implemented the handshake in the setup method of my handler class however when I open my simple HTML document for testing the server I don't get my desired result in the browser console (blank)
this is my html code:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Socket test</title>
</head>
<body>
<h1>Sample text1</h1>
<script>
var ws = new WebSocket("ws://127.0.0.1:2449/")
ws.onopen = function() {
console.log("Connection Established!")
}
ws.onmessage = function(msg)
{
console.log("message received!")
console.log(msg)
}
ws.onerror = function(err){
console.log(err)
}
</script>
</body>
</html>
The html page sends a socket request which triggers the server handler and upon the handshake the request closes. is there any reason as to why this happens?
Related
I would like to understand better how flask path interact with JS code , specifically fetch api
#app.route('/submit', methods=['POST'])
def submit():
return render_template("apology.html") #test line of code
try:
with limiter.limit("1/10second"):
if request.method == "POST":
logging.info("msg sended")
data = request.json
user_id = session["user_id"]
if not data:
logging.info("DATA IS MISSION")
return render_template("apology.html")
if not session.get("user_id"):
logging.critical("This is a critical message")
logging.info("SESSION IS MISSSION")
return render_template("apology.html")
db.execute("INSERT INTO user_messages_2 (user_id, user_message) VALUES (?,?)", user_id, data)
return render_template("apology.html")
except RateLimitExceeded:
return "Too many requests, please try again later"
async function frontend_msg(){
const value_message = document.getElementById("chat_message").value
const response = await fetch("/submit", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(value_message) //message or data in here and make it a string
});
if (response.status != 200) {
console.log('Error: Response status is not 200');
await new Promise(resolve => setTimeout(resolve, 6000));
// you can call the function again to retry sending the message
frontend_msg();}
//document.getElementById('bad_form_design').reset();
document.getElementById('chat_message').value = ""
}
in this example I am sending "post" request to my flask app with the value of what user typed in inputform
and as you can see I added this line of code
return render_template("apology.html") #test line of code
and I was expecting this to happen when someone sends "post" request to my flask app
post request gets send to the "/submit" path
the page instantly renders return render_template("apology.html") #test line of code
instead the code below "return render_template("apology.html") #test line of code" just does not run which it makes sense I guess
context matters here and it looks like that "/submit" returns "render_template("apology.html")" to javascript and javascript has no idea what "render_template("apology.html")" means so it just does nothing
so I was wondering am I correct in this theory or is something else happening , and how else can I change the page after someone types in that input field and sends json data to flask
}
The Fetch-API is to be used if you want to send or load data without having to reload the entire page. Basically, the call initially does nothing other than send a request.
If a response from the server is expected and should be processed, this must be specified in the code. The browser does not do this by itself. Either a then block is used or the response was awaited and then something is done with the response.
In your example, however, the response data is ignored and therefore nothing is done.
If the template rendered and sent by the server is to be inserted into the page, the data must first be read from the stream. The data obtained can then be added to the page.
The following simple example shows you one possible use of the Fetch-API in conjunction with render_template.
./app.py
from flask import (
Flask,
render_template
)
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.post('/data')
def index_data():
return render_template('index_data.html')
./templates/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Index</title>
</head>
<body>
<button id="btn-demo">Click Me</button>
<output id="value"></output>
<script type="text/javascript">
(function() {
const outValue = document.getElementById('value');
const btnDemo = document.getElementById('btn-demo');
btnDemo.addEventListener('click', async () => {
const data = await fetch('/data', { method: 'post' })
.then(resp => resp.ok && resp.text());
if (data) {
outValue.innerHTML = data;
}
});
})();
</script>
</body>
</html>
./templates/index_data.html
<p>Hello World</p>
I 've searched here for similar problems but couldn't find an exact answer. I'm definately no socketio specialist, so please forgive me in advance...
To put it simple and in short: when I perform send() from my server (py script) preceded by a '#socket.on('message')' all is received fine on the client side. However when I use emit() on a manually triggered def, nothing is shown on client side.
Here is my simplified code:
server - app.py
app=Flask(__name__) app.config["SECRET_KEY"]="secretkey"
socket = SocketIO(app, logger=True)
somelist = ["el1", "el2", "el3", "el4"]
i=0
#app.route("/") def main():
return render_template("main.html")
#app.route("/test2")
def test2():
print("called test2")
manual_callback("note 1")
#socket.on('message')
def handlemsg(msg):
global i
if i < len(somelist):
socket.send(somelist[i] + " - " + msg)
i+=1
def manual_callback(note):
print("manual_callback triggered")
socket.emit('updateData', {"data": "this the data to show - " + note}, namespace="/test2")
print("trigger done")
if __name__ == "__main__":
socket.run(app)
Client - main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.1/socket.io.js"></script>
<script>
socket = io();
socket.connect('http://' + document.domain + ':' + location.port);
socket.on('updateData',function(data){
console.log('called updateData');
console.log(data);
console.log('ended call');
})
socket.on('connect',function(){
console.log('CONNECT');
socket.send('a');
})
socket.on('message',function(msg){
console.log(msg);
socket.send('b');
})
</script>
</head>
<body>
<h1>test</h1>
</body>
</html>
What works fine:
After connecting, 'socket.on('connect',...) is triggered in main.html and 'CONNECT' is printed in the browser log followed by the 4 elements in somelist.
What doesn't work like I would expect:
After navigating to localhost/test2, def manual_callback() is called. So I would expect that on client side, 'socket.on('updateData'...)' gets triggered and messages 'called UpdateData', 'this the data to show - note 1' and 'ended call' appear in the browser's log. But the latter does not seem to happen.
What am I doing wrong here?
Thank you in advance.
My goal is for my Flask server to send the client data either every three seconds, or when a function is called. To do this I am using SocketIO. However based on some example code I am working with, it seems that I can only send data after a client requests something. I don't want the client to have to 'poll' to find if there is new data, so I want the server to push it when it is ready.
Here is what I tried so far. (some of the code is unnecessary since it is based off an example) This should use socketio to push the time to the client every few seconds.
HTML
<!DOCTYPE HTML>
<html>
<head>
<title>Socket-Test</title>
<script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
namespace = '/test';
var socket = io(namespace);
socket.on('my_response', function(msg, cb) {
$('#log').text( $('<div/>').text(msg.data).html());
if (cb)
cb();
});
});
</script>
</head>
<body style="background-color:white;">
<h1 style="background-color:white;">Socket</h1>
<div id="time" ></div>
</body>
</html>
Python
import threading
from flask import Flask, render_template, session, copy_current_request_context,request
from flask_socketio import SocketIO, emit, disconnect
from threading import Lock
import time
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socket_ = SocketIO(app, async_mode=async_mode)
thread = None
thread_lock = Lock()
clients = []
def update():
time.sleep(1)
emit('my_response',
{'data': time.time},
room=clients[0])
t=threading.Thread(target=update)
#socket_.on('connect')
def handle_connect():
print('Client connected')
clients.append(request.sid)
t.start()
#app.route('/')
def index():
return render_template('index.html', async_mode=socket_.async_mode)
#socket_.on('my_event', namespace='/test')
def test_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response',
{'data': message['data'], 'count': session['receive_count']})
#socket_.on('my_broadcast_event', namespace='/test')
def test_broadcast_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response',
{'data': time.time},
broadcast=True)
socket_.run(app,port=8050)
I try to run it but it gives me the error RuntimeError: Working outside of request context.
I fixed my code by following this tutorial: https://www.shanelynn.ie/asynchronous-updates-to-a-webpage-with-flask-and-socket-io/
import threading
from flask import Flask, render_template, session, copy_current_request_context,request
from flask_socketio import SocketIO, emit, disconnect
from threading import Lock
import time
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socket_ = SocketIO(app, async_mode=async_mode)
thread = None
thread_lock = Lock()
def update():
time.sleep(1)
socket_.emit('my_response',
{'data': time.time()},
namespace='/test')
print("emitted")
update()
t=threading.Thread(target=update)
#socket_.on('connect', namespace='/test')
def handle_connect():
print('Client connected')
if not t.isAlive():
t.start()
#app.route('/')
def index():
return render_template('index.html', async_mode=socket_.async_mode)
socket_.run(app,port=8070)
HTML
<!DOCTYPE HTML>
<html>
<head>
<title>Socket-Test</title>
<script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
namespace = '/test';
var socket = io(namespace);
console.log(("test"));
socket.on('my_response', function(msg) {
$('#time').text( $('<div/>').text(msg.data).html());
console.log(msg);
});
});
</script>
</head>
<body style="background-color:white;">
<h1 style="background-color:white;">Socket</h1>
<div id="time" ></div>
</body>
</html>
I would like to point out that using recursion in this case is not the best choice.
you call the update function inside the update and do not have the completion of this process.
the best option would be to use a loop(as done in the link you attached)
def update():
while True:
time.sleep(1)
socket_.emit('my_response', {'data': time.time()}, namespace='/test')
print("emitted")
t=threading.Thread(target=update)
also, it would be better to write "while is_work_var" instead of "while True"
I'm writing a basic socket programming code in python which takes any kind of URL and returns the content of the body in bytes. I need to only use socket library and nothing else. When I pass different URLs, I get full response of the body for some URLs and only a partial response for some URLs. I'm not sure why it is so.
This is my code:
import socket
def retrieve_url(url):
url1 = url.split("http://", 1)
empty = url1[1].find("/")
if empty > 0:
url2 = url1[1].split("/", 1)
else:
url2 = url1[1].split("/", 1)
url2.append('')
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
soc.connect((url2[0], 80))
soc.sendall(b"GET /" + bytes(url2[1], 'utf8') + b" HTTP/1.1\r\nHost: " + bytes(url2[0],'utf8') + b"\r\nConnection: close\r\n\r\n")
spl = soc.recv(8192)
soc.close()
a = spl.split(b"\r\n\r\n", 1)
b = spl.split(None, 2)
if b[1] == b'200':
return a[1]
else:
return None
except:
return None
if __name__ == "__main__":
print(retrieve_url("http://bombus.myspecies.info/node/24"))
This is the output I'm getting:
b'007a84\r\n<!DOCTYPE html PUBLIC "-//W3C//DTD HTML+RDFa 1.1//EN">\n<html lang="en" dir="ltr" version="HTML+RDFa 1.1"\n xmlns:content="http://purl.org/rss/1.0/modules/content/"\n xmlns:dc="http://purl.org/dc/terms/"\n xmlns:foaf="http://xmlns.com/foaf/0.1/"\n xmlns:og="http://ogp.me/ns#"\n xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"\n xmlns:sioc="http://rdfs.org/sioc/ns#"\n xmlns:sioct="http://rdfs.org/sioc/types#"\n xmlns:skos="http://www.w3.org/2004/02/skos/core#"\n xmlns:xsd="http://www.w3.org/2001/XMLSchema#">\n<head profile="http://www.w3.org/1999/xhtml/vocab">\n <!--[if IE]><![endif]-->\n<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />\n<![endif]--><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n<meta name="Generator" content="Drupal 7 (http://drupal.org)" />\n<link rel="canonical" href="/node/24" />\n<link rel="shortlink" href="/node/24" />\n<link rel="shortcut icon" href="http://bombus.myspecies.info/sites/all/themes/scratchpads/favicon.ico" type="image/vnd.microsoft.icon" />\n<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />\n <title>Bumblebee links | Genus Bombus</title>\n <link type="text/css" rel="stylesheet" href="//bombus.myspecies.info/sites/bombus.myspecies.info/files/advagg_css/css__-thr5kmN-aeH-BTlyCidKsE4D9T2geiRzcvwxBTJ3sU__VQtLPGzb9rjfNLJ2SaVDJUKhxtssNArRk3nO7wMUGoA__CirpVkWrddCrpKWbZfWXvbwVN8pmqviBo8YZAKaYUQg.css" media="all" />\n\n<!--[if (lt IE 9)]>\n<link type="text/css" rel="stylesheet" href="//bombus.myspecies.info/sites/bombus.myspecies.info/files/advagg_css/css__GuBcvhFB_-fswxhbycYya2JRgqrqDq5y-pWYcuQbqp4__5h6_elrgEAXONSci50a6ewD4zUldIVoOgFFSjk7rVzg__CirpVkWrddCrpKWbZfWXvbwVN8pmqviBo8YZAKaYUQg.css" media="all" />\n<![endif]-->\n<link type="text/css" rel="stylesheet" href="//bombus.myspecies.info/sites/bombus.myspecies.info/files/advagg_css/css__wxRBHPf0PIq6kCJXm57TkZCESl8zp_O0VduSG6wH0S0__ov4XZtBPHqH1F5cpK65jxEp1K5zF3dLEO4ihA2xTbE8__CirpVkWrddCrpKWbZfWXvbwVN8pmqviBo8YZAKaYUQg.css" media="all" />\n\n<!--[if (lt IE 9)&(!IEMobile)]>\n<link type="text/css" rel="stylesheet" href="//bombus.myspecies.info/sites/bombus.myspecies.info/files/advagg_css/css__ISa-1zfLkp-52D_pPloP6gBpDvJwKu9Kitwbfnm33JY__PDj463LZhTo68R-x__a4AOf-EyYxSMW6sZpiNjtiKlQ__CirpVkWrddCrpKWbZfWXvbwVN8pmqviBo8YZAKaYUQg.css" media="all" />\n<![endif]-->\n\n<!--[if gte IE 9]><!-->\n<link type="text/css" rel="stylesheet" href="//bombus.myspecies.info/sites/bombus.myspecies.info/files/advagg_css/css__Sisgbo-UreLp3cHBWfv37bIck8X5olI1W5xaYzDaATc__9XoSDv750KzHbfRkMww8VsZREmLh-SRR3SnhCYOEF2Q__CirpVkWrddCrpKWbZfWXvbwVN8pmqviBo8YZAKaYUQg.css" media="all" />\n<!--<![endif]-->\n <script type="text/javascript" src="//bombus.myspecies.info/sites/bombus.myspecies.info/files/advagg_js/js__jQBI8pfG-VfYV1aN0gSeRXUYps9-4-M-XVb2H2ZbWuw__SyNVdbb0UiBMvI1oo0AzTY4CH83E7BmTR7ZP1Wwz_VE__CirpVkWrddCrpKWbZfWXvbwVN8pmqviBo8YZAKaYUQg.js"></script>\n<script type="text/javascript" src="//bombus.myspecies.info/sites/all/libraries/mediaelement/build/mediaelement-and-player.min.js?v=2.1.6"></script>\n<script type="text/javascript" src="//bombus.myspecies.info/sites/bombus.myspecies.info/files/advagg_js/js__O2-Mfrpb6mHF0S5LAfIan_d38-kqXvv66sN_ZsHG9Qo__caMiUBuMxDF7rNYJXFf8geEVfuxGw22B96ouV1h3-1Q__CirpVkWrddCrpKWbZfWXvbwVN8pmqviBo8YZAKaYUQg.js"></script>\n<script type="text/javascript">\n<!--//--><![CDATA[//><!--\njQuery.extend(Drupal.settings,{"basePath":"\\/","pathPrefix":"","ajaxPageState":{"theme":"scratchpads","theme_token":"u7Gd7GPT7EPAVsIznB6HVhd9aHAwVUfjG4LrulLh3ak","jquery_version":"1.8","css":{"modules\\/system\\/system.base.css":1,"modules\\/system\\/system.menus.css":1,"modules\\/system\\/system.messages.css":1,"modules\\/system\\/system.theme.css":1,"sites\\/all\\/libraries\\/mediaelement\\/build\\/mediaelementplayer.min.css":1,"misc\\/ui\\/jquery.ui.core.css":1,"misc\\/ui\\/jquery.ui.theme.css":1,"modules\\/overlay\\/overlay-parent.css":1,"sites\\/all\\/modules\\/contrib\\/comment_notify\\/comment_notify.css":1,"modules\\/aggregator\\/aggregator.css":1,"modules\\/comment\\/comment.css":1,"sites\\/all\\/modules\\/contrib\\/date\\/date_api\\/date.css":1,"sites\\/all\\/modules\\/custom\\/entityfilter\\/ckeditor\\/entityfilter.css":1,"sites\\/all\\/modules\\/custom\\/field_quick_delete\\/theme\\/field.css":1,"modules\\/node\\/node.css":1,"sites\\/all\\/modules\\/custom\\/remote_issue_tab\\/css\\/remote_issue_tab.css":1,"sites\\/all\\/modules\\/custom\\/scratchpads\\/scratchpads_biography\\/css\\/scratchpads_biography.css":1,"sites\\/all\\/modules\\/custom\\/scratchpads\\/scratchpads_show_taxa_revisions\\/css\\/scratchpads_show_taxa_revisions.css":1,"modules\\/search\\/search.css":1,"sites\\/all\\/modules\\/custom\\/spm\\/css\\/spm.css":1,"sites\\/all\\/modules\\/custom\\/twitter_filter\\/css\\/twitter_filter.css":1,"sites\\/all\\/modules\\/custom\\/twitterscript\\/css\\/twitterscript.css":1,"modules\\/user\\/user.css":1,"sites\\/all\\/modules\\/contrib\\/views\\/css\\/views.css":1,"sites\\/all\\/modules\\/contrib\\/ckeditor\\/ckeditor.css":1,"sites\\/all\\/modules\\/contrib\\/colorbox\\/styles\\/default\\/colorbox_default_style.css":1,"sites\\/all\\/modules\\/contrib\\/ctools\\/css\\/ctools.css":1,"sites\\/all\\/modules\\/contrib\\/ctools\\/css\\/modal.css":1,"sites\\/all\\/modules\\/contrib\\/modal_forms\\/css\\/modal_forms_popup.css":1,"sites\\/all\\/modules\\/contrib\\/biblio\\/biblio.css":1,"modules\\/openid\\/openid.css":1,"public:\\/\\/spamicide\\/feed_me.css":1,"sites\\/all\\/modules\\/custom\\/scratchpads\\/scratchpads_search_block\\/css\\/scratchpads_search_block.css":1,"sites\\/all\\/modules\\/custom\\/creative_commons\\/css\\/creative_commons.css":1,"sites\\/all\\/themes\\/scratchpads\\/css\\/ie8.css":1,"public:\\/\\/css\\/css_tcVOMdlRmJTsBkm7ZJABjZ3Oct1H-tB7QsRkmUkgNco.css":1,"sites\\/all\\/themes\\/scratchpads\\/css\\/tabs.css":1,"sites\\/all\\/themes\\/scratchpads\\/css\\/sites.css":1,"sites\\/all\\/themes\\/omega\\/alpha\\/css\\/alpha-reset.css":1,"sites\\/all\\/themes\\/omega\\/alpha\\/css\\/alpha-mobile.css":1,"sites\\/all\\/themes\\/omega\\/alpha\\/css\\/alpha-alpha.css":1,"sites\\/all\\/themes\\/omega\\/omega\\/css\\/formalize.css":1,"sites\\/all\\/themes\\/omega\\/omega\\/css\\/omega-text.css":1,"sites\\/all\\/themes\\/omega\\/omega\\/css\\/omega-branding.css":1,"sites\\/all\\/themes\\/omega\\/omega\\/css\\/omega-menu.css":1,"sites\\/all\\/themes\\/omega\\/omega\\/css\\/omega-forms.css":1,"sites\\/all\\/themes\\/scratchpads\\/css\\/global.css":1,"ie::normal::sites\\/all\\/themes\\/scratchpads\\/css\\/scratchpads-alpha-default.css":1,"ie::normal::sites\\/all\\/themes\\/scratchpads\\/css\\/scratchpads-alpha-default-normal.css":1,"ie::normal::sites\\/all\\/themes\\/omega\\/alpha\\/css\\/grid\\/alpha_default\\/normal\\/alpha-default-normal-12.css":1,"narrow::sites\\/all\\/themes\\/scratchpads\\/css\\/scratchpads-alpha-default.css":1,"narrow::sites\\/all\\/themes\\/scratchpads\\/css\\/scratchpads-alpha-default-narrow.css":1,"sites\\/all\\/themes\\/omega\\/alpha\\/css\\/grid\\/alpha_default\\/narrow\\/alpha-default-narrow-12.css":1,"normal::sites\\/all\\/themes\\/scratchpads\\/css\\/scratchpads-alpha-default.css":1,"normal::sites\\/all\\/themes\\/scratchpads\\/css\\/scratchpads-alpha-default-normal.css":1,"sites\\/all\\/themes\\/omega\\/alpha\\/css\\/grid\\/alpha_default\\/normal\\/alpha-default-normal-12.css":1,"wide::sites\\/all\\/themes\\/scratchpads\\/css\\/scratchpads-alpha-default.css":1,"wide::sites\\/all\\/themes\\/scratchpads\\/css\\/scratchpads-alpha-default-wide.css":1,"sites\\/all\\/themes\\/omega\\/alpha\\/css\\/grid\\/alpha_default\\/wide\\/alpha-default-wide-12.css":1},"js":{"modules\\/statistics\\/statistics.js":1,"sites\\/all\\/modules\\/contrib\\/jquer'
I only get partial response for this, but what I need is the full response.
Any help is appreciated. Thanks in advance.
soc.sendall(b"GET /" + bytes(url2[1], 'utf8') + b" HTTP/1.1\r\nHost: " + bytes(url2[0],'utf8') + b"\r\nConnection: close\r\n\r\n")
spl = soc.recv(8192)
soc.close()
You assume that all data will be retrieved with a single recv. This assumption is wrong. Given that you use an explicit Connection: close you can rely on the server closing the connection after the response is done, i.e. you should call recv as long as it returns more data and concatenate all these date for the full response.
You also assume that the answer is contained in plain in the response body. While this might be true in your specific case your use of HTTP/1.1 makes it possible for the server to send a chunked response too - which you need to handle differently. In order to avoid this better use HTTP/1.0 only (which also has an implicit Connection: close).
I had a small sandbox server on gevent-socketio.
The backend was
from flask import Flask, render_template, request
from socketio import socketio_manage
from socketio.namespace import BaseNamespace
from socketio.server import SocketIOServer
class FirstNamespace(BaseNamespace):
def on_make_upper_hook(self, msg):
response = {'response from make_upper':msg.upper()}
self.emit('response_channel', response)
return True
app = Flask(__name__)
app.config['DEBUG'] = True
#app.route('/')
def index():
return render_template('index.html')
#app.route('/socket.io/')
#app.route('/socket.io/<path:remaining>')
def socketio_endpoint(remaining=None):
print('remaining path: {}'.format(remaining))
socketio_manage(request.environ,
{'/chat': FirstNamespace})
return 'ok'
if __name__ == '__main__':
SocketIOServer(('0.0.0.0', 8080), app,
resource="socket.io").serve_forever()
And the frontend (with socket.io 0.9.6) was
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="static/socket.io.js"></script>
<script>
var socket = io.connect('/chat');
socket.on('connect', function () {
console.log('socket connected');
});
</script>
</head>
<body>
</body>
</html>
In the browser console I saw that connection went fine. The server was also fine:
127.0.0.1 - - [2015-07-05 10:30:55] "GET / HTTP/1.1" 200 420 0.006791
remaining path: 1/websocket/683868734428
But!
When I tried to upgrade socket.io like so:
<script src="https://cdn.socket.io/socket.io-1.3.5.js"></script>
<script>
var socket = io('http://localhost:8080/chat');
socket.on('connect', function () {
console.log('socket connected');
});
</script>
I started to get client errors
GET http://localhost:8080/socket.io/?EIO=3&transport=polling&t=1436081649609-0 500 (Internal Server Error)
and server errors:
File "/Users/1111/.virtualenvs/stuff/lib/python2.7/site-packages/socketio/__init__.py",
line 67, in socketio_manage
socket = environ['socketio']
KeyError: 'socketio'
So somehow my precious socketio key disappeared from request.environ and I don't know how to get it back. How to fix this?