Commit c8caecc3 authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

[805] Interface of the socket requestor

Only the test initialization and the singleton accessor functions are
implemented for now (and tests for those).
parent 5345a3c2
......@@ -21,6 +21,7 @@ libserver_common_la_SOURCES = client.h client.cc
libserver_common_la_SOURCES += keyring.h keyring.cc
libserver_common_la_SOURCES += portconfig.h portconfig.cc
libserver_common_la_SOURCES += logger.h logger.cc
libserver_common_la_SOURCES += socket_request.h socket_request.cc
nodist_libserver_common_la_SOURCES = server_common_messages.h
nodist_libserver_common_la_SOURCES += server_common_messages.cc
libserver_common_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
......
// 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 "socket_request.h"
namespace isc {
namespace server_common {
namespace {
SocketRequestor* requestor(NULL);
}
SocketRequestor&
socketRequestor() {
if (requestor != NULL) {
return (*requestor);
} else {
isc_throw(InvalidOperation, "The socket requestor is not initialized");
}
}
void
SocketRequestor::initTest(SocketRequestor* new_requestor) {
requestor = new_requestor;
}
}
}
// 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
......@@ -29,6 +29,7 @@ run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += client_unittest.cc
run_unittests_SOURCES += portconfig_unittest.cc
run_unittests_SOURCES += keyring_test.cc
run_unittests_SOURCES += socket_requestor_test.cc
nodist_run_unittests_SOURCES = data_path.h
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
......
// 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);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment