io_asio_socket.h 15 KB
Newer Older
1
// Copyright (C) 2010-2017 Internet Systems Consortium, Inc. ("ISC")
Stephen Morris's avatar
Stephen Morris committed
2
//
3 4 5
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
Stephen Morris's avatar
Stephen Morris committed
6

7 8
#ifndef IO_ASIO_SOCKET_H
#define IO_ASIO_SOCKET_H 1
Stephen Morris's avatar
Stephen Morris committed
9 10 11 12 13 14 15 16 17 18 19

// 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>

20
#include <util/buffer.h>
21

Stephen Morris's avatar
Stephen Morris committed
22 23 24
#include <asiolink/io_error.h>
#include <asiolink/io_socket.h>

25 26
#include <boost/asio/coroutine.hpp>

27
namespace isc {
Stephen Morris's avatar
Stephen Morris committed
28 29 30 31 32 33 34 35 36 37 38
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) {}
};

39
/// \brief Error setting socket options
40 41 42 43 44 45 46
///
/// Thrown if attempt to change socket options fails.
class SocketSetError : public IOError {
public:
    SocketSetError(const char* file, size_t line, const char* what) :
        IOError(file, line, what) {}
};
Stephen Morris's avatar
Stephen Morris committed
47

48
/// \brief Buffer overflow
49 50 51 52 53 54 55 56 57
///
/// Thrown if an attempt is made to receive into an area beyond the end of
/// the receive data buffer.
class BufferOverflow : public IOError {
public:
    BufferOverflow(const char* file, size_t line, const char* what) :
        IOError(file, line, what) {}
};

Stephen Morris's avatar
Stephen Morris committed
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
/// 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).
///
/// \param C Template parameter identifying type of the callback object.

template <typename C>
class IOAsioSocket : public IOSocket {
82

Stephen Morris's avatar
Stephen Morris committed
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
    ///
    /// \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.
    ///
105 106 107
    /// 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.
Stephen Morris's avatar
Stephen Morris committed
108
    ///
109 110 111 112 113 114 115
    /// 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 what would happen if
    /// 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
116
    /// limited purposes.
Stephen Morris's avatar
Stephen Morris committed
117 118 119 120
    ///
    /// This method never throws an exception.
    ///
    /// \return The native representation of the socket.  This is the socket
121
    ///         file descriptor for UNIX-like systems.
Stephen Morris's avatar
Stephen Morris committed
122 123 124 125 126 127 128 129 130
    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.
    ///
131
    /// \return \c IPPROTO_UDP for UDP sockets, \c IPPROTO_TCP for TCP sockets
Stephen Morris's avatar
Stephen Morris committed
132 133
    virtual int getProtocol() const = 0;

134
    /// \brief Is Open() synchronous?
Stephen Morris's avatar
Stephen Morris committed
135
    ///
136 137 138 139
    /// On a TCP socket, an "open" operation is a call to the socket's "open()"
    /// method followed by a connection to the remote system: it is an
    /// asynchronous operation.  On a UDP socket, it is just a call to "open()"
    /// and completes synchronously.
140 141 142 143
    ///
    /// 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
144 145 146 147 148
    /// incur additional overhead in the most common case.  So we give the
    /// caller the choice for calling this open() method synchronously or
    /// asynchronously.
    ///
    /// Owing to the way that the stackless coroutines are implemented, we need
149 150
    /// to know _before_ executing the "open" function whether or not it is
    /// asynchronous.  So this method is called to provide that information.
151 152 153 154
    ///
    /// (The reason there is a need to know is because the call to open() passes
    /// in the state of the coroutine at the time the call is made.  On an
    /// asynchronous I/O, we need to set the state to point to the statement
155
    /// after the call to open() _before_ we pass the coroutine to the open()
156 157 158 159
    /// call.  Unfortunately, the macros that set the state of the coroutine
    /// also yield control - which we don't want to do if the open is
    /// synchronous.  Hence we need to know before we make the call to open()
    /// whether that call will complete asynchronously.)
160 161 162 163 164 165
    virtual bool isOpenSynchronous() const = 0;

    /// \brief Open AsioSocket
    ///
    /// Opens the socket for asynchronous I/O.  The open will complete
    /// synchronously on UCP or asynchronously on TCP (in which case a callback
166
    /// will be queued).
Stephen Morris's avatar
Stephen Morris committed
167 168
    ///
    /// \param endpoint Pointer to the endpoint object.  This is ignored for
169 170
    ///        a UDP socket (the target is specified in the send call), but
    ///        should be of type TCPEndpoint for a TCP connection.
171
    /// \param callback I/O Completion callback, called when the operation has
172 173
    ///        completed, but only if the operation was asynchronous. (It is
    ///        ignored on a UDP socket.)
174
    virtual void open(const IOEndpoint* endpoint, C& callback) = 0;
Stephen Morris's avatar
Stephen Morris committed
175 176 177 178 179 180 181 182 183 184 185 186

    /// \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,
187
                           const IOEndpoint* endpoint, C& callback) = 0;
Stephen Morris's avatar
Stephen Morris committed
188 189 190

    /// \brief Receive Asynchronously
    ///
191
    /// This corresponds to async_receive_from() for UDP sockets and
Stephen Morris's avatar
Stephen Morris committed
192 193 194 195 196 197
    /// 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
