Python代码实现http/https代理服务器的脚本

一个几百行代码做出http/https代理服务器的脚本,启动即可做http https透明代理使用

python proxy.py 8992

使用非阻塞io模式,性能还可以。

可以和浏览器一样保持长连接,代码有点乱,不管那么多了能跑就行

几百行代码做出http/https代理服务器代码片段

*1. * [代码] [Python]代码

#!/usr/bin/python

#-*- coding:utf-8 -*-

import socket, logging

import select, errno

import os

import sys

import traceback

import gzip

from StringIO import StringIO

import Queue

import threading

import time

import thread

import cgi

from cgi import parse_qs

import json

import imp

from os.path import join, getsize

import re

import ssl

##################user config ##################

logger = logging.getLogger("network-server")

#############################################

def getTraceStackMsg():

tb = sys.exc_info()[2]

msg = ''

for i in traceback.format_tb(tb):

msg += i

return msg

def InitLog():

logger.setLevel(logging.DEBUG)

fh = logging.FileHandler("network-server.log")

fh.setLevel(logging.DEBUG)

ch = logging.StreamHandler()

ch.setLevel(logging.ERROR)

formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")

ch.setFormatter(formatter)

fh.setFormatter(formatter)

logger.addHandler(fh)

logger.addHandler(ch)

def clearfdpro(epoll_fd, params, fd):

try:

fd_check = int(fd)

except Exception, e:

print "fd error"

sys.exit(1)

try:

#print "pid:%s, close fd:%s" % (os.getpid(), fd)

epoll_fd.unregister(fd)

except Exception, e:

#print str(e)+getTraceStackMsg()

pass

try:

param = params[fd]

try:

addr = param["addr"]

if "next" in param:

print "close sock, %s:%s" % (addr[0], addr[1])

except Exception, e:

pass

param["connections"].shutdown(socket.SHUT_RDWR)

param["connections"].close()

f = param.get("f", None)

if f != None:

f.close()

rc = param.get("rc", None)

if rc != None:

rc.close()

if "read_cache_name" in param:

os.remove(param["read_cache_name"])

except Exception, e:

#print str(e)+getTraceStackMsg()

pass

try:

del params[fd]

#logger.error(getTraceStackMsg())

#logger.error("clear fd:%s" % fd)

except Exception, e:

#print str(e)+getTraceStackMsg()

pass

def clearfd(epoll_fd, params, fd):

try:

param = params[fd]

if "nextfd" in param:

nextfd = param["nextfd"]

next_param = params[nextfd]

del param["nextfd"]

del next_param["nextfd"]

if not "next" in param: #masterfd

clearfdpro(epoll_fd, params, nextfd)

else: # nextfd

if not "writedata" in next_param or len(next_param["writedata"]) == 0:

clearfdpro(epoll_fd, params, nextfd)

else:

next_param["sendandclose"] = "true"

clearfdpro(epoll_fd, params, fd)

except Exception, e:

#print str(e)+getTraceStackMsg()

pass

def FindHostPort(datas):

host_s = -1

host_e = -1

host_str = None

host = ""

port = ""

if not datas.startswith("CONNECT"):

host_s = datas.find("Host:")

if host_s < 0:

host_s = datas.find("host:")

if host_s > 0:

host_e = datas.find("\r\n", host_s)

if host_s > 0 and host_e > 0:

host_str = datas[host_s+5:host_e].strip()

add_list = host_str.split(":")

if len(add_list) == 2:

host = add_list[0]

port = add_list[1]

else:

host = add_list[0]

port = 80

first_seg = datas.find("\r\n")

first_line = datas[0:first_seg]

first_line = first_line.replace(" http://%s" % host_str, " ")

datas = first_line + datas[first_seg:]

else:

first_seg = datas.find("\r\n")

head_e = datas.find("\r\n\r\n")

if first_seg > 0 and head_e > 0:

first_line = datas[0:first_seg]

36a0

com,host_str,http_version = re.split('\s+', first_line)

add_list = host_str.split(":")

if len(add_list) == 2:

host = add_list[0]

port = add_list[1]

else:

host = add_list[0]

port = 443

host_s = 1

host_e = 1

return host_str,host_s,host_e,host,port,datas

def connect_pro(params, param, epoll_fd, datas, fd, cur_time, host, port):

