我的博客

django 2.2 中的 request.META['wsgi.input'] 类型

目录
  1. WSGIServer
  2. WSGIRequestHandler
  3. StaticFilesHandler
  4. WSGIHandler

版本 VERSION = (2, 2, 1, 'final', 0)

执行 py manage.py runserver 之后,会启动开发服务器,我们直接从 django.core.management.commands.runserver.Command 开始看。它的 inner_run 方法中调用 run 函数启动了开发服务器。

1
2
run(self.addr, int(self.port), handler,
ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)

关于参数,addr、port 分别是服务器绑定的地址和端口号。handler 是 django.contrib.staticfiles.handlers.StaticFilesHandler 对象。 ipv6 是 False, threading 是 True,server_cls 是 django.core.servers.basehttp.WSGIServer。

run 函数的定义在 django.core.servers.basehttp 中。

  1. 创建了 httpd 对象类型是 django.core.servers.basehttp.WSGIServer。
  2. 调用 httpd 对象的 set_app 方法,设置其 application 属性为上文传进来的那个 django.contrib.staticfiles.handlers.StaticFilesHandler
  3. 调用 httpd 的 server_forever() 循环接受请求。

下面看 WSGIServer 对象如何接收和处理请求

WSGIServer

先看继承关系

graph BT
    WSGIServer --> socketserver.ThreadingMixIn
    WSGIServer --> simple_server.WSGIServer
    simple_server.WSGIServer --> HTTPServer
    HTTPServer --> socketserver.TCPServer
    socketserver.TCPServer --> BaseServer

构造 WSGIServer 时传入三个参数

1
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)

暂且略过,先看 serve_forever()。这个函数来自于 BaseServer,里面有一个 while 循环,掉用 self._handle_request_noblock()。

_handle_request_noblock 还是在 BaseServer 里,它又调了 self.process_request(rqeust, client_address),这个 request 参数时 socket 类型。

process_request 来自于 ThreadingMixIn,这里以 self.process_request_thread 为 target,创建新线程并传入上文提到的两个参数 (rqeust, client_address)。

新线程中调用了 self.finish_request(request, client_address)

finish_request 又回到了 BaseServer。

1
2
3
4
class BaseServer:
def finish_request(self, request, client_address):
"""Finish one request by instantiating RequestHandlerClass."""
self.RequestHandlerClass(request, client_address, self)

这里创建了 self.RequestHandlerClass 的对象,这个对象的构造函数就是实际上处理请求的代码。

这个对象的类型就是 django.core.servers.basehttp.WSGIRequestHandler。

于是再看 WSGIRequestHandler 对于请求的处理。

WSGIRequestHandler

看继承关系

graph BT;
    WSGIRequestHandler --> simple_server.WSGIRequestHandler
    simple_server.WSGIRequestHandler --> BaseHTTPRequestHandler
    BaseHTTPRequestHandler --> socketserver.StreamRequestHandler
    socketserver.StreamRequestHandler --> BaseRequestHandler

WSGIRequestHandler 本身无显式构造函数。只有基类 BaseRequestHandler 有显式构造函数

1
2
3
4
5
6
7
8
9
10
11

class BaseRequestHandler:
def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
finally:
self.finish()

