http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/cpp/ssl.cpp ---------------------------------------------------------------------- diff --git a/examples/cpp/ssl.cpp b/examples/cpp/ssl.cpp deleted file mode 100644 index 2e901c2..0000000 --- a/examples/cpp/ssl.cpp +++ /dev/null @@ -1,238 +0,0 @@ -/* - * - * 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 "options.hpp" - -#include <proton/connection_options.hpp> -#include <proton/connection.hpp> -#include <proton/container.hpp> -#include <proton/default_container.hpp> -#include <proton/error_condition.hpp> -#include <proton/listener.hpp> -#include <proton/message.hpp> -#include <proton/messaging_handler.hpp> -#include <proton/ssl.hpp> -#include <proton/thread_safe.hpp> -#include <proton/tracker.hpp> -#include <proton/transport.hpp> - -#include <iostream> - -#include "fake_cpp11.hpp" - -using proton::connection_options; -using proton::ssl_client_options; -using proton::ssl_server_options; -using proton::ssl_certificate; - -// Helper functions defined below. -bool using_OpenSSL(); -std::string platform_CA(const std::string &base_name); -ssl_certificate platform_certificate(const std::string &base_name, const std::string &passwd); -std::string find_CN(const std::string &); - -namespace { - std::string verify_full("full"); // Normal verification - std::string verify_noname("noname"); // Skip matching host name against the certificate - std::string verify_fail("fail"); // Force name mismatch failure - std::string verify(verify_full); // Default for example - std::string cert_directory; - - class example_cert_error : public std::runtime_error - { - public: - explicit example_cert_error(const std::string& s) : std::runtime_error(s) {} - }; - -} - - -struct server_handler : public proton::messaging_handler { - std::string url; - - void on_connection_open(proton::connection &c) OVERRIDE { - std::cout << "Inbound server connection connected via SSL. Protocol: " << - c.transport().ssl().protocol() << std::endl; - c.container().stop_listening(url); // Just expecting the one connection. - } - - void on_transport_error(proton::transport &t) OVERRIDE { - t.connection().container().stop_listening(url); - } - - void on_message(proton::delivery &, proton::message &m) OVERRIDE { - std::cout << m.body() << std::endl; - } -}; - - -class hello_world_direct : public proton::messaging_handler { - private: - std::string url; - server_handler s_handler; - - public: - hello_world_direct(const std::string& u) : url(u) {} - - void on_container_start(proton::container &c) OVERRIDE { - // Configure listener. Details vary by platform. - ssl_certificate server_cert = platform_certificate("tserver", "tserverpw"); - ssl_server_options ssl_srv(server_cert); - connection_options server_opts; - server_opts.ssl_server_options(ssl_srv).handler(s_handler); - c.server_connection_options(server_opts); - - // Configure client with a Certificate Authority database - // populated with the server's self signed certificate. - connection_options client_opts; - if (verify == verify_full) { - ssl_client_options ssl_cli(platform_CA("tserver")); - client_opts.ssl_client_options(ssl_cli); - // The next line is optional in normal use. Since the - // example uses IP addresses in the connection string, use - // the virtual_host option to set the server host name - // used for certificate verification: - client_opts.virtual_host("test_server"); - } else if (verify == verify_noname) { - // Downgrade the verification from VERIFY_PEER_NAME to VERIFY_PEER. - ssl_client_options ssl_cli(platform_CA("tserver"), proton::ssl::VERIFY_PEER); - client_opts.ssl_client_options(ssl_cli); - } else if (verify == verify_fail) { - ssl_client_options ssl_cli(platform_CA("tserver")); - client_opts.ssl_client_options(ssl_cli); - client_opts.virtual_host("wrong_name_for_server"); // Pick any name that doesn't match. - } else throw std::logic_error("bad verify mode: " + verify); - - c.client_connection_options(client_opts); - s_handler.url = url; - c.listen(url); - c.open_sender(url); - } - - void on_connection_open(proton::connection &c) OVERRIDE { - std::string subject = c.transport().ssl().remote_subject(); - std::cout << "Outgoing client connection connected via SSL. Server certificate identity " << - find_CN(subject) << std::endl; - } - - void on_transport_error(proton::transport &t) OVERRIDE { - std::string err = t.error().what(); - if (err.find("certificate")) - throw example_cert_error(err); - } - - void on_sendable(proton::sender &s) OVERRIDE { - proton::message m; - m.body("Hello World!"); - s.send(m); - s.close(); - } - - void on_tracker_accept(proton::tracker &t) OVERRIDE { - // All done. - t.connection().close(); - } -}; - -int main(int argc, char **argv) { - // Pick an "unusual" port since we are going to be talking to - // ourselves, not a broker. - // Note the use of "amqps" as the URL scheme to denote a TLS/SSL connection. - std::string address("amqps://127.0.0.1:8888/examples"); - example::options opts(argc, argv); - opts.add_value(address, 'a', "address", "connect and send to URL", "URL"); - opts.add_value(cert_directory, 'c', "cert_directory", - "directory containing SSL certificates and private key information", "CERTDIR"); - opts.add_value(verify, 'v', "verify", "verify type: \"minimum\", \"full\", \"fail\"", "VERIFY"); - - try { - opts.parse(); - - size_t sz = cert_directory.size(); - if (sz && cert_directory[sz -1] != '/') - cert_directory.append("/"); - else cert_directory = "ssl_certs/"; - - if (verify != verify_noname && verify != verify_full && verify != verify_fail) - throw std::runtime_error("bad verify argument: " + verify); - - hello_world_direct hwd(address); - proton::default_container(hwd).run(); - return 0; - } catch (const example_cert_error& ce) { - if (verify == verify_fail) { - std::cout << "Expected failure of connection with wrong peer name: " << ce.what() << std::endl; - return 0; - } - std::cerr << "unexpected internal certificate failure: " << ce.what() << std::endl; - } catch (const std::exception& e) { - std::cerr << e.what() << std::endl; - } - return 1; -} - - -bool using_OpenSSL() { - // Current defaults. -#if defined(WIN32) - return false; -#else - return true; -#endif -} - -ssl_certificate platform_certificate(const std::string &base_name, const std::string &passwd) { - if (using_OpenSSL()) { - // The first argument will be the name of the file containing the public certificate, the - // second argument will be the name of the file containing the private key. - return ssl_certificate(cert_directory + base_name + "-certificate.pem", - cert_directory + base_name + "-private-key.pem", passwd); - } - else { - // Windows SChannel - // The first argument will be the database or store that contains one or more complete certificates - // (public and private data). The second will be an optional name of the certificate in the store - // (not used in this example with one certificate per store). - return ssl_certificate(cert_directory + base_name + "-full.p12", "", passwd); - } -} - -std::string platform_CA(const std::string &base_name) { - if (using_OpenSSL()) { - // In this simple example with self-signed certificates, the peer's certificate is the CA database. - return cert_directory + base_name + "-certificate.pem"; - } - else { - // Windows SChannel. Use a pkcs#12 file with just the peer's public certificate information. - return cert_directory + base_name + "-certificate.p12"; - } -} - -std::string find_CN(const std::string &subject) { - // The subject string is returned with different whitespace and component ordering between platforms. - // Here we just return the common name by searching for "CN=...." in the subject, knowing that - // the test certificates do not contain any escaped characters. - size_t pos = subject.find("CN="); - if (pos == std::string::npos) throw std::runtime_error("No common name in certificate subject"); - std::string cn = subject.substr(pos); - pos = cn.find(','); - return pos == std::string::npos ? cn : cn.substr(0, pos); -}
http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/cpp/ssl_certs/README.txt ---------------------------------------------------------------------- diff --git a/examples/cpp/ssl_certs/README.txt b/examples/cpp/ssl_certs/README.txt deleted file mode 100644 index 9a8a4f9..0000000 --- a/examples/cpp/ssl_certs/README.txt +++ /dev/null @@ -1,24 +0,0 @@ -This directory contains basic self signed test certificates for use by -proton examples. - -The ".pem" files are in the format expected by proton implementations -using OpenSSL. The ".p12" file are for Windows implementations using -SChannel. - -The commands used to generate the certificates follow. - - -make_pn_cert() -{ - name=$1 - subject=$2 - passwd=$3 - # create the pem files - openssl req -newkey rsa:2048 -keyout $name-private-key.pem -out $name-certificate.pem -subj $subject -passout pass:$passwd -x509 -days 3650 - # create the p12 files - openssl pkcs12 -export -out $name-full.p12 -passin pass:$passwd -passout pass:$passwd -inkey $name-private-key.pem -in $name-certificate.pem -name $name - openssl pkcs12 -export -out $name-certificate.p12 -in $name-certificate.pem -name $name -nokeys -passout pass: -} - -make_pn_cert tserver /CN=test_server/OU=proton_test tserverpw -make_pn_cert tclient /CN=test_client/OU=proton_test tclientpw http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/cpp/ssl_certs/tclient-certificate.p12 ---------------------------------------------------------------------- diff --git a/examples/cpp/ssl_certs/tclient-certificate.p12 b/examples/cpp/ssl_certs/tclient-certificate.p12 deleted file mode 100644 index 4d0e000..0000000 Binary files a/examples/cpp/ssl_certs/tclient-certificate.p12 and /dev/null differ http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/cpp/ssl_certs/tclient-certificate.pem ---------------------------------------------------------------------- diff --git a/examples/cpp/ssl_certs/tclient-certificate.pem b/examples/cpp/ssl_certs/tclient-certificate.pem deleted file mode 100644 index 8088e2e..0000000 --- a/examples/cpp/ssl_certs/tclient-certificate.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDKzCCAhOgAwIBAgIJAIV7frIjftgcMA0GCSqGSIb3DQEBCwUAMCwxFDASBgNV -BAMMC3Rlc3RfY2xpZW50MRQwEgYDVQQLDAtwcm90b25fdGVzdDAeFw0xNTExMjcx -ODEwMzlaFw0yNTExMjQxODEwMzlaMCwxFDASBgNVBAMMC3Rlc3RfY2xpZW50MRQw -EgYDVQQLDAtwcm90b25fdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAPCIS4qUdOtQplUxZ6WW0LXcvosqFP6qOiCARLSEWpR3B8bq213rzefwwfcM -4TtMr88bP+huLKmlyMfwpl8yB88eXkscPgaAce2zk24urWkFXKSQ6GPitWBLGqBa -V+W0wJ4mfW7MwefVslWfGXI381QEUlBHjkFG30AtzMMTRj2GK2JqUlRXZPljGyB7 -WcXwxcoS+HkKV7FtHWSkLAzyXwQ9vsCUEYdWTUaGXfCUNRSRV7h1LIANbu03NxV0 -XdEl7WXcr7tuTw3axeUGhRFVhLegrxKLuZTTno4aAJnEr8uaDzjxvXnv3Ne2igvy -gRfZgOMx+XrZEob9OpAoRghQt4cCAwEAAaNQME4wHQYDVR0OBBYEFE4vbyiM0RjG -TLMLLGGhMZE/5x1GMB8GA1UdIwQYMBaAFE4vbyiM0RjGTLMLLGGhMZE/5x1GMAwG -A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAErr/rvLS9Ig0UCMwh1J1lA9 -/gvXf93iIK/SjrFIAqYRmfZxg4husfoes8t2hFUeuqoH05TuSOoXG8p8DpgTSGmF -jAFe+T90vJZTm0oqZkkkI/hdzjGQoHURRp9/O2Z/lm39KSKGVAN5pUWCUDi/G5iS -P9LZPJN6a5syXMrR6x62IPxAXowlpXkRghKClF3zPOaOBTzT1V27EkI8IEgC+p45 -246EooLnw8ibB+ucNc3KHNzpgKGVd/622+I+Q5eg9AT9PLFttP+R2ECsrVDDPYuA -p0qaSnwgeozj/d6K3FOgKKEKbzBmpWgkv0jdcVk18aPMHypI/RDtZ/+3ET2Ksi8= ------END CERTIFICATE----- http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/cpp/ssl_certs/tclient-full.p12 ---------------------------------------------------------------------- diff --git a/examples/cpp/ssl_certs/tclient-full.p12 b/examples/cpp/ssl_certs/tclient-full.p12 deleted file mode 100644 index ad2d7d3..0000000 Binary files a/examples/cpp/ssl_certs/tclient-full.p12 and /dev/null differ http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/cpp/ssl_certs/tclient-private-key.pem ---------------------------------------------------------------------- diff --git a/examples/cpp/ssl_certs/tclient-private-key.pem b/examples/cpp/ssl_certs/tclient-private-key.pem deleted file mode 100644 index e5c114d..0000000 --- a/examples/cpp/ssl_certs/tclient-private-key.pem +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQICy6ghWp45z4CAggA -MBQGCCqGSIb3DQMHBAiVdDoo4NIghQSCBMixGm1bm/omMxsaKnIPO7zm5dyLexJ+ -yTFpmh2KV7kQqmpzCyIOdoG6K8YqFnie2XdFWm3S8faRHoMq54bDmyEWIxfQPq5f -I1iYFbIZkbnhUvK53RActsEUMf0locS4xylU7VQK3XTAwp0TVip3Lp3ehEMEdcXL -iUWibGsoTPKcY9MIWGXZAJXsEXoeHt6k2hHo1G4E0/Bi6mLW1LY/cxZCjHTGD6qI -Kt54SCCDvinqVa+rixw6yX9F14EA6bhALami8e+Ccd3lqHOyYlXcBaSS1ezCg6ig -oNK97mC+gEGy1KlkZDKWXclFoOCBXRBe4DByre6Rlq3yeI9L42bvAuSBSmf5QT5g -73Yl8vjEAKR65awBT09dPuKu7t+Fb6vkwF8/t+uyj9IuL+42UuXhMLK3ohf+6DbU -8/zB4y3GXI80QmWM0+Wx4n6khFhPFLHt2q0Sn6V9PG1vtHyiq50oSCoyrPQLaecp -hefnMCFBYTcT3JUwmoVGGy0boIAwL7T4aGsMt7QhwOx5tU35tKFxyY7m4fX14AKo -2EIy+TPQwCGkGf3Puy/Pc9VA8IAxB5+WwSrjk+NeCv88eIX7gy43k4rCr+OmD9FF -wknr3xoP3KYhNXjdZ4Ep/1UHSK+JAtzzbNLQjDcqN+gQPg/yUX6ih0j5K3Wvh9bK -E/DvzbpJroUZPgzR+8z5O68CfsD+OIdpHBFTKqAFmzvUuqpADpr998LdCjD+lW+V -xZZgZa8KEblwgiH3fdGbYl46Ho1zrZisf439DbqyybAuBIQB4NSZcL/MAgVGO17k -QDpVElWZWYrFm4CFTcvS2HvIzRmbefF5m5oJedsN7Q6WQCp+3gnwYx1xIOknd7pW -N4AHNnqjscSj9yACj/EiBVKAKNnC5H7ZGZTsaAjMETZyjLXfI2AZ3Fviz4zFR+oz -NkAfFB6WUpRpl7H02FzrzYT7XkkLcXd6H6g+mv2iDa9uKWk/PS2QlqnJt8/dHEHD -JKTG331yDK5GHlKAVGF3nP5BwFGgTQMuSoeiOervMXPUwDpQ8OaYkuaRej0cZLgT -kAF9sUjqdsoYNcXDFHALp6y5g8qYkfrxrlIbKs82zIsmB5I+dtZbUaD3a0zAUrmW -5Xm3Pc9dVP0EXKwfHz6zqPReEw2yYLisB5IoHd4M2wa3GzHBdra1ij4QTmvd3o7e -buGFoX8KJQAcig0zpbYkoDP2gPhIh9rY4unVPQNX1Q8/wRsiJAZZsYvZY+A+SmuZ -bwSwk+8ZJRsFzdYYYhQeRytD5cDAIQiClcI5Yj4T9dWQV/gf0N/wIBDNTMp0jJAy -1l7PuXTfGZodNJWZH0oqsrNoWbn/k67NildvvofIKX+h09Nxszr670Pvj0qoHd5/ -CWq30lnxoJBUgbikFOz6ZuuHi/ZiCXL+haH+v8hJKN5ptRKnyYJQHchRB/IOGRoT -5lmWxo8a7K+yXhp0VBDHJfw3685ms0xQX8Xj4X3MEuN64zd0fB1JmhtP12ydK85J -ABawNKlRQPw5weckwtCviXQX+vX25S/xu3xA6IuqlHyqL/1t3DICzuxeOyT2mZxD -tKQxEgNihPvu32vn9m74qA3adEaxuWPRkPZuTeITHOkMTZolvqYX/5olBsSgYwka -7/g= ------END ENCRYPTED PRIVATE KEY----- http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/cpp/ssl_certs/tserver-certificate.p12 ---------------------------------------------------------------------- diff --git a/examples/cpp/ssl_certs/tserver-certificate.p12 b/examples/cpp/ssl_certs/tserver-certificate.p12 deleted file mode 100644 index f38b67d..0000000 Binary files a/examples/cpp/ssl_certs/tserver-certificate.p12 and /dev/null differ http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/cpp/ssl_certs/tserver-certificate.pem ---------------------------------------------------------------------- diff --git a/examples/cpp/ssl_certs/tserver-certificate.pem b/examples/cpp/ssl_certs/tserver-certificate.pem deleted file mode 100644 index 86231f3..0000000 --- a/examples/cpp/ssl_certs/tserver-certificate.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDKzCCAhOgAwIBAgIJAPnYOOQCJ3kDMA0GCSqGSIb3DQEBCwUAMCwxFDASBgNV -BAMMC3Rlc3Rfc2VydmVyMRQwEgYDVQQLDAtwcm90b25fdGVzdDAeFw0xNTExMjcx -ODEwMzlaFw0yNTExMjQxODEwMzlaMCwxFDASBgNVBAMMC3Rlc3Rfc2VydmVyMRQw -EgYDVQQLDAtwcm90b25fdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAKJNB78lgw4KtXDAvXocTLud6mbn6zgfB6ETIF+kcrukOH9DnPxjLBBM4Lig -sp1+kmeudFK5/X8riDrvIW52b/rlEBLgLB+oDtI74m6OTbBs9L+FUFYOuxApetQF -qoJy2vf9pWfy4uku24vCpeo7eVLi6ypu4lXE3LR+Km3FruHI1NKonHBMhwXSOWqF -pYM6/4IZJ4fbV0+eU0Jrx+05s6XHg5vone2BVJKxeSIBje+zWnNnh8+qG0Z70Jgp -aMetME5KGnLNgD1okpH0vb3lwjvuqkkx4WswGVZGbLLkSqqBpXPyM9fCFVy5aKSL -DBq7IABQtO67O2nBzK3OyigHrUUCAwEAAaNQME4wHQYDVR0OBBYEFGV1PY0FCFbJ -gpcDVKI6JGiRTt3kMB8GA1UdIwQYMBaAFGV1PY0FCFbJgpcDVKI6JGiRTt3kMAwG -A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIx1TOTGWnnbpan4bse7wuvH -GYSNDJhoTVS+X1TC63xukJD1JBAsCNTqg/ZV6lN3XEl7vvOXfGoCiyXM6a9XOKUo -gSDtMrIr+wTh6Ss1yRO8QcCJmxH5JDXNu1ojtwsjFW/vneI4IL9kwpDsSlMQEX/E -EkkQwtAx/Cvfe7pecZL4qSeykJOUMTts9H8fCAZqEiRZBA3ugJxqF8jwLP3DoFVQ -6QZzKDY6CSPqfMnVb5i0MAIYVDpau+e3N9dgQpZD22F/zbua0OVbfAPdiRMnYxML -FT4sxLnh+5YVqwpVWbEKp4onHe2Fq6YIvAxUYAJ3SBA2C8O2RAVKWxf1jko3jYI= ------END CERTIFICATE----- http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/cpp/ssl_certs/tserver-full.p12 ---------------------------------------------------------------------- diff --git a/examples/cpp/ssl_certs/tserver-full.p12 b/examples/cpp/ssl_certs/tserver-full.p12 deleted file mode 100644 index d4a0e40..0000000 Binary files a/examples/cpp/ssl_certs/tserver-full.p12 and /dev/null differ http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/cpp/ssl_certs/tserver-private-key.pem ---------------------------------------------------------------------- diff --git a/examples/cpp/ssl_certs/tserver-private-key.pem b/examples/cpp/ssl_certs/tserver-private-key.pem deleted file mode 100644 index 91dcf0e..0000000 --- a/examples/cpp/ssl_certs/tserver-private-key.pem +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI1cT0c2J3GcQCAggA -MBQGCCqGSIb3DQMHBAi1hxSX2LJ+EgSCBMheHJ0iXr5A36Natjk/LcAEeKUMT9s+ -sMzoQceCWe8qMlQluWksr9iDdZ4JRIE8cpK8dbmx4dLY/SShUzdlhJHCSa4zZBHq -8cZ/jGUF/RF1rqdgjK589eUq+uOl3/gXKzG/SxBqayy6PSn12kX3qnvmlkXCmtwU -lg+iBm5wRcJ0MyVHaJkyA8sW8gr186C/VAau6Yu0crQXN7NRo9snrd4ewuYMIEhZ -hgaG9XsYQWB1bPhAaKj80CZGxsQbJyTwcbKKkB3IY4WXx8mmhuiNl+vKT3HBJ9Ju -YB6tgIjs8CJ4X2P4aU3yNJwG1QldgHSqmFGQ19bcZAw3s3kzwjdzRf4H2V16XOBd -zQ5AEs/ffVMzMIAfkb1gYwgunZ2CVwwDJ2mi1RcgkX+Og2aFQc+fxXcVOnDcGLxV -6fuCuZ2lsXfoiIyRh9kj3L75N12GtVUvgBdnMuOc1wPw6XnGQtDwt0acJpdexLMG -k0j57r/gcgzTcmF3qNM+y9L/HLssgrJkvVJw2Np5gmtIyfDocsDUWUbClS4dTpYf -oTngUTU+vWtHBuaUnb+f5/WJaRS/S7mmR8usbVG3i9WnEr/vlPJpbJFSjW2S6u/H -7cFxKUmmBZsSuEv/EKt9a+Sh62kprOChm4myqfCI1/gvNKfUZC6m0Vp8zf+2LgAq -2RgbMuqysMjWUtV4kDRZT7oCYckUDwsCHdbLES3nmVrtBk2ShMKHBpDp8/GoRuiV -jdV7/EjKM/M1kXtFYYe3z7Mxv++lKYIJ7bNwVrQ8nrhce/VwHw6D5emWXNCJXhKZ -FW7EM2ZOZ9eaKOlCsIi8sbjV6Yie9IY6HJKKmi3CpO0Tv5kLBdHkru8vGCSFm3O1 -n7wz7Ys5FBSlZ19X0NwQSCQX1Q4w+tido6i1SCRX0qJEdTNGuGwVXMHCf4/1zyHV -hj8vnxh8fzo79LFrwlTTgwLg1Mr8sEUFFDJ/raJ1AhFXi8n24trtNR8EHxRW8wtD -CLCKaqkEqfBiFXK/Yq3RrefCayPHiD+DaNsI8BwefMGpED3vD8YYCjAzXNPh/CSF -sc1i1jWMzbJhzOoFSPNXhlfusbUFMFQ/6olatmH47SY6HBBOL3DDP5uQ0jw8P454 -QBjlMOpEZmZxO6TcEtJwu0vzgog4rQ5g3NWy6SIpjWehNwTynLt7yM3R5WTI6cZs -0GTv/rqo2/SUoNsFmnGIUwj/DrBe4XOAq1nS2ZlEctxKhBsKH0hMFp6D1rXOzrgl -bwcq+oistoB0TLcThShyNgSqzW1znQ1n5SVUk9b5rRhSttJxn3yOMewH0i3v8bPo -HOhP5kaGjblPsCYyhlL/SNVF0OXEGTwLNey7FQdWFOwVwTRRXe7k+uGZ2d5hg+Jn -It/trDZ1RDYbVmB7/Qy73c16J4mvhOUJ2de5ZciFBjkidbiiUKLj9xnjK9k9Sauo -MKhNnDMAEU5VDQM3xNe5BRdX8dFLwfF5H64sU3nROF83aUnDgvfFEowYPnCuPYfm -m4aQHfoBSg4j3v1OeOwktcl+Q2TjxPHfWhbWeRBfxOTqQ/suYhnQChuFSK/qyo9K -ccgotqghhunRsWMoZT25H7AZM6yKb1sMz/0oyMRIKeGqoYh+ULM5XLY0xNYd4/xU -WtQ= ------END ENCRYPTED PRIVATE KEY----- http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/cpp/ssl_client_cert.cpp ---------------------------------------------------------------------- diff --git a/examples/cpp/ssl_client_cert.cpp b/examples/cpp/ssl_client_cert.cpp deleted file mode 100644 index 630e74b..0000000 --- a/examples/cpp/ssl_client_cert.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/* - * - * 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 <proton/connection.hpp> -#include <proton/connection_options.hpp> -#include <proton/container.hpp> -#include <proton/default_container.hpp> -#include <proton/listener.hpp> -#include <proton/message.hpp> -#include <proton/messaging_handler.hpp> -#include <proton/sasl.hpp> -#include <proton/ssl.hpp> -#include <proton/thread_safe.hpp> -#include <proton/tracker.hpp> -#include <proton/transport.hpp> - -#include <iostream> - -#include "fake_cpp11.hpp" - -using proton::connection_options; -using proton::ssl_client_options; -using proton::ssl_server_options; -using proton::ssl_certificate; -using proton::sasl; - -// Helper functions defined below. -bool using_OpenSSL(); -std::string platform_CA(const std::string &base_name); -ssl_certificate platform_certificate(const std::string &base_name, const std::string &passwd); -static std::string cert_directory; -static std::string find_CN(const std::string &); - - -struct server_handler : public proton::messaging_handler { - proton::listener listener; - - void on_connection_open(proton::connection &c) OVERRIDE { - std::cout << "Inbound server connection connected via SSL. Protocol: " << - c.transport().ssl().protocol() << std::endl; - if (c.transport().sasl().outcome() == sasl::OK) { - std::string subject = c.transport().ssl().remote_subject(); - std::cout << "Inbound client certificate identity " << find_CN(subject) << std::endl; - } - else { - std::cout << "Inbound client authentication failed" <<std::endl; - c.close(); - } - listener.stop(); - } - - void on_message(proton::delivery &, proton::message &m) OVERRIDE { - std::cout << m.body() << std::endl; - } -}; - - -class hello_world_direct : public proton::messaging_handler { - private: - std::string url; - server_handler s_handler; - - public: - hello_world_direct(const std::string& u) : url(u) {} - - void on_container_start(proton::container &c) OVERRIDE { - // Configure listener. Details vary by platform. - ssl_certificate server_cert = platform_certificate("tserver", "tserverpw"); - std::string client_CA = platform_CA("tclient"); - // Specify an SSL domain with CA's for client certificate verification. - ssl_server_options srv_ssl(server_cert, client_CA); - connection_options server_opts; - server_opts.ssl_server_options(srv_ssl).handler(s_handler); - server_opts.sasl_allowed_mechs("EXTERNAL"); - c.server_connection_options(server_opts); - - // Configure client. - ssl_certificate client_cert = platform_certificate("tclient", "tclientpw"); - std::string server_CA = platform_CA("tserver"); - // Since the test certifcate's credentials are unlikely to match this host's name, downgrade the verification - // from VERIFY_PEER_NAME to VERIFY_PEER. - ssl_client_options ssl_cli(client_cert, server_CA, proton::ssl::VERIFY_PEER); - connection_options client_opts; - client_opts.ssl_client_options(ssl_cli).sasl_allowed_mechs("EXTERNAL"); - c.client_connection_options(client_opts); - - s_handler.listener = c.listen(url); - c.open_sender(url); - } - - void on_connection_open(proton::connection &c) OVERRIDE { - std::string subject = c.transport().ssl().remote_subject(); - std::cout << "Outgoing client connection connected via SSL. Server certificate identity " << - find_CN(subject) << std::endl; - } - - void on_sendable(proton::sender &s) OVERRIDE { - proton::message m; - m.body("Hello World!"); - s.send(m); - s.close(); - } - - void on_tracker_accept(proton::tracker &t) OVERRIDE { - // All done. - t.connection().close(); - } -}; - -int main(int argc, char **argv) { - try { - // Pick an "unusual" port since we are going to be talking to ourselves, not a broker. - // Note the use of "amqps" as the URL scheme to denote a TLS/SSL connection. - std::string url = argc > 1 ? argv[1] : "amqps://127.0.0.1:8888/examples"; - // Location of certificates and private key information: - if (argc > 2) { - cert_directory = argv[2]; - size_t sz = cert_directory.size(); - if (sz && cert_directory[sz -1] != '/') - cert_directory.append("/"); - } - else cert_directory = "ssl_certs/"; - - hello_world_direct hwd(url); - proton::default_container(hwd).run(); - return 0; - } catch (const std::exception& e) { - std::cerr << e.what() << std::endl; - } - return 1; -} - - -bool using_OpenSSL() { - // Current defaults. -#if defined(WIN32) - return false; -#else - return true; -#endif -} - -ssl_certificate platform_certificate(const std::string &base_name, const std::string &passwd) { - if (using_OpenSSL()) { - // The first argument will be the name of the file containing the public certificate, the - // second argument will be the name of the file containing the private key. - return ssl_certificate(cert_directory + base_name + "-certificate.pem", - cert_directory + base_name + "-private-key.pem", passwd); - } - else { - // Windows SChannel - // The first argument will be the database or store that contains one or more complete certificates - // (public and private data). The second will be an optional name of the certificate in the store - // (not used in this example with one certificate per store). - return ssl_certificate(cert_directory + base_name + "-full.p12", "", passwd); - } -} - -std::string platform_CA(const std::string &base_name) { - if (using_OpenSSL()) { - // In this simple example with self-signed certificates, the peer's certificate is the CA database. - return cert_directory + base_name + "-certificate.pem"; - } - else { - // Windows SChannel. Use a pkcs#12 file with just the peer's public certificate information. - return cert_directory + base_name + "-certificate.p12"; - } -} - -std::string find_CN(const std::string &subject) { - // The subject string is returned with different whitespace and component ordering between platforms. - // Here we just return the common name by searching for "CN=...." in the subject, knowing that - // the test certificates do not contain any escaped characters. - size_t pos = subject.find("CN="); - if (pos == std::string::npos) throw std::runtime_error("No common name in certificate subject"); - std::string cn = subject.substr(pos); - pos = cn.find(','); - return pos == std::string::npos ? cn : cn.substr(0, pos); -} http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/cpp/tutorial.dox ---------------------------------------------------------------------- diff --git a/examples/cpp/tutorial.dox b/examples/cpp/tutorial.dox deleted file mode 100644 index 56345a1..0000000 --- a/examples/cpp/tutorial.dox +++ /dev/null @@ -1,432 +0,0 @@ -// -*-markdown-*- -// NOTE: doxygen can include markdown pages directly but there seems to be a bug -// that shows messed-up line numbers in \skip \until code extracts. This file -// is markdown wrapped in a doxygen comment - which works. The file is best viewed/editied -// as markdown. - -/** - -@page tutorial Tutorial - -This is a brief tutorial that will walk you through the fundamentals -of building messaging applications in incremental steps. There are -further examples, in addition the ones mentioned in the tutorial. - -Proton provides an "event-driven" programming model, where you -implement a subclass of `proton::messaging_handler` and override -functions that react to various AMQP events (connections opening and -closing, messages being delivered, and so on). - -The examples below show how to implement handlers for clients and -servers and how to run them using the `proton::default_container`, a -portable, easy-to-use way to build single-threaded clients or servers. - -Some of the examples require an AMQP *broker* that can receive, store, -and send messages. @ref broker.hpp and @ref broker.cpp define a simple -example broker. If run without arguments, it listens on -`0.0.0.0:5672`, the standard AMQP port on all network interfaces. To -use a different port or network interface: - - broker -a <host>:<port> - -Instead of the example broker, you can use any AMQP 1.0-compliant -broker. You must configure your broker to have a queue (or topic) -named "examples". - -The `helloworld` examples take an optional URL argument. The other -examples take an option `-a URL`. A URL looks like this: - - HOST:PORT/ADDRESS - -It usually defaults to `127.0.0.1:5672/examples`, but you can change -this if your broker is on a different host or port, or you want to use -a different queue or topic name (the ADDRESS part of the URL). URL -details are at `proton::url`. - -Hello World! ------------- - -\dontinclude helloworld.cpp - -Tradition dictates that we start with Hello World! This example -demonstrates sending and receiving by sending a message to a broker -and then receiving the same message back. In a realistic system the -sender and receiver would normally be in different processes. The -complete example is @ref helloworld.cpp - -We will include the following classes: `proton::default_container` (an -implementation of `proton::container`) runs an event loop which -dispatches events to a `proton::messaging_handler`. This allows a -*reactive* style of programming which is well suited to messaging -applications. `proton::connection` and `proton::delivery` are AMQP -entities used in the handler functions. `proton::url` is a simple -parser for the URL format mentioned above. - -\skip proton/connection -\until proton/url - -We will define a class `hello_world` which is a subclass of -`proton::messaging_handler` and overrides functions to handle the -events of interest in sending and receiving a message. - -\skip class hello_world -\until {} - -`proton::messaging_handler::on_container_start()` is called when the -event loop first starts. We handle that by establishing a connection -and creating a sender and a receiver. - -\skip on_container_start -\until } -\until } - -`proton::messaging_handler::on_sendable()` is called when the message -can be transferred over the associated sender link to the remote -peer. We create a `proton::message`, set the message body to `"Hello -World!"`, and send the message. Then we close the sender, since we -only want to send one message. Closing the sender will prevent further -calls to `proton::messaging_handler::on_sendable()`. - -\skip on_sendable -\until } - -`proton::messaging_handler::on_message()` is called when a message is -received. We just print the body of the message and close the -connection, as we only want one message - -\skip on_message -\until } - -The message body is a `proton::value`, see the documentation for more on how to -extract the message body as type-safe C++ values. - -Our `main` function creates an instance of the `hello_world` handler -and a `proton::default_container` using that handler. Calling -`proton::container::run` sets things in motion and returns when we -close the connection as there is nothing further to do. It may throw -an exception, which will be a subclass of `proton::error`. That in -turn is a subclass of `std::exception`. - -\skip main -\until } -\until } -\until } - -Hello World, direct! --------------------- - -\dontinclude helloworld_direct.cpp - -Though often used in conjunction with a broker, AMQP does not -*require* this. It also allows senders and receivers to communicate -directly if desired. - -We will modify our example to send a message directly to itself. This -is a bit contrived but illustrates both sides of the direct send and -receive scenario. The full code is at @ref helloworld_direct.cpp. - -The first difference is that, rather than creating a receiver on the -same connection as our sender, we listen for incoming connections by -invoking the `proton::container::listen()` method on the container. - -\skip on_container_start -\until } - -As we only need then to initiate one link, the sender, we can do that -by passing in a url rather than an existing connection, and the -connection will also be automatically established for us. - -We send the message in response to the -`proton::messaging_handler::on_sendable()` callback and print the -message out in response to the -`proton::messaging_handler::on_message()` callback exactly as before. - -\skip on_sendable -\until } -\until } - -However, we also handle two new events. We now close the connection -from the sender's side once the message has been accepted. The -acceptance of the message is an indication of successful transfer to -the peer. We are notified of that event through the -`proton::messaging_handler::on_tracker_accept()` callback. - -\skip on_tracker_accept -\until } - -Then, once the connection has been closed, of which we are notified -through the `proton::messaging_handler::on_connection_close()` -callback, we stop accepting incoming connections. A that point there -is no work to be done, the event loop exits, and the -`proton::container::run()` method returns. - -\skip on_connection_close -\until } - -So now we have our example working without a broker involved! - -Note that for this example we pick an "unusual" port 8888 since we are talking -to ourselves rather than a broker. - -\skipline url = - -Asynchronous send and receive ------------------------------ - -Of course, these `Hello World!` examples are very artificial, -communicating as they do over a network connection but with the same -process. A more realistic example involves communication between -separate processes, which could indeed be running on completely -separate machines. - -Let's separate the sender from the receiver, and transfer more than a -single message between them. - -We'll start with a simple sender, @ref simple_send.cpp. - -\dontinclude simple_send.cpp - -As with the previous example, we define the application logic in a -class that handles events. Because we are transferring more than one -message, we need to keep track of how many we have sent. We'll use a -`sent` member variable for that. The `total` member variable will -hold the number of messages we want to send. - -\skip class simple_send -\until total - -As before, we use the -`proton::messaging_handler::on_container_start()` event to establish -our sender link over which we will transfer messages. - -\skip on_container_start -\until } - -AMQP defines a credit-based flow-control mechanism. Flow control -allows the receiver to control how many messages it is prepared to -receive at a given time and thus prevents any component being -overwhelmed by the number of messages it is sent. - -In the `proton::messaging_handler::on_sendable()` callback, we check -that our sender has credit before sending messages. We also check that -we haven't already sent the required number of messages. - -\skip on_sendable -\until } -\until } - -The `proton::sender::send()` call above is asynchronous. When it -returns, the message has not yet actually been transferred across the -network to the receiver. By handling the -`proton::messaging_handler::on_tracker_accept()` event, we can get -notified when the receiver has received and accepted the message. In -our example we use this event to track the confirmation of the -messages we have sent. We only close the connection and exit when the -receiver has received all the messages we wanted to send. - -\skip on_tracker_accept -\until } -\until } - -If we are disconnected after a message is sent and before it has been -confirmed by the receiver, it is said to be "in doubt". We don't know -whether or not it was received. In this example, we will handle that -by resending any in-doubt messages. This is known as an -"at-least-once" guarantee, since each message should eventually be -received at least once, though a given message may be received more -than once (i.e., duplicates are possible). In the -`proton::messaging_handler::on_transport_close()` callback, we reset -the sent count to reflect only those that have been confirmed. The -library will automatically try to reconnect for us, and when our -sender is sendable again, we can restart from the point we know the -receiver got to. - -\skip on_transport_close -\until } - -\dontinclude simple_recv.cpp - -Now let's look at the corresponding receiver, @ref simple_recv.cpp. - -This time we'll use an `expected` member variable for for the number -of messages we expect and a `received` variable to count how many we -have received so far. - -\skip class simple_recv -\until received - -We handle `proton::messaging_handler::on_container_start()` by -creating our receiver, much like we did for the sender. - -\skip on_container_start -\until } - -We also handle the `proton::messaging_handler::on_message()` event for -received messages and print the message out as in the `Hello World!` -examples. However, we add some logic to allow the receiver to wait -for a given number of messages and then close the connection and -exit. We also add some logic to check for and ignore duplicates, using -a simple sequential ID scheme. - -\skip on_message -\until } - -Direct send and receive ------------------------ - -Sending between these two examples requires an intermediary broker -since neither accepts incoming connections. AMQP allows us to send -messages directly between two processes. In that case, one or other of -the processes needs to accept incoming connections. Let's create a -modified version of the receiving example that does this with @ref -direct_recv.cpp. - -\dontinclude direct_recv.cpp - -There are only two differences here. Instead of initiating a link (and -implicitly a connection), we listen for incoming connections. - -\skip on_container_start -\until } - -When we have received all the expected messages, we then stop -listening for incoming connections by calling -`proton::listener::stop()` - -\skip on_message -\until } -\until } -\until } -\until } - -You can use the @ref simple_send.cpp example to send to this receiver -directly. (Note: you will need to stop any broker that is listening on -the 5672 port, or else change the port used by specifying a different -address to each example via the `-a` command-line switch). - -We can also modify the sender to allow the original receiver to -connect to it, in @ref direct_send.cpp. Again, that requires just two -modifications: - -\dontinclude direct_send.cpp - -As with the modified receiver, instead of initiating establishment of a -link, we listen for incoming connections. - -\skip on_container_start -\until } - -When we have received confirmation of all the messages we sent, we call -`container::listener::stop()` to exit. - -\skip on_tracker_accept -\until } -\until } - -To try this modified sender, run the original @ref simple_recv.cpp -against it. - -The symmetry in the underlying AMQP wire protocol that enables this is -quite unique and elegant, and in reflecting this the Proton API -provides a flexible toolkit for implementing all sorts of interesting -intermediaries. - -Request and response --------------------- - -A common pattern is to send a request message and expect a response -message in return. AMQP has special support for this pattern. Let's -have a look at a simple example. We'll start with @ref server.cpp, the -program that will process the request and send the response. Note that -we are still using a broker in this example. - -Our server will provide a very simple service: it will respond with -the body of the request converted to uppercase. - -\dontinclude server.cpp -\skip class server -\until }; - -The code here is not too different from the simple receiver example. -However, when we receive a request in -`proton::messaging_handler::on_message`, we look at the -`proton::message::reply_to` address and create a sender with that -address for the response. We'll cache the senders in case we get -further requests with the same `reply_to`. - -Now let's create a simple @ref client.cpp to test this service out. - -\dontinclude client.cpp - -Our client takes a list of strings to send as requests. - -\skipline client( - -Since we will be sending and receiving, we create a sender and a -receiver in `proton::messaging_handler::on_container_start`. Our -receiver has a blank address and sets the `dynamic` flag to true, -which means we expect the remote end (the broker or server) to assign -a unique address for us. - -\skip on_container_start -\until } - -Now we need a function to send the next request from our list of -requests. We set the `reply_to` address to be the dynamically assigned -address of our receiver. - -\skip send_request -\until } - -We need to use the address assigned by the broker as the `reply_to` -address of our requests, so we can't send them until our receiver has -been set up. To do that, we add an -`proton::messaging_handler::on_receiver_open()` method to our handler -class and use that as the trigger to send our first request. - -\skip on_receiver_open -\until } - -When we receive a reply, we send the next request. - -\skip on_message -\until } -\until } -\until } - -Direct request and response ---------------------------- - -We can avoid the intermediary process by writing a server that accepts -connections directly, @ref server_direct.cpp. It involves the -following changes to our original server: - -\dontinclude server_direct.cpp - -Our server must generate unique `reply-to` addresses for links from -the client that request a dynamic address (previously this was done by -the broker). We use a simple counter. - -\skip generate_address -\until } - -Next we need to handle incoming requests for links with dynamic -addresses from the client. We give the link a unique address and -record it in our `senders` map. - -\skip on_sender_open -\until } - -Note that we are interested in *sender* links above because we are -implementing the server. A *receiver* link created on the client -corresponds to a *sender* link on the server. - -Finally when we receive a message we look up its `reply_to` in our -senders map and send the reply. - -\skip on_message -\until } -\until } -\until } - -*/ http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/exampletest.py ---------------------------------------------------------------------- diff --git a/examples/exampletest.py b/examples/exampletest.py deleted file mode 100644 index d40b9cb..0000000 --- a/examples/exampletest.py +++ /dev/null @@ -1,183 +0,0 @@ -# -# 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 -# - -# A test library to make it easy to run unittest tests that start, -# monitor, and report output from sub-processes. In particular -# it helps with starting processes that listen on random ports. - -import unittest -import os, sys, socket, time, re, inspect, errno, threading -from random import randrange -from subprocess import Popen, PIPE, STDOUT -from copy import copy -import platform -from os.path import dirname as dirname - -def pick_port(): - """Pick a random port.""" - p = randrange(10000, 20000) - return p - -class ProcError(Exception): - """An exception that captures failed process output""" - def __init__(self, proc, what="bad exit status"): - out = proc.out.strip() - if out: - out = "\nvvvvvvvvvvvvvvvv\n%s\n^^^^^^^^^^^^^^^^\n" % out - else: - out = ", no output)" - super(Exception, self, ).__init__( - "%s %s, code=%s%s" % (proc.args, what, proc.returncode, out)) - -class NotFoundError(ProcError): - pass - -class Proc(Popen): - """A example process that stores its output, optionally run with valgrind.""" - - if "VALGRIND" in os.environ and os.environ["VALGRIND"]: - env_args = [os.environ["VALGRIND"], "--error-exitcode=42", "--quiet", "--leak-check=full"] - else: - env_args = [] - - @property - def out(self): - self._out.seek(0) - return self._out.read() - - def __init__(self, args, **kwargs): - """Start an example process""" - args = list(args) - self.args = args - self._out = os.tmpfile() - try: - Popen.__init__(self, self.env_args + self.args, stdout=self._out, stderr=STDOUT, **kwargs) - except OSError, e: - if e.errno == errno.ENOENT: - raise NotFoundError(self, str(e)) - raise ProcError(self, str(e)) - except Exception, e: - raise ProcError(self, str(e)) - - def kill(self): - try: - if self.poll() is None: - Popen.kill(self) - except: - pass # Already exited. - return self.out - - def wait_out(self, timeout=10, expect=0): - """Wait for process to exit, return output. Raise ProcError on failure.""" - t = threading.Thread(target=self.wait) - t.start() - t.join(timeout) - if self.poll() is None: # Still running - self.kill() - raise ProcError(self, "timeout") - if expect is not None and self.poll() != expect: - raise ProcError(self) - return self.out - -# Work-around older python unittest that lacks setUpClass. -if hasattr(unittest.TestCase, 'setUpClass') and hasattr(unittest.TestCase, 'tearDownClass'): - TestCase = unittest.TestCase -else: - class TestCase(unittest.TestCase): - """ - Roughly provides setUpClass and tearDownClass functionality for older python - versions in our test scenarios. If subclasses override setUp or tearDown - they *must* call the superclass. - """ - def setUp(self): - if not hasattr(type(self), '_setup_class_count'): - type(self)._setup_class_count = len( - inspect.getmembers( - type(self), - predicate=lambda(m): inspect.ismethod(m) and m.__name__.startswith('test_'))) - type(self).setUpClass() - - def tearDown(self): - self.assertTrue(self._setup_class_count > 0) - self._setup_class_count -= 1 - if self._setup_class_count == 0: - type(self).tearDownClass() - -class ExampleTestCase(TestCase): - """TestCase that manages started processes""" - def setUp(self): - super(ExampleTestCase, self).setUp() - self.procs = [] - - def tearDown(self): - for p in self.procs: - p.kill() - super(ExampleTestCase, self).tearDown() - - def proc(self, *args, **kwargs): - p = Proc(*args, **kwargs) - self.procs.append(p) - return p - -def wait_port(port, timeout=10): - """Wait up to timeout for port to be connectable.""" - if timeout: - deadline = time.time() + timeout - while (timeout is None or time.time() < deadline): - try: - s = socket.create_connection((None, port), timeout) # Works for IPv6 and v4 - s.close() - return - except socket.error, e: - if e.errno != errno.ECONNREFUSED: # Only retry on connection refused error. - raise - raise socket.timeout() - - -class BrokerTestCase(ExampleTestCase): - """ - ExampleTest that starts a broker in setUpClass and kills it in tearDownClass. - Subclass must set `broker_exe` class variable with the name of the broker executable. - """ - - @classmethod - def setUpClass(cls): - cls.port = pick_port() - cls.addr = "127.0.0.1:%s/examples" % (cls.port) - cls.broker = None # In case Proc throws, create the attribute. - cls.broker = Proc(cls.broker_exe + ["-a", cls.addr]) - try: - wait_port(cls.port) - except Exception, e: - cls.broker.kill() - raise ProcError(cls.broker, "timed out waiting for port") - - @classmethod - def tearDownClass(cls): - if cls.broker: cls.broker.kill() - - def tearDown(self): - b = type(self).broker - if b and b.poll() != None: # Broker crashed - type(self).setUpClass() # Start another for the next test. - raise ProcError(b, "broker crash") - super(BrokerTestCase, self).tearDown() - -if __name__ == "__main__": - unittest.main() http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/go/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/examples/go/CMakeLists.txt b/examples/go/CMakeLists.txt deleted file mode 100644 index c9aba01..0000000 --- a/examples/go/CMakeLists.txt +++ /dev/null @@ -1,54 +0,0 @@ -# -# 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. -# - -if(BUILD_GO) - - set(examples electron/broker electron/receive electron/send proton/broker) - file(GLOB_RECURSE example_source FOLLOW_SYMLINKS ${CMAKE_CURRENT_SOURCE_DIR}/*.go) - - # Build example exes - foreach(example ${examples}) - string(REPLACE / _ target ${example}) - set(target "go_example_${target}") - set(output ${CMAKE_CURRENT_BINARY_DIR}/${example}) - # Always run go_build, it will do nothing if there is nothing to do. - # Otherwise it's too hard to get the dependencies right. - add_custom_target(${target} ALL - COMMAND ${GO_BUILD} ${GO_EXAMPLE_FLAGS} -o ${output} ${CMAKE_CURRENT_SOURCE_DIR}/${example}.go - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS go-build) - list(APPEND example_targets ${target}) - endforeach() - - # Build test driver exe - set(test_exe ${CMAKE_CURRENT_BINARY_DIR}/example_test) - add_custom_target(go_example_test ALL - COMMAND ${GO_TEST} -c -o ${test_exe} ${CMAKE_CURRENT_SOURCE_DIR}/example_test.go - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) - - add_test( - NAME go-example-electron - COMMAND ${GO_ENV} ${test_exe} -dir ${CMAKE_CURRENT_BINARY_DIR}/electron -broker broker) - - add_test( - NAME go-example-proton - COMMAND ${GO_ENV} ${test_exe} -dir ${CMAKE_CURRENT_BINARY_DIR}/electron -broker ../proton/broker) - - list(APPEND ADDITIONAL_MAKE_CLEAN_FILES ${examples}) -endif() http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/go/README.md ---------------------------------------------------------------------- diff --git a/examples/go/README.md b/examples/go/README.md deleted file mode 100644 index 24f4d2a..0000000 --- a/examples/go/README.md +++ /dev/null @@ -1,131 +0,0 @@ -# Go examples - -## Electron examples - -[qpid.apache.org/electron](http://godoc.org/qpid.apache.org/electron) is a -simple API for writing concurrent AMQP clients and servers. - -- [receive.go](electron/receive.go) receive from many connections concurrently. -- [send.go](electron/send.go) send to many connections concurrently. -- [broker.go](electron/broker.go) a simple broker using the electron API -n -## Proton examples - -[qpid.apache.org/proton](http://godoc.org/qpid.apache.org/proton) is an -event-driven, concurrent-unsafe Go wrapper for the proton-C library. The -[electron](http://godoc.org/qpid.apache.org/electron) package provides a more -Go-friendly concurrent API built on top of proton. - -- [broker.go](proton/broker.go) a simple broker using the proton API - -See [A Tale of Two Brokers](#a-tale-of-two-brokers) for a comparison of the two APIs. - -## Using the Go packages - -If you have the proton-C library and headers installed you can get the latest go -packages with - - go get qpid.apache.org/electron - -If Proton-C is installed in a non-standard place (other than /usr or /usr/local) -you should set these environment variables before `go get`: - - export CGO_LDFLAGS="-L/<my-proton>/lib[64]" - export CGO_CFLAGS="-I/<my-proton>/include" - go get qpid.apache.org/electron - -If you have a proton build you don't need to `go get`, you can set your GOPATH -to use the binding from the checkout with: - - source <path-to-proton>/config.sh - -Once you are set up, the go tools will work as normal. You can see documentation -in your web browser at `localhost:6060` by running: - - godoc -http=:6060 - -## Running the examples - -You can run the examples directly from source like this: - - go run <program>.go - -This is a little slow (a couple of seconds) as it compiles the program and runs it in one step. -You can compile the program first and then run the executable to avoid the delay: - - go build <program>.go - ./<program> - -All the examples take a `-h` flag to show usage information, and the comments in -the example source have more details. - -First start the broker (the optional `-debug` flag will print extra information about -what the broker is doing) - - go run broker.go -debug - -Send messages concurrently to queues "foo" and "bar", 10 messages to each queue: - - go run send.go -count 10 localhost:/foo localhost:/bar - -Receive messages concurrently from "foo" and "bar". Note -count 20 for 10 messages each on 2 queues: - - go run receive.go -count 20 localhost:/foo localhost:/bar - -The broker and clients use the standard AMQP port (5672) on the local host by -default, to use a different address use the `-addr host:port` flag. - -If you have other Proton examples available you can try communicating between -programs in in different languages. For example use the python broker with Go -clients: - - python ../python/broker.py - go run send.go -count 10 localhost:/foo localhost:/bar - -Or use the Go broker and the python clients: - - go run broker.go -debug - python ../python/simple_send.py - python ../python/simple_recv.py - - -## A tale of two brokers. - -The [proton](http://godoc.org/qpid.apache.org/proton) and -[electron](http://godoc.org/qpid.apache.org/electron) packages provide two -different APIs for building AMQP applications. For most applications, -[electron](http://godoc.org/qpid.apache.org/electron) is easier to use. -[The proton Go README](https://github.com/apache/qpid-proton/blob/master/proton-c/bindings/go/src/qpid.apache.org/README.md) -has some discussion about why there are two APIs. - -The examples [proton/broker.go](proton/broker.go) and -[electron/broker.go](electron/broker.go) implement the same simple broker -functionality using each of the two APIs. They both handle multiple connections -concurrently and store messages on bounded queues implemented by Go channels. - -However the [electron/broker.go](electron/broker.go) is less than half as long as the -[proton/broker.go](proton/broker.go) illustrating why it is better suited for most Go -applications. - -[proton/broker.go](proton/broker.go) implements an event-driven loop per connection that reacts -to events like 'incoming link', 'incoming message' and 'sender has credit'. It -uses channels to exchange data between the event-loop goroutine for each -connection and shared queues that are accessible to all connections. Sending -messages is particularly tricky, the broker must monitor the queue for available -messages and the sender link for available credit. - - -[electron/broker.go](electron/broker.go) does not need any "upside-down" -event-driven code, it is implemented as straightforward loops. The broker is a -loop listening for connections. Each connection is a loop accepting for incoming -sender or recdiver links. Each receiving link is a loop that receives a message -and pushes it to a queue. Each sending link is a loop that pops a message from -a queue and sends it. - -Queue bounds and credit manage themselves: popping from a queue blocks till -there is a message, sending blocks until there is credit, receiving blocks till -something is received and pushing onto a queue blocks until there is -space. There's no need for code that monitors the state of multiple queues and -links. Each loop has one simple job to do, and the Go run-time schedules them -efficiently. - http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/go/electron/broker.go ---------------------------------------------------------------------- diff --git a/examples/go/electron/broker.go b/examples/go/electron/broker.go deleted file mode 100644 index d698838..0000000 --- a/examples/go/electron/broker.go +++ /dev/null @@ -1,204 +0,0 @@ -/* -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. -*/ - -// -// This is a simple AMQP broker implemented using the procedural electron package. -// -// It maintains a set of named in-memory queues of messages. Clients can send -// messages to queues or subscribe to receive messages from them. -// - -package main - -import ( - "../util" - "flag" - "fmt" - "log" - "net" - "os" - "qpid.apache.org/amqp" - "qpid.apache.org/electron" -) - -// Usage and command-line flags -func usage() { - fmt.Fprintf(os.Stderr, ` -Usage: %s -A simple broker-like demo. Queues are created automatically for sender or receiver addrsses. -`, os.Args[0]) - flag.PrintDefaults() -} - -var addr = flag.String("addr", ":amqp", "Listening address") -var credit = flag.Int("credit", 100, "Receiver credit window") -var qsize = flag.Int("qsize", 1000, "Max queue size") - -func main() { - flag.Usage = usage - flag.Parse() - b := &broker{ - queues: util.MakeQueues(*qsize), - container: electron.NewContainer(fmt.Sprintf("broker[%s]", os.Getpid())), - acks: make(chan electron.Outcome), - sent: make(chan sentMessage), - } - if err := b.run(); err != nil { - log.Fatal(err) - } -} - -// State for the broker -type broker struct { - queues util.Queues // A collection of queues. - container electron.Container // electron.Container manages AMQP connections. - sent chan sentMessage // Channel to record sent messages. - acks chan electron.Outcome // Channel to receive the Outcome of sent messages. -} - -// Record of a sent message and the queue it came from. -// If a message is rejected or not acknowledged due to a failure, we will put it back on the queue. -type sentMessage struct { - m amqp.Message - q util.Queue -} - -// run listens for incoming net.Conn connections and starts an electron.Connection for each one. -func (b *broker) run() error { - listener, err := net.Listen("tcp", *addr) - if err != nil { - return err - } - defer listener.Close() - fmt.Printf("Listening on %v\n", listener.Addr()) - - go b.acknowledgements() // Handles acknowledgements for all connections. - - // Start a goroutine for each new connections - for { - c, err := b.container.Accept(listener) - if err != nil { - util.Debugf("Accept error: %v", err) - continue - } - cc := &connection{b, c} - go cc.run() // Handle the connection - util.Debugf("Accepted %v", c) - } -} - -// State for a broker connectoin -type connection struct { - broker *broker - connection electron.Connection -} - -// accept remotely-opened endpoints (Session, Sender and Receiver) on a connection -// and start goroutines to service them. -func (c *connection) run() { - for in := range c.connection.Incoming() { - switch in := in.(type) { - - case *electron.IncomingSender: - if in.Source() == "" { - in.Reject(fmt.Errorf("no source")) - } else { - go c.sender(in.Accept().(electron.Sender)) - } - - case *electron.IncomingReceiver: - if in.Target() == "" { - in.Reject(fmt.Errorf("no target")) - } else { - in.SetPrefetch(true) - in.SetCapacity(*credit) // Pre-fetch up to credit window. - go c.receiver(in.Accept().(electron.Receiver)) - } - - default: - in.Accept() // Accept sessions unconditionally - } - util.Debugf("incoming: %v", in) - } - util.Debugf("incoming closed: %v", c.connection) -} - -// receiver receives messages and pushes to a queue. -func (c *connection) receiver(receiver electron.Receiver) { - q := c.broker.queues.Get(receiver.Target()) - for { - if rm, err := receiver.Receive(); err == nil { - util.Debugf("%v: received %v", receiver, util.FormatMessage(rm.Message)) - q <- rm.Message - rm.Accept() - } else { - util.Debugf("%v error: %v", receiver, err) - break - } - } -} - -// sender pops messages from a queue and sends them. -func (c *connection) sender(sender electron.Sender) { - q := c.broker.queues.Get(sender.Source()) - for { - if sender.Error() != nil { - util.Debugf("%v closed: %v", sender, sender.Error()) - return - } - select { - - case m := <-q: - util.Debugf("%v: sent %v", sender, util.FormatMessage(m)) - sm := sentMessage{m, q} - c.broker.sent <- sm // Record sent message - sender.SendAsync(m, c.broker.acks, sm) // Receive outcome on c.broker.acks with Value sm - - case <-sender.Done(): // break if sender is closed - break - } - } -} - -// acknowledgements keeps track of sent messages and receives outcomes. -// -// We could have handled outcomes separately per-connection, per-sender or even -// per-message. Message outcomes are returned via channels defined by the user -// so they can be grouped in any way that suits the application. -func (b *broker) acknowledgements() { - sentMap := make(map[sentMessage]bool) - for { - select { - case sm, ok := <-b.sent: // A local sender records that it has sent a message. - if ok { - sentMap[sm] = true - } else { - return // Closed - } - case outcome := <-b.acks: // The message outcome is available - sm := outcome.Value.(sentMessage) - delete(sentMap, sm) - if outcome.Status != electron.Accepted { // Error, release or rejection - sm.q.PutBack(sm.m) // Put the message back on the queue. - util.Debugf("message %v put back, status %v, error %v", - util.FormatMessage(sm.m), outcome.Status, outcome.Error) - } - } - } -} http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/go/electron/receive.go ---------------------------------------------------------------------- diff --git a/examples/go/electron/receive.go b/examples/go/electron/receive.go deleted file mode 100644 index 7a505d8..0000000 --- a/examples/go/electron/receive.go +++ /dev/null @@ -1,112 +0,0 @@ -/* -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. -*/ - -package main - -import ( - "../util" - "flag" - "fmt" - "log" - "os" - "qpid.apache.org/amqp" - "qpid.apache.org/electron" - "sync" -) - -// Usage and command-line flags -func usage() { - fmt.Fprintf(os.Stderr, `Usage: %s url [url ...] -Receive messages from all the listed URLs concurrently and print them. -`, os.Args[0]) - flag.PrintDefaults() -} - -var count = flag.Uint64("count", 1, "Stop after receiving this many messages.") - -func main() { - flag.Usage = usage - flag.Parse() - - urls := flag.Args() // Non-flag arguments are URLs to receive from - if len(urls) == 0 { - log.Println("No URL provided") - usage() - os.Exit(1) - } - - messages := make(chan amqp.Message) // Channel for messages from goroutines to main() - defer close(messages) - - var wait sync.WaitGroup // Used by main() to wait for all goroutines to end. - wait.Add(len(urls)) // Wait for one goroutine per URL. - - container := electron.NewContainer(fmt.Sprintf("receive[%s]", os.Getpid())) - connections := make(chan electron.Connection, len(urls)) // Connections to close on exit - - // Start a goroutine to for each URL to receive messages and send them to the messages channel. - // main() receives and prints them. - for _, urlStr := range urls { - util.Debugf("Connecting to %s\n", urlStr) - go func(urlStr string) { // Start the goroutine - - defer wait.Done() // Notify main() when this goroutine is done. - url, err := amqp.ParseURL(urlStr) // Like net/url.Parse() but with AMQP defaults. - util.ExitIf(err) - - // Open a new connection - c, err := container.Dial("tcp", url.Host) // Note net.URL.Host is actually "host:port" - util.ExitIf(err) - connections <- c // Save connection so we can Close() when main() ends - - // Create a Receiver using the path of the URL as the source address - r, err := c.Receiver(electron.Source(url.Path)) - util.ExitIf(err) - - // Loop receiving messages and sending them to the main() goroutine - for { - if rm, err := r.Receive(); err != nil { - util.Debugf("closed %v: %v", urlStr, err) - return - } else { - rm.Accept() - messages <- rm.Message - } - } - }(urlStr) - } - - // All goroutines are started, we are receiving messages. - fmt.Printf("Listening on %d connections\n", len(urls)) - - // print each message until the count is exceeded. - for i := uint64(0); i < *count; i++ { - m := <-messages - util.Debugf("%s\n", util.FormatMessage(m)) - } - fmt.Printf("Received %d messages\n", *count) - - // Close all connections, this will interrupt goroutines blocked in Receiver.Receive() - for i := 0; i < len(urls); i++ { - c := <-connections - util.Debugf("close %s", c) - c.Close(nil) - } - wait.Wait() // Wait for all goroutines to finish. -} http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/go/electron/send.go ---------------------------------------------------------------------- diff --git a/examples/go/electron/send.go b/examples/go/electron/send.go deleted file mode 100644 index 4ea93ec..0000000 --- a/examples/go/electron/send.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -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. -*/ - -package main - -import ( - "../util" - "flag" - "fmt" - "log" - "os" - "qpid.apache.org/amqp" - "qpid.apache.org/electron" - "sync" -) - -// Usage and command-line flags -func usage() { - fmt.Fprintf(os.Stderr, `Usage: %s url [url ...] -Send messages to each URL concurrently with body "<url-path>-<n>" where n is the message number. -`, os.Args[0]) - flag.PrintDefaults() -} - -var count = flag.Int64("count", 1, "Send this may messages per address.") - -func main() { - flag.Usage = usage - flag.Parse() - - urls := flag.Args() // Non-flag arguments are URLs to receive from - if len(urls) == 0 { - log.Println("No URL provided") - flag.Usage() - os.Exit(1) - } - - sentChan := make(chan electron.Outcome) // Channel to receive acknowledgements. - - var wait sync.WaitGroup - wait.Add(len(urls)) // Wait for one goroutine per URL. - - container := electron.NewContainer(fmt.Sprintf("send[%s]", os.Getpid())) - connections := make(chan electron.Connection, len(urls)) // Connctions to close on exit - - // Start a goroutine for each URL to send messages. - for _, urlStr := range urls { - util.Debugf("Connecting to %v\n", urlStr) - go func(urlStr string) { - - defer wait.Done() // Notify main() that this goroutine is done. - url, err := amqp.ParseURL(urlStr) // Like net/url.Parse() but with AMQP defaults. - util.ExitIf(err) - - // Open a new connection - c, err := container.Dial("tcp", url.Host) // Note net.URL.Host is actually "host:port" - util.ExitIf(err) - connections <- c // Save connection so we can Close() when main() ends - - // Create a Sender using the path of the URL as the AMQP address - s, err := c.Sender(electron.Target(url.Path)) - util.ExitIf(err) - - // Loop sending messages. - for i := int64(0); i < *count; i++ { - m := amqp.NewMessage() - body := fmt.Sprintf("%v-%v", url.Path, i) - m.Marshal(body) - s.SendAsync(m, sentChan, body) // Outcome will be sent to sentChan - } - }(urlStr) - } - - // Wait for all the acknowledgements - expect := int(*count) * len(urls) - util.Debugf("Started senders, expect %v acknowledgements\n", expect) - for i := 0; i < expect; i++ { - out := <-sentChan // Outcome of async sends. - if out.Error != nil { - util.Debugf("acknowledgement[%v] %v error: %v\n", i, out.Value, out.Error) - } else { - util.Debugf("acknowledgement[%v] %v (%v)\n", i, out.Value, out.Status) - } - } - fmt.Printf("Received all %v acknowledgements\n", expect) - - wait.Wait() // Wait for all goroutines to finish. - close(connections) - for c := range connections { // Close all connections - if c != nil { - c.Close(nil) - } - } -} http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/2f85988e/examples/go/example_test.go ---------------------------------------------------------------------- diff --git a/examples/go/example_test.go b/examples/go/example_test.go deleted file mode 100644 index 6de309e..0000000 --- a/examples/go/example_test.go +++ /dev/null @@ -1,274 +0,0 @@ -/* -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. -*/ - -// Tests to verify that example code behaves as expected. -// Run in this directory with `go test example_test.go` -// -package main - -import ( - "bufio" - "bytes" - "flag" - "fmt" - "io" - "log" - "math/rand" - "net" - "os" - "os/exec" - "path" - "path/filepath" - "reflect" - "testing" - "time" -) - -func fatalIf(t *testing.T, err error) { - if err != nil { - t.Fatalf("%s", err) - } -} - -// A demo broker process -type broker struct { - cmd *exec.Cmd - addr string - runerr chan error - err error -} - -// Try to connect to the broker to verify it is ready, give up after a timeout -func (b *broker) check() error { - dialer := net.Dialer{Deadline: time.Now().Add(time.Second * 10)} - for { - c, err := dialer.Dial("tcp", b.addr) - if err == nil { // Success - c.Close() - return nil - } - select { - case runerr := <-b.runerr: // Broker exited. - return runerr - default: - } - if neterr, ok := err.(net.Error); ok && neterr.Timeout() { // Running but timed out - b.stop() - return fmt.Errorf("timed out waiting for broker") - } - time.Sleep(time.Second / 10) - } -} - -// Start the demo broker, wait till it is listening on *addr. No-op if already started. -func (b *broker) start(t *testing.T) error { - if b.cmd == nil { // Not already started - b.addr = fmt.Sprintf("127.0.0.1:%d", rand.Intn(10000)+10000) - b.cmd = exampleCommand(t, *brokerName, "-addr", b.addr) - b.runerr = make(chan error) - b.cmd.Stderr, b.cmd.Stdout = os.Stderr, os.Stdout - b.err = b.cmd.Start() - if b.err == nil { - go func() { b.runerr <- b.cmd.Wait() }() - } else { - b.runerr <- b.err - } - b.err = b.check() - } - return b.err -} - -func (b *broker) stop() { - if b != nil && b.cmd != nil { - b.cmd.Process.Kill() - <-b.runerr - } -} - -func checkEqual(want interface{}, got interface{}) error { - if reflect.DeepEqual(want, got) { - return nil - } - return fmt.Errorf("%#v != %#v", want, got) -} - -// exampleCommand returns an exec.Cmd to run an example. -func exampleCommand(t *testing.T, prog string, arg ...string) (cmd *exec.Cmd) { - args := []string{} - if *debug { - args = append(args, "-debug=true") - } - args = append(args, arg...) - prog, err := filepath.Abs(path.Join(*dir, prog)) - fatalIf(t, err) - if _, err := os.Stat(prog); err == nil { - cmd = exec.Command(prog, args...) - } else if _, err := os.Stat(prog + ".go"); err == nil { - args = append([]string{"run", prog + ".go"}, args...) - cmd = exec.Command("go", args...) - } else { - t.Fatalf("Cannot find binary or source for %s", prog) - } - cmd.Stderr = os.Stderr - return cmd -} - -// Run an example Go program, return the combined output as a string. -func runExample(t *testing.T, prog string, arg ...string) (string, error) { - cmd := exampleCommand(t, prog, arg...) - out, err := cmd.Output() - return string(out), err -} - -func prefix(prefix string, err error) error { - if err != nil { - return fmt.Errorf("%s: %s", prefix, err) - } - return nil -} - -func runExampleWant(t *testing.T, want string, prog string, args ...string) error { - out, err := runExample(t, prog, args...) - if err != nil { - return fmt.Errorf("%s failed: %s: %s", prog, err, out) - } - return prefix(prog, checkEqual(want, out)) -} - -func exampleArgs(args ...string) []string { - for i := 0; i < *connections; i++ { - args = append(args, fmt.Sprintf("%s/%s%d", testBroker.addr, "q", i)) - } - return args -} - -// Send then receive -func TestExampleSendReceive(t *testing.T) { - if testing.Short() { - t.Skip("Skip demo tests in short mode") - } - testBroker.start(t) - err := runExampleWant(t, - fmt.Sprintf("Received all %d acknowledgements\n", expected), - "send", - exampleArgs("-count", fmt.Sprintf("%d", *count))...) - if err != nil { - t.Fatal(err) - } - err = runExampleWant(t, - fmt.Sprintf("Listening on %v connections\nReceived %v messages\n", *connections, *count**connections), - "receive", - exampleArgs("-count", fmt.Sprintf("%d", *count**connections))...) - if err != nil { - t.Fatal(err) - } -} - -var ready error - -func init() { ready = fmt.Errorf("Ready") } - -// Run receive in a goroutine. -// Send ready on errchan when it is listening. -// Send final error when it is done. -// Returns the Cmd, caller must Wait() -func goReceiveWant(t *testing.T, errchan chan<- error, want string, arg ...string) *exec.Cmd { - cmd := exampleCommand(t, "receive", arg...) - go func() { - pipe, err := cmd.StdoutPipe() - if err != nil { - errchan <- err - return - } - out := bufio.NewReader(pipe) - cmd.Start() - line, err := out.ReadString('\n') - if err != nil && err != io.EOF { - errchan <- err - return - } - listening := "Listening on 3 connections\n" - if line != listening { - errchan <- checkEqual(listening, line) - return - } - errchan <- ready - buf := bytes.Buffer{} - io.Copy(&buf, out) // Collect the rest of the output - cmd.Wait() - errchan <- checkEqual(want, buf.String()) - close(errchan) - }() - return cmd -} - -// Start receiver first, wait till it is running, then send. -func TestExampleReceiveSend(t *testing.T) { - if testing.Short() { - t.Skip("Skip demo tests in short mode") - } - testBroker.start(t) - - // Start receiver, wait for "listening" message on stdout - recvCmd := exampleCommand(t, "receive", exampleArgs(fmt.Sprintf("-count=%d", expected))...) - pipe, err := recvCmd.StdoutPipe() - if err != nil { - t.Fatal(err) - } - recvCmd.Start() - out := bufio.NewReader(pipe) - line, err := out.ReadString('\n') - if err := checkEqual("Listening on 3 connections\n", line); err != nil { - t.Fatal(err) - } - - if err := runExampleWant(t, - fmt.Sprintf("Received all %d acknowledgements\n", expected), - "send", - exampleArgs("-count", fmt.Sprintf("%d", *count))...); err != nil { - t.Fatal(err) - } - - buf := bytes.Buffer{} - io.Copy(&buf, out) - if err := checkEqual(fmt.Sprintf("Received %d messages\n", expected), buf.String()); err != nil { - t.Fatal(err) - } -} - -var testBroker *broker - -var debug = flag.Bool("debug", false, "Debugging output from examples") -var brokerName = flag.String("broker", "broker", "Name of broker executable to run") -var count = flag.Int("count", 3, "Count of messages to send in tests") -var connections = flag.Int("connections", 3, "Number of connections to make in tests") -var dir = flag.String("dir", "electron", "Directory containing example sources or binaries") -var expected int - -func TestMain(m *testing.M) { - if out, err := exec.Command("go", "install", "qpid.apache.org/...").CombinedOutput(); err != nil { - log.Fatalf("go install failed: %s\n%s", err, out) - } - expected = (*count) * (*connections) - rand.Seed(time.Now().UTC().UnixNano()) - testBroker = &broker{} // Broker is started on-demand by tests. - status := m.Run() - testBroker.stop() - os.Exit(status) -} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
