wsgiserver python 漏洞_Python模块WSGI使用详解

WSGI(Web Server Gateway Interface):Web服务网关接口,是Python中定义的服务器程序和应用程序之间的接口。

Web程序开发中,一般分为服务器程序和应用程序。服务器程序负责对socket服务的数据进行封装和整理,而应用程序则负责对Web请求进行逻辑处理。

Web应用本质上也是一个socket服务器,用户的浏览器就是一个socket客户端。

我们先用socket编程实现一个简单的Web服务器:

import socket

def handle_request(client):

buf = client.recv(1024)

print(buf)

msg = "HTTP/1.1 200 OK\r\n\r\n" #HTTP头信息

client.send(('%s' % msg).encode())

msg = "Hello, World!"

client.send(('%s' % msg).encode())

def main():

ip_port = ("localhost", 8000)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.bind(ip_port)

sock.listen(5)

while True:

conn, addr = sock.accept()

handle_request(conn)

conn.close()

if __name__ == "__main__":

main()

上述代码中,main()函数就是服务器函数,handle_request()就是应用程序。

下面我们再用python的wsgiref模块来实现跟上述代码一样的Web服务器:

from wsgiref.simple_server import make_server

def handle_request(env, res):

res("200 OK",[("Content-Type","text/html")])

body = "

Hello World!

"

return [body.encode("utf-8")]

if __name__ == "__main__":

httpd = make_server("",8000,handle_request)

print("Serving http on port 80000")

httpd.serve_forever()

上面两份代码实现的效果是一样的,调用wsgiref模块则明显节省了代码量,是整个程序更加简洁。

wsgiref模块封装了socket服务端的代码,只留下一个调用的接口,省去了程序员的麻烦,程序员可以将精力放在Web请求的逻辑处理中。

以上述的代码为例,详细看一下wsgiref模块的源码中一些关键的地方:

if __name__ == "__main__":

httpd = make_server("",8000,handle_request)

print("Serving http on port 80000")

httpd.serve_forever()

1、整个程序的入口为make_server()函数:

def make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler):

"""Create a new WSGI server listening on `host` and `port` for `app`"""

server = server_class((host, port), handler_class) #默认创建一个WSGIServer类

server.set_app(app) #将应用程序,即逻辑处理函数传给类

return server

2、make_server()函数默认生成一个WSGIServer类:

class WSGIServer(HTTPServer):

class HTTPServer(socketserver.TCPServer):

class TCPServer(BaseServer):

WSGIServer,HTTPServer两个类没有初始化函数,调用父类的初始化函数,TCPServer类的__init__()函数拓展了BaseServer

类的__init__()函数:

#BaseServer类的__init__()函数:

def __init__(self, server_address, RequestHandlerClass):

"""Constructor. May be extended, do not override."""

self.server_address = server_address

self.RequestHandlerClass = RequestHandlerClass

self.__is_shut_down = threading.Event()

self.__shutdown_request = False

#TCPServer类的__init__()函数:

def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):

"""Constructor. May be extended, do not override."""

BaseServer.__init__(self, server_address, RequestHandlerClass)

self.socket = socket.socket(self.address_family,self.socket_type)

if bind_and_activate:

try:

self.server_bind()

self.server_activate()

except:

self.server_close()

raise

TCPServer类的初始化函数还调用了server_bind(self),server_bind(self)两个函数:

def server_bind(self):

"""Called by constructor to bind the socket.May be overridden."""

if self.allow_reuse_address:

self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

self.socket.bind(self.server_address)

self.server_address = self.socket.getsockname()

def self.server_activate(self):

"""Called by constructor to activate the server.May be overridden."""

self.socket.listen(self.request_queue_size)

可以看到server.bind()函数调用了socket.bind()函数,而server_activate()调用了socket.listen()函数:

3、server.set_app(app),此处传入Web请求的处理逻辑:

def set_app(self,application):

self.application = application

4、httpd.serve_forever()函数调用BaseServer类的_handle_request_noblock()函数处理多路请求:

def _handle_request_noblock(self):

try:

request, client_address = self.get_request() #get_request()调用了socket.accept()函数

except OSError:

return

if self.verify_request(request, client_address):

try:

self.process_request(request, client_address)

except:

self.handle_error(request, client_address)

self.shutdown_request(request)

else:

self.shutdown_request(request)

def process_request(self, request, client_address):

self.finish_request(request, client_address)

