def run(self, host="127.0.0.1", port=8000, debug=False, before_start=None, after_start=None, before_stop=None, after_stop=None, ssl=None, sock=None, workers=1, loop=None, protocol=None, backlog=100, stop_event=None, register_sys_signals=True): """Run the HTTP Server and listen until keyboard interrupt or term signal. On termination, drain connections before closing. :param host: Address to host on :param port: Port to host on :param debug: Enables debug output (slows server) :param before_start: Functions to be executed before the server starts accepting connections :param after_start: Functions to be executed after the server starts accepting connections :param before_stop: Functions to be executed when a stop signal is received before it is respected :param after_stop: Functions to be executed when all requests are complete :param ssl: SSLContext, or location of certificate and key for SSL encryption of worker(s) :param sock: Socket for the server to accept connections from :param workers: Number of processes received before it is respected :param loop: :param backlog: :param stop_event: :param register_sys_signals: :param protocol: Subclass of asyncio protocol class :return: Nothing """ if protocol is None: protocol = (WebSocketProtocol if self.websocket_enabled else HttpProtocol) if stop_event is not None: if debug: warnings.simplefilter('default') warnings.warn("stop_event will be removed from future versions.", DeprecationWarning) server_settings = self._helper( host=host, port=port, debug=debug, before_start=before_start, after_start=after_start, before_stop=before_stop, after_stop=after_stop, ssl=ssl, sock=sock, workers=workers, loop=loop, protocol=protocol, backlog=backlog, register_sys_signals=register_sys_signals) try: self.is_running = True if workers == 1: serve(**server_settings) else: serve_multiple(server_settings, workers) except: log.exception( 'Experienced exception while trying to serve') finally: self.is_running = False log.info("Server Stopped")
def run(self, host=None, port=None, debug=False, ssl=None, sock=None, workers=1, protocol=None, backlog=100, stop_event=None, register_sys_signals=True, log_config=None): """Run the HTTP Server and listen until keyboard interrupt or term signal. On termination, drain connections before closing. :param host: Address to host on :param port: Port to host on :param debug: Enables debug output (slows server) :param ssl: SSLContext, or location of certificate and key for SSL encryption of worker(s) :param sock: Socket for the server to accept connections from :param workers: Number of processes received before it is respected :param backlog: :param stop_event: :param register_sys_signals: :param protocol: Subclass of asyncio protocol class :return: Nothing """ if sock is None: host, port = host or "127.0.0.1", port or 8000 if log_config: self.log_config = log_config logging.config.dictConfig(log_config) if protocol is None: protocol = (WebSocketProtocol if self.websocket_enabled else HttpProtocol) if stop_event is not None: if debug: warnings.simplefilter('default') warnings.warn("stop_event will be removed from future versions.", DeprecationWarning) server_settings = self._helper( host=host, port=port, debug=debug, ssl=ssl, sock=sock, workers=workers, protocol=protocol, backlog=backlog, register_sys_signals=register_sys_signals, has_log=self.log_config is not None) try: self.is_running = True if workers == 1: serve(**server_settings) else: serve_multiple(server_settings, workers) except: log.exception( 'Experienced exception while trying to serve') raise finally: self.is_running = False log.info("Server Stopped")
def run(self, host="127.0.0.1", port=8000, debug=False, sock=None, workers=1, loop=None, protocol=HttpProtocol, backlog=100, stop_event=None): """ 运行 HTTP 服务器并一直监听,直到收到键盘终端操作或终止信号。 在终止时,在关闭时释放所有连接。 :param host: 服务器地址 :param port: 服务器端口 :param debug: 开启 debug 输出 :param sock: 服务器接受数据的套接字 :param workers: 进程数 :param loop: 异步事件循环 :param protocol: 异步协议子类 """ self.error_handler.debug = True self.debug = debug self.loop = loop # 配置 server 参数 server_settings = { 'protocol': protocol, 'host': host, 'port': port, 'sock': sock, 'debug': debug, 'request_handler': self.handle_request, 'error_handler': self.error_handler, 'request_timeout': self.config.REQUEST_TIMEOUT, 'request_max_size': self.config.REQUEST_MAX_SIZE, 'loop': loop, 'backlog': backlog } if debug: log.setLevel(logging.DEBUG) # 启动服务进程 log.info('Goin\' Fast @ http://{}:{}'.format(host, port)) try: if workers == 1: serve(**server_settings) # 传入 server 参数 else: log.info('Spinning up {} workers...'.format(workers)) self.serve_multiple(server_settings, workers, stop_event) except Exception as e: log.exception( 'Experienced exception while trying to serve') log.info("Server Stopped")
def run(self, host="127.0.0.1", port=8000, debug=False, before_start=None, after_start=None, before_stop=None, after_stop=None, ssl=None, sock=None, workers=1, loop=None, protocol=HttpProtocol, backlog=100, stop_event=None, register_sys_signals=True): """Run the HTTP Server and listen until keyboard interrupt or term signal. On termination, drain connections before closing. :param host: Address to host on :param port: Port to host on :param debug: Enables debug output (slows server) :param before_start: Functions to be executed before the server starts accepting connections :param after_start: Functions to be executed after the server starts accepting connections :param before_stop: Functions to be executed when a stop signal is received before it is respected :param after_stop: Functions to be executed when all requests are complete :param ssl: SSLContext for SSL encryption of worker(s) :param sock: Socket for the server to accept connections from :param workers: Number of processes received before it is respected :param loop: :param backlog: :param stop_event: :param register_sys_signals: :param protocol: Subclass of asyncio protocol class :return: Nothing """ server_settings = self._helper( host=host, port=port, debug=debug, before_start=before_start, after_start=after_start, before_stop=before_stop, after_stop=after_stop, ssl=ssl, sock=sock, workers=workers, loop=loop, protocol=protocol, backlog=backlog, stop_event=stop_event, register_sys_signals=register_sys_signals) try: if workers == 1: serve(**server_settings) else: serve_multiple(server_settings, workers, stop_event) except Exception as e: log.exception( 'Experienced exception while trying to serve') log.info("Server Stopped")
async def process(req): # pragma: no cover ''' Await-обертка над process ''' try: result = await req.app.loop.run_in_executor(req.app.executor, _process, req.app.model, req.json) return json({'result': result, 'error': None, 'success': True}) except Exception as exc: log.exception('process error') return json( { 'success': False, 'error': 'process error: {}'.format(exc), 'result': None }, status=400)
def form(self): if self.parsed_form is None: self.parsed_form = RequestParameters() self.parsed_files = RequestParameters() content_type = self.headers.get('Content-Type', DEFAULT_HTTP_CONTENT_TYPE) content_type, parameters = parse_header(content_type) try: if content_type == 'application/x-www-form-urlencoded': self.parsed_form = RequestParameters( parse_qs(self.body.decode('utf-8'))) elif content_type == 'multipart/form-data': # TODO: Stream this instead of reading to/from memory boundary = parameters['boundary'].encode('utf-8') self.parsed_form, self.parsed_files = ( parse_multipart_form(self.body, boundary)) except Exception: log.exception("Failed when parsing form") return self.parsed_form
def form(self): if self.parsed_form is None: self.parsed_form = RequestParameters() self.parsed_files = RequestParameters() content_type = self.headers.get( 'Content-Type', DEFAULT_HTTP_CONTENT_TYPE) content_type, parameters = parse_header(content_type) try: if content_type == 'application/x-www-form-urlencoded': self.parsed_form = RequestParameters( parse_qs(self.body.decode('utf-8'))) elif content_type == 'multipart/form-data': # TODO: Stream this instead of reading to/from memory boundary = parameters['boundary'].encode('utf-8') self.parsed_form, self.parsed_files = ( parse_multipart_form(self.body, boundary)) except Exception: log.exception("Failed when parsing form") return self.parsed_form
def form(self): """ 返回解析头部表单的信息 """ if self.parsed_form is None: self.parsed_form = RequestParameters() self.parsed_files = RequestParameters() content_type = self.headers.get( 'Content-Type', DEFAULT_HTTP_CONTENT_TYPE) # 设为默认的媒体类型 content_type, parameters = parse_header(content_type) try: if content_type == 'application/x-www-form-urlencoded': # GET 方式提交表单 self.parsed_form = RequestParameters( parse_qs(self.body.decode('utf-8'))) elif content_type == 'multipart/form-data': # POST 方式提交表单 boundary = parameters['boundary'].encode('utf-8') self.parsed_form, self.parsed_files = ( parse_multipart_form(self.body, boundary)) # 此解析方法后面实现 except Exception: log.exception("Failed when parsing form") # 日志记录异常信息 return self.parsed_form
async def handle_request(self, request, write_callback, stream_callback): """Take a request from the HTTP Server and return a response object to be sent back The HTTP Server only expects a response object, so exception handling must be done here :param request: HTTP Request object :param write_callback: Synchronous response function to be called with the response as the only argument :param stream_callback: Coroutine that handles streaming a StreamingHTTPResponse if produced by the handler. :return: Nothing """ try: # -------------------------------------------- # # Request Middleware # -------------------------------------------- # request.app = self response = await self._run_request_middleware(request) # No middleware results if not response: # -------------------------------------------- # # Execute Handler # -------------------------------------------- # # Fetch handler from router handler, args, kwargs, uri = self.router.get(request) request.uri_template = uri if handler is None: raise ServerError( ("'None' was returned while requesting a " "handler from the router")) # Run response handler response = handler(request, *args, **kwargs) if isawaitable(response): response = await response except Exception as e: # -------------------------------------------- # # Response Generation Failed # -------------------------------------------- # try: response = self.error_handler.response(request, e) if isawaitable(response): response = await response except Exception as e: if self.debug: response = HTTPResponse( "Error while handling error: {}\nStack: {}".format( e, format_exc())) else: response = HTTPResponse( "An error occurred while handling an error") finally: # -------------------------------------------- # # Response Middleware # -------------------------------------------- # try: response = await self._run_response_middleware(request, response) except: log.exception( 'Exception occured in one of response middleware handlers' ) # pass the response to the correct callback if isinstance(response, StreamingHTTPResponse): await stream_callback(response) else: write_callback(response)
def serve(host, port, request_handler, error_handler, before_start=None, after_start=None, before_stop=None, after_stop=None, debug=False, request_timeout=60, ssl=None, sock=None, request_max_size=None, reuse_port=False, loop=None, protocol=HttpProtocol, backlog=100, register_sys_signals=True, run_async=False, connections=None, signal=Signal(), request_class=None, has_log=True, keep_alive=True, is_request_stream=False, router=None, websocket_max_size=None, websocket_max_queue=None, state=None): """Start asynchronous HTTP Server on an individual process. :param host: Address to host on :param port: Port to host on :param request_handler: Sanic request handler with middleware :param error_handler: Sanic error handler with middleware :param before_start: function to be executed before the server starts listening. Takes arguments `app` instance and `loop` :param after_start: function to be executed after the server starts listening. Takes arguments `app` instance and `loop` :param before_stop: function to be executed when a stop signal is received before it is respected. Takes arguments `app` instance and `loop` :param after_stop: function to be executed when a stop signal is received after it is respected. Takes arguments `app` instance and `loop` :param debug: enables debug output (slows server) :param request_timeout: time in seconds :param ssl: SSLContext :param sock: Socket for the server to accept connections from :param request_max_size: size in bytes, `None` for no limit :param reuse_port: `True` for multiple workers :param loop: asyncio compatible event loop :param protocol: subclass of asyncio protocol class :param request_class: Request class to use :param has_log: disable/enable access log and error log :param is_request_stream: disable/enable Request.stream :param router: Router object :return: Nothing """ if not run_async: loop = async_loop.new_event_loop() asyncio.set_event_loop(loop) if debug: loop.set_debug(debug) connections = connections if connections is not None else set() server = partial( protocol, loop=loop, connections=connections, signal=signal, request_handler=request_handler, error_handler=error_handler, request_timeout=request_timeout, request_max_size=request_max_size, request_class=request_class, has_log=has_log, keep_alive=keep_alive, is_request_stream=is_request_stream, router=router, websocket_max_size=websocket_max_size, websocket_max_queue=websocket_max_queue, state=state, debug=debug, ) server_coroutine = loop.create_server(server, host, port, ssl=ssl, reuse_port=reuse_port, sock=sock, backlog=backlog) # Instead of pulling time at the end of every request, # pull it once per minute loop.call_soon(partial(update_current_time, loop)) if run_async: return server_coroutine trigger_events(before_start, loop) try: http_server = loop.run_until_complete(server_coroutine) except: log.exception("Unable to start server") return trigger_events(after_start, loop) # Register signals for graceful termination if register_sys_signals: for _signal in (SIGINT, SIGTERM): try: loop.add_signal_handler(_signal, loop.stop) except NotImplementedError: log.warn('Sanic tried to use loop.add_signal_handler but it is' ' not implemented on this platform.') pid = os.getpid() try: log.info('Starting worker [{}]'.format(pid)) loop.run_forever() finally: log.info("Stopping worker [{}]".format(pid)) # Run the on_stop function if provided trigger_events(before_stop, loop) # Wait for event loop to finish and all connections to drain http_server.close() loop.run_until_complete(http_server.wait_closed()) # Complete all tasks on the loop signal.stopped = True for connection in connections: connection.close_if_idle() while connections: loop.run_until_complete(asyncio.sleep(0.1)) trigger_events(after_stop, loop) loop.close()
async def handle_request(self, request, write_callback, stream_callback): """Take a request from the HTTP Server and return a response object to be sent back The HTTP Server only expects a response object, so exception handling must be done here :param request: HTTP Request object :param write_callback: Synchronous response function to be called with the response as the only argument :param stream_callback: Coroutine that handles streaming a StreamingHTTPResponse if produced by the handler. :return: Nothing """ try: # -------------------------------------------- # # Request Middleware # -------------------------------------------- # request.app = self response = await self._run_request_middleware(request) # No middleware results if not response: # -------------------------------------------- # # Execute Handler # -------------------------------------------- # # Fetch handler from router handler, args, kwargs, uri = self.router.get(request) request.uri_template = uri if handler is None: raise ServerError( ("'None' was returned while requesting a " "handler from the router")) # Run response handler response = handler(request, *args, **kwargs) if isawaitable(response): response = await response except Exception as e: # -------------------------------------------- # # Response Generation Failed # -------------------------------------------- # try: response = self.error_handler.response(request, e) if isawaitable(response): response = await response except Exception as e: if self.debug: response = HTTPResponse( "Error while handling error: {}\nStack: {}".format( e, format_exc())) else: response = HTTPResponse( "An error occurred while handling an error") finally: # -------------------------------------------- # # Response Middleware # -------------------------------------------- # try: response = await self._run_response_middleware(request, response) except: log.exception( 'Exception occured in one of response middleware handlers' ) # pass the response to the correct callback if isinstance(response, StreamingHTTPResponse): await stream_callback(response) else: write_callback(response)
def serve(host, port, request_handler, error_handler, before_start=None, after_start=None, before_stop=None, after_stop=None, debug=False, request_timeout=60, ssl=None, sock=None, request_max_size=None, reuse_port=False, loop=None, protocol=HttpProtocol, backlog=100, register_sys_signals=True, run_async=False, connections=None, signal=Signal(), request_class=None, has_log=True, keep_alive=True, is_request_stream=False, router=None, websocket_max_size=None, websocket_max_queue=None, state=None, graceful_shutdown_timeout=15.0): """Start asynchronous HTTP Server on an individual process. :param host: Address to host on :param port: Port to host on :param request_handler: Sanic request handler with middleware :param error_handler: Sanic error handler with middleware :param before_start: function to be executed before the server starts listening. Takes arguments `app` instance and `loop` :param after_start: function to be executed after the server starts listening. Takes arguments `app` instance and `loop` :param before_stop: function to be executed when a stop signal is received before it is respected. Takes arguments `app` instance and `loop` :param after_stop: function to be executed when a stop signal is received after it is respected. Takes arguments `app` instance and `loop` :param debug: enables debug output (slows server) :param request_timeout: time in seconds :param ssl: SSLContext :param sock: Socket for the server to accept connections from :param request_max_size: size in bytes, `None` for no limit :param reuse_port: `True` for multiple workers :param loop: asyncio compatible event loop :param protocol: subclass of asyncio protocol class :param request_class: Request class to use :param has_log: disable/enable access log and error log :param is_request_stream: disable/enable Request.stream :param router: Router object :return: Nothing """ if not run_async: loop = async_loop.new_event_loop() asyncio.set_event_loop(loop) if debug: loop.set_debug(debug) connections = connections if connections is not None else set() server = partial( protocol, loop=loop, connections=connections, signal=signal, request_handler=request_handler, error_handler=error_handler, request_timeout=request_timeout, request_max_size=request_max_size, request_class=request_class, has_log=has_log, keep_alive=keep_alive, is_request_stream=is_request_stream, router=router, websocket_max_size=websocket_max_size, websocket_max_queue=websocket_max_queue, state=state, debug=debug, ) server_coroutine = loop.create_server( server, host, port, ssl=ssl, reuse_port=reuse_port, sock=sock, backlog=backlog ) # Instead of pulling time at the end of every request, # pull it once per minute loop.call_soon(partial(update_current_time, loop)) if run_async: return server_coroutine trigger_events(before_start, loop) try: http_server = loop.run_until_complete(server_coroutine) except: log.exception("Unable to start server") return trigger_events(after_start, loop) # Register signals for graceful termination if register_sys_signals: for _signal in (SIGINT, SIGTERM): try: loop.add_signal_handler(_signal, loop.stop) except NotImplementedError: log.warn('Sanic tried to use loop.add_signal_handler but it is' ' not implemented on this platform.') pid = os.getpid() try: log.info('Starting worker [{}]'.format(pid)) loop.run_forever() finally: log.info("Stopping worker [{}]".format(pid)) # Run the on_stop function if provided trigger_events(before_stop, loop) # Wait for event loop to finish and all connections to drain http_server.close() loop.run_until_complete(http_server.wait_closed()) # Complete all tasks on the loop signal.stopped = True for connection in connections: connection.close_if_idle() # Gracefully shutdown timeout. # We should provide graceful_shutdown_timeout, # instead of letting connection hangs forever. # Let's roughly calcucate time. start_shutdown = 0 while connections and (start_shutdown < graceful_shutdown_timeout): loop.run_until_complete(asyncio.sleep(0.1)) start_shutdown = start_shutdown + 0.1 # Force close non-idle connection after waiting for # graceful_shutdown_timeout coros = [] for conn in connections: if hasattr(conn, "websocket") and conn.websocket: coros.append(conn.websocket.close_connection(force=True)) else: conn.close() _shutdown = asyncio.gather(*coros, loop=loop) loop.run_until_complete(_shutdown) trigger_events(after_stop, loop) loop.close()
async def handle_request(self, request, write_callback=None, stream_callback=None): """ Takes a request from the HTTP Server and returns a response object to be sent back The HTTP Server only expects a response object, so exception handling must be done here :param request: HTTP Request object :param response_callback: Response function to be called with the response as the only argument :return: Nothing """ try: # -------------------------------------------- # # Request Middleware # -------------------------------------------- # response = False # The if improves speed. I don't know why if self.request_middleware: for middleware in self.request_middleware: response = middleware(request, env=self.env) if isawaitable(response): response = await response if response: break # No middleware results if not response: # -------------------------------------------- # # Execute Handler # -------------------------------------------- # # Fetch handler from router handler, args, kwargs = self.router.get(request) if handler is None: raise ServerError( ("'None' was returned while requesting a " "handler from the router")) # Run response handler response = handler(request, self.env, *args, **kwargs)() if isawaitable(response): response = await response # -------------------------------------------- # # Response Middleware # -------------------------------------------- # if self.response_middleware: for middleware in self.response_middleware: _response = middleware(request, response, env=self.env) if isawaitable(_response): _response = await _response if _response: response = _response break except BaseExcep as e: if e.log: log.exception(e.args) response = json( { 'code': e.code, 'data': e.data, 'id': None, 'msg': e.msg }, 200) except Exception as e: # -------------------------------------------- # # Response Generation Failed # -------------------------------------------- # try: log.exception(e.args) response = self.error_handler.response(request, e) if isawaitable(response): response = await response if response.status == 500: if self.debug: response = json( { 'code': -1, 'data': format_exc(), 'msg': e.__repr__(), 'id': None }, 200) else: response = json( { 'code': -1, 'data': e.__repr__(), 'msg': '系统出错', 'id': None }, 200) except Exception as e: if self.debug: response = HTTPResponse( "Error while handling error: {}\nStack: {}".format( e, format_exc())) else: response = HTTPResponse( "An error occured while handling an error") write_callback(response)
def serve(host, port, request_handler, error_handler, debug=False, request_timeout=60, sock=None, request_max_size=None, reuse_port=False, loop=None, protocol=HttpProtocol, backlog=100): """ 在一个独立进程中启动异步 HTTP 服务器. :param host: 服务器地址 :param port: 服务器端口 :param request_handler: 请求处理器 :param error_handler: 异常处理器 :param debug: 开启 debug 输出 :param request_timeout: 以秒为单位,请求超时时间 :param sock: 接受连接的套接字 :param request_max_size: 大小以字节为单位,`None`代表无限制 :param reuse_port: `True` for multiple workers :param loop: 异步事件循环 :param protocol: 异步协议类的子类 """ # 创建事件循环 loop = loop or async_loop.new_event_loop() asyncio.set_event_loop(loop) # 开启 debug if debug: loop.set_debug(debug) connections = set() signal = Signal() # 配置 server 参数 server = partial( protocol, loop=loop, connections=connections, signal=signal, request_handler=request_handler, error_handler=error_handler, request_timeout=request_timeout, request_max_size=request_max_size, ) # 创建 server 协程 server_coroutine = loop.create_server( server, host, port, reuse_port=reuse_port, sock=sock, backlog=backlog ) # 每分钟都 pull time,而不是在每个请求结束后 loop.call_soon(partial(update_current_time, loop)) try: http_server = loop.run_until_complete(server_coroutine) # 启动协程 except Exception: log.exception("Unable to start server") return # Register signals for graceful termination for _signal in (SIGINT, SIGTERM): loop.add_signal_handler(_signal, loop.stop) # 启动服务器 try: loop.run_forever() finally: log.info("Stop requested, draining connections...") # 事件循环解说后释放所有连接 http_server.close() loop.run_until_complete(http_server.wait_closed()) # 再循环中完成所有 tasks signal.stopped = True for connection in connections: connection.close_if_idle() while connections: loop.run_until_complete(asyncio.sleep(0.1)) loop.close()