tgsou / ipv4_proxy.py
xibalami's picture
Fix API search using telegram.me parser
abbf2d7 verified
Raw
History Blame Contribute Delete
3.31 kB
import socket
import socketserver
import select
import urllib.parse
BUFFER_SIZE = 65536
TIMEOUT = 30
def connect_upstream(host, port):
return socket.create_connection((host, port), timeout=TIMEOUT)
class ProxyHandler(socketserver.BaseRequestHandler):
def handle(self):
client = self.request
client.settimeout(TIMEOUT)
data = b''
while b'\r\n\r\n' not in data and len(data) < 65536:
chunk = client.recv(BUFFER_SIZE)
if not chunk:
return
data += chunk
header_blob, _, extra = data.partition(b'\r\n\r\n')
lines = header_blob.decode('iso-8859-1', 'replace').split('\r\n')
if not lines:
return
parts = lines[0].split()
if len(parts) < 3:
return
method, target, version = parts[0].upper(), parts[1], parts[2]
header_lines = lines[1:]
if method == 'CONNECT':
host, _, port_s = target.partition(':')
port = int(port_s or 443)
remote = None
try:
remote = connect_upstream(host, port)
remote.sendall(extra)
client.sendall(b'HTTP/1.1 200 Connection Established\r\n\r\n')
self.relay(client, remote)
except Exception as e:
try:
msg = ('HTTP/1.1 502 Bad Gateway\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n' + str(e)).encode()
client.sendall(msg)
except Exception:
pass
if remote:
remote.close()
return
url = urllib.parse.urlsplit(target)
host = url.hostname
port = url.port or (443 if url.scheme == 'https' else 80)
if not host:
return
path = urllib.parse.urlunsplit(('', '', url.path or '/', url.query, ''))
remote = None
try:
remote = connect_upstream(host, port)
remote.sendall(f'{method} {path} {version}\r\n'.encode('iso-8859-1'))
for h in header_lines:
if not h.lower().startswith('proxy-connection:'):
remote.sendall((h + '\r\n').encode('iso-8859-1'))
remote.sendall(b'\r\n')
if extra:
remote.sendall(extra)
self.relay(client, remote)
except Exception:
if remote:
remote.close()
def relay(self, client, remote):
sockets = [client, remote]
try:
while True:
readable, _, _ = select.select(sockets, [], [], TIMEOUT)
if not readable:
break
for s in readable:
data = s.recv(BUFFER_SIZE)
if not data:
return
(remote if s is client else client).sendall(data)
finally:
remote.close()
class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
allow_reuse_address = True
daemon_threads = True
if __name__ == '__main__':
with ThreadingTCPServer(('127.0.0.1', 18888), ProxyHandler) as server:
print('Local proxy listening on 127.0.0.1:18888', flush=True)
server.serve_forever()