Python3 socket通過代理訪問web服務實現


一、說明

1.1 背景說明

關於“代理”,從burpsuite到ss這類正向代理,再從nginx到haproxy這類反向代理,也用了好多年配置了好多年了。在日積月累之下也確認了以下幾個問題:

正向代理和反向代理的區別----正常訪問路徑是client----public network----server,如果代理服務器處於client和public network之間就是正向代理,如果代理服務器處於public network和server之間就是反向代理。

代理從連接的角度是怎么實現的----A建立一個tcp連接到B發送應用層內容,B另建立一個tcp連接到C轉發A應用層的內容。

socks5是什么和socket庫有什關系和區別----socket我們可以認為是“數據鏈路層+網絡層+傳輸層”的實現庫,socks5是傳輸層之上的一種協議(類比ssl層)

ssl密鑰協商過程是怎樣的----SSL密鑰協商過程分析

怎么編寫代碼實現ssl通信----Python3+ssl實現加密通信

但是仍是有一種不甚了解的感覺,比如這兩天實現socket使用代理訪問web服務代碼就又費了好多功夫。

 

1.2 環境說明

代理服務----windows本地運行的whistle代理服務,監聽端口8899

實現語言----python3

實現功能----socket通過代理訪問http服務(第二大點)、socket通過代理訪問https服務(第三大點)

 

二、socket實現通過代理訪問http服務

2.1 實現代碼

import socket


# 以http請求www.baidu.com的請求包內容
# http以換行表示請求頭完結,所以最后的空行千萬不要刪掉
http_layer_data = """
GET / HTTP/1.1
Host: www.baidu.com
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3

"""

# 第一步,和代理服務器建立連接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 8899))

# 第二步,向代理服務器發送請求
# 代理服務器識別接收到是http協議內容后,會根據Host頭進行轉發
s.sendall(http_layer_data.encode())

# 第三步,接收到服務器響應並打印
response_data = s.recv(1024)
print(response_data.decode())
s.close()

 

2.2 響應結果

如下圖所示,成功接收到302重定向響應

 

三、socket實現通過代理訪問https服務

現在大多數服務都不再使用http協議,而是轉而使用https協議。

如果是代碼直接訪問https服務,那問題還是比較明朗的,我們只要先與https服務端建立一個ssl連接,然后再發送http層內容即可。

但是如果中間插入一個代理,這問題就比較棘手:客戶端該用socket和代理服務器連接呢還是該用ssl和代理服務器連接、客戶端又該如何通知代理服務器使用https向https服務端轉發內容。

 

3.1 代碼實現

import socket
import ssl

# 以http請求www.baidu.com的請求包內容
# http以換行表示請求頭完結,所以最后的空行千萬不要刪掉
http_layer_data = """
GET / HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Sec-Fetch-User: ?1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

"""

# 第一步,和代理服務器建立socket連接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1", 8899))

# 第二步,代理服務器與https服務端進行密鑰協商並建立ssl連接,用於后續轉發
connect_request_http_layer_data = """
CONNECT www.baidu.com:443 HTTP/1.0
Connection: close

"""
s.sendall(connect_request_http_layer_data.encode())
response_data = s.recv(1024)
print(response_data.decode())

# 第三步,建立ssl連接
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
ss = context.wrap_socket(s)

# 第四步,向代理服務器發送請求
# 代理服務器會解出其中的http層內容,使用先前和https服務端建立的連接,進行轉發
ss.send(http_layer_data.encode())

# 第五步,接收到服務器響應並打印
response_data = ss.recv(1024)
print(response_data.decode())
ss.close()

 

3.2 響應結果

 

四、不建議使用socket訪問應用層服務的原因

不建議使用socket訪問應用層服務的原因,其實除了代理這種功能比較費勁外,還有一個更主要的原因。

在上邊的代碼中我們發送一個請求然后接收響應,好像一切都正常沒什么毛病,但這只是因為返回的結果比較短;

當服務端內容比較多時會把結果分成多個包返回來,socket是不管應用層內容的定界的,哪幾個tcp包是才一個完整的http響應需要你自己去定界,這是一個很痛苦的事情。http服務如此,其他應用層服務也如此。

python訪問http服務,還是推薦使用requests庫

 

五、關於只有IDE自己的請求才走IDE代理的說明

PyCharm這種IDE都有一個代理配置,當我和測試同事說想讓他們的測試用例走代理時他第一反應是找IDE的這個代配置,我一時也有點懵了這到底能這么實現的嗎。

回頭測試了一下,這個代理設置只能是說讓IDE本身的如檢查更新這類流量走這個代理,IDE中你自己的編寫的代碼的請求他是管不到的。

 

參考:

http://www.fridayhaohao.com/articles/24/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM