if use_reloader: # # run_simple 函数中,最后会执行到 "省略部分代码" else: inner()
run_simple 调用了 make_server,看下 make_server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
################ serving.py 文件下的 make_server 方法 ##################### # 根据参数不同,实例化不同的 server defmake_server(host=None, port=None, app=None, threaded=False, processes=1, request_handler=None, passthrough_errors=False, ssl_context=None, fd=None): """Create a new server instance that is either threaded, or forks or just processes one request after another. """ if threaded and processes > 1: raise ValueError("cannot have a multithreaded and multi process server.") elif threaded: return ThreadedWSGIServer(host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd) elif processes > 1: return ForkingWSGIServer(host, port, app, processes, request_handler, passthrough_errors, ssl_context, fd=fd) else: return BaseWSGIServer(host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd)
run_simple 调用了 serve_forever,看下 serve_forever
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
############### BaseWSGIServer 类下的 serve_forever 方法 ####################### from http.server import HTTPServer
########## BaseServer 下的 serve_forever ####### # 具体的监听 socket ,当有请求到来时,执行传入的第三个参数,执行格式为 func(self, environ, start_response),并接受返回值 classBaseServer: defserve_forever(self, poll_interval=0.5): """Handle one request at a time until shutdown. Polls for shutdown every poll_interval seconds. Ignores self.timeout. If you need to do periodic tasks, do them in another thread. """ self.__is_shut_down.clear() try: # XXX: Consider using another file descriptor or connecting to the # socket to wake this up instead of polling. Polling reduces our # responsiveness to a shutdown request and wastes cpu at all other # times. with _ServerSelector() as selector: # 具体监听过程,就是个无限循环 接收一个请求处理一个请求 selector.register(self, selectors.EVENT_READ)
def_handle_request_noblock(self): """Handle one request, without blocking.
I assume that selector.select() has returned that the socket is readable before this function was called, so there should be no risk of blocking in get_request(). """ try: request, client_address = self.get_request() except OSError: return if self.verify_request(request, client_address): try: # 接收到具体请求后 进行处理 self.process_request(request, client_address) except Exception: self.handle_error(request, client_address) self.shutdown_request(request) except: self.shutdown_request(request) raise else: self.shutdown_request(request)
defwrite(data: bytes) -> None: nonlocal status_sent, headers_sent, chunk_response assert status_set isnotNone, "write() before start_response" assert headers_set isnotNone, "write() before start_response" if status_sent isNone: status_sent = status_set headers_sent = headers_set try: code_str, msg = status_sent.split(None, 1) except ValueError: code_str, msg = status_sent, "" code = int(code_str) self.send_response(code, msg) header_keys = set() for key, value in headers_sent: self.send_header(key, value) header_keys.add(key.lower())
# Use chunked transfer encoding if there is no content # length. Do not use for 1xx and 204 responses. 304 # responses and HEAD requests are also excluded, which # is the more conservative behavior and matches other # parts of the code. # https://httpwg.org/specs/rfc7230.html#rfc.section.3.3.1 if ( not ( "content-length"in header_keys or environ["REQUEST_METHOD"] == "HEAD" or (100 <= code < 200) or code in {204, 304} ) and self.protocol_version >= "HTTP/1.1" ): chunk_response = True self.send_header("Transfer-Encoding", "chunked")
# Always close the connection. This disables HTTP/1.1 # keep-alive connections. They aren't handled well by # Python's http.server because it doesn't know how to # drain the stream before the next request line. self.send_header("Connection", "close") self.end_headers()
assert isinstance(data, bytes), "applications must write bytes"
if data: if chunk_response: self.wfile.write(hex(len(data))[2:].encode()) self.wfile.write(b"\r\n")
# 经典的 wsgi app 调用 返回值可迭代 # 此处的 app 是 Flask 实例,实例可调用是因为 Flask 实现了 __call__ 方法 defexecute(app: "WSGIApplication") -> None: application_iter = app(environ, start_response) # 这里这里 try: for data in application_iter: write(data) ifnot headers_sent: write(b"") if chunk_response: self.wfile.write(b"0\r\n\r\n") finally: if hasattr(application_iter, "close"): application_iter.close()
try: # 执行此方法 参数为flask实例 execute(self.server.app) except (ConnectionError, socket.timeout) as e: self.connection_dropped(e, environ) except Exception as e: if self.server.passthrough_errors: raise
if status_sent isnotNoneand chunk_response: self.close_connection = True
try: # if we haven't yet sent the headers but they are set # we roll back to be able to set them again. if status_sent isNone: status_set = None headers_set = None execute(InternalServerError()) except Exception: pass
from .debug.tbtools import DebugTraceback
msg = DebugTraceback(e).render_traceback_text() self.server.log("error", f"Error on request:\n{msg}")
# 上一个类调用的handle就是这个 defhandle(self) -> None: """Handles a request ignoring dropped connections.""" try: # 调用父类的handle super().handle() except (ConnectionError, socket.timeout) as e: self.connection_dropped(e) except Exception as e: if self.server.ssl_context isnotNoneand is_ssl_error(e): self.log_error("SSL error occurred: %s", e) else: raise # 没错 下面的类又调用了这个方法 defhandle_one_request(self): """Handle a single HTTP request.""" self.raw_requestline = self.rfile.readline() ifnot self.raw_requestline: self.close_connection = 1 elif self.parse_request(): # 最终最终最终 走了这个方法 return self.run_wsgi()
if content_type isNone: if mimetype isNoneand'content-type'notin self.headers: mimetype = self.default_mimetype if mimetype isnotNone: mimetype = get_content_type(mimetype, self.charset) content_type = mimetype if content_type isnotNone: self.headers['Content-Type'] = content_type if status isNone: status = self.default_status if isinstance(status, integer_types): self.status_code = status else: self.status = status
# we set the response after the headers so that if a class changes # the charset attribute, the data is set in the correct charset. if response isNone: self.response = [] elif isinstance(response, (text_type, bytes, bytearray)): self.set_data(response) #################看这里 else: self.response = response
def__call__(self, environ, start_response): """The WSGI server calls the Flask application object as the WSGI application. This calls :meth:`wsgi_app` which can be wrapped to applying middleware.""" return self.wsgi_app(environ, start_response)
deffull_dispatch_request(self) -> Response: ifnot self._got_first_request: with self._before_request_lock: ifnot self._got_first_request: for func in self.before_first_request_funcs: self.ensure_sync(func)()
defdispatch_request(self) -> ft.ResponseReturnValue: # # 直接从上下文栈中获取当前的请求上下文 req = request_ctx.request if req.routing_exception isnotNone: self.raise_routing_exception(req) rule = req.url_rule # type: ignore[assignment] # if we provide automatic options for this URL and the # request came with the OPTIONS method, reply automatically if ( getattr(rule, "provide_automatic_options", False) and req.method == "OPTIONS" ): return self.make_default_options_response()