add rtorrent scgi proxy for dumb use case
This commit is contained in:
parent
3f1d8c4ae4
commit
485d1e0e4f
100
rtorrent_proxy.py
Normal file
100
rtorrent_proxy.py
Normal file
|
@ -0,0 +1,100 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
This file handles a rather silly use case:
|
||||
You have two servers, A and B, each with various rtorrent instances running.
|
||||
You want one instance of Aberrant, running on server A. In your infinite
|
||||
wisdom, you bound the SCGI ports of the rtorrent instances on server B to
|
||||
localhost, leaving Aberrant unable to query them. Rtorrent won't allow you
|
||||
to rebind the SCGI port while it's running, and you don't want to restart
|
||||
the rtorrent instance(s) because restarting stuff sucks. Therefore, you
|
||||
need another way for Aberrant to communicate to them.
|
||||
|
||||
This script acts as a proxy to the rtorrent instances on server B. It will
|
||||
listen on a port (don't forget to open the port in your iptables), and
|
||||
relay all data it receives to the torrent SCGI port and vice versa.
|
||||
|
||||
To use:
|
||||
Configure `port_mapping` in the form `listen_port`: `rtorrent_port`.
|
||||
Run the script on server B.
|
||||
"""
|
||||
import socket
|
||||
import threading
|
||||
|
||||
port_mapping = {
|
||||
4000: 5000,
|
||||
4001: 5001,
|
||||
4002: 5002,
|
||||
}
|
||||
|
||||
class SocketProxy(threading.Thread):
|
||||
"""Represents a proxy to an rTorrent SCGI port."""
|
||||
def __init__(self, listen_port, rtorrent_port):
|
||||
super().__init__()
|
||||
self.listen_port = listen_port
|
||||
self.rtorrent_port = rtorrent_port
|
||||
self._stop_event = threading.Event()
|
||||
|
||||
def stop(self):
|
||||
"""Sets the internal 'stop running' flag."""
|
||||
self._stop_event.set()
|
||||
|
||||
def stopped(self):
|
||||
"""Returns true if the internal stop flag has been set."""
|
||||
return self._stop_event.is_set()
|
||||
|
||||
def run(self):
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.settimeout(1)
|
||||
s.bind(('0.0.0.0', self.listen_port))
|
||||
s.listen(1)
|
||||
while not self.stopped():
|
||||
try:
|
||||
conn, addr = s.accept() # client connected
|
||||
except socket.timeout:
|
||||
continue
|
||||
with conn:
|
||||
# receive data from client
|
||||
data = []
|
||||
data.append(conn.recv(1024)) # the header really shouldn't be longer than this...
|
||||
header = data[0].partition(b',')[0]
|
||||
body_len = int(header.split(b'\x00')[1])
|
||||
recv_len = len(data[0])
|
||||
while recv_len < body_len + len(header) + 1:
|
||||
data.append(conn.recv(1024))
|
||||
recv_len += len(data[-1])
|
||||
data = b''.join(data)
|
||||
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as rt_conn:
|
||||
rt_conn.connect(('127.0.0.1', self.rtorrent_port))
|
||||
rt_conn.sendall(data) # send to rtorrent
|
||||
|
||||
# receive data from rtorrent
|
||||
data = []
|
||||
data.append(rt_conn.recv(1024))
|
||||
header = data[0].partition(b'<?xml')[0]
|
||||
body_len = header.split(b'\r\n')[2].partition(b' ')[2]
|
||||
body_len = int(body_len)
|
||||
recv_len = len(data[0])
|
||||
while recv_len < body_len + len(header):
|
||||
data.append(rt_conn.recv(1024))
|
||||
recv_len += len(data[-1])
|
||||
data = b''.join(data)
|
||||
conn.sendall(data) # send to client
|
||||
|
||||
def run_proxies():
|
||||
"""Run all proxies."""
|
||||
proxies = []
|
||||
for listen_port, rtorrent_port in port_mapping.items():
|
||||
p = SocketProxy(listen_port, rtorrent_port)
|
||||
p.start()
|
||||
proxies.append(p)
|
||||
|
||||
try:
|
||||
proxies[0].join()
|
||||
except KeyboardInterrupt:
|
||||
for p in proxies:
|
||||
p.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_proxies()
|
Loading…
Reference in New Issue
Block a user