From d04e2065cae70ac72e29a03f9e3465994611bf2e Mon Sep 17 00:00:00 2001 From: Davide Date: Sun, 7 Jun 2015 16:40:54 +0200 Subject: [PATCH] Update server.py Now python3.x compatible! --- server.py | 145 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 89 insertions(+), 56 deletions(-) diff --git a/server.py b/server.py index c324d400..d8b03e5a 100644 --- a/server.py +++ b/server.py @@ -13,17 +13,41 @@ See the License for the specific language governing permissions and limitations under the License. """ - -import urllib2 +try: + from urllib.parse import unquote +except: + import urllib2 + unquote = urllib2.unquote from configuration import runtimeInstances, MULTIPLE_INSTANCE, ENABLE_FILE_CACHE, BASE_ADDRESS, HTTP_PORT_NUMBER, WEBSOCKET_PORT_NUMBER, IP_ADDR, UPDATE_INTERVAL -from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler -import SocketServer +try: + from http.server import HTTPServer, BaseHTTPRequestHandler +except: + from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +try: + import socketserver +except: + import SocketServer as socketserver import webbrowser import struct +import binascii from base64 import b64encode +import hashlib from hashlib import sha1 -from mimetools import Message -from StringIO import StringIO +import sys +try: + import email + Message = email.message_from_string +except: + from mimetools import Message +try: + import io + def StringIO(v): + v=io.BytesIO(v) + v=b''.join(v.readlines()).decode("utf-8") + return v + #StringIO = io.BytesIO +except: + from StringIO import StringIO import threading from threading import Timer @@ -31,6 +55,7 @@ clients = {} updateTimerStarted = False # to start the update timer only once +pyLessThan3 = sys.version_info < (3,) def get_method_by(rootNode, idname): if idname.isdigit(): @@ -74,16 +99,16 @@ def get_method_by_id(rootNode, _id, maxIter=5): class ThreadedWebsocketServer( - SocketServer.ThreadingMixIn, SocketServer.TCPServer): + socketserver.ThreadingMixIn, socketserver.TCPServer): allow_reuse_address = True -class WebSocketsHandler(SocketServer.StreamRequestHandler): - magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' +class WebSocketsHandler(socketserver.StreamRequestHandler): + magic = b'258EAFA5-E914-47DA-95CA-C5AB0DC85B11' def setup(self): global clients - SocketServer.StreamRequestHandler.setup(self) + socketserver.StreamRequestHandler.setup(self) print('websocket connection established', self.client_address) self.handshake_done = False @@ -94,53 +119,61 @@ def handle(self): self.handshake() else: if not self.read_next_message(): + print('ending websocket service...') break + + def bytetonum(self,b): + if pyLessThan3: + b = ord(b) + return b def read_next_message(self): - try: - print('read_next_message\n') - length = ord(self.rfile.read(2)[1]) & 127 - if length == 126: - length = struct.unpack('>H', self.rfile.read(2))[0] - elif length == 127: - length = struct.unpack('>Q', self.rfile.read(8))[0] - masks = [ord(byte) for byte in self.rfile.read(4)] - decoded = '' - for char in self.rfile.read(length): - decoded += chr(ord(char) ^ masks[len(decoded) % 4]) - self.on_message(decoded) - return True - except: - return False + print('read_next_message\n') + length = self.rfile.read(2) + length = self.bytetonum(length[1]) & 127 + if length == 126: + length = struct.unpack('>H', self.rfile.read(2))[0] + elif length == 127: + length = struct.unpack('>Q', self.rfile.read(8))[0] + masks = [self.bytetonum(byte) for byte in self.rfile.read(4)] + decoded = '' + for char in self.rfile.read(length): + decoded += chr(self.bytetonum(char) ^ masks[len(decoded) % 4]) + self.on_message(decoded) + return True def send_message(self, message): + out = bytearray() + out.append(129) print('send_message\n') - self.request.send(chr(129)) length = len(message) if length <= 125: - self.request.send(chr(length)) + out.append(length) elif length >= 126 and length <= 65535: - self.request.send(chr(126)) - self.request.send(struct.pack('>H', length)) + out.append(126) + out = out + struct.pack('>H', length) else: - self.request.send(chr(127)) - self.request.send(struct.pack('>Q', length)) - self.request.send(message) + out.append(127) + out = out + struct.pack('>Q', length) + out = out + message.encode() + self.request.send(out) def handshake(self): print('handshake\n') data = self.request.recv(1024).strip() - headers = Message(StringIO(data.split('\r\n', 1)[1])) - # if headers.get("Upgrade", None) != "websocket": - # return + headers = Message(StringIO(data.split(b'\r\n', 1)[1])) print('Handshaking...') key = headers['Sec-WebSocket-Key'] - digest = b64encode(sha1(key + self.magic).hexdigest().decode('hex')) + key = key + digest = hashlib.sha1((key.encode("utf-8")+self.magic)) + digest = digest.digest() + digest = b64encode(digest) response = 'HTTP/1.1 101 Switching Protocols\r\n' response += 'Upgrade: websocket\r\n' response += 'Connection: Upgrade\r\n' - response += 'Sec-WebSocket-Accept: %s\r\n\r\n' % digest - self.handshake_done = self.request.send(response) + response += 'Sec-WebSocket-Accept: %s\r\n\r\n' % digest.decode("utf-8") + self.request.sendall(response.encode("utf-8")) + self.handshake_done = True def on_message(self, message): # saving the websocket in order to update the client @@ -243,11 +276,11 @@ def gui_updater(client, leaf): if leaf.repr_without_children() != client.old_runtime_widgets[__id]: #client.old_runtime_widgets[__id] = repr(leaf) for ws in client.websockets: - try: - print('update_widget: ' + __id + ' type: ' + str(type(leaf))) - ws.send_message('update_widget,' + __id + ',' + repr(leaf)) - except: - print('exception here, server.py - gui_updater, id2') + #try: + print('update_widget: ' + __id + ' type: ' + str(type(leaf))) + ws.send_message('update_widget,' + __id + ',' + repr(leaf)) + #except: + # print('exception here, server.py - gui_updater, id2') client.old_runtime_widgets[__id] = leaf.repr_without_children() return True # widget NOT changed @@ -371,15 +404,15 @@ def do_POST(self): self.instance() varLen = int(self.headers['Content-Length']) postVars = self.rfile.read(varLen) - postVars = str(urllib2.unquote(postVars).decode('utf8')) + postVars = str(unquote(postVars)) paramDict = parse_parametrs(postVars) - function = str(urllib2.unquote(self.path).decode('utf8')) + function = str(unquote(self.path)) self.process_all(function, paramDict, True) def do_GET(self): """Handler for the GET requests.""" self.instance() - params = str(urllib2.unquote(self.path).decode('utf8')) + params = str(unquote(self.path)) params = params.split('?') function = params[0] @@ -431,13 +464,13 @@ def process_all(self, function, paramDict, isPost): self.send_header('Content-type', 'text/html') self.end_headers() - self.wfile.write(self.client.attachments) - self.wfile.write( + self.wfile.write(self.client.attachments.encode('utf-8')) + self.wfile.write(( "") + "style.css' rel='stylesheet' />").encode('utf-8')) - self.wfile.write(repr(self.client.root)) + self.wfile.write(repr(self.client.root).encode('utf-8')) else: # here is the function that should return the content type self.send_response(200) @@ -448,13 +481,13 @@ def process_all(self, function, paramDict, isPost): # if is requested a widget, but not by post, so we suppose is # requested to show a new page, we attach javascript and style if(ret[1] == 'text/html' and isPost == False): - self.wfile.write(self.client.attachments) - self.wfile.write( + self.wfile.write(self.client.attachments.encode('utf-8')) + self.wfile.write(( "") + "style.css' rel='stylesheet' />").encode('utf-8')) - self.wfile.write(ret[0]) + self.wfile.write(ret[0].encode('utf-8')) else: self.send_response(200) @@ -464,12 +497,12 @@ def process_all(self, function, paramDict, isPost): self.end_headers() f = open('./' + function, 'r+b') - content = ''.join(f.readlines()) + content = b''.join(f.readlines()) f.close() self.wfile.write(content) -class ThreadedHTTPServer(SocketServer.ThreadingMixIn, HTTPServer): +class ThreadedHTTPServer(socketserver.ThreadingMixIn, HTTPServer): """This class allows to handle requests in separated threads.