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 ...@@ -12,24 +12,28 @@ CLEANFILES = *.gcno *.gcda
# have some code fragments that would hit gcc's unused-parameter warning, # have some code fragments that would hit gcc's unused-parameter warning,
# which would make the build fail with -Werror (our default setting). # which would make the build fail with -Werror (our default setting).
lib_LTLIBRARIES = libasiolink.la lib_LTLIBRARIES = libasiolink.la
libasiolink_la_SOURCES = asiolink.h 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 += dns_answer.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 += interval_timer.h interval_timer.cc
libasiolink_la_SOURCES += recursive_query.h recursive_query.cc libasiolink_la_SOURCES += io_address.h io_address.cc
libasiolink_la_SOURCES += io_socket.cc io_socket.h 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_message.h
libasiolink_la_SOURCES += io_address.cc io_address.h libasiolink_la_SOURCES += io_service.h io_service.cc
libasiolink_la_SOURCES += io_endpoint.cc io_endpoint.h libasiolink_la_SOURCES += io_socket.h io_socket.cc
libasiolink_la_SOURCES += udp_endpoint.h udp_socket.h libasiolink_la_SOURCES += recursive_query.h recursive_query.cc
libasiolink_la_SOURCES += udp_server.h udp_server.cc libasiolink_la_SOURCES += simple_callback.h
libasiolink_la_SOURCES += udp_query.h udp_query.cc libasiolink_la_SOURCES += tcp_endpoint.h
libasiolink_la_SOURCES += tcp_endpoint.h tcp_socket.h
libasiolink_la_SOURCES += tcp_server.h tcp_server.cc 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 # Note: the ordering matters: -Wno-... must follow -Wextra (defined in
# B10_CXXFLAGS) # B10_CXXFLAGS)
libasiolink_la_CXXFLAGS = $(AM_CXXFLAGS) libasiolink_la_CXXFLAGS = $(AM_CXXFLAGS)
......
...@@ -33,7 +33,7 @@ This is intended to simplify development a bit, since it allows the ...@@ -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 routines to be written in a straightfowrard step-step-step fashion rather
than as a complex chain of separate handler functions. 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 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 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 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: ...@@ -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 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 it will be necessary to write code to fall back to TCP when circumstances
require it. 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 @@ ...@@ -32,6 +32,7 @@
#include <asiolink/io_endpoint.h> #include <asiolink/io_endpoint.h>
#include <asiolink/io_message.h> #include <asiolink/io_message.h>
#include <asiolink/io_socket.h> #include <asiolink/io_socket.h>
#include <asiolink/io_error.h>
/// \namespace asiolink /// \namespace asiolink
/// \brief A wrapper interface for the ASIO library. /// \brief A wrapper interface for the ASIO library.
...@@ -83,20 +84,6 @@ ...@@ -83,20 +84,6 @@
/// the placeholder of callback handlers: /// the placeholder of callback handlers:
/// http://think-async.com/Asio/asio-1.3.1/doc/asio/reference/asio_handler_allocate.html /// 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 #endif // __ASIOLINK_H
// Local Variables: // Local Variables:
......
...@@ -12,21 +12,23 @@ ...@@ -12,21 +12,23 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE. // 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> #include <config.h>
// unistd is needed for asio.hpp with SunStudio #include <log/dummylog.h>
#include <unistd.h>
#include <asio.hpp> #include <asio.hpp>
#include <asiolink/dns_service.h>
#include <asiolink/io_service.h>
#include <asiolink/io_service.h> #include <asiolink/io_service.h>
#include <asiolink/tcp_server.h> #include <asiolink/tcp_server.h>
#include <asiolink/udp_server.h> #include <asiolink/udp_server.h>
#include <log/dummylog.h>
#include <boost/lexical_cast.hpp>
using isc::log::dlog; using isc::log::dlog;
namespace asiolink { namespace asiolink {
......
...@@ -97,6 +97,12 @@ public: ...@@ -97,6 +97,12 @@ public:
/// It will eventually be removed once the wrapper interface is /// It will eventually be removed once the wrapper interface is
/// generalized. /// generalized.
asio::io_service& get_io_service() { return io_service_.get_io_service(); } 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: private:
DNSServiceImpl* impl_; DNSServiceImpl* impl_;
IOService& io_service_; 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 // Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above // purpose with or without fee is hereby granted, provided that the above
...@@ -12,26 +12,34 @@ ...@@ -12,26 +12,34 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE. // PERFORMANCE OF THIS SOFTWARE.
#ifndef __UDP_QUERY_H #ifndef __IOFETCH_H
#define __UDP_QUERY_H 1 #define __IOFETCH_H 1
#ifndef ASIO_HPP #include <config.h>
#error "asio.hpp must be included before including this, see asiolink.h as to why"
#endif #include <asio.hpp>
#include <boost/shared_array.hpp>
#include <boost/shared_ptr.hpp>
#include <dns/buffer.h> #include <dns/buffer.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
#include <asiolink/io_address.h> #include <asiolink/asiolink.h>
#include <coroutine.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: 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 /// \brief Result of the query
/// ///
...@@ -43,12 +51,12 @@ public: ...@@ -43,12 +51,12 @@ public:
TIME_OUT, TIME_OUT,
STOPPED STOPPED
}; };
/// Abstract callback for the UDPQuery. /// Abstract callback for the IOFetch.
class Callback { class Callback {
public: public:
virtual ~Callback() {} 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; virtual void operator()(Result result) = 0;
}; };
/// ///
...@@ -59,11 +67,12 @@ public: ...@@ -59,11 +67,12 @@ public:
/// delete it if allocated on heap. /// delete it if allocated on heap.
///@param timeout in ms. ///@param timeout in ms.
/// ///
explicit UDPQuery(asio::io_service& io_service, IOFetch(asio::io_service& io_service,
const isc::dns::Question& q, const isc::dns::Question& q,
const IOAddress& addr, uint16_t port, const IOAddress& addr, uint16_t port,
isc::dns::OutputBufferPtr buffer, 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(), void operator()(asio::error_code ec = asio::error_code(),
size_t length = 0); size_t length = 0);
/// Terminate the query. /// Terminate the query.
...@@ -80,9 +89,37 @@ private: ...@@ -80,9 +89,37 @@ private:
/// to many async_*() functions) and we want keep the same data. Some of /// to many async_*() functions) and we want keep the same data. Some of
/// the data is not copyable too. /// the data is not copyable too.
/// ///
struct PrivateData; //struct IOFetchProtocol;
boost::shared_ptr<PrivateData> data_; //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 // Local Variables:
#endif // __UDP_QUERY_H // mode: c++
// End:
...@@ -14,18 +14,18 @@ ...@@ -14,18 +14,18 @@
#include <config.h> #include <config.h>
// unistd is needed for asio.hpp with SunStudio #include <unistd.h> // for some IPC/network system calls
#include <unistd.h> #include <sys/socket.h>
#include <netinet/in.h>
#include <asio.hpp> #include <boost/bind.hpp>
#include <exceptions/exceptions.h> #include <exceptions/exceptions.h>
#include <asio.hpp>
#include <asiolink/interval_timer.h> #include <asiolink/interval_timer.h>
#include <asiolink/io_service.h> #include <asiolink/io_service.h>
#include <boost/bind.hpp>
namespace asiolink { namespace asiolink {
class IntervalTimerImpl { class IntervalTimerImpl {
......
...@@ -20,7 +20,10 @@ ...@@ -20,7 +20,10 @@
#include <asio.hpp> #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 namespace asio;
using asio::ip::udp; using asio::ip::udp;
......
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE. // PERFORMANCE OF THIS SOFTWARE.
#ifndef __IOADDRESS_H #ifndef __IO_ADDRESS_H
#define __IOADDRESS_H 1 #define __IO_ADDRESS_H 1
// IMPORTANT NOTE: only very few ASIO headers files can be included in // IMPORTANT NOTE: only very few ASIO headers files can be included in
// this file. In particular, asio.hpp should never be included here. // this file. In particular, asio.hpp should never be included here.
...@@ -120,7 +120,7 @@ private: ...@@ -120,7 +120,7 @@ private:
}; };
} // asiolink } // asiolink
#endif // __IOADDRESS_H #endif // __IO_ADDRESS_H
// Local Variables: // Local Variables:
// mode: c++ // 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;