Commit 1a8337f2 authored by Manuel Franceschini's avatar Manuel Franceschini
Browse files

rapi.client, http.client: Format url correctly when using IPv6



This patch moves the FormatAddress helper function from daemon.py to
netutils.py. This enables its use in http.client as well as in
rapi.client. Furthermore this adds functionality to format IPv6
addresses according to RFC 3986.

It is required for use of literal IPv6 addresses in URLs in pycurl.
For some reason it worked also without the bracketing ("["<address>"]"),
but we do not want to rely on that.
Signed-off-by: default avatarManuel Franceschini <livewire@google.com>
Reviewed-by: default avatarMichael Hanselmann <hansmi@google.com>
parent db4e138b
#
#
# Copyright (C) 2006, 2007, 2008 Google Inc.
# Copyright (C) 2006, 2007, 2008, 2010 Google Inc.
#
# 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
......@@ -100,23 +100,6 @@ class GanetiBaseAsyncoreDispatcher(asyncore.dispatcher):
return False
def FormatAddress(family, address):
"""Format a client's address
@type family: integer
@param family: socket family (one of socket.AF_*)
@type address: family specific (usually tuple)
@param address: address, as reported by this class
"""
if family == socket.AF_INET and len(address) == 2:
return "%s:%d" % address
elif family == socket.AF_UNIX and len(address) == 3:
return "pid=%s, uid=%s, gid=%s" % address
else:
return str(address)
class AsyncStreamServer(GanetiBaseAsyncoreDispatcher):
"""A stream server to use with asyncore.
......@@ -159,7 +142,7 @@ class AsyncStreamServer(GanetiBaseAsyncoreDispatcher):
# is passed in from accept anyway
client_address = netutils.GetSocketCredentials(connected_socket)
logging.info("Accepted connection from %s",
FormatAddress(self.family, client_address))
netutils.FormatAddress(self.family, client_address))
self.handle_connection(connected_socket, client_address)
def handle_connection(self, connected_socket, client_address):
......@@ -290,7 +273,7 @@ class AsyncTerminatedMessageStream(asynchat.async_chat):
def close_log(self):
logging.info("Closing connection from %s",
FormatAddress(self.family, self.peer_address))
netutils.FormatAddress(self.family, self.peer_address))
self.close()
# this method is overriding an asyncore.dispatcher method
......
......@@ -28,6 +28,7 @@ from cStringIO import StringIO
from ganeti import http
from ganeti import compat
from ganeti import netutils
class HttpClientRequest(object):
......@@ -104,8 +105,13 @@ class HttpClientRequest(object):
"""Returns the full URL for this requests.
"""
if netutils.IPAddress.IsValid(self.host):
family = netutils.IPAddress.GetAddressFamily(self.host)
address = netutils.FormatAddress(family, (self.host, self.port))
else:
address = "%s:%s" % (self.host, self.port)
# TODO: Support for non-SSL requests
return "https://%s:%s%s" % (self.host, self.port, self.path)
return "https://%s%s" % (address, self.path)
@property
def identity(self):
......
......@@ -472,3 +472,29 @@ class IP6Address(IPAddress):
address_int = (address_int << 16) + int(part or '0', 16)
return address_int
def FormatAddress(family, address):
"""Format a socket address
@type family: integer
@param family: socket family (one of socket.AF_*)
@type address: family specific (usually tuple)
@param address: address, as reported by this class
"""
if family == socket.AF_UNIX and len(address) == 3:
return "pid=%s, uid=%s, gid=%s" % address
if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2:
host, port = address
if family == socket.AF_INET6:
res = "[%s]" % host
else:
res = host
if port is not None:
res += ":%s" % port
return res
raise errors.ParameterError(family, address)
......@@ -35,6 +35,7 @@
import logging
import simplejson
import socket
import urllib
import threading
import pycurl
......@@ -265,7 +266,13 @@ class GanetiRapiClient(object):
self._curl_config_fn = curl_config_fn
self._curl_factory = curl_factory
self._base_url = "https://%s:%s" % (host, port)
try:
socket.inet_pton(socket.AF_INET6, host)
address = "[%s]:%s" % (host, port)
except socket.error:
address = "%s:%s" % (host, port)
self._base_url = "https://%s" % address
if username is not None:
if password is None:
......
......@@ -385,5 +385,35 @@ class TestIP6TcpPingDeaf(unittest.TestCase, _BaseTcpPingDeafTest):
_BaseTcpPingDeafTest.tearDown(self)
class TestFormatAddress(unittest.TestCase):
"""Testcase for FormatAddress"""
def testFormatAddressUnixSocket(self):
res1 = netutils.FormatAddress(socket.AF_UNIX, ("12352", 0, 0))
self.assertEqual(res1, "pid=12352, uid=0, gid=0")
def testFormatAddressIP4(self):
res1 = netutils.FormatAddress(socket.AF_INET, ("127.0.0.1", 1234))
self.assertEqual(res1, "127.0.0.1:1234")
res2 = netutils.FormatAddress(socket.AF_INET, ("192.0.2.32", None))
self.assertEqual(res2, "192.0.2.32")
def testFormatAddressIP6(self):
res1 = netutils.FormatAddress(socket.AF_INET6, ("::1", 1234))
self.assertEqual(res1, "[::1]:1234")
res2 = netutils.FormatAddress(socket.AF_INET6, ("::1", None))
self.assertEqual(res2, "[::1]")
res2 = netutils.FormatAddress(socket.AF_INET6, ("2001:db8::beef", "80"))
self.assertEqual(res2, "[2001:db8::beef]:80")
def testInvalidFormatAddress(self):
self.assertRaises(errors.ParameterError,
netutils.FormatAddress, None, ("::1", None))
self.assertRaises(errors.ParameterError,
netutils.FormatAddress, socket.AF_INET, "127.0.0.1")
self.assertRaises(errors.ParameterError,
netutils.FormatAddress, socket.AF_INET, ("::1"))
if __name__ == "__main__":
testutils.GanetiTestProgram()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment