I haven’t had very much time to write some ruby code to communicate with rtorrent through the local domain socket. But i got something that starts to work.
The main problem is that Ruby XMLRPC library is unfriendly with user with special needs. There is no way to get the xml data, send it the way i want and get it back and let the library parse it!
To solve this i had to modify the library, with a patch that when is ready i’ll subit to the ruby developers.
I have added a class that represents a socket factory! the easy way to solve the problem. In less then 30 lines it just works. There is a new method to initialize the Client class too, and even the hashed (new3) method will work if the field socket is specified.
class SocketFactory
def issue() # returns a new socket to be used with the library
raise "This class must be subclassed"
end
end
def new4( socket )
self.new(nil,nil,nil,nil,nil,nil,nil,nil,nil,socket)
end
alias new_with_socket new4
After these modifications to the initialization code i had to modify the private method do_rpc adding this at the beginning of the method
data = nil
if @socket != nil then # using user socket
# must do the same things, send the same things
# but to the socket! not to a http/https server
sock = @socket.issue()
if sock.write(request) != request.length then
raise "Not all the data has been sent"
end
# read the whole data until the socket is closed
data = sock.read()
# after any answer the socket is closed
return data # return data to the parser
else
and an end just after the return statement of the original method. There is an additional check to verify that the socket object is a subclass of the SocketFactory class.
I wrote a simple socket factory to test the communications with rtorrent itself in the real world. I had to write to it using the SCGI protocol, which obviously is supported in ruby, but it doesn’t support local domain socket, for no real reasons, since it is indistinguishable from any other kind of socket, but that was the statement in rdoc
One thing that SCGI doesn‘t support is using UNIX Domain sockets in addition to TCP/IP sockets. This isn‘t really needed, but it is handy in a shared hosting situation where you don‘t want others connecting to your processes or if you have to request open ports. Sorry, no UNIX Domain sockets in SCGI.
It’s crazy… so i wrote a simple scgi wrapper. which is incredibly simple
def self.wrap( content, uri, method="POST" )
null=""
header = "CONTENT_LENGTH #{content.length} SCGI#{null}1
REQUEST_METHOD #{method} REQUEST_URI #{uri} "
return "#{header.length}:#{header},#{content}"
end
There are some missing chars (many “\0″), in the string header and the null string are just “\0″, but can’t be shown here as in any my previous post, the files in the git repository are ok.
To use this wrapper i have to write a socket factory that will create sockets that will wrap the xmldata in a scgi header.
So i wrote a class SCGIFactory, which is still incomplete (no error checking) but is working.
def issue()
if @method != nil
SCGIWrappedSocket.new(UNIXSocket.new(@path),@uri,@method)
else
SCGIWrappedSocket.new(UNIXSocket.new(@path),@uri)
end
end
def write(x)
@sock.write(SCGI.wrap(x,@uri,@method))
end
def read()
data = @sock.read()
# receiving an html response (very dumb parsing)
# divide in 2
# 1 -> status + headers
# 2 -> data
return data.split("\r\n\r\n").last
end
As you can see the read method need many enhancements. I need a way to parse http data from a string in any default library, but no default library do this. If anybody knows hot to do that, please tell me.
I wrote this test
require 'xmlrpc/client'
require 'SCGIFactory'
f=SCGIFactory::Unix.new("/rtorrent.sock","/RPC2")
c=XMLRPC::Client.new_with_socket(f)
puts "#{c.call("system.listMethods")}"
And I can get the whole list of the supported methods directly from the program itself
All the code is available in a git repository, different from the ruby upnp one.. Git repository can be found in the code page
[Continued by: XMLRPC sockets changes]
Posted by Dario Meloni