werkzeug中服务器处理请求的实现
当成功建立好服务器后,接下来就是等待请求并处理请求通过路由分配给相应的视图函数了,以下是函数调用过程
-> self._handle_request_noblock()
/usr/lib/python2.7/SocketServer.py(295)_handle_request_noblock()
-> self.process_request(request, client_address)
/usr/lib/python2.7/SocketServer.py(321)process_request()
-> self.finish_request(request, client_address)
/usr/lib/python2.7/SocketServer.py(334)finish_request()
-> self.RequestHandlerClass(request, client_address, self)
/usr/lib/python2.7/SocketServer.py(649)__init__()
-> self.handle()
/home/steinliber/flask-source-code/env/local/lib/python2.7/site-packages/werkzeug/serving.py(217)handle()
-> rv = BaseHTTPRequestHandler.handle(self)
/usr/lib/python2.7/BaseHTTPServer.py(340)handle()
-> self.handle_one_request()
/home/steinliber/flask-source-code/env/local/lib/python2.7/site-packages/werkzeug/serving.py(252)handle_one_request()
-> return self.run_wsgi()
/home/steinliber/flask-source-code/env/local/lib/python2.7/site-packages/werkzeug/serving.py(194)run_wsgi()
-> execute(self.server.app)
/home/steinliber/flask-source-code/env/local/lib/python2.7/site-packages/werkzeug/serving.py(184)execute()
-> for data in application_iter:
/home/steinliber/flask-source-code/env/local/lib/python2.7/site-packages/werkzeug/debug/__init__.py(199)debug_application()
首先在刚建立的HTTPServer上会对发来的请求进行处理,得到请求的内容,headers,method等,并通过初始化HTTPBASERequestHandler将这些值都设为HTTPBASERequestHandler的属性。因为WSGIRequestHandler是继承HTTPBASERequestHandler的,所以他也可以使用这些属性来构建APP运行的environ。接着HTTPServer会调用handle方法,WSGIRequestHandler的源码如下
class WSGIRequestHandler(BaseHTTPRequestHandler, object): """A request handler that implements WSGI dispatching.""" @property
def server_version(self):
return 'Werkzeug/' + werkzeug.__version__ def make_environ(self):
request_url = url_parse(self.path) def shutdown_server():
self.server.shutdown_signal = True url_scheme = self.server.ssl_context is None and 'http' or 'https'
path_info = url_unquote(request_url.path) environ = {
'wsgi.version': (1, 0),
'wsgi.url_scheme': url_scheme,
'wsgi.input': self.rfile,
'wsgi.errors': sys.stderr,
'wsgi.multithread': self.server.multithread,
'wsgi.multiprocess': self.server.multiprocess,
'wsgi.run_once': False,
'werkzeug.server.shutdown': shutdown_server,
'SERVER_SOFTWARE': self.server_version,
'REQUEST_METHOD': self.command,
'SCRIPT_NAME': '',
'PATH_INFO': wsgi_encoding_dance(path_info),
'QUERY_STRING': wsgi_encoding_dance(request_url.query),
'CONTENT_TYPE': self.headers.get('Content-Type', ''),
'CONTENT_LENGTH': self.headers.get('Content-Length', ''),
'REMOTE_ADDR': self.client_address[0],
'REMOTE_PORT': self.client_address[1],
'SERVER_NAME': self.server.server_address[0],
'SERVER_PORT': str(self.server.server_address[1]),
'SERVER_PROTOCOL': self.request_version
} for key, value in self.headers.items():
key = 'HTTP_' + key.upper().replace('-', '_')
if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
environ[key] = value if request_url.netloc:
environ['HTTP_HOST'] = request_url.netloc return environ 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, ""
self.send_response(int(code), msg)
header_keys = set()
for key, value in response_headers:
self.send_header(key, value)
key = key.lower()
header_keys.add(key)
if 'content-length' not in header_keys:
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() def start_response(status, response_headers, exc_info=None):
if exc_info:
try:
if headers_sent:
reraise(*exc_info)
finally:
exc_info = None
elif headers_set:
raise AssertionError('Headers already set')
headers_set[:] = [status, response_headers]
return write def execute(app):
application_iter = app(environ, start_response)
try:
for data in application_iter:
write(data)
if not headers_sent:
write(b'')
finally:
if hasattr(application_iter, 'close'):
application_iter.close()
application_iter = None try:
execute(self.server.app)
except (socket.error, socket.timeout) as e:
self.connection_dropped(e, environ)
except Exception:
if self.server.passthrough_errors:
raise
from werkzeug.debug.tbtools import get_current_traceback
traceback = get_current_traceback(ignore_system_exceptions=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 not headers_sent:
del headers_set[:]
execute(InternalServerError())
except Exception:
pass
self.server.log('error', 'Error on request:\n%s',
traceback.plaintext) def handle(self):
"""Handles a request ignoring dropped connections."""
rv = None
try:
rv = BaseHTTPRequestHandler.handle(self)
except (socket.error, socket.timeout) as e:
self.connection_dropped(e)
except Exception:
if self.server.ssl_context is None or not is_ssl_error():
raise
if self.server.shutdown_signal:
self.initiate_shutdown()
return rv def initiate_shutdown(self):
"""A horrible, horrible way to kill the server for Python 2.6 and
later. It's the best we can do.
"""
# Windows does not provide SIGKILL, go with SIGTERM then.
sig = getattr(signal, 'SIGKILL', signal.SIGTERM)
# reloader active
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
os.kill(os.getpid(), sig)
# python 2.7
self.server._BaseServer__shutdown_request = True
# python 2.6
self.server._BaseServer__serving = False def connection_dropped(self, error, environ=None):
"""Called if the connection was closed by the client. By default
nothing happens.
""" def handle_one_request(self):
"""Handle a single HTTP request."""
self.raw_requestline = self.rfile.readline()
if not self.raw_requestline:
self.close_connection = 1
elif self.parse_request():
return self.run_wsgi() def send_response(self, code, message=None):
"""Send the response header and log the response code."""
self.log_request(code)
if message is None:
message = code in self.responses and self.responses[code][0] or ''
if self.request_version != 'HTTP/0.9':
hdr = "%s %d %s\r\n" % (self.protocol_version, code, message)
self.wfile.write(hdr.encode('ascii')) def version_string(self):
return BaseHTTPRequestHandler.version_string(self).strip() def address_string(self):
return self.environ['REMOTE_ADDR'] def log_request(self, code='-', size='-'):
self.log('info', '"%s" %s %s', self.requestline, code, size) def log_error(self, *args):
self.log('error', *args) def log_message(self, format, *args):
self.log('info', format, *args) def log(self, type, message, *args):
_log(type, '%s - - [%s] %s\n' % (self.address_string(),
self.log_date_time_string(),
message % args))
在其中的handle方法会包装了BASERequestHandler的handle方法,如果socket出现错误或超时,它会继续尝试链接,initiate_shutdown是当收到关闭信号时,将服务器强制关闭,这个handle方法在官方文档描述会返回handle_one_request方法,他得到一个HTTP请求,并调用run_wsgi方法进行处理,在run_wsgi中,先将先前设为类属性的Request环境变量储存在environ字典中。这是WSGI的规范,服务器要向app提供environ和start_response两个参数,start_response方法会设置status和reponse_headers,具体可看PEP333规范。然后,会尝试运行excute,如果发生了除socket.error或socket.timeout之外的错误,在debug模式下会返回调试页面。
再接下来的excute函数中,他调用APP并传递environ和start_response两个参数,根据WSGI规范,它应该得到可迭代的对象,然后就调用write将这些数据写出去。在write方法中,他会首先确定start_response先被调用,首先若还没有发送headers,会先将headers_set中的status以及response_header发送出去,之后再将得到的数据发送出去,这样,服务器端的任务基本就完成了。
werkzeug中服务器处理请求的实现的更多相关文章
- C#中使用Socket请求Web服务器过程
最开始我们需要明白一件事情,因为这是这篇文章的前提: HTTP协议只是一个应用层协议,它底层是通过TCP进行传输数据的.因此,浏览器访问Web服务器的过程必须先有“连接建立”的发生. 而有人或许会问: ...
- [转] c# 模拟Asp.net页面中的某个按钮的点击,向web服务器发出请求
在没有做题目中所述的内容的时候,感觉这应该是很简单的东西,但是当真正开始做的时候却发现,有很多问题现在在这里写出来,供和我一样水平不高的参考一下. 在写本文之前参照了一下文章 欢迎使用CSDN论坛阅读 ...
- ajax-向服务器发送请求
ajax-向服务器发送请求 1.将请求发送到服务器,使用XMLHttpRequest对象的 open() 和 send() 方法. xmlhttp. open(method,url,async ...
- HTTP协议---HTTP请求中的常用请求字段和HTTP的响应状态码及响应头
http://blog.csdn.net/qxs965266509/article/details/8082810 用于HTTP请求中的常用请求头字段 Accept:用于高速服务器,客户机支持的数据类 ...
- javascrpt 中的Ajax请求
回顾下javascript中的Ajax请求,写一个小例子: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN& ...
- android中用get和post方式向服务器提交请求
通过get和post方式向服务器发送请求首先说一下get和post的区别get请求方式是将提交的参数拼接在url地址后面,例如http://www.baidu.com/index.jsp?num=23 ...
- ASP.NET Core Razor中处理Ajax请求
如何ASP.NET Core Razor中处理Ajax请求 在ASP.NET Core Razor(以下简称Razor)刚出来的时候,看了一下官方的文档,一直没怎么用过.今天闲来无事,准备用Rozor ...
- for循环中进行联网请求数据、for循环中进行异步数据操作,数据排序错乱问题解决;
for循环中进行联网请求数据,由于网络请求是异步的,第一个网络请求还没有回调,第二次第三次以及后续的网络请求又已经发出去了,有可能后续的网络请求会先回调:这时我们接收到的数据的排序就会错乱:怎么才能让 ...
- (转)在.net中检索HTTP请求
原文转载:https://www.west-wind.com/presentations/dotnetWebRequest/dotnetWebRequest.htm HTTP内容检索是应用程序的重要组 ...
随机推荐
- Codeforces 711E ZS and The Birthday Paradox(乘法逆元)
[题目链接] http://codeforces.com/problemset/problem/711/E [题目大意] 假设一年有2^n天,问k个小朋友中有两个小朋友生日相同的概率. 假设该概率约分 ...
- swith
“开关”(Switch)有时也被划分为一种“选择语句”.根据一个整数表达式的值,switch语句可从一系列代码选出一段执行.它的格式如下: switch(整数选择因子) { case 整数值1 : 语 ...
- IE能够打开网页 可是chrome和火狐打不开网页解决的方法
一次偶然.电脑的浏览器打不开经常使用的网页,奇怪的是IE能够打开 之外的其它浏览器都不能够,结果百度一下.找到一个帖子,亲自測试一下,果真能够解决.记录例如以下: (1)開始-执行-输入CMD-确定- ...
- myeclipse 配置weblogic 异常
java.lang.UnsupportedClassVersionError: Bad version number in .class file当前JDK与weblogic版本不匹配.
- NSLog 输出文件名、方法名、行号
项目中经常会需要根据日志输出来寻找源代码,通过以下方法可以让它自动输出文件名.方法.行号,非常方便. 找到项目的pch文件,添加以下内容即可: ...为三个英文句号(复制粘贴后可能会变化). /** ...
- Unity 通过NGUI 完成单摄像机 制作地图
本次思想主要是通过 Ngui的Scroll View 主要是UIPanel的Clipping属性的Alipha Clip 调节窗口大小,遮蔽地图试地图实现在屏幕的部分显示.此方法的好处是不用担心sha ...
- JS图表组件 highcharts 简单的介绍
把highcharts拿来做个简单的介绍,希望更多的朋友可以用到这个用来做图表的js插件. preparation Highcharts Highcharts是一个制作图表的纯Javascript类库 ...
- Integer Inquiry(大数相加)
Description One of the first users of BIT's new supercomputer was Chip Diller. He extended his explo ...
- 2045不容易系列之(3)—— LELE的RPG难题
Problem Description人称“AC女之杀手”的超级偶像LELE最近忽然玩起了深沉,这可急坏了众多“Cole”(LELE的粉丝,即”可乐”),经过多方打探,某资深Cole终于知道了原因,原 ...
- hihocoder #1260 : String Problem I
题目链接 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 我们有一个字符串集合S,其中有N个两两不同的字符串. 还有M个询问,每个询问给出一个字符串w,求有多少S中的 ...