这里:

  1. 调用了 self.setup() (是 StreamRequestHandler.setup)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class StreamRequestHandler(BaseRequestHandler):
    rbufsize = -1
    wbufsize = 0
    timeout = None
    disable_nagle_algorithm = False

    def setup(self):
    self.connection = self.request
    if self.timeout is not None:
    self.connection.settimeout(self.timeout)
    if self.disable_nagle_algorithm:
    self.connection.setsockopt(socket.IPPROTO_TCP,
    socket.TCP_NODELAY, True)
    self.rfile = self.connection.makefile('rb', self.rbufsize)
    if self.wbufsize == 0:
    self.wfile = _SocketWriter(self.connection)
    else:
    self.wfile = self.connection.makefile('wb', self.wbufsize)
  1. 调用了 self.handle() (是 WSGIRequestHandler.handle)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    class WSGIRequestHandler(simple_server.WSGIRequestHandler):
    protocol_version = 'HTTP/1.1'

    def handle(self):
    self.close_connection = True
    self.handle_one_request()
    while not self.close_connection:
    self.handle_one_request()
    try:
    self.connection.shutdown(socket.SHUT_WR)
    except (socket.error, AttributeError):
    pass

    def handle_one_request(self):
    """Copy of WSGIRequestHandler.handle() but with different ServerHandler"""
    self.raw_requestline = self.rfile.readline(65537)
    if len(self.raw_requestline) > 65536:
    self.requestline = ''
    self.request_version = ''
    self.command = ''
    self.send_error(414)
    return

    if not self.parse_request(): # An error code has been sent, just exit
    return

    handler = ServerHandler(
    self.rfile, self.wfile, self.get_stderr(), self.get_environ()
    )
    handler.request_handler = self # backpointer for logging & connection closing
    handler.run(self.server.get_app())

    这里

    1. 构造了 ServerHandler (定义就在同一个文件的上文中)

      先看一下 ServerHandler 的继承关系

            graph BT;
          ServerHandler --> simple_server.ServerHandler
          simple_server.ServerHandler --> SimpleHandler
          SimpleHandler --> BaseHandler
      1
      2
      3
      4
      5
      6
      7
      8
      9
      class ServerHandler(simple_server.ServerHandler):
      http_version = '1.1'

      def __init__(self, stdin, stdout, stderr, environ, **kwargs):
      try:
      content_length = int(environ.get('CONTENT_LENGTH'))
      except (ValueError, TypeError):
      content_length = 0
      super().__init__(LimitedStream(stdin, content_length), stdout, stderr, environ, **kwargs)

      这里的 stdin 就是上文的 rfile,是 _io.BufferedReader 这与 django 1.9 中以一样的。

      后面又执行父类构造函数时,stdin 参数使用了 LimitedStream 类包装了,所以这样父类(simple_server.SimpleHandler)中的构造函数接收到的 stdin 就不再是 django 1.9 中的 _io.BufferedReader 了,而是 django.core.handlers.wsgi.LimitedStream 类型。

      再看 LimitedStream 的构造函数

      1
      2
      3
      4
      5
      6
      class LimitedStream:    
      def __init__(self, stream, limit, buf_size=64 * 1024 * 1024):
      self.stream = stream
      self.remaining = limit
      self.buffer = b''
      self.buf_size = buf_size

      也就是说 LimitedStream.stream 实际上才是原来的 _io.BufferedReader 类型。

    2. 调用了 handler.run(self.server.get_app())

      这个 run 来自于 BaseHandler

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      class BaseHandler:
      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.

      因为这个 BaseHandler 来自于 wsgiref.handlers 所以这后面的过程与 django 1.9 是相同的,只是这里的 stdin 的类型已经不是 _io.BufferedReader 而变成了 LimitedStream。所以这里的 stdin.stream 才是之前的 stdin。

      下面调用 django.contrib.staticfiles.handlers.StaticFilesHandler 的 __call__ 方法。

StaticFilesHandler

1
2
3
4
5
class StaticFilesHandler(WSGIHandler):
def __call__(self, environ, start_response):
if not self._should_handle(get_path_info(environ)):
return self.application(environ, start_response)
return super().__call__(environ, start_response)

这里执行的是第四行,self.application 是 django.core.handlers.wsgi.WSGIHandler 实际上也是 StaticFilesHandler 的父类。

WSGIHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_middleware()

def __call__(self, environ, start_response):
set_script_prefix(get_script_name(environ))
signals.request_started.send(sender=self.__class__, environ=environ)
request = self.request_class(environ)
response = self.get_response(request)
response._handler_class = self.__class__
status = '%d %s' % (response.status_code, response.reason_phrase)
response_headers = [
*response.items(),
*(('Set-Cookie', c.output(header='')) for c in response.cookies.values()),
]
start_response(status, response_headers)
if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
response = environ['wsgi.file_wrapper'](response.file_to_stream)
return response

这里构建了 request,类型是 WSGIRequest。

这里和 django 1.9 是相同的。

同时我们也发现其实 django 1.9 和 django 2.2 的 request 对象都有一个 self._stream = LimitedStream(self.environ[‘wsgi.input’], content_length) 是相同的,但是实际上两个版本的 self.environ[‘wsgi.input’] 的类型是不同的,那么这或许是 django 2.2 代码中的一个 bug。

实际上引发这两个版本 request.META[‘wsgi.input’] 类型不同的原因在于 django.core.servers.basehttp.ServerHandler 的构造函数的最后一句里的改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ServerHandler(simple_server.ServerHandler):
http_version = '1.1'

def __init__(self, stdin, stdout, stderr, environ, **kwargs):
"""
Use a LimitedStream so that unread request data will be ignored at
the end of the request. WSGIRequest uses a LimitedStream but it
shouldn't discard the data since the upstream servers usually do this.
This fix applies only for testserver/runserver.
"""
try:
content_length = int(environ.get('CONTENT_LENGTH'))
except (ValueError, TypeError):
content_length = 0
print('stdin')
print(stdin)
super().__init__(LimitedStream(stdin, content_length), stdout, stderr, environ, **kwargs)

在这个函数的注释里我们看到了他们这样做的原因。

文件在这里 https://github.com/django/django/blob/master/django/core/servers/basehttp.py

评论无需登录,可以匿名,欢迎评论!