198 199 200 201 202
    /// \param offset Offset into buffer where data is to be put.  Although the
    ///        offset could be implied by adjusting "data" and "length"
    ///        appropriately, using this argument allows data to be specified as
    ///        "const void*" - the overhead of converting it to a pointer to a
    ///        set of bytes is hidden away here.
Stephen Morris's avatar
Stephen Morris committed
203 204
    /// \param endpoint Source of the communication
    /// \param callback Callback object
205 206
    virtual void asyncReceive(void* data, size_t length, size_t offset,
                              IOEndpoint* endpoint, C& callback) = 0;
Stephen Morris's avatar
Stephen Morris committed
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
    /// \brief Processes received data
    ///
    /// In the IOFetch code, data is received into a staging buffer before being
    /// copied into the target buffer.  (This is because (a) we don't know how
    /// much data we will be receiving, so don't know how to size the output
    /// buffer and (b) TCP data is preceded by a two-byte count field that needs
    /// to be discarded before being returned to the user.)
    ///
    /// An additional consideration is that TCP data is not received in one
    /// I/O - it may take a number of I/Os - each receiving any non-zero number
    /// of bytes - to read the entire message.
    ///
    /// So the IOFetch code has to loop until it determines that all the data
    /// has been read.  This is where this method comes in.  It has several
    /// functions:
    ///
    /// - It checks if the received data is complete.
    /// - If data is not complete, decides if the next set of data is to go into
    ///   the start of the staging buffer or at some offset into it.  (This
    ///   simplifies the case we could have in a TCP receive where the two-byte
    ///   count field is received in one-byte chunks: we put off interpreting
    ///   the count until we have all of it.  The alternative - copying the
    ///   data to the output buffer and interpreting the count from there -
    ///   would require moving the data in the output buffer by two bytes before
    ///   returning it to the caller.)
    /// - Copies data from the staging buffer into the output buffer.
    ///
    /// This functionality mainly applies to TCP receives.  For UDP, all the
    /// data is received in one I/O, so this just copies the data into the
    /// output buffer.
    ///
    /// \param staging Pointer to the start of the staging buffer.
    /// \param length Amount of data in the staging buffer.
    /// \param cumulative Amount of data received before the staging buffer is
    ///        processed (this includes the TCP count field if appropriate).
    ///        The value should be set to zero before the receive loop is
    ///        entered, and it will be updated by this method as required.
    /// \param offset Offset into the staging buffer where the next read should
    ///        put the received data.  It should be set to zero before the first
    ///        call and may be updated by this method.
    /// \param expected Expected amount of data to be received.  This is
    ///        really the TCP count field and is set to that value when enough
    ///        of a TCP message is received.  It should be initialized to -1
    ///        before the first read is executed.
    /// \param outbuff Output buffer.  Data in the staging buffer may be copied
    ///        to this output buffer in the call.
Stephen Morris's avatar
Stephen Morris committed
254 255
    ///
    /// \return true if the receive is complete, false if another receive is
256 257 258 259 260 261 262 263 264
    ///         needed.  This is always true for UDP, but for TCP involves
    ///         checking the amount of data received so far against the amount
    ///         expected (as indicated by the two-byte count field).  If this
    ///         method returns false, another read should be queued and data
    ///         should be read into the staging buffer at offset given by the
    ///         "offset" parameter.
    virtual bool processReceivedData(const void* staging, size_t length,
                                     size_t& cumulative, size_t& offset,
                                     size_t& expected,
265
                                     isc::util::OutputBufferPtr& outbuff) = 0;
266

Stephen Morris's avatar
Stephen Morris committed
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
    /// \brief Cancel I/O On AsioSocket
    virtual void cancel() = 0;

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


/// \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_); }


310 311 312 313 314 315 316
    /// \brief Is socket opening synchronous?
    ///
    /// \return true - it is for this class.
    bool isOpenSynchronous() const {
        return true;
    }

Stephen Morris's avatar
Stephen Morris committed
317 318 319 320
    /// \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.
321
    /// The endpoint and callback are unused.
Stephen Morris's avatar
Stephen Morris committed
322
    ///
323
    /// \return false indicating that the operation completed synchronously.
Stephen Morris's avatar
Stephen Morris committed
324 325 326 327 328 329 330
    virtual bool open(const IOEndpoint*, C&) {
        return (false);
    }

    /// \brief Send Asynchronously
    ///
    /// Must be supplied as it is abstract in the base class.
331
    /// This is unused.
Stephen Morris's avatar
Stephen Morris committed
332 333 334 335 336 337
    virtual void asyncSend(const void*, size_t, const IOEndpoint*, C&) {
    }

    /// \brief Receive Asynchronously
    ///
    /// Must be supplied as it is abstract in the base class.
338
    /// The parameters are unused.
339
    virtual void asyncReceive(void*, size_t, size_t, IOEndpoint*, C&) {
340 341
    }

Stephen Morris's avatar
Stephen Morris committed
342 343
    /// \brief Checks if the data received is complete.
    ///
344
    /// The parameters are unused.
Stephen Morris's avatar
Stephen Morris committed
345
    /// \return Always true
346 347
    virtual bool receiveComplete(const void*, size_t, size_t&, size_t&,
                                 size_t&, isc::util::OutputBufferPtr&)
348
    {
349
        return (true);
350
    }
Stephen Morris's avatar
Stephen Morris committed
351

352

Stephen Morris's avatar
Stephen Morris committed
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
    /// \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
370
} // namespace isc
Stephen Morris's avatar
Stephen Morris committed
371

372
#endif // IO_ASIO_SOCKET_H