关于WSGI相关的概念
WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述web server如何与web application通信的规范。server和application的规范在PEP 3333中有具体描述。要实现WSGI协议,必须同时实现web server和web application,当前运行在WSGI协议之上的web框架有Torando,Flask,Django, Flask 基于werkzeug 库,而werkzeug则是对WSGI 具体的实现
一、Flask 如何处理客户端发送过来的请求?
通过实例化Flask对象,调用run方法开启flask服务器,进run一看是通过调用werkzeug库中的run_simple()方法。
这里看到我们经常设置flask启动时需要传入的默认参数,值得关注是参数application、request_handler ,这里application为flask的实例,我们接着往下看
通过make_server函数返回了一个基础wsgi服务对象BaseWSGIServer,并在初始化中对request_handle进行了初始化赋值类型为WSGIRequestHandler,此类为客户端请求消息实际
处理类,后面会涉及通过server_forever()开启服务, 一路跟下去找到了处理客户段请求的循环处
我们在进去 _handle_request_noblock()这个方法看下
BaseWSGIServer类继承HTTPServer, 并重写了get_request()方法,此方式其实就说获取客户端套接字连接对象,我们继续往下跟入
finish_request是最后处理request请求的地方,RequestHandlerClass这个对象实际就是WSGI基础服务实话赋值的RequestHandlerClass 实例,他将request请求及客户端的地址
作为初始化参数
WSGIRequestHandler继承BaseHTTPRequestHandler,我们继续往上找,找到对应参数初始化的地方
可以看到此处为实际参数初始化的地方,子类通过重写handle实现具体的处理request的方法,继续往下走,来到request解析的地方
def parse_request(self): """Parse a request (internal). The request should be stored in self.raw_requestline; the results are in self.command, self.path, self.request_version and self.headers. Return True for success, False for failure; on failure, an error is sent back. """ self.command = None # set in case of error on the first line self.request_version = version = self.default_request_version self.close_connection = True requestline = str(self.raw_requestline, 'iso-8859-1') requestline = requestline.rstrip('\r\n') self.requestline = requestline words = requestline.split() if len(words) == 3: command, path, version = words if version[:5] != 'HTTP/': self.send_error( HTTPStatus.BAD_REQUEST, "Bad request version (%r)" % version) return False try: base_version_number = version.split('/', 1)[1] version_number = base_version_number.split(".") # RFC 2145 section 3.1 says there can be only one "." and # - major and minor numbers MUST be treated as # separate integers; # - HTTP/2.4 is a lower version than HTTP/2.13, which in # turn is lower than HTTP/12.3; # - Leading zeros MUST be ignored by recipients. if len(version_number) != 2: raise ValueError version_number = int(version_number[0]), int(version_number[1]) except (ValueError, IndexError): self.send_error( HTTPStatus.BAD_REQUEST, "Bad request version (%r)" % version) return False if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1": self.close_connection = False if version_number >= (2, 0): self.send_error( HTTPStatus.HTTP_VERSION_NOT_SUPPORTED, "Invalid HTTP Version (%s)" % base_version_number) return False elif len(words) == 2: command, path = words self.close_connection = True if command != 'GET': self.send_error( HTTPStatus.BAD_REQUEST, "Bad HTTP/0.9 request type (%r)" % command) return False elif not words: return False else: self.send_error( HTTPStatus.BAD_REQUEST, "Bad request syntax (%r)" % requestline) return False self.command, self.path, self.request_version = command, path, version # Examine the headers and look for a Connection directive. try: self.headers = http.client.parse_headers(self.rfile, _class=self.MessageClass) except http.client.LineTooLong: self.send_error( HTTPStatus.BAD_REQUEST, "Line too long") return False except http.client.HTTPException as err: self.send_error( HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE, "Too many headers", str(err) ) return False conntype = self.headers.get('Connection', "") if conntype.lower() == 'close': self.close_connection = True elif (conntype.lower() == 'keep-alive' and self.protocol_version >= "HTTP/1.1"): self.close_connection = False # Examine the headers and look for an Expect directive expect = self.headers.get('Expect', "") if (expect.lower() == "100-continue" and self.protocol_version >= "HTTP/1.1" and self.request_version >= "HTTP/1.1"): if not self.handle_expect_100(): return False return True
parse_request 这个方法完成对客户端请求的解析,包括请求方法,请求路径,请求协议头等
def run_wsgi(self): if self.headers.get('Expect', '').lower().strip() == '100-continue': self.wfile.write(b'HTTP/1.1 100 Continue\r\n\r\n') self.environ = environ = self.make_environ() headers_set = [] headers_sent = [] def write(data): assert headers_set, 'write() before start_response' if not headers_sent: status, response_headers = headers_sent[:] = headers_set try: code, msg = status.split(None, 1) except ValueError: code, msg = status, "" code = int(code) self.send_response(code, msg) header_keys = set() for key, value in response_headers: self.send_header(key, value) key = key.lower() header_keys.add(key) if not ('content-length' in header_keys or environ['REQUEST_METHOD'] == 'HEAD' or code < 200 or code in (204, 304)): self.close_connection = True self.send_header('Connection', 'close') if 'server' not in header_keys: self.send_header('Server', self.version_string()) if 'date' not in header_keys: self.send_header('Date', self.date_time_string()) self.end_headers() assert isinstance(data, bytes), 'applications must write bytes' self.wfile.write(data) self.wfile.flush()