self.shutdown_request(request)#shutdown_request()调用socket.close()关闭socket

def finish_request(self, request, client_address):

"""Finish one request by instantiating RequestHandlerClass."""

self.RequestHandlerClass(request, client_address, self)

5、process_request()函数调用了finish_request()函数,简介调用了make_server函数的默认参数WSGIRequestHandler类:

class WSGIRequestHandler(BaseHTTPRequestHandler):

class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):

class StreamRequestHandler(BaseRequestHandler):

#调用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()

6、初始化函数调用之后调用WSGIRequestHandler类的handle()函数获取server的逻辑处理函数:

def handle(self):

"""Handle a single HTTP request"""

try:

handler = ServerHandler(self.rfile, stdout, self.get_stderr(), self.get_environ())

handler.request_handler = self # backpointer for logging

handler.run(self.server.get_app()) #此处调用server的逻辑处理函数

finally:

stdout.detach()

7、BaseHandler类的handler.run()函数执行逻辑处理:

def run(self, application):

try:

self.setup_environ()

self.result = application(self.environ, self.start_response)

self.finish_response()

except:

try:

self.handle_error()

except:

self.close()

raise # ...and let the actual server figure it out.

self.environ:一个包含所有HTTP请求信息的dict对象

self.start_response:一个发送HTTP响应的函数。

在application函数中,调用:

res("200 OK",[("Content-Type","text/html")])

这样就发送了HTTP响应的头信息

8、BaseHandler类的setup_environ()函数获取HTTP请求的头信息:

def setup_environ(self):

"""Set up the environment for one request"""

env = self.environ = self.os_environ.copy()

os_environ= read_environ()

read_environ()函数:

def read_environ():

"""Read environment, fixing HTTP variables"""

enc = sys.getfilesystemencoding()

esc = 'surrogateescape'

try:

''.encode('utf-8', esc)

except LookupError:

esc = 'replace'

environ = {}

# Take the basic environment from native-unicode os.environ. Attempt to

# fix up the variables that come from the HTTP request to compensate for

# the bytes->unicode decoding step that will already have taken place.

for k, v in os.environ.items():

if _needs_transcode(k):

# On win32, the os.environ is natively Unicode. Different servers

# decode the request bytes using different encodings.

if sys.platform == 'win32':

software = os.environ.get('SERVER_SOFTWARE', '').lower()

# On IIS, the HTTP request will be decoded as UTF-8 as long

# as the input is a valid UTF-8 sequence. Otherwise it is

# decoded using the system code page (mbcs), with no way to

# detect this has happened. Because UTF-8 is the more likely

# encoding, and mbcs is inherently unreliable (an mbcs string

# that happens to be valid UTF-8 will not be decoded as mbcs)

# always recreate the original bytes as UTF-8.

if software.startswith('microsoft-iis/'):

v = v.encode('utf-8').decode('iso-8859-1')

# Apache mod_cgi writes bytes-as-unicode (as if ISO-8859-1) direct

# to the Unicode environ. No modification needed.

elif software.startswith('apache/'):

pass

# Python 3's http.server.CGIHTTPRequestHandler decodes

# using the urllib.unquote default of UTF-8, amongst other

# issues.

elif (

software.startswith('simplehttp/')

and 'python/3' in software

):

v = v.encode('utf-8').decode('iso-8859-1')

# For other servers, guess that they have written bytes to

# the environ using stdio byte-oriented interfaces, ending up

# with the system code page.

else:

v = v.encode(enc, 'replace').decode('iso-8859-1')

# Recover bytes from unicode environ, using surrogate escapes

# where available (Python 3.1+).

else:

v = v.encode(enc, esc).decode('iso-8859-1')

environ[k] = v

return environ

9、BaseHandler类的start_response()函数:

def start_response(self, status, headers,exc_info=None):

"""'start_response()' callable as specified by PEP 3333"""

if exc_info:

try:

if self.headers_sent:

# Re-raise original exception if headers sent

raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])

finally:

exc_info = None # avoid dangling circular ref

elif self.headers is not None:

raise AssertionError("Headers already set!")

self.status = status

self.headers = self.headers_class(headers)

status = self._convert_string_type(status, "Status")

assert len(status)>=4,"Status must be at least 4 characters"

assert status[:3].isdigit(), "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:

name = self._convert_string_type(name, "Header name")

val = self._convert_string_type(val, "Header value")

return self.write

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值