connect to rtorrent directly through scgi
This commit is contained in:
parent
203c460210
commit
5d5cfd2f30
|
@ -5,9 +5,10 @@ This module handles the interface with rTorrent via XMLRPC.
|
|||
import re
|
||||
import time
|
||||
import threading
|
||||
import xmlrpc.client
|
||||
from collections import defaultdict
|
||||
|
||||
import rtorrent_xmlrpc
|
||||
|
||||
NUM_INST = 10
|
||||
WATCH_HANDLE = None
|
||||
sp = []
|
||||
|
@ -144,7 +145,7 @@ def init():
|
|||
global WATCH_HANDLE
|
||||
global sp
|
||||
for n in range(NUM_INST):
|
||||
s = xmlrpc.client.ServerProxy(f"http://localhost:8000/RPC{n}")
|
||||
s = rtorrent_xmlrpc.SCGIServerProxy(f"scgi://localhost:500{n}")
|
||||
sp.append(s)
|
||||
WATCH_HANDLE = Watch()
|
||||
WATCH_HANDLE.start()
|
||||
|
|
204
rtorrent_xmlrpc.py
Normal file
204
rtorrent_xmlrpc.py
Normal file
|
@ -0,0 +1,204 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# rtorrent_xmlrpc
|
||||
# (c) 2011 Roger Que <alerante@bellsouth.net>
|
||||
#
|
||||
# Python module for interacting with rtorrent's XML-RPC interface
|
||||
# directly over SCGI, instead of through an HTTP server intermediary.
|
||||
# Inspired by Glenn Washburn's xmlrpc2scgi.py [1], but subclasses the
|
||||
# built-in xmlrpc.client classes so that it is compatible with features
|
||||
# such as MultiCall objects.
|
||||
#
|
||||
# [1] <http://libtorrent.rakshasa.no/wiki/UtilsXmlrpc2scgi>
|
||||
#
|
||||
# Usage: server = SCGIServerProxy('scgi://localhost:7000/')
|
||||
# server = SCGIServerProxy('scgi:///path/to/scgi.sock')
|
||||
# print(server.system.listMethods())
|
||||
# mc = xmlrpc.client.MultiCall(server)
|
||||
# mc.get_up_rate()
|
||||
# mc.get_down_rate()
|
||||
# print(mc())
|
||||
#
|
||||
#
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# In addition, as a special exception, the copyright holders give
|
||||
# permission to link the code of portions of this program with the
|
||||
# OpenSSL library under certain conditions as described in each
|
||||
# individual source file, and distribute linked combinations
|
||||
# including the two.
|
||||
#
|
||||
# You must obey the GNU General Public License in all respects for
|
||||
# all of the code used other than OpenSSL. If you modify file(s)
|
||||
# with this exception, you may extend this exception to your version
|
||||
# of the file(s), but you are not obligated to do so. If you do not
|
||||
# wish to do so, delete this exception statement from your version.
|
||||
# If you delete this exception statement from all source files in the
|
||||
# program, then also delete it here.
|
||||
#
|
||||
#
|
||||
#
|
||||
# Portions based on Python's xmlrpc.client:
|
||||
#
|
||||
# Copyright (c) 1999-2002 by Secret Labs AB
|
||||
# Copyright (c) 1999-2002 by Fredrik Lundh
|
||||
#
|
||||
# By obtaining, using, and/or copying this software and/or its
|
||||
# associated documentation, you agree that you have read, understood,
|
||||
# and will comply with the following terms and conditions:
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software and
|
||||
# its associated documentation for any purpose and without fee is
|
||||
# hereby granted, provided that the above copyright notice appears in
|
||||
# all copies, and that both that copyright notice and this permission
|
||||
# notice appear in supporting documentation, and that the name of
|
||||
# Secret Labs AB or the author not be used in advertising or publicity
|
||||
# pertaining to distribution of the software without specific, written
|
||||
# prior permission.
|
||||
#
|
||||
# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
||||
# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
|
||||
# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
|
||||
# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
|
||||
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
# OF THIS SOFTWARE.
|
||||
|
||||
# Updated to python3 by iou1name
|
||||
|
||||
import re
|
||||
import socket
|
||||
import xmlrpc.client
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
class SCGITransport(xmlrpc.client.Transport):
|
||||
def single_request(self, host, handler, request_body, verbose=0):
|
||||
# Add SCGI headers to the request.
|
||||
headers = {'CONTENT_LENGTH': str(len(request_body)), 'SCGI': '1'}
|
||||
header = '\x00'.join(('%s\x00%s' % item for item in headers.items())) + '\x00'
|
||||
header = '%d:%s' % (len(header), header)
|
||||
request_body = '%s,%s' % (header, request_body)
|
||||
|
||||
sock = None
|
||||
|
||||
try:
|
||||
if host:
|
||||
host, _, port = host.partition(':')
|
||||
addrinfo = socket.getaddrinfo(host, port, socket.AF_INET,
|
||||
socket.SOCK_STREAM)
|
||||
sock = socket.socket(*addrinfo[0][:3])
|
||||
sock.connect(addrinfo[0][4])
|
||||
else:
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
sock.connect(handler)
|
||||
|
||||
self.verbose = verbose
|
||||
|
||||
sock.send(bytes(request_body, 'utf8'))
|
||||
return self.parse_response(sock.makefile())
|
||||
finally:
|
||||
if sock:
|
||||
sock.close()
|
||||
|
||||
def parse_response(self, response):
|
||||
p, u = self.getparser()
|
||||
|
||||
response_body = ''
|
||||
while True:
|
||||
data = response.read(1024)
|
||||
if not data:
|
||||
break
|
||||
response_body += data
|
||||
|
||||
# Remove SCGI headers from the response.
|
||||
response_header, response_body = re.split(r'\n\s*?\n', response_body,
|
||||
maxsplit=1)
|
||||
|
||||
if self.verbose:
|
||||
print('body:', repr(response_body))
|
||||
|
||||
p.feed(response_body)
|
||||
p.close()
|
||||
|
||||
return u.close()
|
||||
|
||||
|
||||
class SCGIServerProxy(xmlrpc.client.ServerProxy):
|
||||
def __init__(self, uri, transport=None, encoding=None, verbose=False,
|
||||
allow_none=False, use_datetime=False):
|
||||
url = urlparse(uri)
|
||||
if url.scheme not in ('scgi'):
|
||||
raise IOError('unsupported XML-RPC protocol')
|
||||
self.__host = url.netloc
|
||||
self.__handler = url.path
|
||||
if not self.__handler:
|
||||
self.__handler = '/'
|
||||
|
||||
if transport is None:
|
||||
transport = SCGITransport(use_datetime=use_datetime)
|
||||
self.__transport = transport
|
||||
|
||||
self.__encoding = encoding
|
||||
self.__verbose = verbose
|
||||
self.__allow_none = allow_none
|
||||
|
||||
def __close(self):
|
||||
self.__transport.close()
|
||||
|
||||
def __request(self, methodname, params):
|
||||
# call a method on the remote server
|
||||
|
||||
request = xmlrpc.client.dumps(params, methodname, encoding=self.__encoding,
|
||||
allow_none=self.__allow_none)
|
||||
|
||||
response = self.__transport.request(
|
||||
self.__host,
|
||||
self.__handler,
|
||||
request,
|
||||
verbose=self.__verbose
|
||||
)
|
||||
|
||||
if len(response) == 1:
|
||||
response = response[0]
|
||||
|
||||
return response
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
"<SCGIServerProxy for %s%s>" %
|
||||
(self.__host, self.__handler)
|
||||
)
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
def __getattr__(self, name):
|
||||
# magic method dispatcher
|
||||
return xmlrpc.client._Method(self.__request, name)
|
||||
|
||||
# note: to call a remote object with an non-standard name, use
|
||||
# result getattr(server, "strange-python-name")(args)
|
||||
|
||||
def __call__(self, attr):
|
||||
"""A workaround to get special attributes on the ServerProxy
|
||||
without interfering with the magic __getattr__
|
||||
"""
|
||||
if attr == "close":
|
||||
return self.__close
|
||||
elif attr == "transport":
|
||||
return self.__transport
|
||||
raise AttributeError("Attribute %r not found" % (attr,))
|
Loading…
Reference in New Issue
Block a user