try:

nextfd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)

nextfd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

nextfd.settimeout(5)

try:

nextfd.connect((host, int(port)))

except Exception, e:

print "########%s,%s connect fail" % (host,port)

nextfd.setblocking(0)

next_fileno = nextfd.fileno()

print "pid:%s, connect %s:%s fd:%s" % (os.getpid(), host, port, next_fileno)

if next_fileno in params:

print "fileno exist"

sys.exit(1)

if not datas.startswith("CONNECT"):

next_param = {"addr":[host,port],"writelen":0, "connections":nextfd, "time":cur_time, "nextfd":fd}

param["nextfd"] = next_fileno

next_param["writedata"] = datas

next_param["writelen"] = 0

next_param["next"] = "true"

param["read_len"] = 0

param["readdata"] = ""

params[next_fileno] = next_param

epoll_fd.register(next_fileno, select.EPOLLIN | select.EPOLLOUT | select.EPOLLERR | select.EPOLLHUP)

epoll_fd.modify(fd, select.EPOLLIN | select.EPOLLERR | select.EPOLLHUP)

else:

next_param = {"addr":[host,port],"writelen":0, "connections":nextfd, "time":cur_time, "nextfd":fd}

param["nextfd"] = next_fileno

next_param["next"] = "true"

params[next_fileno] = next_param

epoll_fd.register(next_fileno, select.EPOLLIN | select.EPOLLERR | select.EPOLLHUP)

param["read_len"] = 0

param["readdata"] = ""

param["writedata"] = "HTTP/1.1 200 Connection Established\r\nConnection: close\r\n\r\n"

param["writelen"] = 0

param["reuse"] = "true"

epoll_fd.modify(fd, select.EPOLLIN | select.EPOLLOUT | select.EPOLLERR | select.EPOLLHUP)

except socket.error, msg:

clearfd(epoll_fd,params,fd)

def process_datas(process_status, params, param, epoll_fd, datas, read_len, fd, cur_time):

if process_status == "close":

clearfd(epoll_fd,params,fd)

else:

need_connect = False

host_str = None

host_s = -1

host_e = -1

if "reuse" in param and "next" not in param:

if not datas.startswith("CONNECT") and \

not datas.startswith("GET") and \

not datas.startswith("POST") and \

not datas.startswith("PUT"):

del param["reuse"]

else:

host_str,host_s,host_e,host,port,datas = FindHostPort(datas)

host_s = int(host_s)

host_e = int(host_e)

next_fileno = param["nextfd"]

next_param = params[next_fileno]

addr = next_param["addr"]

if host_s > 0 and host_e > 0:

if host != addr[0] or str(port) != str(addr[1]):

print "%s,%s neq %s,%s" % (host,port,addr[0],addr[1])

need_connect = True

del param["nextfd"]

del next_param["nextfd"]

clearfd(epoll_fd,params,next_fileno)

del param["reuse"]

else:

param["read_len"] = read_len

param["readdata"] = datas

return None

if need_connect or not "nextfd" in param:

if host_str == None or not host_s > 0 or not host_e > 0:

host_str,host_s,host_e,host,port,datas = FindHostPort(datas)

host_s = int(host_s)

host_e = int(host_e)

if host_s > 0 and host_e > 0:

if not datas.startswith("CONNECT"):

epoll_fd.modify(fd, select.EPOLLERR | select.EPOLLHUP) # 简单处理,http连接时把读去掉,避免内存攻击

thread.start_new_thread(connect_pro,(params, param, epoll_fd, datas, fd, cur_time, host, port))

else:

param["read_len"] = read_len

param["readdata"] = datas

else:

next_fileno = param["nextfd"]

next_param = params[next_fileno]

if "next" in param:

next_param["reuse"] = "true"

write_data = next_param.get("writedata", "")

write_data += datas

next_param["writedata"] = write_data

param["read_len"] = 0

param["readdata"] = ""

epoll_fd.modify(next_fileno, select.EPOLLIN | select.EPOLLOUT | select.EPOLLERR | select.EPOLLHUP)

if process_status == "close_after_process":

print "close after process"

clearfd(epoll_fd,params,fd)

def run_main(listen_fd):

try:

epoll_fd = select.epoll()

epoll_fd.register(listen_fd.fileno(), select.EPOLLIN | select.EPOLLERR | select.EPOLLHUP)

