io_asio_socket.h 11.6 KB
Newer Older
Stephen Morris's avatar
Stephen Morris committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
// Copyright (C) 2010  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 __IO_ASIO_SOCKET_H
#define __IO_ASIO_SOCKET_H 1

// IMPORTANT NOTE: only very few ASIO headers files can be included in
// this file.  In particular, asio.hpp should never be included here.
// See the description of the namespace below.
#include <unistd.h>             // for some network system calls

#include <functional>
#include <string>

#include <exceptions/exceptions.h>
#include <coroutine.h>

#include <asiolink/io_error.h>
#include <asiolink/io_socket.h>


namespace asiolink {

/// \brief Socket not open
///
/// Thrown on an attempt to do read/write to a socket that is not open.
class SocketNotOpen : public IOError {
public:
    SocketNotOpen(const char* file, size_t line, const char* what) :
        IOError(file, line, what) {}
};



/// Forward declaration of an IOEndpoint
class IOEndpoint;


/// \brief I/O Socket with asynchronous operations
///
/// This class is a wrapper for the ASIO socket classes such as
/// \c ip::tcp::socket and \c ip::udp::socket.
///
/// This is the basic IOSocket with additional operations - open, send, receive
/// and close.  Depending on how the asiolink code develops, it may be a
/// temporary class: its main use is to add the template parameter needed for
/// the derived classes UDPSocket and TCPSocket but without changing the
/// signature of the more basic IOSocket class.
///
/// We may revisit this decision when we generalize the wrapper and more
/// modules use it.  Also, at that point we may define a separate (visible)
/// derived class for testing purposes rather than providing factory methods
/// (i.e., getDummy variants below).
///
/// TODO: Check if IOAsioSocket class is still needed
///
/// \param C Template parameter identifying type of the callback object.

template <typename C>
class IOAsioSocket : public IOSocket {
    ///
    /// \name Constructors and Destructor
    ///
    /// Note: The copy constructor and the assignment operator are
    /// intentionally defined as private, making this class non-copyable.
    //@{
private:
    IOAsioSocket(const IOAsioSocket<C>& source);
    IOAsioSocket& operator=(const IOAsioSocket<C>& source);
protected:
    /// \brief The default constructor.
    ///
    /// This is intentionally defined as \c protected as this base class
    /// should never be instantiated (except as part of a derived class).
    IOAsioSocket() {}
public:
    /// The destructor.
    virtual ~IOAsioSocket() {}
    //@}

    /// \brief Return the "native" representation of the socket.
    ///
    /// In practice, this is the file descriptor of the socket for
    /// UNIX-like systems so the current implementation simply uses
    /// \c int as the type of the return value.
    /// We may have to need revisit this decision later.
    ///
    /// In general, the application should avoid using this method;
    /// it essentially discloses an implementation specific "handle" that
    /// can change the internal state of the socket (consider the
    /// application closes it, for example).
    /// But we sometimes need to perform very low-level operations that
    /// requires the native representation.  Passing the file descriptor
    /// to a different process is one example.
    /// This method is provided as a necessary evil for such limited purposes.
    ///
    /// This method never throws an exception.
    ///
    /// \return The native representation of the socket.  This is the socket
    /// file descriptor for UNIX-like systems.
    virtual int getNative() const = 0;

    /// \brief Return the transport protocol of the socket.
    ///
    /// Currently, it returns \c IPPROTO_UDP for UDP sockets, and
    /// \c IPPROTO_TCP for TCP sockets.
    ///
    /// This method never throws an exception.
    ///
    /// \return IPPROTO_UDP for UDP sockets
    /// \return IPPROTO_TCP for TCP sockets
    virtual int getProtocol() const = 0;

    /// \brief Open AsioSocket
    ///
127 128 129 130 131 132 133 134 135 136 137 138 139
    /// Opens the socket for asynchronous I/O.  On a UDP socket, this is merely
    /// an "open()" on the underlying socket (so completes immediately), but on
    /// a TCP socket it also connects to the remote end (which is done as an
    /// asynchronous operation).
    ///
    /// For TCP, signalling of the completion of the operation is done by
    /// by calling the callback function in the normal way.  This could be done
    /// for UDP (by posting en event on the event queue); however, that will
    /// incur additional overhead in the most common case.  Instead, the return
    /// value indicates whether the operation was asynchronous or not. If yes,
    /// (i.e. TCP) the callback has been posted to the event queue: if no (UDP),
    /// no callback has been posted (in which case it is up to the caller as to
    /// whether they want to manually post the callback themself.)
Stephen Morris's avatar
Stephen Morris committed
140 141 142 143
    ///
    /// \param endpoint Pointer to the endpoint object.  This is ignored for
    /// a UDP socket (the target is specified in the send call), but should
    /// be of type TCPEndpoint for a TCP connection.
144 145
    /// \param callback I/O Completion callback, called when the operation has
    /// completed, but only if the operation was asynchronous.
Stephen Morris's avatar
Stephen Morris committed
146 147
    ///
    /// \return true if an asynchronous operation was started and the caller
148 149
    /// should yield and wait for completion, false if the operation was
    /// completed synchronously and no callback was queued.
Stephen Morris's avatar
Stephen Morris committed
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
    virtual bool open(const IOEndpoint* endpoint, C& callback) = 0;

