Commit 9739cbce authored by Stephen Morris's avatar Stephen Morris
Browse files

Merge branch 'trac554'

parents 955a1552 da6a33e3
......@@ -12,24 +12,28 @@ CLEANFILES = *.gcno *.gcda
# have some code fragments that would hit gcc's unused-parameter warning,
# which would make the build fail with -Werror (our default setting).
lib_LTLIBRARIES = libasiolink.la
libasiolink_la_SOURCES = asiolink.h
libasiolink_la_SOURCES += io_service.cc io_service.h
libasiolink_la_SOURCES += dns_service.cc dns_service.h
libasiolink_la_SOURCES += dns_server.h
libasiolink_la_SOURCES += dns_lookup.h
libasiolink_la_SOURCES = asiolink.h
libasiolink_la_SOURCES += dns_answer.h
libasiolink_la_SOURCES += simple_callback.h
libasiolink_la_SOURCES += dns_lookup.h
libasiolink_la_SOURCES += dns_server.h
libasiolink_la_SOURCES += dns_service.h dns_service.cc
libasiolink_la_SOURCES += dummy_io_cb.h
libasiolink_la_SOURCES += interval_timer.h interval_timer.cc
libasiolink_la_SOURCES += recursive_query.h recursive_query.cc
libasiolink_la_SOURCES += io_socket.cc io_socket.h
libasiolink_la_SOURCES += io_address.h io_address.cc
libasiolink_la_SOURCES += io_endpoint.h io_endpoint.cc
libasiolink_la_SOURCES += io_error.h
libasiolink_la_SOURCES += io_fetch.h io_fetch.cc
libasiolink_la_SOURCES += io_message.h
libasiolink_la_SOURCES += io_address.cc io_address.h
libasiolink_la_SOURCES += io_endpoint.cc io_endpoint.h
libasiolink_la_SOURCES += udp_endpoint.h udp_socket.h
libasiolink_la_SOURCES += udp_server.h udp_server.cc
libasiolink_la_SOURCES += udp_query.h udp_query.cc
libasiolink_la_SOURCES += tcp_endpoint.h tcp_socket.h
libasiolink_la_SOURCES += io_service.h io_service.cc
libasiolink_la_SOURCES += io_socket.h io_socket.cc
libasiolink_la_SOURCES += recursive_query.h recursive_query.cc
libasiolink_la_SOURCES += simple_callback.h
libasiolink_la_SOURCES += tcp_endpoint.h
libasiolink_la_SOURCES += tcp_server.h tcp_server.cc
libasiolink_la_SOURCES += tcp_socket.h
libasiolink_la_SOURCES += udp_endpoint.h
libasiolink_la_SOURCES += udp_server.h udp_server.cc
libasiolink_la_SOURCES += udp_socket.h
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS)
libasiolink_la_CXXFLAGS = $(AM_CXXFLAGS)
......
......@@ -33,7 +33,7 @@ This is intended to simplify development a bit, since it allows the
routines to be written in a straightfowrard step-step-step fashion rather
than as a complex chain of separate handler functions.
Coroutine objects (i.e., UDPServer, TCPServer and UDPQuery) are objects
Coroutine objects (i.e., UDPServer, TCPServer and IOFetch) are objects
with reenterable operator() members. When an instance of one of these
classes is called as a function, it resumes at the position where it left
off. Thus, a UDPServer can issue an asynchronous I/O call and specify
......@@ -101,3 +101,82 @@ when the answer has arrived. In simplified form, the DNSQuery routine is:
Currently, DNSQuery is only implemented for UDP queries. In future work
it will be necessary to write code to fall back to TCP when circumstances
require it.
Upstream Fetches
================
Upstream fetches (queries by the resolver on behalf of a client) are made
using a slightly-modified version of the pattern described above.
Sockets
-------
First, it will be useful to understand the class hierarchy used in the
fetch logic:
IOSocket
|
IOAsioSocket
|
+-----+-----+
| |
UDPSocket TCPSocket
IOSocket is a wrapper class for a socket and is used by the authoritative
server code. It is an abstract base class, providing little more that the ability to hold the socket and to return the protocol in use.
Built on this is IOAsioSocket, which adds the open, close, asyncSend and
asyncReceive methods. This is a template class, which takes as template
argument the class of the object that will be used as the callback when the
asynchronous operation completes. This object can be of any type, but must
include an operator() method with the signature:
operator()(asio::error_code ec, size_t length)
... the two arguments being the status of the completed I/O operation and
the number of bytes transferred. (In the case of the open method, the second
argument will be zero.)
Finally, the TCPSocket and UDPSocket classes provide the body of the
asynchronous operations.
Fetch Sequence
--------------
The fetch is implemented by the IOFetch class, which takes as argument the
protocol to use. The sequence is:
REENTER:
render the question into a wire-format query packet
open() // Open socket and optionally connect
if (! synchronous) {
YIELD;
}
YIELD asyncSend(query) // Send query
do {
YIELD asyncReceive(response) // Read response
} while (! complete(response))
close() // Drop connection and close socket
server->resume
The open() method opens a socket for use. On TCP, it also makes a
connection to the remote end. So under UDP the operation will complete
immediately, but under TCP it could take a long time. One solution would be
for the open operation to post an event to the I/O queue; then both cases
could be regarded as being equivalent, with the completion being signalled
by the posting of the completion event. However UDP is the most common case
and that would involve extra overhead. So the open() returns a status
indicating whether the operation completed asynchronously. If it did, the
code yields back to the coroutine; if not the yield is bypassed.
The asynchronous send is straightforward, invoking the underlying ASIO
function. (Note that the address/port is supplied to both the open() and
asyncSend() methods - it is used by the TCPSocket in open() and by the
UDPSocket in asyncSend().)
The asyncReceive() method issues an asynchronous read and waits for completion.
The fetch object keeps track of the amount of data received so far and when
the receive completes it calls a method on the socket to determine if the
entire message has been received. (This will always be the case for UDP. On
TCP though, the message is preceded by a count field as several reads may be
required to read all the data.) The fetch loops until all the data is read.
Finally, the socket is closed and the server called to resume operation.
......@@ -32,6 +32,7 @@
#include <asiolink/io_endpoint.h>
#include <asiolink/io_message.h>
#include <asiolink/io_socket.h>
#include <asiolink/io_error.h>
/// \namespace asiolink
/// \brief A wrapper interface for the ASIO library.
......@@ -83,20 +84,6 @@
/// the placeholder of callback handlers:
/// http://think-async.com/Asio/asio-1.3.1/doc/asio/reference/asio_handler_allocate.html
namespace asiolink {
/// \brief An exception that is thrown if an error occurs within the IO
/// module. This is mainly intended to be a wrapper exception class for
/// ASIO specific exceptions.
class IOError : public isc::Exception {
public:
IOError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
} // asiolink
#endif // __ASIOLINK_H
// Local Variables:
......
......@@ -12,21 +12,23 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h> // for some IPC/network system calls
#include <boost/lexical_cast.hpp>
#include <config.h>
// unistd is needed for asio.hpp with SunStudio
#include <unistd.h>
#include <log/dummylog.h>
#include <asio.hpp>
#include <asiolink/dns_service.h>
#include <asiolink/io_service.h>
#include <asiolink/io_service.h>
#include <asiolink/tcp_server.h>
#include <asiolink/udp_server.h>
#include <log/dummylog.h>
#include <boost/lexical_cast.hpp>
using isc::log::dlog;
namespace asiolink {
......
......@@ -97,6 +97,12 @@ public:
/// It will eventually be removed once the wrapper interface is
/// generalized.
asio::io_service& get_io_service() { return io_service_.get_io_service(); }
/// \brief Return the IO Service Object
///
/// \return IOService object for this DNS service.
asiolink::IOService& getIOService() { return (io_service_);}
private:
DNSServiceImpl* impl_;
IOService& io_service_;
......
// 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 __DUMMY_IO_CB_H
#define __DUMMY_IO_CB_H
#include <iostream>
#include <asio/error.hpp>
#include <asio/error_code.hpp>
namespace asiolink {
/// \brief Asynchronous I/O Completion Callback
///
/// The two socket classes (UDPSocket and TCPSocket) require that the I/O
/// completion callback function have an operator() method with the appropriate
/// signature. The classes are templates, any class with that method and
/// signature can be passed as the callback object - there is no need for a
/// base class defining the interface. However, some users of the socket
/// classes do not use the asynchronous I/O operations, yet have to supply a
/// template parameter. This is the reason for this class - it is the dummy
/// template parameter.
class DummyIOCallback {
public:
/// \brief Asynchronous I/O callback method
///
/// \param error Unused
/// \param length Unused
void operator()(asio::error_code, size_t)
{
// TODO: log an error if this method ever gets called.
}
};
} // namespace asiolink
#endif // __DUMMY_IO_CB_H
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
// 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
......@@ -12,26 +12,34 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef __UDP_QUERY_H
#define __UDP_QUERY_H 1
#ifndef __IOFETCH_H
#define __IOFETCH_H 1
#ifndef ASIO_HPP
#error "asio.hpp must be included before including this, see asiolink.h as to why"
#endif
#include <config.h>
#include <asio.hpp>
#include <boost/shared_array.hpp>
#include <boost/shared_ptr.hpp>
#include <dns/buffer.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
#include <asiolink/io_address.h>
#include <coroutine.h>
#include <asiolink/asiolink.h>
#include <asiolink/internal/coroutine.h>
namespace asiolink {
// This file contains TCP/UDP-specific implementations of generic classes
// defined in asiolink.h. It is *not* intended to be part of the public
// API.
namespace asiolink {
//
// Asynchronous UDP coroutine for upstream queries
// Asynchronous UDP/TCP coroutine for upstream fetches
//
class UDPQuery : public coroutine {
//class IOFetch : public coroutine, public UdpFetch, public TcpFetch {
class IOFetch : public coroutine {
public:
// TODO Maybe this should be more generic than just for UDPQuery?
// TODO Maybe this should be more generic than just for IOFetch?
///
/// \brief Result of the query
///
......@@ -43,12 +51,12 @@ public:
TIME_OUT,
STOPPED
};
/// Abstract callback for the UDPQuery.
/// Abstract callback for the IOFetch.
class Callback {
public:
virtual ~Callback() {}
/// This will be called when the UDPQuery is completed
/// This will be called when the IOFetch is completed
virtual void operator()(Result result) = 0;
};
///
......@@ -59,11 +67,12 @@ public:
/// delete it if allocated on heap.
///@param timeout in ms.
///
explicit UDPQuery(asio::io_service& io_service,
IOFetch(asio::io_service& io_service,
const isc::dns::Question& q,
const IOAddress& addr, uint16_t port,
isc::dns::OutputBufferPtr buffer,
Callback* callback, int timeout = -1);
Callback* callback, int timeout = -1,
int protocol = IPPROTO_UDP);
void operator()(asio::error_code ec = asio::error_code(),
size_t length = 0);
/// Terminate the query.
......@@ -80,9 +89,37 @@ private:
/// to many async_*() functions) and we want keep the same data. Some of
/// the data is not copyable too.
///
struct PrivateData;
boost::shared_ptr<PrivateData> data_;
//struct IOFetchProtocol;
//boost::shared_ptr<IOFetchProtocol> data_;
//struct UdpData;
//struct TcpData;
boost::shared_ptr<UdpFetch> data_;
boost::shared_ptr<TcpFetch> tcp_data_;
};
class UdpFetch : public IOFetch {
public:
struct UdpData;
explicit UdpFetch(asio::io_service& io_service,
const isc::dns::Question& q,
const IOAddress& addr,
uint16_t port,
isc::dns::OutputBufferPtr buffer,
IOFetch::Callback *callback,
int timeout);
};
class TcpFetch : public IOFetch {
public:
struct TcpData;
explicit TcpFetch(io_service& io_service, const Question& q,
const IOAddress& addr, uint16_t port,
OutputBufferPtr buffer, Callback *callback, int timeout);
};
}
#endif // __IOFETCH_H
} // namespace asiolink
#endif // __UDP_QUERY_H
// Local Variables:
// mode: c++
// End:
......@@ -14,18 +14,18 @@
#include <config.h>
// unistd is needed for asio.hpp with SunStudio
#include <unistd.h>
#include <unistd.h> // for some IPC/network system calls
#include <sys/socket.h>
#include <netinet/in.h>
#include <asio.hpp>
#include <boost/bind.hpp>
#include <exceptions/exceptions.h>
#include <asio.hpp>
#include <asiolink/interval_timer.h>
#include <asiolink/io_service.h>
#include <boost/bind.hpp>
namespace asiolink {
class IntervalTimerImpl {
......
......@@ -20,7 +20,10 @@
#include <asio.hpp>
#include <asiolink/asiolink.h>
#include <exceptions/exceptions.h>
#include <asiolink/io_address.h>
#include <asiolink/io_error.h>
using namespace asio;
using asio::ip::udp;
......
......@@ -12,8 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef __IOADDRESS_H
#define __IOADDRESS_H 1
#ifndef __IO_ADDRESS_H
#define __IO_ADDRESS_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.
......@@ -120,7 +120,7 @@ private:
};
} // asiolink
#endif // __IOADDRESS_H
#endif // __IO_ADDRESS_H
// Local Variables:
// mode: c++
......
// 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
///
/// 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.)
///
/// \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.
/// \param callback I/O Completion callback, called when the operation has
/// completed, but only if the operation was asynchronous.
///
/// \return true if an asynchronous operation was started and the caller
/// should yield and wait for completion, false if the operation was
/// completed synchronously and no callback was queued.
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.
///