print "listen_fd:%s" % listen_fd.fileno()

except select.error, msg:

logger.error(msg)

params = {}

last_min_time = -1

while True:

epoll_list = epoll_fd.poll()

cur_time = time.time()

for fd, events in epoll_list:

if fd == listen_fd.fileno():

while True:

try:

conn, addr = listen_fd.accept()

conn.setblocking(0)

epoll_fd.register(conn.fileno(), select.EPOLLIN | select.EPOLLERR | select.EPOLLHUP)

conn.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

#conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)

params[conn.fileno()] = {"addr":addr,"writelen":0, "connections":conn, "time":cur_time}

except socket.error, msg:

break

elif select.EPOLLIN & events:

param = params.get(fd,None)

if param == None:

continue

param["time"] = cur_time

datas = param.get("readdata","")

cur_sock = params[fd]["connections"]

read_len = param.get("read_len", 0)

process_status = "close"

while True:

try:

data = cur_sock.recv(102400)

if not data:

if datas == "":

break

else:

raise Exception("close after process")

else:

datas += data

read_len += len(data)

except socket.error, msg:

if msg.errno == errno.EAGAIN:

process_status = "process"

break

else:

break

except Exception, e:

process_status = "close_after_process"

break

process_datas(process_status, params, param, epoll_fd, datas, read_len, fd, cur_time)

elif select.EPOLLHUP & events or select.EPOLLERR & events:

clearfd(epoll_fd,params,fd)

logger.error("sock: %s error" % fd)

elif select.EPOLLOUT & events:

param = params.get(fd,None)

if param == None:

continue

param["time"] = cur_time

sendLen = param.get("writelen",0)

writedata = param.get("writedata", "")

total_write_len = len(writedata)

cur_sock = param["connections"]

f = param.get("f", None)

totalsenlen = param.get("totalsenlen", None)

if writedata == "":

clearfd(epoll_fd,params,fd)

continue

while True:

try:

sendLen += cur_sock.send(writedata[sendLen:])

if sendLen == total_write_len:

if f != None and totalsenlen != None:

readmorelen = 102400

if readmorelen > totalsenlen:

readmorelen = totalsenlen

morefiledata = ""

if readmorelen > 0:

morefiledata = f.read(readmorelen)

if morefiledata != "":

writedata = morefiledata

sendLen = 0

total_write_len = len(writedata)

totalsenlen -= total_write_len

param["writedata"] = writedata

param["totalsenlen"] = totalsenlen

continue

else:

f.close()

del param["f"]

del param["totalsenlen"]

if not "sendandclose" in param:

param["writedata"] = ""

param["writelen"] = 0

epoll_fd.modify(fd, select.EPOLLIN | select.EPOLLERR | select.EPOLLHUP)

else:

clearfd(epoll_fd,params,fd)

break

except socket.error, msg:

if msg.errno == errno.EAGAIN:

param["writelen"] = sendLen

break

clearfd(epoll_fd,params,fd)

break

else:

continue

#check time out

if cur_time - last_min_time > 20:

last_min_time = cur_time

objs = params.items()

for (key_fd,value) in objs:

fd_time = value.get("time", 0)

del_time = cur_time - fd_time

if del_time > 20:

clearfd(epoll_fd,params,key_fd)

elif fd_time < last_min_time:

last_min_time = fd_time

if __name__ == "__main__":

reload(sys)

sys.setdefaultencoding('utf8')

InitLog()

port = int(sys.argv[1])

try:

listen_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)

except socket.error, msg:

logger.error("create socket failed")

try:

listen_fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

except socket.error, msg:

logger.error("setsocketopt SO_REUSEADDR failed")

try:

listen_fd.bind(('', port))

except socket.error, msg:

logger.error("bind failed")

try:

listen_fd.listen(10240)

listen_fd.setblocking(0)

except socket.error, msg:

logger.error(msg)

child_num = 19

c = 0

while c < child_num:

c = c + 1

newpid = os.fork()

if newpid == 0:

run_main(listen_fd)

run_main(listen_fd)

总结

以上所述是小编给大家介绍的Python代码实现http/https代理服务器,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

以上是 Python代码实现http/https代理服务器的脚本 的全部内容, 来源链接: utcz.com/z/358812.html

回到顶部