    /// \brief Send Asynchronously
    ///
    /// This corresponds to async_send_to() for UDP sockets and async_send()
    /// for TCP.  In both cases an endpoint argument is supplied indicating the
    /// target of the send - this is ignored for TCP.
    ///
    /// \param data Data to send
    /// \param length Length of data to send
    /// \param endpoint Target of the send
    /// \param callback Callback object.
    virtual void asyncSend(const void* data, size_t length,
        const IOEndpoint* endpoint, C& callback) = 0;

    /// \brief Receive Asynchronously
    ///
    /// This correstponds to async_receive_from() for UDP sockets and
    /// async_receive() for TCP.  In both cases, an endpoint argument is
    /// supplied to receive the source of the communication.  For TCP it will
    /// be filled in with details of the connection.
    ///
    /// \param data Buffer to receive incoming message
    /// \param length Length of the data buffer
    /// \param cumulative Amount of data that should already be in the buffer.
    /// \param endpoint Source of the communication
    /// \param callback Callback object
    virtual void asyncReceive(void* data, size_t length, size_t cumulative,
        IOEndpoint* endpoint, C& callback) = 0;

    /// \brief Checks if the data received is complete.
    ///
    /// This applies to TCP receives, where the data is a byte stream and a
    /// receive is not guaranteed to receive the entire message.  DNS messages
    /// over TCP are prefixed by a two-byte count field.  This method takes the
    /// amount received so far and the amount received in this I/O and checks
    /// if the message is complete, returning the appropriate indication.  As
    /// a side-effect, it also updates the amount received.
    ///
    /// For a UDP receive, all the data is received in one I/O, so this is
    /// effectively a no-op (although it does update the amount received).
    ///
    /// \param data Data buffer containing data to date
    /// \param length Amount of data received in last asynchronous I/O
    /// \param cumulative On input, amount of data received before the last
    /// I/O.  On output, the total amount of data received to date.
    ///
    /// \return true if the receive is complete, false if another receive is
    /// needed.
    virtual bool receiveComplete(void* data, size_t length,
        size_t& cumulative) = 0;

    /// \brief Cancel I/O On AsioSocket
    virtual void cancel() = 0;

    /// \brief Close socket
    virtual void close() = 0;
};


#include "io_socket.h"

/// \brief The \c DummyAsioSocket class is a concrete derived class of
/// \c IOAsioSocket that is not associated with any real socket.
///
/// This main purpose of this class is tests, where it may be desirable to
/// instantiate an \c IOAsioSocket object without involving system resource
/// allocation such as real network sockets.
///
/// \param C Template parameter identifying type of the callback object.

template <typename C>
class DummyAsioSocket : public IOAsioSocket<C> {
private:
    DummyAsioSocket(const DummyAsioSocket<C>& source);
    DummyAsioSocket& operator=(const DummyAsioSocket<C>& source);
public:
    /// \brief Constructor from the protocol number.
    ///
    /// The protocol must validly identify a standard network protocol.
    /// For example, to specify TCP \c protocol must be \c IPPROTO_TCP.
    ///
    /// \param protocol The network protocol number for the socket.
    DummyAsioSocket(const int protocol) : protocol_(protocol) {}

    /// \brief A dummy derived method of \c IOAsioSocket::getNative().
    ///
    /// \return Always returns -1 as the object is not associated with a real
    /// (native) socket.
    virtual int getNative() const { return (-1); }

    /// \brief A dummy derived method of \c IOAsioSocket::getProtocol().
    ///
    /// \return Protocol socket was created with
    virtual int getProtocol() const { return (protocol_); }


    /// \brief Open AsioSocket
    ///
    /// A call that is a no-op on UDP sockets, this opens a connection to the
    /// system identified by the given endpoint.
    ///
    /// \param endpoint Unused
    /// \param callback Unused.
    ///false indicating that the operation completed synchronously.
    virtual bool open(const IOEndpoint*, C&) {
        return (false);
    }

    /// \brief Send Asynchronously
    ///
    /// Must be supplied as it is abstract in the base class.
    ///
    /// \param data Unused
    /// \param length Unused
    /// \param endpoint Unused
    /// \param callback Unused
    virtual void asyncSend(const void*, size_t, const IOEndpoint*, C&) {
    }

    /// \brief Receive Asynchronously
    ///
    /// Must be supplied as it is abstract in the base class.
    ///
    /// \param data Unused
    /// \param length Unused
    /// \param cumulative Unused
    /// \param endpoint Unused
    /// \param callback Unused
    virtual void asyncReceive(void* data, size_t, size_t, IOEndpoint*, C&) { } 
    /// \brief Checks if the data received is complete.
    ///
    /// \param data Unused
    /// \param length Unused
    /// \param cumulative Unused
    ///
    /// \return Always true
    virtual bool receiveComplete(void*, size_t, size_t&) {
        return (true);
    }

    /// \brief Cancel I/O On AsioSocket
    ///
    /// Must be supplied as it is abstract in the base class.
    virtual void cancel() {
    }

    /// \brief Close socket
    ///
    /// Must be supplied as it is abstract in the base class.
    virtual void close() {
    }

private:
    const int protocol_;
};

} // namespace asiolink

#endif // __IO_ASIO_SOCKET_H