You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kg...@apache.org on 2016/05/13 20:50:59 UTC

qpid-proton git commit: PROTON-1133: Policy for setting the virtual-host using the Reactor

Repository: qpid-proton
Updated Branches:
  refs/heads/master 95c99503a -> 02fee21c5


PROTON-1133: Policy for setting the virtual-host using the Reactor

The pn_connection_set_hostname() interface is used to set the
'hostname' field in the Open performative.  By definition this is the
'virtual host' and should not be used for the transport network address.

The network address for outgoing connections should be set
by using the reactor's pn_reactor_connection_to_host() factory, or the
pn_reactor_set_connection_host() when re-connecting to a different
host.

For inbound connections the peer address is provided by the acceptor
and cannot be modified.  In both cases, the
pn_reactor_get_connection_address() method can be used to obtain the
peer's network address.

By default if the application does not explicitly set the connection's
virtual host on an outbound connection Reactor will use the host
name/address from the network address.  Otherwise the virtual host is
sent as configured, with one exception: if the application sets the
virtual host to an empty string ("") then no virtual host will be sent
in the Open performative (e.g. disables the default behavior).

The Python Container now provides a virtual-host option for setting
the virtual host value via the connect() method.


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/02fee21c
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/02fee21c
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/02fee21c

Branch: refs/heads/master
Commit: 02fee21c51fa3d6383fa10037fdbc08ffabcbe3b
Parents: 95c9950
Author: Ken Giusti <kg...@apache.org>
Authored: Sun Apr 24 11:58:44 2016 -0400
Committer: Ken Giusti <kg...@apache.org>
Committed: Fri May 13 16:30:51 2016 -0400

----------------------------------------------------------------------
 proton-c/bindings/cpp/CMakeLists.txt            |   1 +
 .../cpp/include/proton/connection_options.hpp   |   1 +
 .../bindings/cpp/src/connection_options.cpp     |   3 +-
 proton-c/bindings/cpp/src/connector.cpp         |   3 +
 proton-c/bindings/cpp/src/container_impl.cpp    |   3 +-
 proton-c/bindings/cpp/src/container_test.cpp    | 127 +++++++++++++++++++
 proton-c/bindings/cpp/src/reactor.cpp           |   4 +
 proton-c/bindings/cpp/src/reactor.hpp           |   2 +
 proton-c/bindings/python/proton/reactor.py      |  33 +++--
 proton-c/include/proton/connection.h            |  20 +--
 proton-c/include/proton/reactor.h               |  15 ++-
 proton-c/src/posix/io.c                         |   1 +
 proton-c/src/reactor/acceptor.c                 |   7 +
 proton-c/src/reactor/connection.c               |  80 ++++++++----
 proton-c/src/reactor/reactor.h                  |   4 +
 proton-c/src/tests/reactor.c                    |  20 +++
 proton-c/src/windows/io.c                       |   1 +
 .../qpid/proton/engine/impl/ConnectionImpl.java |   2 +-
 .../org/apache/qpid/proton/reactor/Reactor.java |   4 +-
 .../qpid/proton/reactor/impl/AcceptorImpl.java  |  12 ++
 .../qpid/proton/reactor/impl/IOHandler.java     |  36 +++++-
 .../qpid/proton/reactor/impl/ReactorImpl.java   |  23 ++--
 .../apache/qpid/proton/reactor/ReactorTest.java |  94 ++++++++++++++
 tests/python/proton_tests/reactor.py            |  84 +++++++++++-
 24 files changed, 503 insertions(+), 77 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/bindings/cpp/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/CMakeLists.txt b/proton-c/bindings/cpp/CMakeLists.txt
index 08da50d..26b8dd4 100644
--- a/proton-c/bindings/cpp/CMakeLists.txt
+++ b/proton-c/bindings/cpp/CMakeLists.txt
@@ -185,3 +185,4 @@ add_cpp_test(interop_test ${CMAKE_SOURCE_DIR}/tests)
 add_cpp_test(message_test)
 add_cpp_test(scalar_test)
 add_cpp_test(value_test)
+add_cpp_test(container_test)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/bindings/cpp/include/proton/connection_options.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/include/proton/connection_options.hpp b/proton-c/bindings/cpp/include/proton/connection_options.hpp
index d736600..c6a7c04 100644
--- a/proton-c/bindings/cpp/include/proton/connection_options.hpp
+++ b/proton-c/bindings/cpp/include/proton/connection_options.hpp
@@ -142,6 +142,7 @@ class connection_options {
   private:
     void apply(connection&) const;
     proton_handler* handler() const;
+    bool is_virtual_host_set() const;
 
     class impl;
     internal::pn_unique_ptr<impl> impl_;

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/bindings/cpp/src/connection_options.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/connection_options.cpp b/proton-c/bindings/cpp/src/connection_options.cpp
index fed645c..ab45e5b 100644
--- a/proton-c/bindings/cpp/src/connection_options.cpp
+++ b/proton-c/bindings/cpp/src/connection_options.cpp
@@ -118,7 +118,7 @@ class connection_options::impl {
                 outbound->reconnect_timer(reconnect.value);
             if (container_id.set)
                 pn_connection_set_container(pnc, container_id.value.c_str());
-            if (virtual_host.set)
+            if (virtual_host.set && !virtual_host.value.empty())
                 pn_connection_set_hostname(pnc, virtual_host.value.c_str());
         }
     }
