Skip to content

Instantly share code, notes, and snippets.

@aaptel
Created January 9, 2018 18:26
Show Gist options
  • Save aaptel/ff1aced55062808c30dd2462bb88a0d9 to your computer and use it in GitHub Desktop.
Save aaptel/ff1aced55062808c30dd2462bb88a0d9 to your computer and use it in GitHub Desktop.
radprox
#!/usr/bin/python3
#
# radprox.py --- web radio proxy
#
# Run and open http://localhost:8080/?url=<any web radio url>
#
# Aurélien Aptel <aurelien.aptel@gmail.com>
#
# based on
# http://voorloopnul.com/blog/a-python-proxy-in-less-than-100-lines-of-code/
import socket
import select
import time
import sys
import re
from urllib.parse import urlsplit
BUFFER_SIZE = 4096
DELAY = 0.0001
URL_RX = re.compile(r'''GET .*?url=(.+) HTTP/''')
PROXY_PORT = 8080
def get_new_http_headers(data):
try:
# extract the url
s = data.decode('utf-8', "ignore")
s, rest = s.split("\n", 1)
m = re.search(URL_RX, s)
url = urlsplit(m.group(1))
# make new headers
final = ('GET %s?%s HTTP/1.0\n' % (url.path, url.query)) + rest
return (url.hostname, url.port, final.encode('utf-8'))
except Exception:
return None
class Forward:
def __init__(self):
self.forward = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def start(self, host, port):
try:
self.forward.connect((host, port))
return self.forward
except Exception as e:
print(e)
return False
class Server:
input_list = []
channel = {}
def __init__(self, host, port):
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind((host, port))
self.server.listen(200)
def main_loop(self):
self.input_list.append(self.server)
while 1:
time.sleep(DELAY)
ss = select.select
inputready, outputready, exceptready = ss(self.input_list, [], [])
for self.s in inputready:
if self.s == self.server:
self.on_accept()
break
try:
self.data = self.s.recv(BUFFER_SIZE)
except Exception:
self.on_close()
break
if len(self.data) == 0:
self.on_close()
break
else:
self.on_recv()
def on_accept(self):
clientsock, clientaddr = self.server.accept()
data = clientsock.recv(BUFFER_SIZE)
r = get_new_http_headers(data)
if not r:
print("Can't find redirection in request")
print("closing connection with client side")
clientsock.close()
return
host, port, newdata = r
forward = Forward().start(host, port)
if not forward:
print("Can't establish connection with remote server.",)
print("Closing connection with client side", clientaddr)
clientsock.close()
return
print(clientaddr, "has connected")
print("sending new headers")
print(newdata)
forward.sendall(newdata)
self.input_list.append(clientsock)
self.input_list.append(forward)
self.channel[clientsock] = forward
self.channel[forward] = clientsock
return
def on_close(self):
print("client has disconnected")
# clear all reference (used in select())
self.input_list.remove(self.s)
self.input_list.remove(self.channel[self.s])
try:
out = self.channel[self.s]
self.channel[out].close()
self.channel[self.s].close()
except Exception:
pass
del self.channel[out]
del self.channel[self.s]
def on_recv(self):
data = self.data
try:
self.channel[self.s].send(data)
except Exception:
self.on_close()
if __name__ == '__main__':
server = Server('', PROXY_PORT)
try:
server.main_loop()
except KeyboardInterrupt:
print("Ctrl C - Stopping server")
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment