UiServer.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import logging
  2. import time
  3. import cgi
  4. import socket
  5. import sys
  6. import gevent
  7. from gevent.pywsgi import WSGIServer
  8. from gevent.pywsgi import WSGIHandler
  9. from lib.geventwebsocket.handler import WebSocketHandler
  10. from UiRequest import UiRequest
  11. from Site import SiteManager
  12. from Config import config
  13. from Debug import Debug
  14. # Skip websocket handler if not necessary
  15. class UiWSGIHandler(WSGIHandler):
  16. def __init__(self, *args, **kwargs):
  17. self.server = args[2]
  18. super(UiWSGIHandler, self).__init__(*args, **kwargs)
  19. self.args = args
  20. self.kwargs = kwargs
  21. def run_application(self):
  22. if "HTTP_UPGRADE" in self.environ: # Websocket request
  23. try:
  24. ws_handler = WebSocketHandler(*self.args, **self.kwargs)
  25. ws_handler.__dict__ = self.__dict__ # Match class variables
  26. ws_handler.run_application()
  27. except Exception, err:
  28. logging.error("UiWSGIHandler websocket error: %s" % Debug.formatException(err))
  29. if config.debug: # Allow websocket errors to appear on /Debug
  30. import sys
  31. sys.modules["main"].DebugHook.handleError()
  32. else: # Standard HTTP request
  33. try:
  34. super(UiWSGIHandler, self).run_application()
  35. except Exception, err:
  36. logging.error("UiWSGIHandler error: %s" % Debug.formatException(err))
  37. if config.debug: # Allow websocket errors to appear on /Debug
  38. import sys
  39. sys.modules["main"].DebugHook.handleError()
  40. def handle(self):
  41. # Save socket to be able to close them properly on exit
  42. self.server.sockets[self.client_address] = self.socket
  43. super(UiWSGIHandler, self).handle()
  44. del self.server.sockets[self.client_address]
  45. class UiServer:
  46. def __init__(self):
  47. self.ip = config.ui_ip
  48. self.port = config.ui_port
  49. if self.ip == "*":
  50. self.ip = "0.0.0.0" # Bind all
  51. if config.ui_host:
  52. self.allowed_hosts = set(config.ui_host)
  53. elif config.ui_ip == "127.0.0.1":
  54. # IP Addresses are inherently allowed as they are immune to DNS
  55. # rebinding attacks.
  56. self.allowed_hosts = set(["zero", "localhost:%s" % config.ui_port])
  57. # "URI producers and normalizers should omit the port component and
  58. # its ':' delimiter if port is empty or if its value would be the
  59. # same as that of the scheme's default."
  60. # Source: https://tools.ietf.org/html/rfc3986#section-3.2.3
  61. # As a result, we need to support portless hosts if port 80 is in
  62. # use.
  63. if config.ui_port == 80:
  64. self.allowed_hosts.update(["localhost"])
  65. else:
  66. self.allowed_hosts = set([])
  67. self.allow_trans_proxy = config.ui_trans_proxy
  68. self.allowed_ws_origins = set()
  69. self.wrapper_nonces = []
  70. self.add_nonces = []
  71. self.websockets = []
  72. self.site_manager = SiteManager.site_manager
  73. self.sites = SiteManager.site_manager.list()
  74. self.log = logging.getLogger(__name__)
  75. # After WebUI started
  76. def afterStarted(self):
  77. from util import Platform
  78. Platform.setMaxfilesopened(config.max_files_opened)
  79. # Handle WSGI request
  80. def handleRequest(self, env, start_response):
  81. path = env["PATH_INFO"]
  82. if env.get("QUERY_STRING"):
  83. get = dict(cgi.parse_qsl(env['QUERY_STRING']))
  84. else:
  85. get = {}
  86. ui_request = UiRequest(self, get, env, start_response)
  87. if config.debug: # Let the exception catched by werkezung
  88. return ui_request.route(path)
  89. else: # Catch and display the error
  90. try:
  91. return ui_request.route(path)
  92. except Exception, err:
  93. logging.debug("UiRequest error: %s" % Debug.formatException(err))
  94. return ui_request.error500("Err: %s" % Debug.formatException(err))
  95. # Reload the UiRequest class to prevent restarts in debug mode
  96. def reload(self):
  97. global UiRequest
  98. import imp
  99. import sys
  100. reload(sys.modules["User.UserManager"])
  101. reload(sys.modules["Ui.UiWebsocket"])
  102. UiRequest = imp.load_source("UiRequest", "src/Ui/UiRequest.py").UiRequest
  103. # UiRequest.reload()
  104. # Bind and run the server
  105. def start(self):
  106. handler = self.handleRequest
  107. if config.debug:
  108. # Auto reload UiRequest on change
  109. from Debug import DebugReloader
  110. DebugReloader(self.reload)
  111. # Werkzeug Debugger
  112. try:
  113. from werkzeug.debug import DebuggedApplication
  114. handler = DebuggedApplication(self.handleRequest, evalex=True)
  115. except Exception, err:
  116. self.log.info("%s: For debugging please download Werkzeug (http://werkzeug.pocoo.org/)" % err)
  117. from Debug import DebugReloader
  118. self.log.write = lambda msg: self.log.debug(msg.strip()) # For Wsgi access.log
  119. self.log.info("--------------------------------------")
  120. self.log.info("Web interface: http://%s:%s/" % (config.ui_ip, config.ui_port))
  121. self.log.info("--------------------------------------")
  122. if config.open_browser and config.open_browser != "False":
  123. logging.info("Opening browser: %s...", config.open_browser)
  124. import webbrowser
  125. try:
  126. if config.open_browser == "default_browser":
  127. browser = webbrowser.get()
  128. else:
  129. browser = webbrowser.get(config.open_browser)
  130. url = "http://%s:%s/%s" % (config.ui_ip if config.ui_ip != "*" else "127.0.0.1", config.ui_port, config.homepage)
  131. gevent.spawn_later(0.3, browser.open, url, new=2)
  132. except Exception as err:
  133. print "Error starting browser: %s" % err
  134. self.server = WSGIServer((self.ip, self.port), handler, handler_class=UiWSGIHandler, log=self.log)
  135. self.server.sockets = {}
  136. self.afterStarted()
  137. try:
  138. self.server.serve_forever()
  139. except Exception, err:
  140. self.log.error("Web interface bind error, must be running already, exiting.... %s" % err)
  141. sys.modules["main"].file_server.stop()
  142. self.log.debug("Stopped.")
  143. def stop(self):
  144. self.log.debug("Stopping...")
  145. # Close WS sockets
  146. if "clients" in dir(self.server):
  147. for client in self.server.clients.values():
  148. client.ws.close()
  149. # Close http sockets
  150. sock_closed = 0
  151. for sock in self.server.sockets.values():
  152. try:
  153. sock.send("bye")
  154. sock.shutdown(socket.SHUT_RDWR)
  155. # sock._sock.close()
  156. # sock.close()
  157. sock_closed += 1
  158. except Exception, err:
  159. self.log.debug("Http connection close error: %s" % err)
  160. self.log.debug("Socket closed: %s" % sock_closed)
  161. time.sleep(0.1)
  162. self.server.socket.close()
  163. self.server.stop()
  164. time.sleep(1)
  165. def updateWebsocket(self, **kwargs):
  166. for ws in self.websockets:
  167. ws.event("serverChanged", kwargs)