python实现一个http服务器

您所在的位置:网站首页 python搭建简单服务器 python实现一个http服务器

python实现一个http服务器

2023-10-12 08:10| 来源: 网络整理| 查看: 265

最近在学习Flask源码的时候,发现了python有一个内置的http,可以用来搭建http服务器,所以花时间研究了一番。

httpserver

基于python的http包构建一个简易http服务器。

使用到的两个类

from http.server import BaseHTTPRequestHandler, HTTPServer 源码

首先查看BaseHTTPRequestHandler处理请求的部分的源码,才知道继承它后要怎么样才能接收处理请求。

# 跳过前面负责的逻辑,直接来到 # handle_one_request # 这个方法就是BaseHTTPRequestHandler处理一个请求的方法。 def handle_one_request(self): try: # 读取请求 self.raw_requestline = self.rfile.readline(65537) if len(self.raw_requestline) > 65536: self.requestline = '' self.request_version = '' self.command = '' self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG) return if not self.raw_requestline: self.close_connection = True return # 这一句是解析http头的(这时的http请求在raw_requestline中,也就是解析raw_requestline) # 就是将类似b'GET /test-request HTTP/1.1\r\n...'的请求头解释出来 # An error code has been sent, just exit if not self.parse_request(): return # self.command 是解释出来的command,如'GET', 'POST' # 注意这里没有使用lower函数 mname = 'do_' + self.command if not hasattr(self, mname): self.send_error( HTTPStatus.NOT_IMPLEMENTED, "Unsupported method (%r)" % self.command) return method = getattr(self, mname) # 所以这个do_command需要是一个可以调用的对象,可以是一个方法。 # 每次有相应的请求到来,就会调用相应的do_command() # 所以我们实现这些方法就可以了: # 如果想要处理get,就在继承中实现do_GET方法, 如果处理post就实现do_POST方法等等。 method() self.wfile.flush() #actually send the response if not already done. except socket.timeout as e: #a read or a write timed out. Discard this connection self.log_error("Request timed out: %r", e) self.close_connection = True return

读了源码之后就大概知道如何实现自己的Http服务器了:

思路是继承BaseHTTPRequestHandler写一个处理类,然后用它初始化一个HTTPServer

继承BaseHTTPRequestHandler

继承BaseHTTPRequestHandler,然后实现相应的方法如do_GET,do_POST,do_DELETE等。(注意大小写一定要对)

class MyHttpRequestHandler(BaseHTTPRequestHandler): def do_GET(self): # 处理逻辑 pass def do_POST(self): pass ... 创建Server

创建一个server,这里的server是HTTPServer类,设置监听地址和端口,将MyHttpRequestHandler传递给它。

ts = HTTPServer(('127.0.0.1', 8899), MyHttpRequestHandler)

然后需要让服务器开始工作

ts.serve_forever()

这时服务器就开始工作了。

所以我们该在BaseHTTPRequestHandler中准备一些简单的逻辑,否则无输出的话我们也不知道服务器是否在运行中。

do_GET

因为GET方法容易测试,所以选择GET方法测试。

在do_GET中添加一些逻辑,在do_GET返回响应头和数据。

def do_GET(self): # 响应头 headers = """HTTP/1.1 200 OK Server: YouFather Accept-Ranges: bytes Content-Length: {data-length} Vary: Accept-Encoding Content-Type: text/html """.replace('\n', '\r\n') + '\r\n' # 响应数据 data = "百度".encode('gbk') # 设置一下这个Content-Length参数,告诉客户端数据的长度。 headers = headers.format_map({'data-length': len(data)}) # 写入响应头和数据 # 这里的wfile是HTTPServer的基类TCPServer为我们准备的一个写入对象。 # wfile = socket.make_file('wb') # 和open(filename,'w')是同一类型。 self.wfile.write(headers.encode()) self.wfile.write(data)

执行一下

浏览器结果

如果方法命名不对如do_get,或请求到没有的方法如没有do_GET,都会引发异常

控制台:

127.0.0.1 - - [05/Aug/2021 17:35:52] code 501, message Unsupported method ('GET') 127.0.0.1 - - [05/Aug/2021 17:35:52] "GET / HTTP/1.1" 501 - 127.0.0.1 - - [05/Aug/2021 17:35:52] code 501, message Unsupported method ('GET') 127.0.0.1 - - [05/Aug/2021 17:35:52] "GET /favicon.ico HTTP/1.1" 501 -

浏览器端:

浏览器的返回

获取Http的请求信息

在do_GET中可能要用到Http相关的请求信息,这些大部分都由BaseHTTPRequestHandler为我们准备好了。

来看看BaseHTTPRequestHandler封装了哪些消息给我们:

# 修改MyHttpRequestHandler class MyHttpRequestHandler(BaseHTTPRequestHandler): is_first_request = True def do_GET(self): if self.is_first_request: self.is_first_request = False for k in dir(self): v = getattr(self, k) # 魔术方法,私有变量和大部分的callable并不是为我们提供的,所以排除 if k.startswith('_') or callable(v): continue print('---arg: ', k, ' type:', v.__class__.__name__, '\n', v, '\n\n', sep='') headers = """HTTP/1.1 200 OK Server: YouFather Accept-Ranges: bytes Content-Length: {data-length} Vary: Accept-Encoding Content-Type: text/html """.replace('\n', '\r\n') + '\r\n' data = "百度".encode('gbk') headers = headers.format_map({'data-length': len(data)}) self.wfile.write(headers.encode()) self.wfile.write(data)

再运行,浏览器访问一下

---arg: client_address type:tuple ('127.0.0.1', 59205) ---arg: close_connection type:bool True ---arg: command type:str GET ---arg: connection type:socket ---arg: default_request_version type:str HTTP/0.9 ---arg: disable_nagle_algorithm type:bool False ---arg: error_content_type type:str text/html;charset=utf-8 ---arg: error_message_format type:str Error response Error response

Error code: %(code)d

Message: %(message)s.

Error code explanation: %(code)s - %(explain)s.

---arg: headers type:HTTPMessage Host: localhost:8899 Connection: keep-alive Cache-Control: max-age=0 sec-ch-ua: "Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92" sec-ch-ua-mobile: ?0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 ---arg: is_first_request type:bool False ---arg: monthname type:list [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] ---arg: path type:str / ---arg: protocol_version type:str HTTP/1.0 ---arg: raw_requestline type:bytes b'GET / HTTP/1.1\r\n' ---arg: rbufsize type:int -1 ---arg: request type:socket ---arg: request_version type:str HTTP/1.1 ---arg: requestline type:str GET / HTTP/1.1 ---arg: responses type:dict {: ('Continue', 'Request received, please continue'), : ('Switching Protocols', 'Switching to new protocol; obey Upgrade header'), : ('Processing', ''), : ('OK', 'Request fulfilled, document follows')...省略节省篇幅} ---arg: rfile type:BufferedReader ---arg: server type:HTTPServer ---arg: server_version type:str BaseHTTP/0.6 ---arg: sys_version type:str Python/3.8.6 ---arg: timeout type:NoneType None ---arg: wbufsize type:int 0 ---arg: weekdayname type:list ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] ---arg: wfile type:_SocketWriter


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3