python 下有个wsgi的封装库.wsgiref.

WSGI 指的是 Web服务器网关接口(Python Web Server Gateway Interface)

django的runserver用到了这个标准库,学习一下。。

涉及到的几个模块:HTTPServer,SocketServer,mimetools.Message(分析HTTP请求中的headers),socket(必须的),threading(用来实现ThreadingServer),select(用来实现非阻塞accept)

wsgi在python的web世界中是相当出名的。apache有个wsgi接口:mod_wsgi,所以只要把web框架做成这种形式的话,可以比较方便的部署到Apache上。

wsgiref中重要的模块有simple_server,handlers模块。分别实现了WSGIServer,SimpleHandler这两个模块。

继承:

Server:

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的数据。。

  

python 从SocketServer到 WSGIServer 源码分析、的更多相关文章

  1. python apschedule安装使用与源码分析

    我们的项目中用apschedule作为核心定时调度模块.所以对apschedule进行了一些调查和源码级的分析. 1.为什么选择apschedule? 听信了一句话,apschedule之于pytho ...

  2. Python之Django rest_Framework框架源码分析

    #!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_fram ...

  3. Python之contextlib库及源码分析

    Utilities for with-statement contexts __all__ = ["contextmanager", "closing", &q ...

  4. Python yield与实现(源码分析 转)

    转自:https://www.cnblogs.com/coder2012/p/4990834.html

  5. python基础-11 socket,IO多路复用,select伪造多线程,select读写分离。socketserver源码分析

    Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. sock ...

  6. Python之美[从菜鸟到高手]--urlparse源码分析

    urlparse是用来解析url格式的,url格式如下:protocol :// hostname[:port] / path / [;parameters][?query]#fragment,其中; ...

  7. Socketserver的源码分析

    Socketserver的源码分析        

  8. [python] 基于词云的关键词提取:wordcloud的使用、源码分析、中文词云生成和代码重写

    1. 词云简介 词云,又称文字云.标签云,是对文本数据中出现频率较高的“关键词”在视觉上的突出呈现,形成关键词的渲染形成类似云一样的彩色图片,从而一眼就可以领略文本数据的主要表达意思.常见于博客.微博 ...

  9. Python源码分析(二) - List对象

    python中的高级特性之一就是内置了list,dict等.今天就先围绕列表(List)进行源码分析. Python中的List对象(PyListObject) Python中的的PyListObje ...

随机推荐

  1. linux-insides-cn-BOOK

    https://www.gitbook.com/book/xinqiu/linux-insides-cn/details https://www.gitbook.com/book/looly/elas ...

  2. android 开源 OCR 项目 及手写识别

    http://blog.csdn.net/archfree/article/details/6023676 1)一个为Android平台,将识别由手机的相机拍摄的图像文本应用程序. http://co ...

  3. iOS 符号表恢复 & 逆向支付宝

    推荐序 本文介绍了恢复符号表的技巧,并且利用该技巧实现了在 Xcode 中对目标程序下符号断点调试,该技巧可以显著地减少逆向分析时间.在文章的最后,作者以支付宝为例,展示出通过在 UIAlertVie ...

  4. Asp.Net 之 调用远程Web_Service

    一.添加web service引用 1.右键 Web 项目 → “添加服务引用”: 2.右键已有的 App_WebReferences 文件夹 → “添加服务引用”: 二.引用远程web servic ...

  5. 数据导入问题:[Err] [Imp] 1406 - Data too long for column 'linkman' at row 20 [Err] [Imp] INSERT INTO `excel_eprinfo`

    导入数据:提示错误 [Err] [Imp] 1406 - Data too long for column 'linkman' at row 20[Err] [Imp] INSERT INTO `ex ...

  6. java.lang.ClassFormatError: Illegal UTF8 string in constant pool in class file Server/Request

    Linux服务器上,将本地编译好的文件上传后,Tomcat启动时报错: Exception in thread "Thread-2" java.lang.ClassFormatEr ...

  7. [记录]java.math.biginteger cannot be cast to java.lang.long

    可以直接使用BigInteger类型进行接收, BigInteger id = (BigInteger)QueryRunner(conn,"SELECT LAST_INSERT_ID&quo ...

  8. android之APP+JNI+Drv框架

    以LED为例 APP: JNI之java JNI之c DRV 项目组成:1.应用部分 1.1 APK(android工程) 1.1.1 java(功能) 1.1.2 xml(界面) 1.1.3 JNI ...

  9. Unity3D 之射线检测

    这里来记录下射线检测的相关内容: 射线检测故名就是通过射线去检测是否和碰撞器产生了交集,和碰撞器与碰撞器发生交集一样,会返回一个真. 射线的用法很多:比如检测是否跳跃,通过向地面投射射线控制在地面时候 ...

  10. SQLite 入门教程(一)基本控制台(终端)命令

    一.基本简介 SQLite 是一个自持的(self-contained).无服务器的.零配置的.事务型的关系型数据库引擎.因为他很小,所以也可以作为嵌入式数据库内建在你的应用程序中.SQLite 被应 ...