text: /*
   * 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/default_container.hpp"
  #include "proton/messaging_handler.hpp"
  #include "proton/listener.hpp"
  #include "proton/listen_handler.hpp"
  #include "proton/thread_safe.hpp"
  
  #include <cstdlib>
  #include <ctime>
  #include <string>
  #include <cstdio>
  #include <sstream>
  
  namespace {
  
  static std::string int2string(int n) {
      std::ostringstream strm;
      strm << n;
      return strm.str();
  }
  
  int listen_on_random_port(proton::container& c, proton::listener& l) {
      int port;
      // I'm going to hell for this:
      std::srand((unsigned int)time(0));
      while (true) {
          port = 20000 + (std::rand() % 30000);
          try {
              l = c.listen("0.0.0.0:" + int2string(port));
              break;
          } catch (...) {
              // keep trying
          }
      }
      return port;
  }
  
  class test_handler : public proton::messaging_handler {
    public:
      const std::string host;
      proton::connection_options opts;
      bool closing;
      bool done;
  
      std::string peer_vhost;
      proton::listener listener;
  
      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) PN_CPP_OVERRIDE {
          int port = listen_on_random_port(c, listener);
          proton::connection conn = c.connect(host + ":" + int2string(port), opts);
      }
  
      void on_connection_open(proton::connection &c) PN_CPP_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 &) PN_CPP_OVERRIDE {
          if (!done) listener.stop();
          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::default_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::default_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::default_container(th).run();
      ASSERT_EQUAL(th.peer_vhost, std::string(""));
      return 0;
  }
  
  struct test_listener : public proton::listen_handler {
      bool on_accept_, on_close_;
      std::string on_error_;
      test_listener() : on_accept_(false), on_close_(false) {}
      proton::connection_options on_accept(proton::listener&) PN_CPP_OVERRIDE {
          on_accept_ = true;
          return proton::connection_options();
      }
      void on_close(proton::listener&) PN_CPP_OVERRIDE { on_close_ = true; }
      void on_error(proton::listener&, const std::string& e) PN_CPP_OVERRIDE { on_error_ = e; }
  };
  
  int test_container_bad_address() {
      // Listen on a bad address, check for leaks
      // Regression test for https://issues.apache.org/jira/browse/PROTON-1217
  
      proton::default_container c;
      // Default fixed-option listener. Valgrind for leaks.
      try { c.listen("999.666.999.666:0"); } catch (const proton::error&) {}
      c.run();
      // Dummy listener.
      test_listener l;
      test_handler h2(std::string("999.999.999.666"), proton::connection_options());
      try { c.listen("999.666.999.666:0", l); } catch (const proton::error&) {}
      c.run();
      ASSERT(!l.on_accept_);
      ASSERT(l.on_close_);
      ASSERT(!l.on_error_.empty());
      return 0;
  }
  
  class stop_tester : public proton::messaging_handler {
      proton::listener listener;
  
      // Set up a listener which would block forever
      void on_container_start(proton::container& c) PN_CPP_OVERRIDE {
          ASSERT(state==0);
          int port = listen_on_random_port(c, listener);
          c.connect("127.0.0.1:" + int2string(port));
          c.auto_stop(false);
          state = 1;
      }
  
      // Get here twice - once for listener, once for connector
      void on_connection_open(proton::connection &c) PN_CPP_OVERRIDE {
          c.close();
          state++;
      }
  
      void on_connection_close(proton::connection &c) PN_CPP_OVERRIDE {
          ASSERT(state==3);
          c.container().stop();
          state = 4;
      }
      void on_container_stop(proton::container & ) PN_CPP_OVERRIDE {
          ASSERT(state==4);
          state = 5;
      }
  
      void on_transport_error(proton::transport & t) PN_CPP_OVERRIDE {
          // Do nothing - ignore transport errors - we're going to get one when
          // the container stops.
      }
  
  public:
      stop_tester(): state(0) {}
  
      int state;
  };
  
  int test_container_stop() {
      stop_tester t;
      proton::default_container(t).run();
      ASSERT(t.state==5);
      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());
      RUN_TEST(failed, test_container_bad_address());
      RUN_TEST(failed, test_container_stop());
      return failed;
  }
  
  