From 58b2e395701c50acb1db488108aadb3a4066eb09 Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Sun, 23 Jan 2011 02:59:09 -0500 Subject: [PATCH 1/4] Allow the specification of address_family for JSONRPC servers, and unlink UNIX sockets before binding them --- jsonrpclib/SimpleJSONRPCServer.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/jsonrpclib/SimpleJSONRPCServer.py b/jsonrpclib/SimpleJSONRPCServer.py index 370ae40..854697b 100644 --- a/jsonrpclib/SimpleJSONRPCServer.py +++ b/jsonrpclib/SimpleJSONRPCServer.py @@ -2,6 +2,8 @@ from jsonrpclib import Fault import SimpleXMLRPCServer import SocketServer +import socket +import os import types import traceback import sys @@ -182,12 +184,22 @@ class SimpleJSONRPCServer(SocketServer.TCPServer, SimpleJSONRPCDispatcher): allow_reuse_address = True def __init__(self, addr, requestHandler=SimpleJSONRPCRequestHandler, - logRequests=True, encoding=None, bind_and_activate=True): + logRequests=True, encoding=None, bind_and_activate=True, + address_family=socket.AF_INET): self.logRequests = logRequests SimpleJSONRPCDispatcher.__init__(self, encoding) # TCPServer.__init__ has an extra parameter on 2.6+, so # check Python version and decide on how to call it vi = sys.version_info + self.address_family = address_family + if address_family == socket.AF_UNIX: + # Unix sockets can't be bound if they already exist in the + # filesystem. The convention of e.g. X11 is to unlink + # before binding again. + try: + os.unlink(addr) + except OSError: + pass # if python 2.5 and lower if vi[0] < 3 and vi[1] < 6: SocketServer.TCPServer.__init__(self, addr, requestHandler) From e321f64ac34f46d29b11d863dafc5bda18ccf3a3 Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Sun, 23 Jan 2011 03:08:52 -0500 Subject: [PATCH 2/4] Support unix:/foo/bar URLs for the client --- jsonrpclib/jsonrpc.py | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/jsonrpclib/jsonrpc.py b/jsonrpclib/jsonrpc.py index 8e50079..b752450 100644 --- a/jsonrpclib/jsonrpc.py +++ b/jsonrpclib/jsonrpc.py @@ -144,6 +144,23 @@ class Transport(TransportMixIn, XMLTransport): class SafeTransport(TransportMixIn, XMLSafeTransport): pass + +from httplib import HTTP, HTTPConnection +from socket import socket, AF_UNIX, SOCK_STREAM +class UnixHTTPConnection(HTTPConnection): + def connect(self): + self.sock = socket(AF_UNIX, SOCK_STREAM) + self.sock.connect(self.host) + +class UnixHTTP(HTTP): + _connection_class = UnixHTTPConnection + +class UnixTransport(TransportMixIn, XMLTransport): + def make_connection(self, host): + import httplib + host, extra_headers, x509 = self.get_host_info(host) + return UnixHTTP(host) + class ServerProxy(XMLServerProxy): """ @@ -158,15 +175,21 @@ def __init__(self, uri, transport=None, encoding=None, version = config.version self.__version = version schema, uri = urllib.splittype(uri) - if schema not in ('http', 'https'): + if schema not in ('http', 'https', 'unix'): raise IOError('Unsupported JSON-RPC protocol.') - self.__host, self.__handler = urllib.splithost(uri) - if not self.__handler: - # Not sure if this is in the JSON spec? - #self.__handler = '/' - self.__handler == '/' + if schema == 'unix': + self.__host = uri + self.__handler = '/' + else: + self.__host, self.__handler = urllib.splithost(uri) + if not self.__handler: + # Not sure if this is in the JSON spec? + #self.__handler = '/' + self.__handler == '/' if transport is None: - if schema == 'https': + if schema == 'unix': + transport = UnixTransport() + elif schema == 'https': transport = SafeTransport() else: transport = Transport() From b3c011fa6a42b9dc419d7c392c663e8302fcb275 Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Sun, 23 Jan 2011 03:13:17 -0500 Subject: [PATCH 3/4] Allow Unix socket unit testing --- tests.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests.py b/tests.py index c030ffe..efce39e 100644 --- a/tests.py +++ b/tests.py @@ -22,6 +22,7 @@ from jsonrpclib import Server, MultiCall, history, config, ProtocolError from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCRequestHandler +import socket import unittest import os try: @@ -40,7 +41,7 @@ class TestCompatibility(unittest.TestCase): def setUp(self): self.port = PORTS.pop() - self.server = server_set_up(port=self.port) + self.server = server_set_up(addr=('', self.port)) self.client = Server('http://localhost:%d' % self.port) # v1 tests forthcoming @@ -261,7 +262,7 @@ class InternalTests(unittest.TestCase): def setUp(self): self.port = PORTS.pop() - self.server = server_set_up(port=self.port) + self.server = server_set_up(addr=('', self.port)) def get_client(self): return Server('http://localhost:%d' % self.port) @@ -367,14 +368,14 @@ def get_data(): def ping(): return True -def server_set_up(port): +def server_set_up(addr, address_family=socket.AF_INET): # Not sure this is a good idea to spin up a new server thread # for each test... but it seems to work fine. def log_request(self, *args, **kwargs): """ Making the server output 'quiet' """ pass SimpleJSONRPCRequestHandler.log_request = log_request - server = SimpleJSONRPCServer(('', port)) + server = SimpleJSONRPCServer(addr, address_family=address_family) server.register_function(summation, 'sum') server.register_function(summation, 'notify_sum') server.register_function(notify_hello) From 84a762b19a301ec66c05d69d765d3db24bb7930b Mon Sep 17 00:00:00 2001 From: Quentin Smith Date: Sun, 23 Jan 2011 12:32:53 -0500 Subject: [PATCH 4/4] Add test cases for Unix sockets --- tests.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests.py b/tests.py index efce39e..7c568b0 100644 --- a/tests.py +++ b/tests.py @@ -345,6 +345,18 @@ def func(): self.assertRaises(raises[i], func) +class UnixSocketInternalTests(InternalTests): + """ + These tests run the same internal communication tests, but over a + Unix socket instead of a TCP socket. + """ + def setUp(self): + self.port = "/tmp/jsonrpc%d.sock" % (PORTS.pop()) + self.server = server_set_up(addr=self.port, address_family=socket.AF_UNIX) + + def get_client(self): + return Server('unix:%s' % self.port) + """ Test Methods """ def subtract(minuend, subtrahend): """ Using the keywords from the JSON-RPC v2 doc """