@@ -183,4 +183,5 @@ connection_options& connection_options::sasl_config_path(const std::string &p) {
 
 void connection_options::apply(connection& c) const { impl_->apply(c); }
 proton_handler* connection_options::handler() const { return impl_->handler.value; }
+bool connection_options::is_virtual_host_set() const { return impl_->virtual_host.set; }
 } // namespace proton

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/bindings/cpp/src/connector.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/connector.cpp b/proton-c/bindings/cpp/src/connector.cpp
index 0bec024..8a70b77 100644
--- a/proton-c/bindings/cpp/src/connector.cpp
+++ b/proton-c/bindings/cpp/src/connector.cpp
@@ -67,6 +67,9 @@ void connector::connect() {
     pn_decref(pnt);
     // Apply options to the new transport.
     options_.apply(connection_);
+    // if virtual-host not set, use host from address as default
+    if (!options_.is_virtual_host_set())
+        pn_connection_set_hostname(unwrap(connection_), address_.host().c_str());
     transport_configured_ = true;
 }
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/bindings/cpp/src/container_impl.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/container_impl.cpp b/proton-c/bindings/cpp/src/container_impl.cpp
index a221f45..19c3c94 100644
--- a/proton-c/bindings/cpp/src/container_impl.cpp
+++ b/proton-c/bindings/cpp/src/container_impl.cpp
@@ -149,8 +149,7 @@ connection container_impl::connect(const proton::url &url, const connection_opti
     proton_handler *h = opts.handler();
 
     internal::pn_ptr<pn_handler_t> chandler = h ? cpp_handler(h) : internal::pn_ptr<pn_handler_t>();
-    pn_connection_t* pnc = pn_reactor_connection_to_host(unwrap(reactor_), url.host().c_str(), url.port().c_str(), chandler.get());
-    connection conn(make_wrapper(pnc));
+    connection conn(reactor_.connection_to_host(url.host(), url.port(), chandler.get()));
     internal::pn_unique_ptr<connector> ctor(new connector(conn, url, opts));
     connection_context& cc(connection_context::get(conn));
     cc.handler.reset(ctor.release());

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/bindings/cpp/src/container_test.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/container_test.cpp b/proton-c/bindings/cpp/src/container_test.cpp
new file mode 100644
index 0000000..54086d7
--- /dev/null
+++ b/proton-c/bindings/cpp/src/container_test.cpp
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+#include "test_bits.hpp"
+#include "proton/connection.hpp"
+#include "proton/connection_options.hpp"
+#include "proton/container.hpp"
+#include "proton/handler.hpp"
+#include "proton/acceptor.hpp"
+
+#include <cstdlib>
+#include <ctime>
+#include <string>
+#include <cstdio>
+
+#if __cplusplus < 201103L
+#define override
+#endif
+
+using namespace test;
+
+static std::string int2string(int n) {
+    char buf[64];
+
+    snprintf(buf, sizeof(buf), "%d", n);
+    return std::string(buf);
+}
+
+class test_handler : public proton::handler {
+  public:
+    const std::string host;
+    proton::connection_options opts;
+    bool closing;
+    bool done;
+
+    std::string peer_vhost;
+    proton::acceptor acptr;
+
+    test_handler(const std::string h, const proton::connection_options& c_opts)
+        : host(h), opts(c_opts), closing(false), done(false)
+    {}
+
+    void on_container_start(proton::container &c) override {
+        int port;
+
+        // I'm going to hell for this:
+        srand((unsigned int)time(0));
+        while (true) {
+            port = 20000 + (rand() % 30000);
+            try {
+                acptr = c.listen("0.0.0.0:" + int2string(port));
+                break;
+            } catch (...) {
+                // keep trying
+            }
+        }
+        proton::connection conn = c.connect(host + ":" + int2string(port), opts);
+    }
+
+    void on_connection_open(proton::connection &c) override {
+        if (peer_vhost.empty() && !c.virtual_host().empty())
+            peer_vhost = c.virtual_host();
+        if (!closing) c.close();
+        closing = true;
+    }
+
+    void on_connection_close(proton::connection &c) override {
+        if (!done) acptr.close();
+        done = true;
+    }
+};
+
+int test_container_vhost() {
+    proton::connection_options opts;
+    opts.virtual_host(std::string("a.b.c"));
+    test_handler th(std::string("127.0.0.1"), opts);
+    proton::container(th).run();
+    ASSERT_EQUAL(th.peer_vhost, std::string("a.b.c"));
+    return 0;
+}
+
+int test_container_default_vhost() {
+    proton::connection_options opts;
+    test_handler th(std::string("127.0.0.1"), opts);
+    proton::container(th).run();
+    ASSERT_EQUAL(th.peer_vhost, std::string("127.0.0.1"));
+    return 0;
+}
+
+int test_container_no_vhost() {
+    // explicitly setting an empty virtual-host will cause the Open
+    // performative to be sent without a hostname field present.
+    // Sadly whether or not a 'hostname' field was received cannot be
+    // determined from here, so just exercise the code
+    proton::connection_options opts;
+    opts.virtual_host(std::string(""));
+    test_handler th(std::string("127.0.0.1"), opts);
+    proton::container(th).run();
+    ASSERT_EQUAL(th.peer_vhost, std::string(""));
+    return 0;
+}
+
+int main(int, char**) {
+    int failed = 0;
+    RUN_TEST(failed, test_container_vhost());
+    RUN_TEST(failed, test_container_default_vhost());
+    RUN_TEST(failed, test_container_no_vhost());
+    return failed;
+}
+

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/bindings/cpp/src/reactor.cpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/reactor.cpp b/proton-c/bindings/cpp/src/reactor.cpp
index 9507d2b..e34a4fc 100644
--- a/proton-c/bindings/cpp/src/reactor.cpp
+++ b/proton-c/bindings/cpp/src/reactor.cpp
@@ -57,6 +57,10 @@ connection reactor::connection(pn_handler_t* h) const {
     return make_wrapper(pn_reactor_connection(pn_object(), h));
 }
 
+connection reactor::connection_to_host(const std::string &host, const std::string &port, pn_handler_t* h) const {
+    return make_wrapper(pn_reactor_connection_to_host(pn_object(), host.c_str(), port.c_str(), h));
+}
+
 pn_io_t* reactor::pn_io() const {
     return pn_reactor_io(pn_object());
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/bindings/cpp/src/reactor.hpp
----------------------------------------------------------------------
diff --git a/proton-c/bindings/cpp/src/reactor.hpp b/proton-c/bindings/cpp/src/reactor.hpp
index 48d9ea1..74bacff 100644
--- a/proton-c/bindings/cpp/src/reactor.hpp
+++ b/proton-c/bindings/cpp/src/reactor.hpp
@@ -78,6 +78,8 @@ class reactor : public internal::object<pn_reactor_t> {
 
     class connection connection(pn_handler_t*) const;
 
+    class connection connection_to_host(const std::string &host, const std::string &port, pn_handler_t*) const;
+
     pn_handler_t* pn_handler() const;
 
     void pn_handler(pn_handler_t* );

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/bindings/python/proton/reactor.py
----------------------------------------------------------------------
diff --git a/proton-c/bindings/python/proton/reactor.py b/proton-c/bindings/python/proton/reactor.py
index 1631c35..202820c 100644
--- a/proton-c/bindings/python/proton/reactor.py
+++ b/proton-c/bindings/python/proton/reactor.py
@@ -205,11 +205,10 @@ class Reactor(Wrapper):
                                        unicode2utf8(str(port)))
 
     def get_connection_address(self, connection):
-        """This may be used to retrieve the host address used by the reactor to
-        establish the outgoing socket connection.
+        """This may be used to retrieve the remote peer address.
         @return: string containing the address in URL format or None if no
-        address assigned.  Use the proton.Url class to create a Url object from
-        the returned value.
+        address is available.  Use the proton.Url class to create a Url object
+        from the returned value.
         """
         _url = pn_reactor_get_connection_address(self._impl, connection._impl)
         return utf82unicode(_url)
@@ -533,11 +532,15 @@ class Connector(Handler):
         self.sasl_enabled = True
         self.user = None
         self.password = None
+        self.virtual_host = None
 
     def _connect(self, connection, reactor):
         assert(reactor is not None)
         url = self.address.next()
         reactor.set_connection_host(connection, url.host, str(url.port))
+        # if virtual-host not set, use host from address as default
+        if self.virtual_host is None:
+            connection.hostname = url.host
         logging.debug("connecting to %s..." % url)
 
         transport = Transport()
@@ -694,27 +697,33 @@ class Container(Reactor):
         called to process any events in the scope of this connection
         or its child links
 
-        @param kwargs: sasl_enabled, which determines whether a sasl
-        layer is used for the connection; allowed_mechs an optional
-        list of SASL mechanisms to allow if sasl is enabled;
-        allow_insecure_mechs a flag indicating whether insecure
-        mechanisms, such as PLAIN over a non-encrypted socket, are
-        allowed. These options can also be set at container scope.
+        @param kwargs: sasl_enabled, which determines whether a sasl layer is
+        used for the connection; allowed_mechs an optional list of SASL
+        mechanisms to allow if sasl is enabled; allow_insecure_mechs a flag
+        indicating whether insecure mechanisms, such as PLAIN over a
+        non-encrypted socket, are allowed; 'virtual_host' the hostname to set
+        in the Open performative used by peer to determine the correct
+        back-end service for the client. If 'virtual_host' is not supplied the
+        host field from the URL is used instead."
 
         """
         conn = self.connection(handler)
         conn.container = self.container_id or str(generate_uuid())
-        
         conn.offered_capabilities = kwargs.get('offered_capabilities')
         conn.desired_capabilities = kwargs.get('desired_capabilities')
         conn.properties = kwargs.get('properties')
-        
+
         connector = Connector(conn)
         connector.allow_insecure_mechs = kwargs.get('allow_insecure_mechs', self.allow_insecure_mechs)
         connector.allowed_mechs = kwargs.get('allowed_mechs', self.allowed_mechs)
         connector.sasl_enabled = kwargs.get('sasl_enabled', self.sasl_enabled)
         connector.user = kwargs.get('user', self.user)
         connector.password = kwargs.get('password', self.password)
+        connector.virtual_host = kwargs.get('virtual_host')
+        if connector.virtual_host:
+            # only set hostname if virtual-host is a non-empty string
+            conn.hostname = connector.virtual_host
+
         conn._overrides = connector
         if url: connector.address = Urls([url])
         elif urls: connector.address = Urls(urls)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/include/proton/connection.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/connection.h b/proton-c/include/proton/connection.h
index da20f94..c5b5490 100644
--- a/proton-c/include/proton/connection.h
+++ b/proton-c/include/proton/connection.h
@@ -326,17 +326,17 @@ PN_EXTERN const char *pn_connection_get_user(pn_connection_t *connection);
 PN_EXTERN const char *pn_connection_get_hostname(pn_connection_t *connection);
 
 /**
- * Set the name of the host (either fully qualified or relative) to which this
- * connection is connecting to.  This information may be used by the remote
- * peer to determine the correct back-end service to connect the client to.
- * This value will be sent in the Open performative, and will be used by SSL
- * and SASL layers to identify the peer.
- *
- * @note Note that it is illegal to set the hostname to a numeric IP address or
- * include a port number.
- *
+ * Set the name of the virtual host (either fully qualified or relative) to
+ * which this connection is connecting to.  This information may be used by the
+ * remote peer to determine the correct back-end service to connect the client
+ * to. This value will be sent in the Open performative, and will be used by
+ * SSL and SASL layers to identify the peer.
+ *
+ * @note Note: the virtual host string is passed verbatim, it is not parsed as
+ * a URL or modified in any way. It should not contain numeric IP addresses or
+ * port numbers unless that is what you intend to send as the virtual host name
  * @param[in] connection the connection object
- * @param[in] hostname the RFC1035 compliant host name
+ * @param[in] hostname the virtual host name
  */
 PN_EXTERN void pn_connection_set_hostname(pn_connection_t *connection, const char *hostname);
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/include/proton/reactor.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/reactor.h b/proton-c/include/proton/reactor.h
index be642a9..5a33a0b 100644
--- a/proton-c/include/proton/reactor.h
+++ b/proton-c/include/proton/reactor.h
@@ -112,10 +112,10 @@ PN_EXTERN pn_connection_t *pn_reactor_connection(pn_reactor_t *reactor,
                                                  pn_handler_t *handler);
 
 /**
- * Change the host address used by the connection.
+ * Change the host address used by an outgoing reactor connection.
  *
  * The address is used by the reactor's iohandler to create an outgoing socket
- * connection.  This must be set prior to opening the connection.
+ * connection.  This must be set prior to (re)opening the connection.
  *
  * @param[in] reactor the reactor that owns the connection.
  * @param[in] connection the connection created by the reactor.
@@ -127,19 +127,22 @@ PN_EXTERN void pn_reactor_set_connection_host(pn_reactor_t *reactor,
                                               const char *host,
                                               const char *port);
 /**
- * Retrieve the host address assigned to a reactor connection.
+ * Retrieve the peer host address for a reactor connection.
  *
  * This may be used to retrieve the host address used by the reactor to
- * establish the outgoing socket connection.
+ * establish the outgoing socket connection.  In the case of an accepted
+ * connection the returned value is the address of the remote.
+ *
+ * @note Note that the returned address may be in numeric IP format.
  *
  * The pointer returned by this operation is valid until either the address is
  * changed via ::pn_reactor_set_connection_host() or the connection object
  * is freed.
  *
  * @param[in] reactor the reactor that owns the connection.
- * @param[in] connection the connection created by ::pn_reactor_connection()
+ * @param[in] connection the reactor connection
  * @return a C string containing the address in URL format or NULL if no
- * address assigned.  ::pn_url_parse() may be used to create a Proton pn_url_t
+ * address available.  ::pn_url_parse() may be used to create a Proton pn_url_t
  * instance from the returned value.
  */
 PN_EXTERN const char *pn_reactor_get_connection_address(pn_reactor_t *reactor,

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/src/posix/io.c
----------------------------------------------------------------------
diff --git a/proton-c/src/posix/io.c b/proton-c/src/posix/io.c
index 3226594..c0dc425 100644
--- a/proton-c/src/posix/io.c
+++ b/proton-c/src/posix/io.c
@@ -204,6 +204,7 @@ pn_socket_t pn_accept(pn_io_t *io, pn_socket_t socket, char *name, size_t size)
 {
   struct sockaddr_storage addr;
   socklen_t addrlen = sizeof(addr);
+  *name = '\0';
   pn_socket_t sock = accept(socket, (struct sockaddr *) &addr, &addrlen);
   if (sock == PN_INVALID_SOCKET) {
     pn_i_error_from_errno(io->error, "accept");

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/src/reactor/acceptor.c
----------------------------------------------------------------------
diff --git a/proton-c/src/reactor/acceptor.c b/proton-c/src/reactor/acceptor.c
index 8f0e99b..f56f7bd 100644
--- a/proton-c/src/reactor/acceptor.c
+++ b/proton-c/src/reactor/acceptor.c
@@ -27,6 +27,8 @@
 #include "reactor.h"
 #include "selectable.h"
 
+#include <string.h>
+
 pn_selectable_t *pn_reactor_selectable_transport(pn_reactor_t *reactor, pn_socket_t sock, pn_transport_t *transport);
 
 PN_HANDLE(PNI_ACCEPTOR_HANDLER)
@@ -42,6 +44,11 @@ void pni_acceptor_readable(pn_selectable_t *sel) {
   pn_record_t *record = pn_selectable_attachments(sel);
   pn_ssl_domain_t *ssl_domain = (pn_ssl_domain_t *) pn_record_get(record, PNI_ACCEPTOR_SSL_DOMAIN);
   pn_connection_t *conn = pn_reactor_connection(reactor, handler);
+  if (name[0]) { // store the peer address of connection in <host>:<port> format
+    char *port = strrchr(name, ':');   // last : separates the port #
+    *port++ = '\0';
+    pni_reactor_set_connection_peer_address(conn, name, port);
+  }
   pn_transport_t *trans = pn_transport();
   pn_transport_set_server(trans);
   if (ssl_domain) {

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/src/reactor/connection.c
----------------------------------------------------------------------
diff --git a/proton-c/src/reactor/connection.c b/proton-c/src/reactor/connection.c
index 336d1f1..a3e7367 100644
--- a/proton-c/src/reactor/connection.c
+++ b/proton-c/src/reactor/connection.c
@@ -33,7 +33,22 @@
 
 // XXX: overloaded for both directions
 PN_HANDLE(PN_TRANCTX)
-PN_HANDLE(PNI_CONN_URL)
+PN_HANDLE(PNI_CONN_PEER_ADDRESS)
+
+void pni_reactor_set_connection_peer_address(pn_connection_t *connection,
+                                             const char *host,
+                                             const char *port)
+{
+    pn_url_t *url = pn_url();
+    pn_url_set_host(url, host);
+    pn_url_set_port(url, port);
+    pn_record_t *record = pn_connection_attachments(connection);
+    if (!pn_record_has(record, PNI_CONN_PEER_ADDRESS)) {
+      pn_record_def(record, PNI_CONN_PEER_ADDRESS, PN_OBJECT);
+    }
+    pn_record_set(record, PNI_CONN_PEER_ADDRESS, url);
+    pn_decref(url);
+}
 
 static pn_transport_t *pni_transport(pn_selectable_t *sel) {
   pn_record_t *record = pn_selectable_attachments(sel);
@@ -112,12 +127,19 @@ void pni_handle_bound(pn_reactor_t *reactor, pn_event_t *event) {
   assert(event);
 
   pn_connection_t *conn = pn_event_connection(event);
+  pn_transport_t *transport = pn_event_transport(event);
   pn_record_t *record = pn_connection_attachments(conn);
-  pn_url_t *url = (pn_url_t *)pn_record_get(record, PNI_CONN_URL);
+  pn_url_t *url = (pn_url_t *)pn_record_get(record, PNI_CONN_PEER_ADDRESS);
   const char *host = NULL;
   const char *port = "5672";
   pn_string_t *str = NULL;
 
+  if (pn_connection_acceptor(conn) != NULL) {
+      // this connection was created by the acceptor.  There is already a
+      // socket assigned to this connection.  Nothing needs to be done.
+      return;
+  }
+
   if (url) {
       host = pn_url_get_host(url);
       const char *uport = pn_url_get_port(url);
@@ -154,24 +176,27 @@ void pni_handle_bound(pn_reactor_t *reactor, pn_event_t *event) {
       }
   }
 
-  // host will be NULL if this connection was created via the acceptor, which
-  // creates its own transport/socket when created.
   if (!host) {
-      return;
-  }
-
-  pn_transport_t *transport = pn_event_transport(event);
-  pn_socket_t sock = pn_connect(pn_reactor_io(reactor), host, port);
-  // invalid sockets are ignored by poll, so we need to do this manualy
-  if (sock == PN_INVALID_SOCKET) {
-    pn_condition_t *cond = pn_transport_condition(transport);
-    pn_condition_set_name(cond, "proton:io");
-    pn_condition_set_description(cond, pn_error_text(pn_io_error(pn_reactor_io(reactor))));
-    pn_transport_close_tail(transport);
-    pn_transport_close_head(transport);
+      // error: no address configured
+      pn_condition_t *cond = pn_transport_condition(transport);
+      pn_condition_set_name(cond, "proton:io");
+      pn_condition_set_description(cond, "Connection failed: no address configured");
+      pn_transport_close_tail(transport);
+      pn_transport_close_head(transport);
+  } else {
+      pn_socket_t sock = pn_connect(pn_reactor_io(reactor), host, port);
+      // invalid sockets are ignored by poll, so we need to do this manualy
+      if (sock == PN_INVALID_SOCKET) {
+          pn_condition_t *cond = pn_transport_condition(transport);
+          pn_condition_set_name(cond, "proton:io");
+          pn_condition_set_description(cond, pn_error_text(pn_io_error(pn_reactor_io(reactor))));
+          pn_transport_close_tail(transport);
+          pn_transport_close_head(transport);
+      } else {
+          pn_reactor_selectable_transport(reactor, sock, transport);
+      }
   }
   pn_free(str);
-  pn_reactor_selectable_transport(reactor, sock, transport);
 }
 
 void pni_handle_final(pn_reactor_t *reactor, pn_event_t *event) {
@@ -319,15 +344,12 @@ void pn_reactor_set_connection_host(pn_reactor_t *reactor,
                                     const char *port)
 {
     (void)reactor;  // ignored
-    pn_url_t *url = pn_url();
-    pn_url_set_host(url, host);
-    pn_url_set_port(url, port);
-    pn_record_t *record = pn_connection_attachments(connection);
-    if (!pn_record_has(record, PNI_CONN_URL)) {
-        pn_record_def(record, PNI_CONN_URL, PN_OBJECT);
+    if (pn_connection_acceptor(connection) != NULL) {
+        // this is an inbound connection created by the acceptor. The peer
+        // address cannot be modified.
+        return;
     }
-    pn_record_set(record, PNI_CONN_URL, url);
-    pn_decref(url);
+    pni_reactor_set_connection_peer_address(connection, host, port);
 }
 
 
@@ -337,7 +359,9 @@ const char *pn_reactor_get_connection_address(pn_reactor_t *reactor,
     (void)reactor;  // ignored
     if (!connection) return NULL;
     pn_record_t *record = pn_connection_attachments(connection);
-    pn_url_t *url = (pn_url_t *)pn_record_get(record, PNI_CONN_URL);
-    if (!url) return NULL;
-    return pn_url_str(url);
+    pn_url_t *url = (pn_url_t *)pn_record_get(record, PNI_CONN_PEER_ADDRESS);
+    if (url) {
+        return pn_url_str(url);
+    }
+    return NULL;
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/src/reactor/reactor.h
----------------------------------------------------------------------
diff --git a/proton-c/src/reactor/reactor.h b/proton-c/src/reactor/reactor.h
index f996dca..461e8b3 100644
--- a/proton-c/src/reactor/reactor.h
+++ b/proton-c/src/reactor/reactor.h
@@ -23,8 +23,12 @@
  */
 
 #include <proton/reactor.h>
+#include <proton/url.h>
 
 void pni_record_init_reactor(pn_record_t *record, pn_reactor_t *reactor);
 void pni_event_set_root(pn_event_t *event, pn_handler_t *handler);
+void pni_reactor_set_connection_peer_address(pn_connection_t *connection,
+                                             const char *host,
+                                             const char *port);
 
 #endif /* src/reactor.h */

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/src/tests/reactor.c
----------------------------------------------------------------------
diff --git a/proton-c/src/tests/reactor.c b/proton-c/src/tests/reactor.c
index 9564569..427ffb5 100644
--- a/proton-c/src/tests/reactor.c
+++ b/proton-c/src/tests/reactor.c
@@ -213,6 +213,25 @@ static void test_reactor_connection(void) {
   pn_free(revents);
 }
 
+static void test_reactor_connection_factory(void)
+{
+  pn_reactor_t *reactor = pn_reactor();
+  pn_connection_t *conn;
+  const char *addr;
+  // use host as connection hostname default
+  conn = pn_reactor_connection_to_host(reactor, "a.test.com", "5678", NULL);
+  pn_connection_set_hostname(conn, "virt.host");
+  addr = pn_reactor_get_connection_address(reactor, conn);
+  assert(addr && strcmp(addr, "a.test.com:5678") == 0);
+  assert(strcmp(pn_connection_get_hostname(conn), "virt.host") == 0);
+  // verify the host address can be changed:
+  pn_reactor_set_connection_host(reactor, conn, "a.different.com", "9999");
+  addr = pn_reactor_get_connection_address(reactor, conn);
+  assert(addr && strcmp(addr, "a.different.com:9999") == 0);
+  assert(strcmp(pn_connection_get_hostname(conn), "virt.host") == 0);
+  pn_reactor_free(reactor);
+}
+
 static void test_reactor_acceptor(void) {
   pn_reactor_t *reactor = pn_reactor();
   assert(reactor);
@@ -521,6 +540,7 @@ int main(int argc, char **argv)
   test_reactor_handler_run();
   test_reactor_handler_run_free();
   test_reactor_connection();
+  test_reactor_connection_factory();
   test_reactor_acceptor();
   test_reactor_acceptor_run();
   test_reactor_connect();

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-c/src/windows/io.c
----------------------------------------------------------------------
diff --git a/proton-c/src/windows/io.c b/proton-c/src/windows/io.c
index 7ff928d..4a87fd2 100644
--- a/proton-c/src/windows/io.c
+++ b/proton-c/src/windows/io.c
@@ -277,6 +277,7 @@ pn_socket_t pn_accept(pn_io_t *io, pn_socket_t listen_sock, char *name, size_t s
   iocpdesc_t *listend = pni_iocpdesc_map_get(io->iocp, listen_sock);
   pn_socket_t accept_sock;
 
+  *name = '\0';
   if (listend)
     accept_sock = pni_iocp_end_accept(listend, (struct sockaddr *) &addr, &addrlen, &io->wouldblock, io->error);
   else {

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-j/src/main/java/org/apache/qpid/proton/engine/impl/ConnectionImpl.java
----------------------------------------------------------------------
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/engine/impl/ConnectionImpl.java b/proton-j/src/main/java/org/apache/qpid/proton/engine/impl/ConnectionImpl.java
index b708d83..2878a39 100644
--- a/proton-j/src/main/java/org/apache/qpid/proton/engine/impl/ConnectionImpl.java
+++ b/proton-j/src/main/java/org/apache/qpid/proton/engine/impl/ConnectionImpl.java
@@ -61,7 +61,7 @@ public class ConnectionImpl extends EndpointImpl implements ProtonJConnection
     private DeliveryImpl _transportWorkTail;
     private int _transportWorkSize = 0;
     private String _localContainerId = "";
-    private String _localHostname = "";
+    private String _localHostname;
     private String _remoteContainer;
     private String _remoteHostname;
     private Symbol[] _offeredCapabilities;

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-j/src/main/java/org/apache/qpid/proton/reactor/Reactor.java
----------------------------------------------------------------------
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/reactor/Reactor.java b/proton-j/src/main/java/org/apache/qpid/proton/reactor/Reactor.java
index a3307d2..a9becfd 100644
--- a/proton-j/src/main/java/org/apache/qpid/proton/reactor/Reactor.java
+++ b/proton-j/src/main/java/org/apache/qpid/proton/reactor/Reactor.java
@@ -281,13 +281,13 @@ public interface Reactor {
     /**
      * Get the address used by the connection
      * <p>
-     * This method will retrieve the Connection's address as set by
-     * {@link #setConnectionHost(Connection, String, int)}.
+     * This may be used to retrieve the remote peer address.
      * @param c the Connection
      * @return a string containing the address in the following format:
      * <pre>
      *   host[:port]
      * </pre>
+     * @note Note that the returned address may be in numeric IP format.
      */
     String getConnectionAddress(Connection c);
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/AcceptorImpl.java
----------------------------------------------------------------------
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/AcceptorImpl.java b/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/AcceptorImpl.java
index fb2f892..bae61bc 100644
--- a/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/AcceptorImpl.java
+++ b/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/AcceptorImpl.java
@@ -37,13 +37,16 @@ import org.apache.qpid.proton.engine.Transport;
 import org.apache.qpid.proton.engine.impl.RecordImpl;
 import org.apache.qpid.proton.reactor.Acceptor;
 import org.apache.qpid.proton.reactor.Reactor;
+import org.apache.qpid.proton.reactor.impl.ReactorImpl;
 import org.apache.qpid.proton.reactor.Selectable;
 import org.apache.qpid.proton.reactor.Selectable.Callback;
+import org.apache.qpid.proton.messenger.impl.Address;
 
 public class AcceptorImpl implements Acceptor {
 
     private Record attachments = new RecordImpl();
     private final SelectableImpl sel;
+    protected static final String CONNECTION_ACCEPTOR_KEY = "pn_reactor_connection_acceptor";
 
     private class AcceptorReadable implements Callback {
         @Override
@@ -59,6 +62,15 @@ public class AcceptorImpl implements Acceptor {
                     handler = reactor.getHandler();
                 }
                 Connection conn = reactor.connection(handler);
+                Record conn_recs = conn.attachments();
+                conn_recs.set(CONNECTION_ACCEPTOR_KEY, Acceptor.class, AcceptorImpl.this);
+                InetSocketAddress peerAddr = (InetSocketAddress)socketChannel.getRemoteAddress();
+                if (peerAddr != null) {
+                    Address addr = new Address();
+                    addr.setHost(peerAddr.getHostString());
+                    addr.setPort(Integer.toString(peerAddr.getPort()));
+                    conn_recs.set(ReactorImpl.CONNECTION_PEER_ADDRESS_KEY, Address.class, addr);
+                }
                 Transport trans = Proton.transport();
                 Sasl sasl = trans.sasl();
                 sasl.server();

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/IOHandler.java
----------------------------------------------------------------------
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/IOHandler.java b/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/IOHandler.java
index 5a32824..c0e51d5 100644
--- a/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/IOHandler.java
+++ b/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/IOHandler.java
@@ -38,10 +38,13 @@ import org.apache.qpid.proton.engine.Event;
 import org.apache.qpid.proton.engine.Sasl;
 import org.apache.qpid.proton.engine.Transport;
 import org.apache.qpid.proton.engine.impl.TransportImpl;
+import org.apache.qpid.proton.engine.Record;
 import org.apache.qpid.proton.reactor.Reactor;
 import org.apache.qpid.proton.reactor.Selectable;
 import org.apache.qpid.proton.reactor.Selectable.Callback;
 import org.apache.qpid.proton.reactor.Selector;
+import org.apache.qpid.proton.reactor.Acceptor;
+import org.apache.qpid.proton.reactor.impl.AcceptorImpl;
 import org.apache.qpid.proton.messenger.impl.Address;
 
 public class IOHandler extends BaseHandler {
@@ -73,11 +76,27 @@ public class IOHandler extends BaseHandler {
     }
 
     // pni_handle_open(...) from connection.c
-    private void handleOpen(Event event) {
+    private void handleOpen(Reactor reactor, Event event) {
         Connection connection = event.getConnection();
         if (connection.getRemoteState() != EndpointState.UNINITIALIZED) {
             return;
         }
+        // Outgoing Reactor connections set the virtual host automatically using the
+        // following rules:
+        String vhost = connection.getHostname();
+        if (vhost == null) {
+            // setHostname never called, use the host from the connection's
+            // socket address as the default virtual host:
+            Address addr = new Address(reactor.getConnectionAddress(connection));
+            connection.setHostname(addr.getHost());
+        } else if (vhost == "") {
+            // setHostname called explictly with a null string. This allows
+            // the application to completely avoid sending a virtual host
+            // name
+            connection.setHostname(null);
+        } else {
+            // setHostname set by application - use it.
+        }
         Transport transport = Proton.transport();
         Sasl sasl = transport.sasl();
         sasl.client();
@@ -86,8 +105,17 @@ public class IOHandler extends BaseHandler {
     }
 
     // pni_handle_bound(...) from connection.c
+    // If this connection is an outgoing connection - not an incoming
+    // connection created by the Acceptor - create a socket connection to
+    // the peer address.
     private void handleBound(Reactor reactor, Event event) {
         Connection connection = event.getConnection();
+        Record conn_recs = connection.attachments();
+        if (conn_recs.get(AcceptorImpl.CONNECTION_ACCEPTOR_KEY, Acceptor.class) != null) {
+            // Connection was created via the Acceptor, so the socket already
+            // exists
+            return;
+        }
         String url = reactor.getConnectionAddress(connection);
         String hostname = connection.getHostname();
         int port = 5672;
@@ -113,9 +141,7 @@ public class IOHandler extends BaseHandler {
                 hostname = hostname.substring(0, colonIndex);
             }
         } else {
-            // The transport connection already exists (like Acceptor), so no
-            // socket needed.
-            return;
+            throw new IllegalStateException("No address provided for Connection");
         }
 
         Transport transport = event.getConnection().getTransport();
@@ -337,7 +363,7 @@ public class IOHandler extends BaseHandler {
                 selectable.release();
                 break;
             case CONNECTION_LOCAL_OPEN:
-                handleOpen(event);
+                handleOpen(reactor, event);
                 break;
             case CONNECTION_BOUND:
                 handleBound(reactor, event);

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/ReactorImpl.java
----------------------------------------------------------------------
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/ReactorImpl.java b/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/ReactorImpl.java
index d13cfbe..5949ae8 100644
--- a/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/ReactorImpl.java
+++ b/proton-j/src/main/java/org/apache/qpid/proton/reactor/impl/ReactorImpl.java
@@ -44,6 +44,7 @@ import org.apache.qpid.proton.engine.impl.CollectorImpl;
 import org.apache.qpid.proton.engine.impl.ConnectionImpl;
 import org.apache.qpid.proton.engine.impl.RecordImpl;
 import org.apache.qpid.proton.reactor.Acceptor;
+import org.apache.qpid.proton.reactor.impl.AcceptorImpl;
 import org.apache.qpid.proton.reactor.Reactor;
 import org.apache.qpid.proton.reactor.ReactorChild;
 import org.apache.qpid.proton.reactor.Selectable;
@@ -70,6 +71,7 @@ public class ReactorImpl implements Reactor, Extendable {
     private Selector selector;
     private Record attachments;
     private final IO io;
+    protected static final String CONNECTION_PEER_ADDRESS_KEY = "pn_reactor_connection_peer_address";
 
     @Override
     public long mark() {
@@ -434,12 +436,10 @@ public class ReactorImpl implements Reactor, Extendable {
         return connection;
     }
 
-    static final String CONNECTION_ADDRESS_KEY = "pn_reactor_address";
-
     @Override
     public String getConnectionAddress(Connection connection) {
         Record r = connection.attachments();
-        Address addr = r.get(CONNECTION_ADDRESS_KEY, Address.class);
+        Address addr = r.get(CONNECTION_PEER_ADDRESS_KEY, Address.class);
         if (addr != null) {
             StringBuilder sb = new StringBuilder(addr.getHost());
             if (addr.getPort() != null)
@@ -453,13 +453,18 @@ public class ReactorImpl implements Reactor, Extendable {
     public void setConnectionHost(Connection connection,
                                   String host, int port) {
         Record r = connection.attachments();
-        Address addr = new Address();
-        addr.setHost(host);
-        if (port == 0) {
-            port = 5672;
+        // cannot set the address on an incoming connection
+        if (r.get(AcceptorImpl.CONNECTION_ACCEPTOR_KEY, Acceptor.class) == null) {
+            Address addr = new Address();
+            addr.setHost(host);
+            if (port == 0) {
+                port = 5672;
+            }
+            addr.setPort(Integer.toString(port));
+            r.set(CONNECTION_PEER_ADDRESS_KEY, Address.class, addr);
+        } else {
+            throw new IllegalStateException("Cannot set the host address on an incoming Connection");
         }
-        addr.setPort(Integer.toString(port));
-        r.set(CONNECTION_ADDRESS_KEY, Address.class, addr);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/proton-j/src/test/java/org/apache/qpid/proton/reactor/ReactorTest.java
----------------------------------------------------------------------
diff --git a/proton-j/src/test/java/org/apache/qpid/proton/reactor/ReactorTest.java b/proton-j/src/test/java/org/apache/qpid/proton/reactor/ReactorTest.java
index 4e92cd9..387446e 100644
--- a/proton-j/src/test/java/org/apache/qpid/proton/reactor/ReactorTest.java
+++ b/proton-j/src/test/java/org/apache/qpid/proton/reactor/ReactorTest.java
@@ -290,6 +290,100 @@ public class ReactorTest {
 
     }
 
+    private String checkVhost(String vhost) throws IOException {
+
+        class ServerVhostHandler extends ServerHandler {
+            public String peerVhost;
+
+            @Override
+            public void onConnectionRemoteOpen(Event event) {
+                super.onConnectionRemoteOpen(event);
+                peerVhost = event.getConnection().getRemoteHostname();
+            }
+        }
+
+        class ClientVhostHandler extends TestHandler {
+            private int port;
+            private String vhost;
+
+            ClientVhostHandler(String vhost, int port) {
+                this.port = port;
+                this.vhost = vhost;
+            }
+
+            @Override
+            public void onConnectionInit(Event event) {
+                super.onConnectionInit(event);
+                event.getReactor().setConnectionHost(event.getConnection(),
+                                                     "127.0.0.1", port);
+                if (vhost != null) {
+                    event.getConnection().setHostname(vhost);
+                }
+                event.getConnection().open();
+            }
+            @Override
+            public void onConnectionRemoteOpen(Event event) {
+                super.onConnectionRemoteOpen(event);
+                event.getConnection().close();
+            }
+            @Override
+            public void onConnectionRemoteClose(Event event) {
+                super.onConnectionRemoteClose(event);
+                event.getConnection().free();
+            }
+        }
+        ServerVhostHandler sh = new ServerVhostHandler();
+        Acceptor acceptor = reactor.acceptor("127.0.0.1",  0, sh);
+        final int listeningPort = ((AcceptorImpl)acceptor).getPortNumber();
+        sh.setAcceptor(acceptor);
+
+        ClientVhostHandler ch = new ClientVhostHandler(vhost, listeningPort);
+        Connection connection = reactor.connection(ch);
+
+        reactor.run();
+        reactor.free();
+        checkForLeaks();
+
+        return sh.peerVhost;
+    }
+
+    /**
+     * Tests the virtual host default configuration - should be set to host
+     * used for the connection.
+     * @throws IOException
+     **/
+    @Test
+    public void checkVhostDefault() throws IOException {
+        String vhost = checkVhost(null);
+        assertEquals("The default virtual host is not correct",
+                     "127.0.0.1", vhost);
+    }
+
+    /**
+     * Tests the virtual host override - should be set to connection's
+     * hostname.
+     * @throws IOException
+     **/
+    @Test
+    public void checkVhostOverride() throws IOException {
+        String vhost = checkVhost("my.vhost");
+        assertEquals("The virtual host is not correct",
+                     "my.vhost", vhost);
+    }
+
+    /**
+     * Tests eliminating the virtual host configuration - expects no vhost for
+     * the connection.
+     * @throws IOException
+     **/
+    @Test
+    public void checkNoVhost() throws IOException {
+        String vhost = checkVhost("");
+        assertEquals("The virtual host is present",
+                     null, vhost);
+    }
+
+
     private static class SinkHandler extends BaseHandler {
         protected int received = 0;
 

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/02fee21c/tests/python/proton_tests/reactor.py
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/reactor.py b/tests/python/proton_tests/reactor.py
index 6ee107d..2f17bb6 100644
--- a/tests/python/proton_tests/reactor.py
+++ b/tests/python/proton_tests/reactor.py
@@ -19,10 +19,11 @@ from __future__ import absolute_import
 #
 
 import time
+import sys
 from .common import Test, SkipTest, TestServer, free_tcp_port, ensureCanTestExtendedSASL
 from proton.reactor import Container, Reactor, ApplicationEvent, EventInjector
 from proton.handlers import CHandshaker, MessagingHandler
-from proton import Handler
+from proton import Handler, Url
 
 class Barf(Exception):
     pass
@@ -539,3 +540,84 @@ class ContainerTest(Test):
         container.connect(test_handler.url, user="user@proton", password="password", reconnect=False)
         container.run()
         assert test_handler.verified
+
+    class _ServerHandler(MessagingHandler):
+        def __init__(self):
+            super(ContainerTest._ServerHandler, self).__init__()
+            port = free_tcp_port()
+            self.port = free_tcp_port()
+            self.client_addr = None
+            self.peer_hostname = None
+
+        def on_start(self, event):
+            self.listener = event.container.listen("0.0.0.0:%s" % self.port)
+
+        def on_connection_opened(self, event):
+            self.client_addr = event.reactor.get_connection_address(event.connection)
+            self.peer_hostname = event.connection.remote_hostname
+
+        def on_connection_closing(self, event):
+            event.connection.close()
+            self.listener.close()
+
+    class _ClientHandler(MessagingHandler):
+        def __init__(self):
+            super(ContainerTest._ClientHandler, self).__init__()
+            self.server_addr = None
+
+        def on_connection_opened(self, event):
+            self.server_addr = event.reactor.get_connection_address(event.connection)
+            event.connection.close()
+
+    def test_numeric_hostname(self):
+        server_handler = ContainerTest._ServerHandler()
+        client_handler = ContainerTest._ClientHandler()
+        container = Container(server_handler)
+        container.connect(url=Url(host="127.0.0.1",
+                                  port=server_handler.port),
+                          handler=client_handler)
+        container.run()
+        assert server_handler.client_addr
+        assert client_handler.server_addr
+        assert server_handler.peer_hostname == "127.0.0.1", server_handler.peer_hostname
+        assert client_handler.server_addr.rsplit(':', 1)[1] == str(server_handler.port)
+
+    def test_non_numeric_hostname(self):
+        server_handler = ContainerTest._ServerHandler()
+        client_handler = ContainerTest._ClientHandler()
+        container = Container(server_handler)
+        container.connect(url=Url(host="localhost",
+                                  port=server_handler.port),
+                          handler=client_handler)
+        container.run()
+        assert server_handler.client_addr
+        assert client_handler.server_addr
+        assert server_handler.peer_hostname == "localhost", server_handler.peer_hostname
+        assert client_handler.server_addr.rsplit(':', 1)[1] == str(server_handler.port)
+
+    def test_virtual_host(self):
+        server_handler = ContainerTest._ServerHandler()
+        container = Container(server_handler)
+        conn = container.connect(url=Url(host="localhost",
+                                         port=server_handler.port),
+                                 handler=ContainerTest._ClientHandler(),
+                                 virtual_host="a.b.c.org")
+        container.run()
+        assert server_handler.peer_hostname == "a.b.c.org", server_handler.peer_hostname
+
+    def test_no_virtual_host(self):
+        # explicitly setting an empty virtual host should prevent the hostname
+        # field from being sent in the Open performative
+        if "java" in sys.platform:
+            # This causes Python Container to *not* set the connection virtual
+            # host, so when proton-j sets up the connection the virtual host
+            # seems to be unset and the URL's host is used (as expected).
+            raise SkipTest("Does not apply for proton-j");
+        server_handler = ContainerTest._ServerHandler()
+        container = Container(server_handler)
+        conn = container.connect(url=Url(host="localhost",
+                                         port=server_handler.port),
+                                 handler=ContainerTest._ClientHandler(),
+                                 virtual_host="")
+        container.run()
+        assert server_handler.peer_hostname is None, server_handler.peer_hostname


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org