python 下有个wsgi的封装库.wsgiref.python
WSGI 指的是 Web服务器网关接口(Python Web Server Gateway Interface) git
django的runserver用到了这个标准库,学习一下。。web
涉及到的几个模块:HTTPServer,SocketServer,mimetools.Message(分析HTTP请求中的headers),socket(必须的),threading(用来实现ThreadingServer),select(用来实现非阻塞accept)apache
wsgi在python的web世界中是至关出名的。apache有个wsgi接口:mod_wsgi,因此只要把web框架作成这种形式的话,能够比较方便的部署到Apache上。django
wsgiref中重要的模块有simple_server,handlers模块。分别实现了WSGIServer,SimpleHandler这两个模块。浏览器
继承:服务器
Server:app
WSGIServer继承自HTTPServer(主要是用到了get_request这个方法去验证一下HTTP请求行。),HTTPServer继承了TCPServerHTTPServer继承自BaseServer。框架
Handlers:涉及两种Handler:异步
WSGIRequestHandler继承自BaseHTTPRequestHandler,后者继承自StreamRequesthandler.功能是TCP请求->HTTP请求->WSGI请求。
ServerHandler继承了wsgiref.handlers.SimpleHandler,后者继承了本身的BaseHandler
至此,理清头绪。
SocketServer.py中发生的事:
从tcp套接字讲起,在SocketServer中使用select.select去轮询server端口,timeout默认的是0.5s.若是发现该端口可读了,说明有请求到来了,否则就一直循环,直到按下“”终止组合键“”
接着:在TCPServer中调用accept,返回响应套接字,今后,request对象的“根”产生了!
拿到这个request,首先去调用verify_request函数验证ip是否符合要求(能够加一个黑白名单什么滴。。)
若是验证经过--》调用process_request函数去处理请求。
process_request函数,对于ThreadingTCPServer或者ForkingTCPServer(混入ThreadingMixin或者ForkingMixin)来讲,就是去创建新的线程或者进程,在新的线程或者进程中去执行finish_request完成这个请求
TCPServer自己没有实现这个方法,由于TCPServer原本就是Server,为何要去实现呢,那是handler须要作的。
接着finish_request函数:
生成一个(TCPHandler)对象,参数就是这个响应套接字地址,address,和self,这个self就是本server对象嘛。
而后就能够去分析TCPHandler了
SocketServer.py中有一个StreamRequestHandler,给了一个模板。这个StreamRequestHandler继承了BaseRequetHandler。
BaseRequestHandler其实没作什么事,在__init__方法中简单的把参数记录到这个handler实例中去,而后setup--》handle--》finish
在StreamRequestHandler中关掉了nagle(TCP默认会开启拥塞控制,减小通信量,关闭后,每一个产生的包会被发送),调用makefile,创建socket的读写文件描述符(rfile和wfile)。在mimetools.Message中,要直接用fp来作参数。。
handle就是要须要被继续覆盖下去的方法,为何呢,谁也不知道要返回什么东西。
下面是BaseHttpServer中发生的事。
BaseHttpServer主要两个类,HTTPServer和BaseHTTPRequestHandler。
HTTPServer没作什么重要事,继承一下,更名换姓,TCPServer就成了HTTPServer,由于Server的任务不就是一直等Request对象么。。(傻瓜同样的每秒两次select...)
重点是BaseHTTPRequestHandler..由于继承了StreamRequestHandler,因此关于接口的事就是去覆盖handle方法:
对于HTTP协议来讲,常见的版本是1.1,不常见的是1.0,很不常见的有0.9,奇葩的是大于1.1的。碰到大于1.1,不该该是正常的浏览器会产生的行为吧。。
HTTP0.9很简单:请求 command path,返回status msg 。
1.0和1.1主要区别在于1.0是会对于url发送一次tcp请求,1.1会在一个tcp连接中传送多个HTTP请求,增长了效率。
继续覆盖handle方法:
BaseHTTPRequestHandler方法主要作的事就是分析请求行中的HTTP协议版本号返回414,400,505.错误。
并调用"do_"+command方法
值得提一下的是这个类提供了send_response,send_hander,end_header,方法。若是想要覆盖掉自带的Server的话,覆盖version_string函数。
接下来就该看看WSGI了:
代码在文件wsgiref.simple_server中
WSGIServer因为继承了HTTPServer,没什么要作的事。
重点仍在于Handler中:WSGIRequestHandler。。
WSGIRequestHandler继承BaseHTTPRequestHandler:
可是并无去实现do_XXX这些方法,而是直接去覆盖了handle这个BaseTCPServer爷爷给他的方法。。
WSGIRequestHandler借用了BaseHTTPRequestHandler的parse_request方法进行分析request,而后生产一个ServerHandler,像StreamRequestHandler同样有个request_handler对象反过来引用这个BaseHTTPRequestHandler
源码是这样的:
handler = ServerHandler( self.rfile, self.wfile, self.get_stderr(), self.get_environ() ) handler.request_handler = self # backpointer for logging handler.run(self.server.get_app())
ServerHandler 就是 来实现WSGI规范的SimpleHandler类。最后一个参数其实就是os.environ字典而且增长了几个变量SERVER_PROTOCOL,REQUEST_METHOD,PATH_INFO,QUERY_STRING,REMOTE_HOST,REMOTE_ADDR,CONTENT_TYPE,CONTENT_LENGTH
随后就调用了这个实例的run方法。run方法的源码:
def run(self, application): """Invoke the application""" # Note to self: don't move the close()! Asynchronous servers shouldn't # call close() from finish_response(), so if you close() anywhere but # the double-error branch here, you'll break asynchronous servers by # prematurely closing. Async servers must return from 'run()' without # closing if there might still be output to iterate over. try: self.setup_environ() self.result = application(self.environ, self.start_response) self.finish_response() except: try: self.handle_error() except: # If we get an error handling an error, just give up already! self.close() raise # ...and let the actual server figure it out.
注释中提示到,WSGIRequestHandler不该该去调用socket的close方法。这是考虑到异步的状况。
setup_environ仍继续增长environ字典的内容:
def setup_environ(self): """Set up the environment for one request""" env = self.environ = self.os_environ.copy() self.add_cgi_vars() env['wsgi.input'] = self.get_stdin() env['wsgi.errors'] = self.get_stderr() env['wsgi.version'] = self.wsgi_version env['wsgi.run_once'] = self.wsgi_run_once env['wsgi.url_scheme'] = self.get_scheme() env['wsgi.multithread'] = self.wsgi_multithread env['wsgi.multiprocess'] = self.wsgi_multiprocess if self.wsgi_file_wrapper is not None: env['wsgi.file_wrapper'] = self.wsgi_file_wrapper if self.origin_server and self.server_software: env.setdefault('SERVER_SOFTWARE',self.server_software)
一个WSGI的application假如是这样子的:
from wsgiref.simple_server import WSGIServer,WSGIRequestHandler def app(env,startresponse): w=startresponse('200 ok',[],) w("hello,world") return []
start_response比较典型,继续看代码。。:
def start_response(self, status, headers,exc_info=None): """'start_response()' callable as specified by PEP 333""" if exc_info: try: if self.headers_sent: # Re-raise original exception if headers sent raise exc_info[0], exc_info[1], exc_info[2] finally: exc_info = None # avoid dangling circular ref elif self.headers is not None: raise AssertionError("Headers already set!") assert type(status) is StringType,"Status must be a string" assert len(status)>=4,"Status must be at least 4 characters" assert int(status[:3]),"Status message must begin w/3-digit code" assert status[3]==" ", "Status message must have a space after code" if __debug__: for name,val in headers: assert type(name) is StringType,"Header names must be strings" assert type(val) is StringType,"Header values must be strings" assert not is_hop_by_hop(name),"Hop-by-hop headers not allowed" self.status = status self.headers = self.headers_class(headers) return self.write
由于start_response要求参数为status,headers(要求为键值元组的列表),才能返回socket的write方法。因此必定会先返回header。而后返回body。
在startresponse简单的判断下参数是否合理,而后封装为Headers类对象这个Headers类不是
而write方法也不是简单的wfile.write()..
根据status和headers调用_write去输出HTTP响应行,Server,Date。
因此,在application中能够经过调用self.headers去修改在start_response中传入的status和headers,这并不会致使什么问题产生。。
而application的返回值对于HTTP协议的内容也就包含headers(参数),status(参数),body(write方法)了,若是application要返回的是文件,application应该返回 self.wsgi_file_wrapper类的实例。
而且,若是application返回的是文件,在application中是不该该调用write方法的。write方法调用会致使输出wfile
ok,WSGI分析完了。具体的如何发送文件,查看utils.FileWrapper,其实就是每次next返回8k的数据。。