标题解释
这实际上实现三个功能
- http服务器
- 代理转发
- 跳转
解释
像nginx的代理转发常用的有http,tcp, udp的类型。
所谓的代理转发,就是一个中转站。
本文是基于socket的实现,如果有需要,还可以基于werkzurg再封装一层,实现http, tcp代理转发。
功能点
- 代理转发
- 重定向
- post, get 带参数形式的请求
引用的库
- gevent
-socket - select或epoll
- werkzurg
werkzeug的http服务器
from gevent.monkey import patch_all;
patch_all()
import random
import socket
from urllib import request
from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple
from werkzeug.routing import Map, Rule
from werkzeug.utils import redirect
import json
addr_proxy = [
{"http": "127.0.0.1:10081"},
]
def proxy_urlib(addr_proxy, addr_target):
proxy_list = addr_proxy
# 随机选择一个代理
proxy = random.choice(proxy_list)
# 使用选择的代理构建代理处理器对象
httpproxy_handler = request.ProxyHandler(proxy)
opener = request.build_opener(httpproxy_handler)
r = request.Request(addr_target)
print(addr_target)
try:
resp = opener.open(r, timeout=3)
return resp
except Exception as e:
raise Exception("{}: {}".format(proxy, str(e)))
def forward_socket(host_forward, port_forward, data_forward):
buffer = 1024
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((host_forward, port_forward))
sock.sendall(data_forward)
data_recv = sock.recv(buffer)
print(data_recv)
return data_recv
def index(req):
path = req.path
print(path)
print("请求方式:{}".format(req.method))
# 测试redirect
# if path == "/redirect":
# return redirect("http://127.0.0.1:10090/baidu", 302)
# 测试代理转发
if path == "/proxy":
try:
print("begin to proxy: {}".format(addr_proxy))
# post请求
post_str = b'POST /post/test HTTP/1.1\r\nHost: %s\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36\r\n\r\n%s\r\n\r\n'
# get请求
get_str = b'GET /get/test?hugo=boss HTTP/1.1\r\nHost: %s\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36\r\nAccept: */*\r\n\r\n'
data_recv = forward_socket("127.0.0.1", 10090, get_str)
return Response("代理转发结果:{}".format(str(data_recv, encoding="utf-8")), status='200 OK', content_type ='application/json', mimetype='application/json')
except Exception as e:
return Response("代理转发异常:{}".format(str(e)), status='200 OK', content_type='application/json', mimetype='application/json')
return Response(json.dumps({"path": 302}), status='200 OK', content_type='application/json', mimetype='application/json')
def proxy_redirect(url, code=302, response=None):
redirect(url,code, response)
url_map = Map([
Rule('/', endpoint=index),
Rule('/',endpoint=index),
Rule('/', endpoint=index),
Rule('///', endpoint=index),
Rule('//', endpoint=index),
Rule('/', endpoint=index)
])
views = {'index': index}
@Request.application
def application(req):
adapter = url_map.bind_to_environ(req.environ)
endpoint, args = adapter.match()
return endpoint(req)
if __name__ == "__main__":
run_simple("0.0.0.0", 10080, application)
代理转发
import gevent.monkey
gevent.monkey.patch_all()
import socket
import threading
from collections import deque
class ProxyForward:
def __init__(self, host_forward: str, port_forward: int):
self.host_server = "127.0.0.1"
self.port_server = 10081
self.host_forward = host_forward
self.port_forward = port_forward
self.listener = 5
self.buffer = 1024
self.data_send = ""
self.queue = deque()
# 接受请求
def server(self):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.bind((self.host_server, self.port_server))
sock.listen(self.listener)
# sock.setblocking(False)
# sock.settimeout(60)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
print("server begin to listen....")
while True:
conn, addr = sock.accept()
with conn:
data_recv = conn.recv(self.buffer)
"""
处理粘包的思路:
1. 类似http的长度判断
2. 类似redis协议flag判断
"""
# 根据长度判断是否接收完
# if len(data_recv) == 100:
# break
post_str = b'POST /post/test HTTP/1.1\r\nHost: %s\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36\r\n\r\n%s\r\n\r\n'
post_str_param = "POST /get/test HTTP/1.1\r\n" \
"Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, */*\r\n" \
"Referer: http://yjsgl.bjut.edu.cn/bgdadmin/servlet/studentMain\r\n" \
"Accept-Language: zh-cn\r\n" \
"Content-Type: application/x-www-form-urlencoded\r\n" \
"Accept-Encoding: gzip, deflate\r\n" \
"User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)\r\n" \
"Host: yjsgl.bjut.edu.cn\r\n" \
"Content-Length: 47\r\n" \
"Connection: Keep-Alive\r\n" \
"Cache-Control: no-cache\r\n" \
"Cookie: JSESSIONID=DgxvXnRhLdSn65nfkyXv4wGXr8xQWb4Vmhkq7GfdhRz3LpdwJ4WC!-611812863\r\n\r\n" \
"TYPE=AUTH&glnj=&USER=xxxxxxxxxx&PASSWORD="
get_str = b'GET /get/test HTTP/1.1\r\nHost: %s\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36\r\nAccept: */*\r\n\r\n'
# 转发
"""
高性能的话,可以加入队列的形式
"""
print("开始转发:{}".format(addr))
resp = self.client(post_str)
print("转发返回的结果: {}".format(resp))
if resp:
conn.sendall(resp)
else:
conn.sendall(b"no data")
# 转发请求
def client(self, data_forward: bytes):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((self.host_forward, self.port_forward))
sock.sendall(data_forward)
data_recv = sock.recv(self.buffer)
return data_recv
if __name__ == "__main__":
thread = threading.Thread(target=ProxyForward(host_forward="127.0.0.1", port_forward=10090).server)
thread.start()
thread.join()
测试效果图
重定向redirect
用 werkzeug的重定向
from werkzeug.utils import redirect
# 测试redirect
# if path == "/redirect":
# return redirect("http://127.0.0.1:10090/test/redirect", 302)
参考
socket接受大数 据
https://www.cnblogs.com/Keep-Ambition/p/7459213.html