我爱编程

http proxy

2018-06-11  本文已影响0人  金刚_30bf

有时在内网无法访问外网资源 , 但有些应用如conda pip yum 需要在线更新,本例使用一台有两网卡的机器(同时上内外网)作为代理,为内网提供在线更新服务。 (在内网机器隔离比较严格的场景不推荐使用!)

使用python做简单的http代理, 支持CONNECT 和GET方法, 支持http 和 https 。


import urllib.parse
import socket 
import select 
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn
import sys

class MyHandler(BaseHTTPRequestHandler):
    def do_CONNECT(self):
        
        print('CONNECT:',self.client_address,self.requestline, self.headers)

        uri = self.path
        print("url:", uri)
        host,port = urllib.parse.splittype(uri)

        address = (host, port or 443)
        
        try:
            targetconn = socket.create_connection(address=address)
        except socket.error:
            self.send_error(504)
            return 
        
        self.send_response(200, 'Connection Established')
        self.send_header('Connection', 'close')
        self.end_headers()
        
        conns = [self.connection, targetconn]
        
        keep_connection = True 
        while(keep_connection):
            keep_connection = False
            rlist, wlist, xlist = select.select(conns, [], conns, self.timeout)
            if (xlist):
                break
            for r in rlist:
                other = conns[1] if r is conns[0] else conns[0]
                data = r.recv(8192)
                if (data):
                    other.sendall(data)
                    keep_connection = True
        
        targetconn.close()
        print("Connect end!========================",self.client_address)
        
    def do_GET(self):
        print('GET:',self.client_address,self.requestline, self.headers)
        uri = self.path
        print("url:", uri)
        protocol,rest = urllib.parse.splittype(uri)
        print("protocol:", protocol)
        host,rest = urllib.parse.splithost(rest)
        
        print("host:", host) 
        
        path = rest
        
        print("Path:", path)
        
        if (path is None or len(path) == 0):
            path = '/'
        
        host,port = urllib.parse.splitnport(host)
        
        print("host:", host)
        port = 80 if port < 0 else port
        
        host_ip = socket.gethostbyname(host)
        
        print(host_ip, port)
        
        # print("headers:", self.headers)
        
        send_data = 'GET ' + path + ' ' + self.protocol_version + '\r\n' 
        
        head = ''
        for key, val in self.headers.items():
            head = head + "%s: %s\r\n" % (key,val)
        send_data = send_data + head + '\r\n' 
        
        print("send_data:" , send_data) 
        
        client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        client.connect((host_ip,port))
        client.send(send_data.encode('utf-8'))
        
        data = bytes()
        
        while True:
            tmp = client.recv(8192)
            if not tmp:
                break
            data = data + tmp
            
        client.close()
        # print("resData:", data)
        print("resData len:", len(data))
        self.wfile.write(data)
        
        print('GET end ! =====================',self.client_address)

class MyHttpServer(ThreadingMixIn, HTTPServer):
    pass


def main(ip, port):
    try:
        server = MyHttpServer((ip, port), MyHandler)
        print('Welcome to the machine...')
        server.serve_forever()
    except KeyboardInterrupt:
        print('^C received, shutting down server')
        server.socket.close()

if __name__ == '__main__':
    # main()
    
    if (len(sys.argv) == 3):
        for arg in sys.argv:
            print(arg)
    else:
        print("Error, arguments less!")
        print("Usage: python httpProxy.py ip  port ")
        sys.exit()
    
    ip = sys.argv[1]
    port = int(sys.argv[2])
    
    print("Starting proxy at ", ip, port )
    main(ip,port)

https CONNECT

CONNECT 用于建立通道,后续将使用建立的通道进行通信 。
在收到CONNECT请求时 , 回复客户端 200 , ‘Connection Established’ 。
然后与目标服务器建立连接,CONNECT头中有host和端口(即目标服务器的主机和端口)。
监控与客户端和目标服务器的连接socket ,当有读事件时, 读取之然后发送出去;
客户端--》 proxy --》 目标服务器;
目标服务器 --》 proxy --》 客户端 ;

select 的使用

select.select(读列表, 写列表, 执行列表, timeout)

循环处理准备好的读写执行列表, 执行相应操作。

上一篇下一篇

猜你喜欢

热点阅读