python應用,python實現一個http服務器

 2023-11-01 阅读 39 评论 0

摘要:最近在學習Flask源碼的時候,發現了python有一個內置的http,可以用來搭建http服務器,所以花時間研究了一番。 httpserver 基于python的http包構建一個簡易http服務器。 python應用、使用到的兩個類 from http.server import BaseHTTPRequestHandler, HTT

最近在學習Flask源碼的時候,發現了python有一個內置的http,可以用來搭建http服務器,所以花時間研究了一番。

httpserver

基于python的http包構建一個簡易http服務器。

python應用、使用到的兩個類

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)returnif not self.raw_requestline:self.close_connection = Truereturn# 這一句是解析http頭的(這時的http請求在raw_requestline中,也就是解析raw_requestline)# 就是將類似b'GET /test-request HTTP/1.1\r\n...'的請求頭解釋出來# An error code has been sent, just exitif 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)returnmethod = 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 connectionself.log_error("Request timed out: %r", e)self.close_connection = Truereturn

讀了源碼之后就大概知道如何實現自己的Http服務器了:

python界面、思路是繼承BaseHTTPRequestHandler寫一個處理類,然后用它初始化一個HTTPServer

繼承BaseHTTPRequestHandler

繼承BaseHTTPRequestHandler,然后實現相應的方法如do_GET,do_POST,do_DELETE等。(注意大小寫一定要對)

class MyHttpRequestHandler(BaseHTTPRequestHandler):def do_GET(self):# 處理邏輯passdef do_POST(self):pass...

創建Server

創建一個server,這里的server是HTTPServer類,設置監聽地址和端口,將MyHttpRequestHandler傳遞給它。

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

python爬蟲教程?然后需要讓服務器開始工作

ts.serve_forever()

這時服務器就開始工作了。

所以我們該在BaseHTTPRequestHandler中準備一些簡單的邏輯,否則無輸出的話我們也不知道服務器是否在運行中。

do_GET

python 類、因為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 = "<a href='http://www.baidu.com'>百度</a>".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)

執行一下

python和java、瀏覽器結果

如果方法命名不對如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 = Truedef do_GET(self):if self.is_first_request:self.is_first_request = Falsefor k in dir(self):v = getattr(self, k)# 魔術方法,私有變量和大部分的callable并不是為我們提供的,所以排除if k.startswith('_') or callable(v): continueprint('---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 = "<a href='http://www.baidu.com'>百度</a>".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
<socket.socket fd=888, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8899), raddr=('127.0.0.1', 59205)>---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
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"><title>Error response</title></head><body><h1>Error response</h1><p>Error code: %(code)d</p><p>Message: %(message)s.</p><p>Error code explanation: %(code)s - %(explain)s.</p></body>
</html>---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
<socket.socket fd=888, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8899), raddr=('127.0.0.1', 59205)>---arg: request_version type:str
HTTP/1.1---arg: requestline type:str
GET / HTTP/1.1---arg: responses type:dict
{<HTTPStatus.CONTINUE: 100>: ('Continue', 'Request received, please continue'), <HTTPStatus.SWITCHING_PROTOCOLS: 101>: ('Switching Protocols', 'Switching to new protocol; obey Upgrade header'), <HTTPStatus.PROCESSING: 102>: ('Processing', ''), <HTTPStatus.OK: 200>: ('OK', 'Request fulfilled, document follows')...省略節省篇幅}---arg: rfile type:BufferedReader
<_io.BufferedReader name=888>---arg: server type:HTTPServer
<http.server.HTTPServer object at 0x0000022A1A1011C0>---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
<socketserver._SocketWriter object at 0x0000022A335CCE20>

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/168091.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息