首页 > 其他 > 详细

werkzeug中服务器处理请求的实现

时间:2016-01-15 16:18:35      阅读:424      评论:0      收藏:0      [点我收藏+]

当成功建立好服务器后,接下来就是等待请求并处理请求通过路由分配给相应的视图函数了,以下是函数调用过程

-> 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(bHTTP/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中服务器处理请求的实现

原文:http://www.cnblogs.com/steinliber/p/5133386.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!