--- /dev/null
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __SOCKET_REQUEST_H
+#define __SOCKET_REQUEST_H 1
+
+#include <exceptions/exceptions.h>
+
+#include <boost/noncopyable.hpp>
+#include <utility>
+#include <string>
+#include <stdint.h>
+
+namespace isc {
+
+namespace config {
+class ModuleCCSession;
+};
+
+namespace server_common {
+
+/// \brief A singleton class for requesting sockets
+///
+/// This class allows requesting sockets from the socket creator.
+///
+/// It is considered to be a singleton - a class which is instantiated
+/// at most once in the whole application. This is because it makes no
+/// sense to have two of them.
+///
+/// This is actually an abstract base class. There'll be one with
+/// hidden implementation and we expect the tests to create it's own
+/// subclass when needed.
+///
+/// \see socketRequestor function to access the object of this class.
+class SocketRequestor : boost::noncopyable {
+protected:
+ /// \brief Protected constructor
+ ///
+ /// The constructor is protected so this class is not created by accident
+ /// (which it can't anyway, as it has pure virtual methods, but just to
+ /// be sure).
+ SocketRequestor() {}
+public:
+ /// \brief virtual destructor
+ ///
+ /// A virtual destructor, as we have virtual methods, to make sure it is
+ /// destroyed by the destructor of the subclass. This shouldn't matter, as
+ /// a singleton class wouldn't get destroyed, but just to be sure.
+
+ virtual ~ SocketRequestor() {}
+ /// \brief A representation of received socket
+ ///
+ /// The pair holds two parts. The OS-level file descriptor acting as the
+ /// socket (you might want to use it directly with functions like recv,
+ /// or fill it into an asio socket). The other part is the token representing
+ /// the socket, which allows it to be given up again.
+ typedef std::pair<int, std::string> SocketID;
+
+ /// \brief The protocol of requested socket
+ ///
+ /// This describes which protocol the socket should have when created.
+ enum Protocol {
+ UDP,
+ TCP
+ };
+
+ /// \brief The share mode of the requested socket
+ ///
+ /// The socket creator is able to "borrow" the same socket to multiple
+ /// applications at once. However, it isn't always what is required. This
+ /// describes the restrains we want to have on our socket regarding the
+ /// sharing. Union of restriction of all requests on the given socket
+ /// is taken (so you still don't have to get your socket even if you
+ /// say SHARE_ANY, because someone else might already asked for the socket
+ /// with DONT_SHARE).
+ enum ShareMode {
+ DONT_SHARE, //< Request an exclusive ownership of the socket.
+ SHARE_SAME, //< It is possible to share the socket with anybody who
+ //< provided the same share_name.
+ SHARE_ANY //< Any sharing is allowed.
+ };
+
+ /// \brief Exception when we can't manipulate a socket
+ ///
+ /// This is thrown if the other side doesn't want to comply to our
+ /// requests, like when we ask for a socket already held by someone
+ /// else or ask for nonsense (releasing a socket we don't own).
+ class SocketError : public Exception {
+ public:
+ SocketError(const char* file, size_t line, const char *what) :
+ Exception(file, line, what)
+ { }
+ };
+
+ /// \brief Ask for a socket
+ ///
+ /// Asks the socket creator to give us a socket. The socket will be bound
+ /// to the given address and port.
+ ///
+ /// \param protocol specifies the protocol of the socket.
+ /// \param address to which the socket should be bound.
+ /// \param port the port to which the socket should be bound (native endian,
+ /// not network byte order).
+ /// \param share_mode how the socket can be shared with other requests.
+ /// \param share_name the name of sharing group, relevant for SHARE_SAME
+ /// (specified by us or someone else).
+ /// \return the socket, as a file descriptor and token representing it on
+ /// the socket creator side.
+ /// \throw CCSessionError when we have a problem talking over the CC
+ /// session.
+ /// \throw SocketError in case the other side doesn't want to give us
+ /// the socket for some reason (common cases are when the socket
+ /// can't be allocated or bound, or when the socket is claimed by
+ /// some other application and the sharing parameters don't allow
+ /// sharing it).
+ virtual SocketID requestSocket(Protocol protocol,
+ const std::string& address,
+ uint16_t port, ShareMode share_mode,
+ const std::string& share_name) = 0;
+
+ /// \brief Tell the socket creator we no longer need the socket
+ ///
+ /// Releases the identified socket. This must be called *after*
+ /// the file descriptor was closed on our side. This will allow
+ /// the remote side to either give it to some other application
+ /// or close it, depending on the situation.
+ ///
+ /// \param token the token representing the socket, as received
+ /// in the second part of the requestSocket result.
+ /// \throw CCSessionError when we have a problem talking over the CC
+ /// session.
+ /// \throw SocketError in case the other side doesn't like the
+ /// release (like we're trying to release a socket that doesn't
+ /// belong to us or exist at all).
+ virtual void releaseSocket(const std::string& token) = 0;
+
+ /// \brief Initialize the singleton object
+ ///
+ /// This creates the object that will be used to request sockets.
+ /// It can be called only once per the life of application.
+ ///
+ /// \param session the CC session that'll be used to talk to the
+ /// socket creator.
+ /// \throw InvalidOperation when it is called more than once.
+ static void init(config::ModuleCCSession& session);
+
+ /// \brief Initialization for tests
+ ///
+ /// This is to support different subclasses in tests. It replaces
+ /// the object used by socketRequestor() function by this one provided
+ /// as parameter. The ownership is not taken, eg. it's up to the caller
+ /// to delete it when necessary.
+ ///
+ /// This is not to be used in production applications. It is meant as
+ /// an replacement of init.
+ ///
+ /// This never throws.
+ ///
+ /// \param requestor the object to be used. It can be NULL to reset to
+ /// an "virgin" state (which acts as if initTest or init was never
+ /// called before).
+ static void initTest(SocketRequestor* requestor);
+};
+
+/// \brief Access the requestor object.
+///
+/// This returns the singleton object for the Requestor.
+///
+/// \return the active socket requestor object.
+/// \throw InvalidOperation if the object was not yet initialized.
+/// \see SocketRequestor::init to initialize the object.
+SocketRequestor&
+socketRequestor();
+
+}
+}
+
+#endif
--- /dev/null
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <server_common/socket_request.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::server_common;
+using namespace isc;
+
+namespace {
+
+// Check it throws an exception when it is not initialized
+TEST(SocketRequestorAccess, unitialized) {
+ // Make sure it is not initialized
+ SocketRequestor::initTest(NULL);
+ EXPECT_THROW(socketRequestor(), InvalidOperation);
+}
+
+// It returns whatever it is initialized to
+TEST(SocketRequestorAccess, initialized) {
+ // A concrete implementation that does nothing, just can exist
+ class DummyRequestor : public SocketRequestor {
+ public:
+ DummyRequestor() : SocketRequestor() {}
+ virtual void releaseSocket(const std::string&) {}
+ virtual SocketID requestSocket(Protocol, const std::string&, uint16_t,
+ ShareMode, const std::string&)
+ {
+ return (SocketID(0, "")); // Just to silence warnings
+ }
+ };
+ DummyRequestor requestor;
+ // Make sure it is initialized (the test way, of course)
+ SocketRequestor::initTest(&requestor);
+ // It returs the same "pointer" as inserted
+ // The casts are there as the template system seemed to get confused
+ // without them, the types should be correct even without them, but
+ // the EXPECT_EQ wanted to use long long int instead of pointers.
+ EXPECT_EQ(static_cast<const SocketRequestor*>(&requestor),
+ static_cast<const SocketRequestor*>(&socketRequestor()));
+ // Just that we don't have an invalid pointer anyway
+ SocketRequestor::initTest(NULL);
+}
+
+}