python之代理服務小程序

   使用某某代理時免費版的限制鏈接數,除此就要購買......實現個簡單的代理服務程序,一探究竟,git

固然複雜的也是這些原理。代理服務原理很簡單,就拿瀏覽器與web服務器來講。無非是A瀏覽器github

發request給B代理,B代理再把request把送給C web服務,而後C的reponse->B->A。web

  要寫web代理服務就要先了解下http協議,固然並不要多深刻,除非要實現強大的功能:修改XX信息、chrome

負載均衡等。http請求由三部分組成:請求行、消息報頭、請求正文;瀏覽器

詳細的網上有,想了解能夠看看。下面是一個正常的GET請求頭(Cookie部分本人沒截屏,使用的系統w7):服務器

能夠看到首行:GET是請求方法, /是路徑,在後面是協議版本;第二行之後是請求報頭,都是鍵值對形式;多線程

GET方法沒有正文。post有正文,除此以外,請求方法頭部基本一致,每一行結尾都是\r\n。負載均衡

  基本的請求方法,以下:
  GET        請求獲取Request-URI所標識的資源
  POST      在Request-URI所標識的資源後附加新的數據
  HEAD      請求獲取由Request-URI所標識的資源的響應消息報頭
  PUT         請求服務器存儲一個資源,並用Request-URI做爲其標識
  DELETE   請求服務器刪除Request-URI所標識的資源
  TRACE     請求服務器回送收到的請求信息,主要用於測試或診斷
  CONNECT 保留未來使用
  OPTIONS  請求查詢服務器的性能,或者查詢與資源相關的選項和需求
socket

 可是使用代理後,從代理服務上獲得的請求以下:函數

與第一張圖片對比一下,有什麼不一樣......第一行的資源路徑不對。當瀏覽器上設置代理請求時把整個url都做爲

資源路徑了,因此咱們要把http://www.cnblogs.com刪掉,而後代理服務器在把修改後的請求發送給目標

web服務器。就這麼簡單,固然CONNECT方法特別,要特別對待,因此先說其餘方法。

 基本的思路:

一、代理服務器運行監聽,當有客戶端瀏覽器請求到來時經過accept()得到client句柄(或者叫描述符);

二、利用client描述符接收瀏覽器發來的request,分離出第一行爲了修改第一行和得到method,

要去掉的(如上圖http://www.cnblogs.com)的部分,除去http://的部分用targetHost表示吧。

三、經過第2步可以得到方法method、request和targetHost,這一步能夠根據不一樣的method作不一樣的處理,

因爲GET、POET、PUT、DELETE等除了CONNECT處理基本一致,因此處理首行,好比:

GET http://www.cnblogs.com/ HTTP/1.1
替換爲
GET / HTTP/1.1

此時targetHost也就是紅色的部分,默認的請求80端口,此時port爲80;若是targetHost中有端口(好比www.cnblogs.com:8081),

就要分理處端口,此時port爲8081。而後根據targetHost和port鏈接到目標服務器target了,實現代碼以下:

 1 def getTargetInfo(self,host): #處理targetHost得到網址和端口,做爲返回值。
 2         port=0
 3         site=None
 4         if ':' in host:
 5             tmp=host.split(':')
 6             site=tmp[0]
 7             port=int(tmp[1])
 8         else:
 9             site=host
10             port=80
11         return site,port
12 
13 def commonMethod(self,request): #處理除CONNECT之外的方法
14         tmp=self.targetHost.split('/')
15         net=tmp[0]+'//'+tmp[2]
16         request=request.replace(net,'') #替換掉首行沒必要要的部分
17 
18 targetAddr=self.getTargetInfo(tmp[2]) #調用上面的函數 19 try: 20 (fam,_,_,_,addr)=socket.getaddrinfo(targetAddr[0],targetAddr[1])[0] 21 except Exception as e: 22 print e 23 return 24 self.target=socket.socket(fam) 25 self.target.connect(addr) #鏈接到目標web服務

四、這一步就好辦了,根據第三步處理後的request就能夠self.target.send(request)發送給web服務器了。

五、這一步web服務器的reponse反響經過代理服務直接轉發給客戶端就好了,本人用了非阻塞select,能夠試試epoll。

    基本步驟就是這樣,使用的方法函數能夠改進,好比主函數部分使用的多線程或者多進程,怎樣選擇......

可是思路差很少都是這樣啦。想測試的話,chrome安裝SwitchySharp插件,設置一下,代理端口8083;

firefox插件autoproxy。

    對於connect的處理還在解決,因此如今這個代理程序不支持https協議(github已更新)。

代理服務能夠得到http協議的全部信息,想了解學習http,利用代理服務器是個不錯的方法。

   下面附上代碼,更新會在https://github.com/915546302/tinyproxy

 1 #-*- coding: UTF-8 -*-
 2 import socket,select
 3 import sys
 4 import thread
 5 from multiprocessing import Process
 6 
 7 class Proxy:
 8     def __init__(self,soc):
 9         self.client,_=soc.accept()
10         self.target=None
11         self.request_url=None
12         self.BUFSIZE=4096
13         self.method=None
14         self.targetHost=None
15     def getClientRequest(self):
16         request=self.client.recv(self.BUFSIZE)
17         if not request:
18             return None
19         cn=request.find('\n')
20         firstLine=request[:cn]
21         print firstLine[:len(firstLine)-9]
22         line=firstLine.split()
23         self.method=line[0]
24         self.targetHost=line[1]
25         return request
26     def commonMethod(self,request):
27         tmp=self.targetHost.split('/')
28         net=tmp[0]+'//'+tmp[2]
29         request=request.replace(net,'')
30         targetAddr=self.getTargetInfo(tmp[2])
31         try:
32             (fam,_,_,_,addr)=socket.getaddrinfo(targetAddr[0],targetAddr[1])[0]
33         except Exception as e:
34             print e
35             return
36         self.target=socket.socket(fam)
37         self.target.connect(addr)
38         self.target.send(request)
39         self.nonblocking()
40     def connectMethod(self,request): #對於CONNECT處理能夠添加在這裏
41         pass
42     def run(self):
43         request=self.getClientRequest()
44         if request:
45             if self.method in ['GET','POST','PUT',"DELETE",'HAVE']:
46                 self.commonMethod(request)
47             elif self.method=='CONNECT':
48                 self.connectMethod(request)
49     def nonblocking(self):
50         inputs=[self.client,self.target]
51         while True:
52             readable,writeable,errs=select.select(inputs,[],inputs,3)
53             if errs:
54                 break
55             for soc in readable:
56                 data=soc.recv(self.BUFSIZE)
57                 if data:
58                     if soc is self.client:
59                         self.target.send(data)
60                     elif soc is self.target:
61                         self.client.send(data)
62                 else:
63                     break
64         self.client.close()
65         self.target.close()
66     def getTargetInfo(self,host):
67         port=0
68         site=None
69         if ':' in host:
70             tmp=host.split(':')
71             site=tmp[0]
72             port=int(tmp[1])
73         else:
74             site=host
75             port=80
76         return site,port
77 
78 if __name__=='__main__':      
79     host = '127.0.0.1' 
80     port = 8083
81     backlog = 5 
82     server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 
83     server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
84     server.bind((host,port)) 
85     server.listen(backlog) 
86     while True:
87         thread.start_new_thread(Proxy(server).run,())
88         # p=Process(target=Proxy(server).run, args=()) #多進程
89         # p.start()
90         
91    
相關文章
相關標籤/搜索