Ticket #1136: websockify-inprocess.patch
File websockify-inprocess.patch, 22.0 KB (added by , 5 years ago) |
---|
-
xpra/log.py
224 224 ("network" , "All network code"), 225 225 ("mmap" , "mmap transfers"), 226 226 ("protocol" , "Packet input and output (formatting, parsing, sending and receiving)"), 227 ("websocket" , "Websocket layer"), 227 228 ("crypto" , "Encryption"), 228 229 ("auth" , "Authentication"), 229 230 ])), -
xpra/net/bytestreams.py
151 151 def untilConcludes(self, *args): 152 152 return untilConcludes(self.is_active, *args) 153 153 154 def peek(self, n): 155 #not implemented 156 return None 157 154 158 def _write(self, *args): 155 159 w = self.untilConcludes(*args) 156 160 self.output_bytecount += w or 0 … … 245 249 self._socket = socket 246 250 self.local = local 247 251 self.remote = remote 252 self.socket_type = "socket" 248 253 if type(remote)==str: 249 254 self.filename = remote 250 255 256 def peek(self, n): 257 self._socket.settimeout(None) 258 return self._socket.recv(n, socket.MSG_PEEK) 259 251 260 def read(self, n): 252 261 return self._read(self._socket.recv, n) 253 262 … … 263 272 264 273 def __repr__(self): 265 274 if self.remote: 266 return "%s socket: %s <- %s" % (self.info, pretty_socket(self.local), pretty_socket(self.remote))267 return "%s socket:%s" % (self.info, pretty_socket(self.local))275 return "%s %s: %s <- %s" % (self.info, self.socket_type, pretty_socket(self.local), pretty_socket(self.remote)) 276 return "%s %s:%s" % (self.info, self.socket_type, pretty_socket(self.local)) 268 277 269 278 def get_info(self): 270 279 d = Connection.get_info(self) 271 280 try: 272 d["type"] = "socket"281 d["type"] = self.socket_type 273 282 s = self._socket 274 283 if s: 275 284 d["socket"] = { -
xpra/net/websocket.py
1 # This file is part of Xpra. 2 # Copyright (C) 2016 Antoine Martin <antoine@devloop.org.uk> 3 # Xpra is released under the terms of the GNU GPL v2, or, at your option, any 4 # later version. See the file COPYING for details. 5 6 import os 7 import posixpath 8 import urllib 9 10 from xpra.log import Logger 11 log = Logger("network", "websocket") 12 13 from xpra.util import AdHocStruct 14 from xpra.net.bytestreams import SocketConnection 15 from websockify.websocket import WebSocketRequestHandler 16 17 WEBSOCKET_TCP_NODELAY = int(os.environ.get("WEBSOCKET_TCP_NODELAY", "1")) 18 WEBSOCKET_TCP_KEEPALIVE = int(os.environ.get("WEBSOCKET_TCP_KEEPALIVE", "1")) 19 20 21 22 class WSRequestHandler(WebSocketRequestHandler): 23 24 disable_nagle_algorithm = WEBSOCKET_TCP_NODELAY 25 keep_alive = WEBSOCKET_TCP_KEEPALIVE 26 27 def __init__(self, sock, addr, new_websocket_client): 28 self.web_root = "/usr/share/xpra/www/" 29 server = AdHocStruct() 30 server.logger = log 31 server.run_once = True 32 self._new_websocket_client = new_websocket_client 33 WebSocketRequestHandler.__init__(self, sock, addr, server) 34 35 def new_websocket_client(self): 36 self._new_websocket_client(self) 37 38 def translate_path(self, path): 39 s = path 40 # abandon query parameters 41 path = path.split('?',1)[0] 42 path = path.split('#',1)[0] 43 # Don't forget explicit trailing slash when normalizing. Issue17324 44 trailing_slash = path.rstrip().endswith('/') 45 path = posixpath.normpath(urllib.unquote(path)) 46 words = path.split('/') 47 words = filter(None, words) 48 path = self.web_root 49 for word in words: 50 word = os.path.splitdrive(word)[1] 51 word = os.path.split(word)[1] 52 if word in (os.curdir, os.pardir): 53 continue 54 path = os.path.join(path, word) 55 if trailing_slash: 56 path += '/' 57 log("translate_path(%s)=%s", s, path) 58 return path 59 60 61 def log_error(self, fmt, *args): 62 log.error(fmt, *args) 63 64 def log_message(self, fmt, *args): 65 #log.warn("%s", (fmt, args)) 66 log(fmt, *args) 67 68 def print_traffic(self, token="."): 69 """ Show traffic flow mode. """ 70 if self.traffic: 71 log(token) 72 73 74 class WebSocketConnection(SocketConnection): 75 76 def __init__(self, socket, local, remote, target, info, ws_handler): 77 SocketConnection.__init__(self, socket, local, remote, target, info) 78 self.socket_type = "websocket" 79 self.ws_handler = ws_handler 80 81 def read(self, n): 82 while self.is_active(): 83 bufs, closed_string = self.ws_handler.recv_frames() 84 if closed_string: 85 self.active = False 86 if len(bufs) == 1: 87 self.input_bytecount += len(bufs[0]) 88 return bufs[0] 89 elif len(bufs) > 1: 90 buf = b''.join(bufs) 91 self.input_bytecount += len(buf) 92 return buf 93 94 def write(self, buf): 95 self.ws_handler.send_frames([buf]) 96 self.output_bytecount += len(buf) 97 return len(buf) -
xpra/platform/win32/shadow_server.py
17 17 log = Logger("shadow", "win32") 18 18 traylog = Logger("tray") 19 19 shapelog = Logger("shape") 20 netlog = Logger("network") 20 21 21 22 from xpra.os_util import StringIOClass 22 23 from xpra.server.gtk_server_base import GTKServerBase … … 248 249 return GTKServerBase._new_connection(self, listener) 249 250 pipe_handle = args[0] 250 251 conn = NamedPipeConnection(listener.pipe_name, pipe_handle) 251 return self.make_protocol(socktype, conn, frominfo=" on %s" % conn.target) 252 netlog.info("New %s connection received on %s", socktype, conn.target) 253 return self.make_protocol(socktype, conn, frominfo=conn.target) 252 254 253 255 254 256 def make_tray_widget(self): -
xpra/scripts/server.py
510 510 s.close() 511 511 return port 512 512 513 def start_websockify(child_reaper, opts, tcp_sockets):514 from xpra.log import Logger515 log = Logger("server")516 # start websockify?517 log("html=%s", opts.html)518 if not opts.html:519 return520 html = opts.html521 if type(html)==str:522 html = html.lower()523 from xpra.scripts.config import FALSE_OPTIONS, TRUE_OPTIONS524 if html in FALSE_OPTIONS:525 #html disabled526 return527 log("tcp_proxy=%s", opts.tcp_proxy)528 if opts.tcp_proxy:529 raise InitException("cannot use tcp-proxy mode with html, use one or the other")530 return 1531 from xpra.platform.paths import get_resources_dir, get_websockify_command532 www_dir = os.path.abspath(os.path.join(get_resources_dir(), "www"))533 if not os.path.exists(www_dir):534 raise InitException("cannot find xpra's html directory (not found in '%s')" % www_dir)535 log("www_dir=%s", www_dir)536 import websockify #@UnresolvedImport537 log("websockify=%s", websockify)538 assert websockify539 html_port = -1540 html_host = "127.0.0.1"541 if html not in TRUE_OPTIONS:542 #we expect either HOST:PORT, or just PORT543 if html.find(":")>=0:544 html_host, html = html.split(":", 1)545 try:546 html_port = int(html)547 except Exception as e:548 raise InitException("invalid html port: %s" % e)549 #we now have the host and port (port may be -1 to mean auto..)550 if html_port==-1:551 #try to find a free port and hope that websockify can then use it..552 html_port = get_free_tcp_port()553 elif os.name=="posix" and html_port<1024 and os.geteuid()!=0:554 log.warn("Warning: the html port specified may require special privileges (%s:%s)", html_host, html_port)555 if len(tcp_sockets)<1:556 raise InitException("html web server requires at least one tcp socket, see 'bind-tcp'")557 #use the first tcp socket for websockify to talk back to us:558 _, xpra_tcp_port = list(tcp_sockets)[0]559 kwargs = {}560 if sys.platform.startswith("win"):561 #this is a "DOS" command, but we want to hide the shell window562 startupinfo = subprocess.STARTUPINFO()563 startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW564 kwargs = {"startupinfo" : startupinfo}565 #add bundler bin directories to path (for OSX):566 websockify_command = get_websockify_command() + ["--web", www_dir, "%s:%s" % (html_host, html_port), "127.0.0.1:%s" % xpra_tcp_port]567 log("websockify_command: %s, kwargs=%s, sys.path=%s", websockify_command, kwargs, sys.path)568 websockify_proc = subprocess.Popen(websockify_command, close_fds=True, **kwargs)569 websockify_proc._closed = False570 start_time = time.time()571 def websockify_ended(proc):572 elapsed = time.time()-start_time573 log("websockify_ended(%s) after %i seconds", proc, elapsed)574 if not websockify_proc._closed:575 log.warn("Warning: websockify has terminated,")576 log.warn(" the html web server will not be available.")577 log.warn(" the command used was:")578 log.warn(" %s", " ".join(websockify_command))579 return False580 child_reaper.add_process(websockify_proc, "websockify", websockify_command, ignore=True, callback=websockify_ended)581 log.info("websockify started, serving %s on %s:%s", www_dir, html_host, html_port)582 def cleanup_websockify():583 log("cleanup_websockify() process.poll()=%s, pid=%s", websockify_proc.poll(), websockify_proc.pid)584 if websockify_proc.poll() is None and not websockify_proc._closed:585 log.info("stopping websockify with pid %s", websockify_proc.pid)586 try:587 websockify_proc._closed = True588 websockify_proc.terminate()589 except:590 log.warn("error trying to stop websockify", exc_info=True)591 _cleanups.append(cleanup_websockify)592 opts.tcp_proxy = "%s:%s" % (html_host, html_port)593 594 595 513 def close_all_fds(exceptions=[]): 596 514 fd_dirs = ["/dev/fd", "/proc/self/fd"] 597 515 for fd_dir in fd_dirs: … … 1303 1221 1304 1222 #honour start child, html webserver, and setup child reaper 1305 1223 if not proxying and not upgrading: 1306 # start websockify?1307 try:1308 start_websockify(app.child_reaper, opts, bind_tcp)1309 #websockify overrides the tcp proxy, so we must re-set it:1310 app._tcp_proxy = opts.tcp_proxy1311 except Exception as e:1312 error_cb("failed to setup websockify html server: %s" % e)1313 1224 if opts.exit_with_children: 1314 1225 assert opts.start_child, "exit-with-children was specified but start-child is missing!" 1315 1226 app.start_commands = opts.start -
xpra/server/server_core.py
28 28 from xpra.server import ClientException 29 29 from xpra.scripts.main import SOCKET_TIMEOUT, _socket_connect 30 30 from xpra.scripts.server import deadly_signal 31 from xpra.scripts.config import InitException 31 from xpra.scripts.config import InitException, parse_bool 32 32 from xpra.net.bytestreams import SocketConnection, pretty_socket, set_socket_timeout 33 33 from xpra.platform import set_name 34 from xpra.os_util import load_binary_file, get_machine_id, get_user_uuid, platform_name, SIGNAMES , Queue34 from xpra.os_util import load_binary_file, get_machine_id, get_user_uuid, platform_name, SIGNAMES 35 35 from xpra.version_util import version_compat_check, get_version_info_full, get_platform_info, get_host_info, local_version 36 36 from xpra.net.protocol import Protocol, get_network_caps, sanity_checks 37 37 from xpra.net.crypto import crypto_backend_init, new_cipher_caps, \ … … 40 40 from xpra.make_thread import make_thread 41 41 from xpra.scripts.fdproxy import XpraProxy 42 42 from xpra.server.control_command import ControlError, HelloCommand, HelpCommand, DebugControl 43 from xpra.util import csv, merge_dicts, typedict, notypedict, flatten_dict, parse_simple_dict, repr_ellipsized, dump_all_frames, \43 from xpra.util import csv, merge_dicts, typedict, notypedict, flatten_dict, parse_simple_dict, repr_ellipsized, dump_all_frames, nonl, \ 44 44 SERVER_SHUTDOWN, SERVER_UPGRADE, LOGIN_TIMEOUT, DONE, PROTOCOL_ERROR, SERVER_ERROR, VERSION_ERROR, CLIENT_REQUEST 45 45 46 46 main_thread = threading.current_thread() … … 147 147 self._potential_protocols = [] 148 148 self._tcp_proxy_clients = [] 149 149 self._tcp_proxy = "" 150 self._html = False 150 151 self._aliases = {} 151 152 self._reverse_aliases = {} 152 153 self.socket_types = {} … … 191 192 self.unix_socket_paths = [] 192 193 self._socket_dir = opts.socket_dir or opts.socket_dirs[0] 193 194 self._tcp_proxy = opts.tcp_proxy 195 self._html = parse_bool("html", opts.html) 196 if self._html: 197 try: 198 from xpra.net.websocket import WebSocketConnection 199 assert WebSocketConnection 200 except ImportError as e: 201 log.error("Error: cannot import websockify connection handler:") 202 log.error(" %s", e) 203 log.error(" the html server will not be available") 204 self._html = False 205 if self._html and self._tcp_proxy: 206 log.warn("Warning: the built in html server is enabled,") 207 log.warn(" disabling the tcp-proxy option") 194 208 self.encryption = opts.encryption 195 209 self.encryption_keyfile = opts.encryption_keyfile 196 210 self.tcp_encryption = opts.tcp_encryption … … 472 486 netlog("new_connection(%s) sock=%s, timeout=%s, sockname=%s, address=%s, peername=%s", args, sock, self._socket_timeout, sockname, address, peername) 473 487 conn = SocketConnection(sock, sockname, address, target, socktype) 474 488 netlog("socket connection: %s", conn) 475 frominfo = ""476 489 if peername: 477 frominfo = " from %s" % pretty_socket(peername) 490 frominfo = pretty_socket(peername) 491 info_msg = "New %s connection received from %s" % (socktype, frominfo) 478 492 elif socktype=="unix-domain": 479 frominfo = " on %s" % sockname 493 frominfo = sockname 494 info_msg = "New %s connection received on %s" % (socktype, frominfo) 495 else: 496 frominfo = "" 497 info_msg = "New %s connection received" 498 499 if socktype=="tcp" and self._html or self._tcp_proxy: 500 #see if the packet data is actually xpra or something else 501 #that we need to handle via a tcp proxy or the websockify adapter: 502 conn._socket.settimeout(5) 503 v = conn.peek(128) 504 netlog("peek()=%s", nonl(v)) 505 if v and v[0] not in ("P", ord("P")): 506 if self._html: 507 line1 = v.splitlines()[0] 508 if line1.find("HTTP/")>0: 509 log.info("New HTTP connection received from %s", frominfo) 510 def run_websockify(): 511 self.start_websockify(conn, frominfo) 512 make_thread(run_websockify, "websockify-proxy-for-%s" % frominfo, daemon=True).start() 513 return True 514 elif self._tcp_proxy: 515 log.info("New TCP proxy connection received from %s", frominfo) 516 def run_proxy(): 517 self.start_tcp_proxy(conn, frominfo) 518 make_thread(run_proxy, "tcp-proxy-for-%s" % frominfo, daemon=True).start() 519 return True 520 netlog("") 521 #FIXME: if we have peek data and it isn't an xpra client, 522 #we can bail out early without making a protocol 523 netlog.info(info_msg) 480 524 return self.make_protocol(socktype, conn, frominfo) 481 525 482 526 def make_protocol(self, socktype, conn, frominfo=""): 483 netlog.info("New %s connection received%s", socktype, frominfo)484 527 protocol = Protocol(self, conn, self.process_packet) 485 528 self._potential_protocols.append(protocol) 486 529 protocol.large_packets.append("info-response") … … 509 552 self.timeout_add(SOCKET_TIMEOUT*1000, self.verify_connection_accepted, protocol) 510 553 return True 511 554 512 513 555 def invalid_header(self, proto, data): 514 netlog("invalid_header(%s, %s bytes: '%s') input_packetcount=%s, tcp_proxy=%s", proto, len(data or ""), repr_ellipsized(data), proto.input_packetcount, self._tcp_proxy) 515 if proto.input_packetcount==0 and self._tcp_proxy and not proto._closed: 516 #start a new proxy in a thread 517 def run_proxy(): 518 self.start_tcp_proxy(proto, data) 519 make_thread(run_proxy, "web-proxy-for-%s" % proto, daemon=True).start() 520 return 556 netlog("invalid_header(%s, %s bytes: '%s') input_packetcount=%s, tcp_proxy=%s, html=%s", proto, len(data or ""), repr_ellipsized(data), proto.input_packetcount, self._tcp_proxy, self._html) 521 557 err = "invalid packet format, not an xpra client?" 522 558 proto.gibberish(err, data) 523 559 524 def start_tcp_proxy(self, proto, data): 525 proxylog("start_tcp_proxy(%s, '%s')", proto, repr_ellipsized(data)) 560 561 def start_websockify(self, conn, frominfo): 562 log("start_websockify(%s, %s)", conn, frominfo) 563 from xpra.net.websocket import WebSocketConnection, WSRequestHandler 526 564 try: 527 self._potential_protocols.remove(proto) 528 except: 529 pass #might already have been removed by now 530 proxylog("start_tcp_proxy: protocol state before stealing: %s", proto.get_info(alias_info=False)) 531 #any buffers read after we steal the connection will be placed in this temporary queue: 532 temp_read_buffer = Queue() 533 client_connection = proto.steal_connection(temp_read_buffer.put) 565 def new_websocket_client(wsh): 566 log("new_websocket_client(%s)", wsh) 567 wsc = WebSocketConnection(conn._socket, conn.local, conn.remote, conn.target, conn.info, wsh) 568 set_socket_timeout(conn, self._socket_timeout) 569 self.make_protocol("tcp", wsc, frominfo) 570 WSRequestHandler(conn._socket, frominfo, new_websocket_client) 571 return 572 except IOError as e: 573 netlog.error("Error: http failure responding to %s:", frominfo) 574 netlog.error(" %s", e) 575 netlog("", exc_info=True) 576 except Exception as e: 577 netlog.error("Error: http failure responding to %s:", frominfo, exc_info=True) 578 try: 579 conn.close() 580 except Exception as ce: 581 netlog("error closing connection following error: %s", ce) 582 583 def start_tcp_proxy(self, conn, frominfo): 584 proxylog("start_tcp_proxy(%s, %s)", conn, frominfo) 534 585 #connect to web server: 535 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)536 sock.settimeout(10)537 586 host, port = self._tcp_proxy.split(":", 1) 538 587 try: 539 web_server_connection = _socket_connect(sock, (host, int(port)), "web-proxy-for-%s" % proto, "tcp") 588 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 589 sock.settimeout(10) 590 host, port = self._tcp_proxy.split(":", 1) 591 tcp_server_connection = _socket_connect(sock, (host, int(port)), "web-proxy-for-%s" % frominfo, "tcp") 540 592 except: 541 593 proxylog.warn("failed to connect to proxy: %s:%s", host, port) 542 proto.gibberish("invalid packet header", data)594 conn.close() 543 595 return 544 proxylog("proxy connected to tcp server at %s:%s : %s", host, port, web_server_connection) 596 proxylog("proxy connected to tcp server at %s:%s : %s", host, port, tcp_server_connection) 597 sock = tcp_server_connection._socket 545 598 sock.settimeout(self._socket_timeout) 546 599 547 ioe = proto.wait_for_io_threads_exit(0.5+self._socket_timeout) 548 if not ioe: 549 proxylog.warn("proxy failed to stop all existing network threads!") 550 self.disconnect_protocol(proto, "internal threading error") 551 return 552 #now that we own it, we can start it again: 553 client_connection.set_active(True) 554 #and we can use blocking sockets: 555 set_socket_timeout(client_connection, None) 556 #prevent deadlocks on exit: 600 #we can use blocking sockets for the client: 601 conn.settimeout(None) 602 #but not for the server, which could deadlock on exit: 557 603 sock.settimeout(1) 558 604 559 proxylog("pushing initial buffer to its new destination: %s", repr_ellipsized(data)) 560 web_server_connection.write(data) 561 while not temp_read_buffer.empty(): 562 buf = temp_read_buffer.get() 563 if buf: 564 proxylog("pushing read buffer to its new destination: %s", repr_ellipsized(buf)) 565 web_server_connection.write(buf) 566 p = XpraProxy(client_connection.target, client_connection, web_server_connection, self.tcp_proxy_quit) 605 #now start forwarding: 606 p = XpraProxy(frominfo, conn, tcp_server_connection, self.tcp_proxy_quit) 567 607 self._tcp_proxy_clients.append(p) 568 proxylog.info("client connection from %s forwarded to proxy server on %s:%s", client_connection.target, host, port)608 proxylog.info("client connection from %s forwarded to proxy server on %s:%s", frominfo, host, port) 569 609 p.start_threads() 570 610 611 571 612 def tcp_proxy_quit(self, proxy): 572 613 proxylog("tcp_proxy_quit(%s)", proxy) 573 614 if proxy in self._tcp_proxy_clients: