mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-29 04:57:52 +00:00
[trac554] Added IOFetch
IOFetch is a general upstream "fetch" class that should be able to operate over TCP or UDP. Related changes have been made in the associated classes. So far, only the unit tests for a UDP fetch have been made (and passed).
This commit is contained in:
parent
ad418dc785
commit
85b6fa72d6
370
INSTALL
370
INSTALL
@ -1,9 +1,365 @@
|
||||
To build "configure" file:
|
||||
autoreconf
|
||||
Installation Instructions
|
||||
*************************
|
||||
|
||||
To then build from source:
|
||||
./configure
|
||||
make
|
||||
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
|
||||
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. This file is offered as-is,
|
||||
without warranty of any kind.
|
||||
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
Briefly, the shell commands `./configure; make; make install' should
|
||||
configure, build, and install this package. The following
|
||||
more-detailed instructions are generic; see the `README' file for
|
||||
instructions specific to this package. Some packages provide this
|
||||
`INSTALL' file but do not implement all of the features documented
|
||||
below. The lack of an optional feature in a given package is not
|
||||
necessarily a bug. More recommendations for GNU packages can be found
|
||||
in *note Makefile Conventions: (standards)Makefile Conventions.
|
||||
|
||||
The `configure' shell script attempts to guess correct values for
|
||||
various system-dependent variables used during compilation. It uses
|
||||
those values to create a `Makefile' in each directory of the package.
|
||||
It may also create one or more `.h' files containing system-dependent
|
||||
definitions. Finally, it creates a shell script `config.status' that
|
||||
you can run in the future to recreate the current configuration, and a
|
||||
file `config.log' containing compiler output (useful mainly for
|
||||
debugging `configure').
|
||||
|
||||
It can also use an optional file (typically called `config.cache'
|
||||
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
||||
the results of its tests to speed up reconfiguring. Caching is
|
||||
disabled by default to prevent problems with accidental use of stale
|
||||
cache files.
|
||||
|
||||
If you need to do unusual things to compile the package, please try
|
||||
to figure out how `configure' could check whether to do them, and mail
|
||||
diffs or instructions to the address given in the `README' so they can
|
||||
be considered for the next release. If you are using the cache, and at
|
||||
some point `config.cache' contains results you don't want to keep, you
|
||||
may remove or edit it.
|
||||
|
||||
The file `configure.ac' (or `configure.in') is used to create
|
||||
`configure' by a program called `autoconf'. You need `configure.ac' if
|
||||
you want to change it or regenerate `configure' using a newer version
|
||||
of `autoconf'.
|
||||
|
||||
The simplest way to compile this package is:
|
||||
|
||||
1. `cd' to the directory containing the package's source code and type
|
||||
`./configure' to configure the package for your system.
|
||||
|
||||
Running `configure' might take a while. While running, it prints
|
||||
some messages telling which features it is checking for.
|
||||
|
||||
2. Type `make' to compile the package.
|
||||
|
||||
3. Optionally, type `make check' to run any self-tests that come with
|
||||
the package, generally using the just-built uninstalled binaries.
|
||||
|
||||
4. Type `make install' to install the programs and any data files and
|
||||
documentation. When installing into a prefix owned by root, it is
|
||||
recommended that the package be configured and built as a regular
|
||||
user, and only the `make install' phase executed with root
|
||||
privileges.
|
||||
|
||||
5. Optionally, type `make installcheck' to repeat any self-tests, but
|
||||
this time using the binaries in their final installed location.
|
||||
This target does not install anything. Running this target as a
|
||||
regular user, particularly if the prior `make install' required
|
||||
root privileges, verifies that the installation completed
|
||||
correctly.
|
||||
|
||||
6. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'. To also remove the
|
||||
files that `configure' created (so you can compile the package for
|
||||
a different kind of computer), type `make distclean'. There is
|
||||
also a `make maintainer-clean' target, but that is intended mainly
|
||||
for the package's developers. If you use it, you may have to get
|
||||
all sorts of other programs in order to regenerate files that came
|
||||
with the distribution.
|
||||
|
||||
7. Often, you can also type `make uninstall' to remove the installed
|
||||
files again. In practice, not all packages have tested that
|
||||
uninstallation works correctly, even though it is required by the
|
||||
GNU Coding Standards.
|
||||
|
||||
8. Some packages, particularly those that use Automake, provide `make
|
||||
distcheck', which can by used by developers to test that all other
|
||||
targets like `make install' and `make uninstall' work correctly.
|
||||
This target is generally not run by end users.
|
||||
|
||||
Compilers and Options
|
||||
=====================
|
||||
|
||||
Some systems require unusual options for compilation or linking that
|
||||
the `configure' script does not know about. Run `./configure --help'
|
||||
for details on some of the pertinent environment variables.
|
||||
|
||||
You can give `configure' initial values for configuration parameters
|
||||
by setting variables in the command line or in the environment. Here
|
||||
is an example:
|
||||
|
||||
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
||||
|
||||
*Note Defining Variables::, for more details.
|
||||
|
||||
Compiling For Multiple Architectures
|
||||
====================================
|
||||
|
||||
You can compile the package for more than one kind of computer at the
|
||||
same time, by placing the object files for each architecture in their
|
||||
own directory. To do this, you can use GNU `make'. `cd' to the
|
||||
directory where you want the object files and executables to go and run
|
||||
the `configure' script. `configure' automatically checks for the
|
||||
source code in the directory that `configure' is in and in `..'. This
|
||||
is known as a "VPATH" build.
|
||||
|
||||
With a non-GNU `make', it is safer to compile the package for one
|
||||
architecture at a time in the source code directory. After you have
|
||||
installed the package for one architecture, use `make distclean' before
|
||||
reconfiguring for another architecture.
|
||||
|
||||
On MacOS X 10.5 and later systems, you can create libraries and
|
||||
executables that work on multiple system types--known as "fat" or
|
||||
"universal" binaries--by specifying multiple `-arch' options to the
|
||||
compiler but only a single `-arch' option to the preprocessor. Like
|
||||
this:
|
||||
|
||||
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||
CPP="gcc -E" CXXCPP="g++ -E"
|
||||
|
||||
This is not guaranteed to produce working output in all cases, you
|
||||
may have to build one architecture at a time and combine the results
|
||||
using the `lipo' tool if you have problems.
|
||||
|
||||
Installation Names
|
||||
==================
|
||||
|
||||
By default, `make install' installs the package's commands under
|
||||
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
||||
can specify an installation prefix other than `/usr/local' by giving
|
||||
`configure' the option `--prefix=PREFIX', where PREFIX must be an
|
||||
absolute file name.
|
||||
|
||||
You can specify separate installation prefixes for
|
||||
architecture-specific files and architecture-independent files. If you
|
||||
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
||||
PREFIX as the prefix for installing programs and libraries.
|
||||
Documentation and other data files still use the regular prefix.
|
||||
|
||||
In addition, if you use an unusual directory layout you can give
|
||||
options like `--bindir=DIR' to specify different values for particular
|
||||
kinds of files. Run `configure --help' for a list of the directories
|
||||
you can set and what kinds of files go in them. In general, the
|
||||
default for these options is expressed in terms of `${prefix}', so that
|
||||
specifying just `--prefix' will affect all of the other directory
|
||||
specifications that were not explicitly provided.
|
||||
|
||||
The most portable way to affect installation locations is to pass the
|
||||
correct locations to `configure'; however, many packages provide one or
|
||||
both of the following shortcuts of passing variable assignments to the
|
||||
`make install' command line to change installation locations without
|
||||
having to reconfigure or recompile.
|
||||
|
||||
The first method involves providing an override variable for each
|
||||
affected directory. For example, `make install
|
||||
prefix=/alternate/directory' will choose an alternate location for all
|
||||
directory configuration variables that were expressed in terms of
|
||||
`${prefix}'. Any directories that were specified during `configure',
|
||||
but not in terms of `${prefix}', must each be overridden at install
|
||||
time for the entire installation to be relocated. The approach of
|
||||
makefile variable overrides for each directory variable is required by
|
||||
the GNU Coding Standards, and ideally causes no recompilation.
|
||||
However, some platforms have known limitations with the semantics of
|
||||
shared libraries that end up requiring recompilation when using this
|
||||
method, particularly noticeable in packages that use GNU Libtool.
|
||||
|
||||
The second method involves providing the `DESTDIR' variable. For
|
||||
example, `make install DESTDIR=/alternate/directory' will prepend
|
||||
`/alternate/directory' before all installation names. The approach of
|
||||
`DESTDIR' overrides is not required by the GNU Coding Standards, and
|
||||
does not work on platforms that have drive letters. On the other hand,
|
||||
it does better at avoiding recompilation issues, and works well even
|
||||
when some directory options were not specified in terms of `${prefix}'
|
||||
at `configure' time.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
If the package supports it, you can cause programs to be installed
|
||||
with an extra prefix or suffix on their names by giving `configure' the
|
||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
`README' should mention any `--enable-' and `--with-' options that the
|
||||
package recognizes.
|
||||
|
||||
For packages that use the X Window System, `configure' can usually
|
||||
find the X include and library files automatically, but if it doesn't,
|
||||
you can use the `configure' options `--x-includes=DIR' and
|
||||
`--x-libraries=DIR' to specify their locations.
|
||||
|
||||
Some packages offer the ability to configure how verbose the
|
||||
execution of `make' will be. For these packages, running `./configure
|
||||
--enable-silent-rules' sets the default to minimal output, which can be
|
||||
overridden with `make V=1'; while running `./configure
|
||||
--disable-silent-rules' sets the default to verbose, which can be
|
||||
overridden with `make V=0'.
|
||||
|
||||
Particular systems
|
||||
==================
|
||||
|
||||
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
|
||||
CC is not installed, it is recommended to use the following options in
|
||||
order to use an ANSI C compiler:
|
||||
|
||||
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
|
||||
|
||||
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
|
||||
|
||||
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
|
||||
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
|
||||
a workaround. If GNU CC is not installed, it is therefore recommended
|
||||
to try
|
||||
|
||||
./configure CC="cc"
|
||||
|
||||
and if that doesn't work, try
|
||||
|
||||
./configure CC="cc -nodtk"
|
||||
|
||||
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
|
||||
directory contains several dysfunctional programs; working variants of
|
||||
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
|
||||
in your `PATH', put it _after_ `/usr/bin'.
|
||||
|
||||
On Haiku, software installed for all users goes in `/boot/common',
|
||||
not `/usr/local'. It is recommended to use the following options:
|
||||
|
||||
./configure --prefix=/boot/common
|
||||
|
||||
Specifying the System Type
|
||||
==========================
|
||||
|
||||
There may be some features `configure' cannot figure out
|
||||
automatically, but needs to determine by the type of machine the package
|
||||
will run on. Usually, assuming the package is built to be run on the
|
||||
_same_ architectures, `configure' can figure that out, but if it prints
|
||||
a message saying it cannot guess the machine type, give it the
|
||||
`--build=TYPE' option. TYPE can either be a short name for the system
|
||||
type, such as `sun4', or a canonical name which has the form:
|
||||
|
||||
CPU-COMPANY-SYSTEM
|
||||
|
||||
where SYSTEM can have one of these forms:
|
||||
|
||||
OS
|
||||
KERNEL-OS
|
||||
|
||||
See the file `config.sub' for the possible values of each field. If
|
||||
`config.sub' isn't included in this package, then this package doesn't
|
||||
need to know the machine type.
|
||||
|
||||
If you are _building_ compiler tools for cross-compiling, you should
|
||||
use the option `--target=TYPE' to select the type of system they will
|
||||
produce code for.
|
||||
|
||||
If you want to _use_ a cross compiler, that generates code for a
|
||||
platform different from the build platform, you should specify the
|
||||
"host" platform (i.e., that on which the generated programs will
|
||||
eventually be run) with `--host=TYPE'.
|
||||
|
||||
Sharing Defaults
|
||||
================
|
||||
|
||||
If you want to set default values for `configure' scripts to share,
|
||||
you can create a site shell script called `config.site' that gives
|
||||
default values for variables like `CC', `cache_file', and `prefix'.
|
||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||
`CONFIG_SITE' environment variable to the location of the site script.
|
||||
A warning: not all `configure' scripts look for a site script.
|
||||
|
||||
Defining Variables
|
||||
==================
|
||||
|
||||
Variables not defined in a site shell script can be set in the
|
||||
environment passed to `configure'. However, some packages may run
|
||||
configure again during the build, and the customized values of these
|
||||
variables may be lost. In order to avoid this problem, you should set
|
||||
them in the `configure' command line, using `VAR=value'. For example:
|
||||
|
||||
./configure CC=/usr/local2/bin/gcc
|
||||
|
||||
causes the specified `gcc' to be used as the C compiler (unless it is
|
||||
overridden in the site shell script).
|
||||
|
||||
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
||||
an Autoconf bug. Until the bug is fixed you can use this workaround:
|
||||
|
||||
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||
|
||||
`configure' Invocation
|
||||
======================
|
||||
|
||||
`configure' recognizes the following options to control how it
|
||||
operates.
|
||||
|
||||
`--help'
|
||||
`-h'
|
||||
Print a summary of all of the options to `configure', and exit.
|
||||
|
||||
`--help=short'
|
||||
`--help=recursive'
|
||||
Print a summary of the options unique to this package's
|
||||
`configure', and exit. The `short' variant lists options used
|
||||
only in the top level, while the `recursive' variant lists options
|
||||
also present in any nested packages.
|
||||
|
||||
`--version'
|
||||
`-V'
|
||||
Print the version of Autoconf used to generate the `configure'
|
||||
script, and exit.
|
||||
|
||||
`--cache-file=FILE'
|
||||
Enable the cache: use and save the results of the tests in FILE,
|
||||
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
||||
disable caching.
|
||||
|
||||
`--config-cache'
|
||||
`-C'
|
||||
Alias for `--cache-file=config.cache'.
|
||||
|
||||
`--quiet'
|
||||
`--silent'
|
||||
`-q'
|
||||
Do not print messages saying which checks are being made. To
|
||||
suppress all normal output, redirect it to `/dev/null' (any error
|
||||
messages will still be shown).
|
||||
|
||||
`--srcdir=DIR'
|
||||
Look for the package's source code in directory DIR. Usually
|
||||
`configure' can determine that directory automatically.
|
||||
|
||||
`--prefix=DIR'
|
||||
Use DIR as the installation prefix. *note Installation Names::
|
||||
for more details, including other options available for fine-tuning
|
||||
the installation locations.
|
||||
|
||||
`--no-create'
|
||||
`-n'
|
||||
Run the configure checks, but stop before creating any output
|
||||
files.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options. Run
|
||||
`configure --help' for more details.
|
||||
|
||||
For detailed installation directions, see the guide
|
||||
at doc/guide/bind10-guide.html.
|
||||
|
@ -12,26 +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 += 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_endpoint.h io_endpoint.cc
|
||||
libasiolink_la_SOURCES += io_error.h
|
||||
libasiolink_la_SOURCES += io_socket.cc io_socket.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
|
||||
libasiolink_la_SOURCES += udp_server.h udp_server.cc
|
||||
libasiolink_la_SOURCES += udp_socket.h udp_socket.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_query.h udp_query.cc
|
||||
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)
|
||||
|
@ -34,9 +34,6 @@
|
||||
#include <asiolink/io_socket.h>
|
||||
#include <asiolink/io_error.h>
|
||||
|
||||
#include <asiolink/udp_endpoint.h>
|
||||
#include <asiolink/udp_socket.h>
|
||||
|
||||
/// \namespace asiolink
|
||||
/// \brief A wrapper interface for the ASIO library.
|
||||
///
|
||||
|
@ -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++
|
||||
|
304
src/lib/asiolink/io_asio_socket.h
Normal file
304
src/lib/asiolink/io_asio_socket.h
Normal file
@ -0,0 +1,304 @@
|
||||
// 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>
|
||||
#include <asiolink/io_completion_cb.h>
|
||||
|
||||
using namespace asio;
|
||||
|
||||
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
|
||||
///
|
||||
/// A call that is a no-op on UDP sockets, this opens a connection to the
|
||||
/// system identified by the given endpoint.
|
||||
///
|
||||
/// \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 connect has
|
||||
/// completed. In the stackless coroutines model, this will be the
|
||||
/// method containing the call to this function, allowing the operation to
|
||||
/// resume after the socket open has completed.
|
||||
///
|
||||
/// \return true if an asynchronous operation was started and the caller
|
||||
/// should yield and wait for completion, false if not. (i.e. The UDP
|
||||
/// derived class will return false, the TCP class will return true). This
|
||||
/// optimisation avoids the overhead required to post a callback to the
|
||||
/// I/O Service queue where nothing is done.
|
||||
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
|
@ -15,74 +15,33 @@
|
||||
#ifndef __IO_COMPLETION_CB_H
|
||||
#define __IO_COMPLETION_CB_H
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <asio/error.hpp>
|
||||
#include <asio/error_code.hpp>
|
||||
#include <coroutine.h>
|
||||
|
||||
namespace asiolink {
|
||||
|
||||
/// \brief Asynchronous I/O Completion Callback
|
||||
///
|
||||
/// The stackless coroutine code requires that there be an "entry function"
|
||||
/// containing the coroutine macros. When the coroutine yields, its state is
|
||||
/// stored and when the "entry function" is called again, it jumps to the
|
||||
/// line when processing left off last time. In BIND-10, that "entry function"
|
||||
/// is the Boost asynchronous I/O completion callback - in essence operator().
|
||||
///
|
||||
/// This class solves the problem of circularity in class definitions. In
|
||||
/// BIND10, classes such as IOFetch contain the coroutine code. These include
|
||||
/// objects of classes such as IOSocket, whose signature has to include the
|
||||
/// callback object - IOFetch. By abstracting the I/O completion callback into
|
||||
/// this class, that circularity is broken.
|
||||
///
|
||||
/// One more point: the asynchronous I/O functions take the callback object by
|
||||
/// reference. But if a derived class object is passed as a reference to its
|
||||
/// base class, "class slicing" takes place - the derived part of the class is
|
||||
/// lost and only the base class functionality remains. By storing a pointer
|
||||
/// to the true object and having the base class method call the derived class
|
||||
/// method through that, the behaviour of class inheritance is restored. In
|
||||
/// other words:
|
||||
/// \code
|
||||
/// class derived: public class base {
|
||||
/// :
|
||||
/// };
|
||||
/// derived drv;
|
||||
///
|
||||
/// // Call with pointer to base class
|
||||
/// void f(base* b, asio::error_code& ec, size_t length) {
|
||||
/// b->operator()(ec, length);
|
||||
/// }
|
||||
///
|
||||
/// // Call with reference to base class
|
||||
/// void g(base& b, asio::error_code& ec, size_t length) {
|
||||
/// b.operator()(ec, length);
|
||||
/// }
|
||||
///
|
||||
/// void function xyz(derived *d, asio::error_code& ec, size_t length) {
|
||||
/// f(d, ec, length); // Calls derived::operator()
|
||||
/// g(*d, ec, length); // Also calls derived::operator()
|
||||
/// }
|
||||
/// \endcode
|
||||
/// 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 IOCompletionCallback : public coroutine {
|
||||
class IOCompletionCallback {
|
||||
public:
|
||||
|
||||
/// \brief Constructor
|
||||
IOCompletionCallback() : self_(this)
|
||||
/// \brief Asynchronous I/O callback method
|
||||
///
|
||||
/// \param error Unused
|
||||
/// \param length Unused
|
||||
void operator()(asio::error_code, size_t)
|
||||
{}
|
||||
|
||||
/// \brief Virtual Destructor
|
||||
virtual ~IOCompletionCallback()
|
||||
{}
|
||||
|
||||
/// \brief Callback Method
|
||||
virtual void operator()(asio::error_code ec = asio::error_code(),
|
||||
size_t length = 0) {
|
||||
(*self_)(ec, length);
|
||||
}
|
||||
|
||||
private:
|
||||
IOCompletionCallback* self_; ///< Pointer to real object
|
||||
};
|
||||
|
||||
} // namespace asiolink
|
||||
|
@ -20,7 +20,8 @@
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <asiolink/asiolink.h>
|
||||
#include <asiolink/io_address.h>
|
||||
#include <asiolink/io_error.h>
|
||||
#include <asiolink/tcp_endpoint.h>
|
||||
#include <asiolink/udp_endpoint.h>
|
||||
|
||||
|
@ -12,8 +12,8 @@
|
||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#ifndef __IOENDPOINT_H
|
||||
#define __IOENDPOINT_H 1
|
||||
#ifndef __IO_ENDPOINT_H
|
||||
#define __IO_ENDPOINT_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.
|
||||
@ -115,7 +115,7 @@ public:
|
||||
};
|
||||
|
||||
} // asiolink
|
||||
#endif // __IOENDPOINT_H
|
||||
#endif // __IO_ENDPOINT_H
|
||||
|
||||
// Local Variables:
|
||||
// mode: c++
|
||||
|
@ -18,95 +18,38 @@
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <asio/deadline_timer.hpp>
|
||||
#include <asio/ip/address.hpp>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
|
||||
#include <dns/buffer.h>
|
||||
#include <dns/message.h>
|
||||
#include <dns/messagerenderer.h>
|
||||
#include <log/dummylog.h>
|
||||
#include <dns/opcode.h>
|
||||
#include <dns/rcode.h>
|
||||
#include <log/dummylog.h>
|
||||
|
||||
#include <asiolink.h>
|
||||
#include <coroutine.h>
|
||||
#include <internal/udpdns.h>
|
||||
#include <internal/tcpdns.h>
|
||||
#include <internal/iofetch.h>
|
||||
#include <asio.hpp>
|
||||
#include <asiolink/io_fetch.h>
|
||||
|
||||
using namespace asio;
|
||||
using asio::ip::udp;
|
||||
using asio::ip::tcp;
|
||||
using isc::log::dlog;
|
||||
|
||||
using namespace std;
|
||||
using namespace isc::dns;
|
||||
using namespace isc::log;
|
||||
using namespace std;
|
||||
|
||||
namespace asiolink {
|
||||
|
||||
// Constructor for the IOFetchData member
|
||||
|
||||
/// \brief Constructor
|
||||
///
|
||||
/// Just fills in the data members of the IOFetchData structure
|
||||
///
|
||||
/// \param io_service I/O Service object to handle the asynchronous
|
||||
/// operations.
|
||||
/// \param question DNS question to send to the upstream server.
|
||||
/// \param address IP address of upstream server
|
||||
/// \param port Port to use for the query
|
||||
/// \param buffer Output buffer into which the response (in wire format)
|
||||
/// is written (if a response is received).
|
||||
/// \param callback Callback object containing the callback to be called
|
||||
/// when we terminate. The caller is responsible for managing this
|
||||
/// object and deleting it if necessary.
|
||||
/// \param timeout Timeout for the fetch (in ms). The default value of
|
||||
/// -1 indicates no timeout.
|
||||
/// \param protocol Protocol to use for the fetch. The default is UDP
|
||||
|
||||
IOFetch::IOFetchData::IOFetchData(IOService& io_service,
|
||||
const isc::dns::Question& query, const IOAddress& address, uint16_t port,
|
||||
isc::dns::OutputBufferPtr buff, Callback* cb, int wait, int protocol)
|
||||
:
|
||||
socket((protocol == IPPROTO_UDP) ?
|
||||
static_cast<IOSocket*>(new UDPSocket(io_service, address)) :
|
||||
static_cast<IOSocket*>(new TCPSocket(io_service, address))
|
||||
),
|
||||
remote((protocol == IPPROTO_UDP) ?
|
||||
static_cast<IOEndpoint*>(new UDPEndpoint(address, port)) :
|
||||
static_cast<IOEndpoint*>(new TCPEndpoint(address, port))
|
||||
),
|
||||
question(query),
|
||||
buffer(buff),
|
||||
msgbuf(new OutputBuffer(512)), // TODO: Why this number?
|
||||
data(new char[IOFetch::MAX_LENGTH]),
|
||||
callback(cb),
|
||||
rcv_amount(0),
|
||||
stopped(false),
|
||||
timer(io_service.get_io_service()),
|
||||
timeout(wait)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// IOFetch Constructor - just initialize the private data
|
||||
IOFetch::IOFetch(IOService& io_service, const Question& question,
|
||||
const IOAddress& address, uint16_t port, OutputBufferPtr buffer,
|
||||
Callback *callback, int timeout, int protocol) :
|
||||
data_(new IOFetch::IOFetchData(io_service, question, address, port,
|
||||
buffer, callback, timeout, protocol)
|
||||
)
|
||||
|
||||
IOFetch::IOFetch(int protocol, IOService& service,
|
||||
const isc::dns::Question& question, const IOAddress& address, uint16_t port,
|
||||
isc::dns::OutputBufferPtr& buff, Callback* cb, int wait)
|
||||
:
|
||||
data_(new IOFetch::IOFetchData(protocol, service, question, address,
|
||||
port, buff, cb, wait))
|
||||
{
|
||||
}
|
||||
|
||||
/// The function operator is implemented with the "stackless coroutine"
|
||||
/// pattern; see internal/coroutine.h for details.
|
||||
|
||||
void
|
||||
IOFetch::operator()(error_code ec, size_t length) {
|
||||
if (ec || data_->stopped) {
|
||||
@ -114,6 +57,7 @@ IOFetch::operator()(error_code ec, size_t length) {
|
||||
}
|
||||
|
||||
CORO_REENTER (this) {
|
||||
|
||||
/// Generate the upstream query and render it to wire format
|
||||
/// This is done in a different scope to allow inline variable
|
||||
/// declarations.
|
||||
@ -130,7 +74,7 @@ IOFetch::operator()(error_code ec, size_t length) {
|
||||
msg.toWire(renderer);
|
||||
|
||||
// As this is a new fetch, clear the amount of data received
|
||||
data_->rcv_amount = 0;
|
||||
data_->cumulative = 0;
|
||||
|
||||
dlog("Sending " + msg.toText() + " to " +
|
||||
data_->remote->getAddress().toText());
|
||||
@ -148,22 +92,30 @@ IOFetch::operator()(error_code ec, size_t length) {
|
||||
|
||||
// Open a connection to the target system. For speed, if the operation
|
||||
// was completed synchronously (i.e. UDP operation) we bypass the yield.
|
||||
bool asynch = data_->socket->open(data->remote.get(), *this);
|
||||
if (asynch) {
|
||||
if (data_->socket->open(data_->remote.get(), *this)) {
|
||||
CORO_YIELD;
|
||||
}
|
||||
|
||||
// Begin an asynchronous send, and then yield. When the
|
||||
// Begin an asynchronous send, and then yield. When the send completes
|
||||
// send completes, we will resume immediately after this point.
|
||||
CORO_YIELD data_->socket->async_send(data_->msgbuf->getData(),
|
||||
CORO_YIELD data_->socket->asyncSend(data_->msgbuf->getData(),
|
||||
data_->msgbuf->getLength(), data_->remote.get(), *this);
|
||||
|
||||
/// Begin an asynchronous receive, and yield. When the receive
|
||||
/// completes, we will resume immediately after this point.
|
||||
CORO_YIELD data_->socket->async_receive(data_->data.get(),
|
||||
static_cast<size_t>(MAX_LENGTH), data_->remote.get(), *this);
|
||||
// Now receive the response. Since TCP may not receive the entire
|
||||
// message in one operation, we need to loop until we have received
|
||||
// it. (This can't be done within the asyncReceive() method because
|
||||
// each I/O operation will be done asynchronously and between each one
|
||||
// we need to yield ... and we *really* don't want to set up another
|
||||
// coroutine within that method.) So after each receive (and yield),
|
||||
// we check if the operation is complete and if not, loop to read again.
|
||||
do {
|
||||
CORO_YIELD data_->socket->asyncReceive(data_->data.get(),
|
||||
static_cast<size_t>(MAX_LENGTH), data_->cumulative,
|
||||
data_->remote.get(), *this);
|
||||
} while (!data_->socket->receiveComplete(data_->data.get(), length,
|
||||
data_->cumulative));
|
||||
|
||||
// The message is not rendered yet, so we can't print it easilly
|
||||
// The message is not rendered yet, so we can't print it easily
|
||||
dlog("Received response from " + data_->remote->getAddress().toText());
|
||||
|
||||
/// Copy the answer into the response buffer. (TODO: If the
|
||||
@ -188,6 +140,7 @@ IOFetch::operator()(error_code ec, size_t length) {
|
||||
// As the function may be entered multiple times as things wind down, the
|
||||
// stopped_ flag checks if stop() has already been called. If it has,
|
||||
// subsequent calls are no-ops.
|
||||
|
||||
void
|
||||
IOFetch::stop(Result result) {
|
||||
if (!data_->stopped) {
|
||||
@ -203,15 +156,23 @@ IOFetch::stop(Result result) {
|
||||
default:
|
||||
;
|
||||
}
|
||||
data_->stopped = true;
|
||||
data_->socket->cancel(); // Cancel outstanding I/O
|
||||
data_->socket->close(); // ... and close the socket
|
||||
data_->timer.cancel(); // Cancel timeout timer
|
||||
|
||||
// Stop requested, cancel and I/O's on the socket and shut it down,
|
||||
// and cancel the timer.
|
||||
data_->socket->cancel();
|
||||
data_->socket->close();
|
||||
|
||||
data_->timer.cancel();
|
||||
|
||||
// Execute the I/O completion callback (if present).
|
||||
if (data_->callback) {
|
||||
(*(data_->callback))(result); // Call callback
|
||||
(*(data_->callback))(result);
|
||||
}
|
||||
|
||||
// Mark that stop() has now been called.
|
||||
data_->stopped = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace asiolink
|
||||
|
||||
|
@ -12,40 +12,44 @@
|
||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#ifndef __IOFETCH_H
|
||||
#define __IOFETCH_H 1
|
||||
|
||||
#include <netinet/in.h>
|
||||
#ifndef __IO_FETCH_H
|
||||
#define __IO_FETCH_H 1
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h> // for some IPC/network system calls
|
||||
|
||||
#include <boost/shared_array.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <asio/deadline_timer.hpp>
|
||||
|
||||
#include <coroutine.h>
|
||||
|
||||
#include <dns/buffer.h>
|
||||
#include <dns/question.h>
|
||||
|
||||
#include <asiolink/asiolink.h>
|
||||
#include <asiolink/ioaddress.h>
|
||||
#include <asiolink/iocompletioncb.h>
|
||||
#include <asiolink/iocompletioncb.h>
|
||||
|
||||
|
||||
#include <asiolink/iosocket.h>
|
||||
#include <asiolink/ioendpoint.h>
|
||||
#include <coroutine.h>
|
||||
|
||||
#include <asiolink/io_asio_socket.h>
|
||||
#include <asiolink/io_endpoint.h>
|
||||
#include <asiolink/io_service.h>
|
||||
#include <asiolink/tcp_socket.h>
|
||||
#include <asiolink/tcp_endpoint.h>
|
||||
#include <asiolink/udp_socket.h>
|
||||
#include <asiolink/udp_endpoint.h>
|
||||
|
||||
|
||||
namespace asiolink {
|
||||
|
||||
|
||||
/// \brief Upstream Fetch Processing
|
||||
///
|
||||
/// IOFetch is the class used to send upstream fetches and to handle responses.
|
||||
/// It is more or less transport-agnostic, although the
|
||||
class IOFetch : public IOCompletionCallback {
|
||||
///
|
||||
/// \param E Endpoint type to use.
|
||||
|
||||
class IOFetch : public coroutine {
|
||||
public:
|
||||
|
||||
/// \brief Result of Upstream Fetch
|
||||
@ -56,8 +60,10 @@ public:
|
||||
enum Result {
|
||||
SUCCESS = 0, ///< Success, fetch completed
|
||||
TIME_OUT, ///< Failure, fetch timed out
|
||||
STOPPED ///< Control code, fetch has been stopped
|
||||
STOPPED, ///< Control code, fetch has been stopped
|
||||
NOTSET ///< For testing, indicates value not set
|
||||
};
|
||||
|
||||
// The next enum is a "trick" to allow constants to be defined in a class
|
||||
// declaration.
|
||||
|
||||
@ -65,8 +71,10 @@ public:
|
||||
enum {
|
||||
MAX_LENGTH = 4096 ///< Maximum size of receive buffer
|
||||
};
|
||||
|
||||
/// \brief I/O Fetch Callback
|
||||
///
|
||||
/// TODO: change documentation
|
||||
/// Callback object for when the fetch itself has completed. Note that this
|
||||
/// is different to the IOCompletionCallback; that is used to signal the
|
||||
/// completion of an asynchronous I/O call. The IOFetch::Callback is called
|
||||
@ -94,9 +102,9 @@ public:
|
||||
///
|
||||
/// The data for IOFetch is held in a separate struct pointed to by a
|
||||
/// shared_ptr object. This is because the IOFetch object will be copied
|
||||
/// often (it is used as a coroutine and passed as callback to many async_*()
|
||||
/// functions) and we want keep the same data). Organising the data this
|
||||
/// way keeps copying to a minimum.
|
||||
/// often (it is used as a coroutine and passed as callback to many
|
||||
/// async_*() functions) and we want keep the same data). Organising the
|
||||
/// data in this way keeps copying to a minimum.
|
||||
struct IOFetchData {
|
||||
|
||||
// The next two members are shared pointers to a base class because what
|
||||
@ -104,15 +112,15 @@ public:
|
||||
// TCP, which is not known until construction of the IOFetch. Use of
|
||||
// a shared pointer here is merely to ensure deletion when the data
|
||||
// object is deleted.
|
||||
boost::shared_ptr<IOSocket> socket; ///< Socket to use for I/O
|
||||
boost::shared_ptr<IOAsioSocket<IOFetch> > socket;
|
||||
///< Socket to use for I/O
|
||||
boost::shared_ptr<IOEndpoint> remote; ///< Where the fetch was sent
|
||||
|
||||
isc::dns::Question question; ///< Question to be asked
|
||||
isc::dns::OutputBufferPtr msgbuf; ///< Wire buffer for question
|
||||
isc::dns::OutputBufferPtr buffer; ///< Received data held here
|
||||
isc::dns::OutputBufferPtr msgbuf; ///< ... and here
|
||||
boost::shared_array<char> data; ///< Temporary array for the data
|
||||
Callback* callback; ///< Called on I/O Completion
|
||||
size_t rcv_amount; ///< Received amount
|
||||
boost::shared_array<char> data; ///< Temporary array for data
|
||||
IOFetch::Callback* callback; ///< Called on I/O Completion
|
||||
size_t cumulative; ///< Cumulative received amount
|
||||
bool stopped; ///< Have we stopped running?
|
||||
asio::deadline_timer timer; ///< Timer to measure timeouts
|
||||
int timeout; ///< Timeout in ms
|
||||
@ -121,7 +129,8 @@ public:
|
||||
///
|
||||
/// Just fills in the data members of the IOFetchData structure
|
||||
///
|
||||
/// \param io_service I/O Service object to handle the asynchronous
|
||||
/// \param protocol either IPPROTO_UDP or IPPROTO_TCP
|
||||
/// \param service I/O Service object to handle the asynchronous
|
||||
/// operations.
|
||||
/// \param query DNS question to send to the upstream server.
|
||||
/// \param address IP address of upstream server
|
||||
@ -131,38 +140,60 @@ public:
|
||||
/// \param cb Callback object containing the callback to be called
|
||||
/// when we terminate. The caller is responsible for managing this
|
||||
/// object and deleting it if necessary.
|
||||
/// \param wait Timeout for the fetch (in ms). The default value of
|
||||
/// -1 indicates no timeout.
|
||||
/// \param protocol Protocol to use for the fetch. The default is UDP
|
||||
|
||||
IOFetchData(IOService& io_service, const isc::dns::Question& query,
|
||||
const IOAddress& address, uint16_t port,
|
||||
isc::dns::OutputBufferPtr buff, Callback* cb, int wait = -1,
|
||||
int protocol = IPPROTO_UDP);
|
||||
/// \param wait Timeout for the fetch (in ms).
|
||||
IOFetchData(int protocol, IOService& service,
|
||||
const isc::dns::Question& query, const IOAddress& address,
|
||||
uint16_t port, isc::dns::OutputBufferPtr& buff, Callback* cb,
|
||||
int wait)
|
||||
:
|
||||
socket((protocol == IPPROTO_UDP) ?
|
||||
static_cast<IOAsioSocket<IOFetch>*>(
|
||||
new UDPSocket<IOFetch>(service)) :
|
||||
static_cast<IOAsioSocket<IOFetch>*>(
|
||||
new TCPSocket<IOFetch>(service))
|
||||
),
|
||||
remote((protocol == IPPROTO_UDP) ?
|
||||
static_cast<IOEndpoint*>(new UDPEndpoint(address, port)) :
|
||||
static_cast<IOEndpoint*>(new TCPEndpoint(address, port))
|
||||
),
|
||||
question(query),
|
||||
msgbuf(new isc::dns::OutputBuffer(512)),
|
||||
buffer(buff),
|
||||
data(new char[IOFetch::MAX_LENGTH]),
|
||||
callback(cb),
|
||||
cumulative(0),
|
||||
stopped(false),
|
||||
timer(service.get_io_service()),
|
||||
timeout(wait)
|
||||
{}
|
||||
};
|
||||
|
||||
/// \brief Constructor.
|
||||
///
|
||||
/// Creates the object that will handle the upstream fetch.
|
||||
///
|
||||
/// \param io_service I/O Service object to handle the asynchronous
|
||||
/// TODO: Need to randomise the source port
|
||||
///
|
||||
/// \param protocol Fetch protocol, either IPPROTO_UDP or IPPROTO_TCP
|
||||
/// \param service I/O Service object to handle the asynchronous
|
||||
/// operations.
|
||||
/// \param question DNS question to send to the upstream server.
|
||||
/// \param address IP address of upstream server
|
||||
/// \param port Port to use for the query
|
||||
/// \param buffer Output buffer into which the response (in wire format)
|
||||
/// \param buff Output buffer into which the response (in wire format)
|
||||
/// is written (if a response is received).
|
||||
/// \param callback Callback object containing the callback to be called
|
||||
/// \param cb Callback object containing the callback to be called
|
||||
/// when we terminate. The caller is responsible for managing this
|
||||
/// object and deleting it if necessary.
|
||||
/// \param timeout Timeout for the fetch (in ms). The default value of
|
||||
/// \param address IP address of upstream server
|
||||
/// \param port Port to which to connect on the upstream server
|
||||
/// (default = 53)
|
||||
/// \param wait Timeout for the fetch (in ms). The default value of
|
||||
/// -1 indicates no timeout.
|
||||
/// \param protocol Protocol to use for the fetch. The default is UDP
|
||||
IOFetch(IOService& io_service, const isc::dns::Question& question,
|
||||
const IOAddress& address, uint16_t port,
|
||||
isc::dns::OutputBufferPtr buffer, Callback* callback,
|
||||
int timeout = -1, int protocol = IPPROTO_UDP);
|
||||
IOFetch(int protocol, IOService& service,
|
||||
const isc::dns::Question& question, const IOAddress& address,
|
||||
uint16_t port, isc::dns::OutputBufferPtr& buff, Callback* cb,
|
||||
int wait = -1);
|
||||
|
||||
// The default constructor and copy constructor are correct for this method.
|
||||
|
||||
/// \brief Coroutine entry point
|
||||
///
|
||||
@ -174,7 +205,6 @@ public:
|
||||
void operator()(asio::error_code ec = asio::error_code(),
|
||||
size_t length = 0);
|
||||
|
||||
|
||||
/// \brief Terminate query
|
||||
///
|
||||
/// This method can be called at any point. It terminates the current
|
||||
@ -184,10 +214,10 @@ public:
|
||||
void stop(Result reason = STOPPED);
|
||||
|
||||
private:
|
||||
boost::shared_ptr<IOFetchData> data_; ///< Private data
|
||||
boost::shared_ptr<IOFetchData> data_; ///< Private data
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace asiolink
|
||||
|
||||
|
||||
#endif // __IOFETCH_H
|
||||
#endif // __IO_FETCH_H
|
||||
|
@ -12,8 +12,8 @@
|
||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#ifndef __IOMESSAGE_H
|
||||
#define __IOMESSAGE_H 1
|
||||
#ifndef __IO_MESSAGE_H
|
||||
#define __IO_MESSAGE_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.
|
||||
@ -97,7 +97,7 @@ private:
|
||||
|
||||
|
||||
} // asiolink
|
||||
#endif // __IOMESSAGE_H
|
||||
#endif // __IO_MESSAGE_H
|
||||
|
||||
// Local Variables:
|
||||
// mode: c++
|
||||
|
@ -14,10 +14,9 @@
|
||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include "io_socket.h"
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
using namespace asio;
|
||||
|
||||
@ -44,76 +43,11 @@ public:
|
||||
|
||||
/// \brief A dummy derived method of \c IOSocket::getNative().
|
||||
///
|
||||
/// \return Always returns -1 as the object is not associated with a real
|
||||
/// (native) socket.
|
||||
/// This version of method 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 IOSocket::getProtocol().
|
||||
///
|
||||
/// \return Protocol socket was created with
|
||||
virtual int getProtocol() const { return (protocol_); }
|
||||
|
||||
|
||||
/// \brief Open Socket
|
||||
///
|
||||
/// 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*, IOCompletionCallback&) {
|
||||
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*,
|
||||
IOCompletionCallback&) {
|
||||
}
|
||||
|
||||
/// \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*,
|
||||
IOCompletionCallback&) {
|
||||
}
|
||||
|
||||
/// \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 Socket
|
||||
///
|
||||
/// 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_;
|
||||
};
|
||||
|
@ -12,8 +12,8 @@
|
||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#ifndef __IOSOCKET_H
|
||||
#define __IOSOCKET_H 1
|
||||
#ifndef __IO_SOCKET_H
|
||||
#define __IO_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.
|
||||
@ -24,16 +24,9 @@
|
||||
#include <string>
|
||||
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <coroutine.h>
|
||||
|
||||
#include <asiolink/io_completion_cb.h>
|
||||
|
||||
namespace asiolink {
|
||||
|
||||
/// Forward declaration of an IOEndpoint
|
||||
class IOEndpoint;
|
||||
|
||||
|
||||
/// \brief The \c IOSocket class is an abstract base class to represent
|
||||
/// various types of network sockets.
|
||||
///
|
||||
@ -102,82 +95,6 @@ public:
|
||||
/// \return IPPROTO_TCP for TCP sockets
|
||||
virtual int getProtocol() const = 0;
|
||||
|
||||
/// \brief Open Socket
|
||||
///
|
||||
/// A call that is a no-op on UDP sockets, this opens a connection to the
|
||||
/// system identified by the given endpoint.
|
||||
///
|
||||
/// \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 connect has
|
||||
/// completed. In the stackless coroutines model, this will be the
|
||||
/// method containing the call to this function, allowing the operation to
|
||||
/// resume after the socket open has completed.
|
||||
///
|
||||
/// \return true if an asynchronous operation was started and the caller
|
||||
/// should yield and wait for completion, false if not. (i.e. The UDP
|
||||
/// derived class will return false, the TCP class will return true). This
|
||||
/// optimisation avoids the overhead required to post a callback to the
|
||||
/// I/O Service queue where nothing is done.
|
||||
virtual bool open(const IOEndpoint* endpoint,
|
||||
IOCompletionCallback& 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, IOCompletionCallback& 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, IOCompletionCallback& 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 Socket
|
||||
virtual void cancel() = 0;
|
||||
|
||||
/// \brief Close socket
|
||||
virtual void close() = 0;
|
||||
|
||||
/// \brief Return a non-usable "dummy" UDP socket for testing.
|
||||
///
|
||||
/// This is a class method that returns a "mock" of UDP socket.
|
||||
@ -202,9 +119,6 @@ public:
|
||||
static IOSocket& getDummyTCPSocket();
|
||||
};
|
||||
|
||||
} // asiolink
|
||||
#endif // __IOSOCKET_H
|
||||
} // namespace asiolink
|
||||
|
||||
// Local Variables:
|
||||
// mode: c++
|
||||
// End:
|
||||
#endif // __IO_SOCKET_H
|
||||
|
@ -20,9 +20,9 @@
|
||||
|
||||
#include <log/dummylog.h>
|
||||
|
||||
#include <asiolink/io_completion_cb.h>
|
||||
#include <asiolink/tcp_endpoint.h>
|
||||
#include <asiolink/tcp_socket.h>
|
||||
|
||||
#include <asiolink/tcp_server.h>
|
||||
|
||||
|
||||
@ -115,7 +115,15 @@ TCPServer::operator()(error_code ec, size_t length) {
|
||||
// that would quickly generate an IOMessage object without
|
||||
// all these calls to "new".)
|
||||
peer_.reset(new TCPEndpoint(socket_->remote_endpoint()));
|
||||
iosock_.reset(new TCPSocket(*socket_));
|
||||
|
||||
// The TCP socket class has been extended with asynchronous functions
|
||||
// and takes as a template parameter a completion callback class. As
|
||||
// TCPServer does not use these extended functions (only those defined
|
||||
// in the IOSocket base class) - but needs a TCPSocket to get hold of
|
||||
// the underlying Boost TCP socket - use "IOCompletionCallback" -
|
||||
// a basic callback class: it is not used but provides the appropriate
|
||||
// signature.
|
||||
iosock_.reset(new TCPSocket<IOCompletionCallback>(*socket_));
|
||||
io_message_.reset(new IOMessage(data_.get(), length, *iosock_, *peer_));
|
||||
bytes_ = length;
|
||||
|
||||
|
@ -19,55 +19,78 @@
|
||||
#error "asio.hpp must be included before including this, see asiolink.h as to why"
|
||||
#endif
|
||||
|
||||
#include <asiolink/io_socket.h>
|
||||
#include <log/dummylog.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h> // for some IPC/network system calls
|
||||
|
||||
#include <iostream>
|
||||
#include <cstddef>
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <asiolink/io_asio_socket.h>
|
||||
#include <asiolink/io_endpoint.h>
|
||||
#include <asiolink/io_service.h>
|
||||
#include <asiolink/tcp_endpoint.h>
|
||||
|
||||
namespace asiolink {
|
||||
|
||||
/// \brief The \c TCPSocket class is a concrete derived class of
|
||||
/// \c IOSocket that represents a TCP socket.
|
||||
///
|
||||
/// In the current implementation, an object of this class is always
|
||||
/// instantiated within the wrapper routines. Applications are expected to
|
||||
/// get access to the object via the abstract base class, \c IOSocket.
|
||||
/// This design may be changed when we generalize the wrapper interface.
|
||||
class TCPSocket : public IOSocket {
|
||||
/// Other notes about \c TCPSocket applies to this class, too.
|
||||
///
|
||||
/// \param C Callback type
|
||||
template <typename C>
|
||||
class TCPSocket : public IOAsioSocket<C> {
|
||||
private:
|
||||
TCPSocket(const TCPSocket& source);
|
||||
TCPSocket& operator=(const TCPSocket& source);
|
||||
/// \brief Class is non-copyable
|
||||
TCPSocket(const TCPSocket&);
|
||||
TCPSocket& operator=(const TCPSocket&);
|
||||
|
||||
public:
|
||||
enum {
|
||||
MAX_SIZE = 4096 // Send and receive size
|
||||
};
|
||||
|
||||
/// \brief Constructor from an ASIO TCP socket.
|
||||
///
|
||||
/// \param socket The ASIO representation of the TCP socket.
|
||||
TCPSocket(asio::ip::tcp::socket& socket) : socket_(socket) {}
|
||||
/// \param socket The ASIO representation of the TCP socket. It
|
||||
/// is assumed that the caller will open and close the socket, so
|
||||
/// these operations are a no-op for that socket.
|
||||
TCPSocket(asio::ip::tcp::socket& socket);
|
||||
|
||||
int getNative() const { return (socket_.native()); }
|
||||
int getProtocol() const { return (IPPROTO_TCP); }
|
||||
/// \brief Constructor
|
||||
///
|
||||
/// Used when the TCPSocket is being asked to manage its own internal
|
||||
/// socket. It is assumed that open() and close() will not be used.
|
||||
///
|
||||
/// \param service I/O Service object used to manage the socket.
|
||||
TCPSocket(IOService& service);
|
||||
|
||||
/// \brief Destructor
|
||||
virtual ~TCPSocket();
|
||||
|
||||
virtual int getNative() const { return (socket_.native()); }
|
||||
virtual int getProtocol() const { return (IPPROTO_TCP); }
|
||||
|
||||
/// \brief Open Socket
|
||||
///
|
||||
/// A call that is a no-op on UDP sockets, this opens a connection to the
|
||||
/// system identified by the given endpoint.
|
||||
/// Opens the TCP socket. In the model for transport-layer agnostic I/O,
|
||||
/// an "open" operation includes a connection to the remote end (which
|
||||
/// may take time). This does not happen for TCP, so the method returns
|
||||
/// "false" to indicate that the operation completed synchronously.
|
||||
///
|
||||
/// \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 connect has
|
||||
/// completed. In the stackless coroutines model, this will be the
|
||||
/// method containing the call to this function, allowing the operation to
|
||||
/// resume after the socket open has completed.
|
||||
/// \param endpoint Endpoint to which the socket will connect to.
|
||||
/// \param callback Unused.
|
||||
///
|
||||
/// \return true if an asynchronous operation was started and the caller
|
||||
/// should yield and wait for completion, false if not. (i.e. The UDP
|
||||
/// derived class will return false, the TCP class will return true). This
|
||||
/// optimisation avoids the overhead required to post a callback to the
|
||||
/// I/O Service queue where nothing is done.
|
||||
virtual bool open(const IOEndpoint*, IOCompletionCallback&) {
|
||||
return false;
|
||||
}
|
||||
/// \return false to indicate that the "operation" completed synchronously.
|
||||
virtual bool open(const IOEndpoint* endpoint, C&);
|
||||
|
||||
/// \brief Send Asynchronously
|
||||
///
|
||||
/// This corresponds to async_send_to() for UDP sockets and async_send()
|
||||
/// This corresponds to async_send_to() for TCP 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.
|
||||
///
|
||||
@ -75,13 +98,12 @@ public:
|
||||
/// \param length Length of data to send
|
||||
/// \param endpoint Target of the send
|
||||
/// \param callback Callback object.
|
||||
virtual void asyncSend(const void*, size_t,
|
||||
const IOEndpoint*, IOCompletionCallback&) {
|
||||
}
|
||||
virtual void asyncSend(const void* data, size_t length,
|
||||
const IOEndpoint* endpoint, C& callback);
|
||||
|
||||
/// \brief Receive Asynchronously
|
||||
///
|
||||
/// This correstponds to async_receive_from() for UDP sockets and
|
||||
/// This correstponds to async_receive_from() for TCP 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.
|
||||
@ -89,18 +111,19 @@ public:
|
||||
/// \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.
|
||||
/// (This is ignored - every UPD receive fills the buffer from the start.)
|
||||
/// \param endpoint Source of the communication
|
||||
/// \param callback Callback object
|
||||
virtual void asyncReceive(void* data, size_t, size_t, IOEndpoint*,
|
||||
IOCompletionCallback&) {
|
||||
}
|
||||
virtual void asyncReceive(void* data, size_t length, size_t cumulative,
|
||||
IOEndpoint* endpoint, C& callback);
|
||||
|
||||
/// \brief Checks if the data received is complete.
|
||||
///
|
||||
/// Checks that the total data received is the amount expected by the
|
||||
/// two-byte header to the message.
|
||||
/// As all the data is received in one I/O, so this is, this is effectively
|
||||
/// a no-op (although it does update the amount of data received).
|
||||
///
|
||||
/// \param data Data buffer containing data to date
|
||||
/// \param data Data buffer containing data to date. (This is ignored
|
||||
/// for TCP receives.)
|
||||
/// \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.
|
||||
@ -113,17 +136,147 @@ public:
|
||||
}
|
||||
|
||||
/// \brief Cancel I/O On Socket
|
||||
virtual void cancel() {
|
||||
}
|
||||
virtual void cancel();
|
||||
|
||||
/// \brief Close socket
|
||||
virtual void close() {
|
||||
}
|
||||
virtual void close();
|
||||
|
||||
|
||||
private:
|
||||
asio::ip::tcp::socket& socket_;
|
||||
// Two variables to hold the socket - a socket and a pointer to it. This
|
||||
// handles the case where a socket is passed to the TCPSocket on
|
||||
// construction, or where it is asked to manage its own socket.
|
||||
asio::ip::tcp::socket* socket_ptr_; ///< Pointer to own socket
|
||||
asio::ip::tcp::socket& socket_; ///< Socket
|
||||
bool isopen_; ///< true when socket is open
|
||||
};
|
||||
|
||||
// Constructor - caller manages socket
|
||||
|
||||
template <typename C>
|
||||
TCPSocket<C>::TCPSocket(asio::ip::tcp::socket& socket) :
|
||||
socket_ptr_(NULL), socket_(socket), isopen_(true)
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor - create socket on the fly
|
||||
|
||||
template <typename C>
|
||||
TCPSocket<C>::TCPSocket(IOService& service) :
|
||||
socket_ptr_(new asio::ip::tcp::socket(service.get_io_service())),
|
||||
socket_(*socket_ptr_), isopen_(false)
|
||||
{
|
||||
}
|
||||
|
||||
// Destructor. Only delete the socket if we are managing it.
|
||||
|
||||
template <typename C>
|
||||
TCPSocket<C>::~TCPSocket()
|
||||
{
|
||||
delete socket_ptr_;
|
||||
}
|
||||
|
||||
// Open the socket. Throws an error on failure
|
||||
// TODO: Make the open more resilient
|
||||
|
||||
template <typename C> bool
|
||||
TCPSocket<C>::open(const IOEndpoint* endpoint, C&) {
|
||||
|
||||
// Ignore opens on already-open socket. Don't throw a failure because
|
||||
// of uncertainties as to what precedes whan when using asynchronous I/O.
|
||||
// At also allows us a treat a passed-in socket as a self-managed socket.
|
||||
|
||||
if (!isopen_) {
|
||||
if (endpoint->getFamily() == AF_INET) {
|
||||
socket_.open(asio::ip::tcp::v4());
|
||||
}
|
||||
else {
|
||||
socket_.open(asio::ip::tcp::v6());
|
||||
}
|
||||
isopen_ = true;
|
||||
|
||||
// TODO: Complete TCPSocket::open()
|
||||
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
// Send a message. Should never do this if the socket is not open, so throw
|
||||
// an exception if this is the case.
|
||||
|
||||
template <typename C> void
|
||||
TCPSocket<C>::asyncSend(const void* data, size_t length,
|
||||
const IOEndpoint* endpoint, C& callback)
|
||||
{
|
||||
if (isopen_) {
|
||||
|
||||
// Upconvert to a TCPEndpoint. We need to do this because although
|
||||
// IOEndpoint is the base class of TCPEndpoint and TCPEndpoint, it
|
||||
// doing cont contain a method for getting at the underlying endpoint
|
||||
// type - those are in the derived class and the two classes differ on
|
||||
// return type.
|
||||
|
||||
assert(endpoint->getProtocol() == IPPROTO_TCP);
|
||||
const TCPEndpoint* tcp_endpoint =
|
||||
static_cast<const TCPEndpoint*>(endpoint);
|
||||
std::cerr << "TCPSocket::asyncSend(): sending to " <<
|
||||
tcp_endpoint->getAddress().toText() <<
|
||||
", port " << tcp_endpoint->getPort() << "\n";
|
||||
|
||||
// TODO: Complete TCPSocket::asyncSend()
|
||||
|
||||
} else {
|
||||
isc_throw(SocketNotOpen,
|
||||
"attempt to send on a TCP socket that is not open");
|
||||
}
|
||||
}
|
||||
|
||||
// Receive a message. Note that the "cumulative" argument is ignored - every TCP
|
||||
// receive is put into the buffer beginning at the start - there is no concept
|
||||
// receiving a subsequent part of a message. Same critera as before concerning
|
||||
// the need for the socket to be open.
|
||||
|
||||
template <typename C> void
|
||||
TCPSocket<C>::asyncReceive(void* data, size_t length, size_t,
|
||||
IOEndpoint* endpoint, C& callback)
|
||||
{
|
||||
if (isopen_) {
|
||||
|
||||
// Upconvert the endpoint again.
|
||||
assert(endpoint->getProtocol() == IPPROTO_TCP);
|
||||
const TCPEndpoint* tcp_endpoint =
|
||||
static_cast<const TCPEndpoint*>(endpoint);
|
||||
std::cerr << "TCPSocket::asyncReceive(): receiving from " <<
|
||||
tcp_endpoint->getAddress().toText() <<
|
||||
", port " << tcp_endpoint->getPort() << "\n";
|
||||
|
||||
// TODO: Complete TCPSocket::asyncReceive()
|
||||
|
||||
} else {
|
||||
isc_throw(SocketNotOpen,
|
||||
"attempt to receive from a TCP socket that is not open");
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel I/O on the socket. No-op if the socket is not open.
|
||||
template <typename C> void
|
||||
TCPSocket<C>::cancel() {
|
||||
if (isopen_) {
|
||||
socket_.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
// Close the socket down. Can only do this if the socket is open and we are
|
||||
// managing it ourself.
|
||||
|
||||
template <typename C> void
|
||||
TCPSocket<C>::close() {
|
||||
if (isopen_ && socket_ptr_) {
|
||||
socket_.close();
|
||||
isopen_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace asiolink
|
||||
|
||||
} // namespace asiolink
|
||||
#endif // __TCP_SOCKET_H
|
||||
|
@ -15,10 +15,12 @@ CLEANFILES = *.gcno *.gcda
|
||||
TESTS =
|
||||
if HAVE_GTEST
|
||||
TESTS += run_unittests
|
||||
run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
|
||||
run_unittests_SOURCES = run_unittests.cc
|
||||
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
|
||||
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
|
||||
run_unittests_SOURCES += io_address_unittest.cc
|
||||
run_unittests_SOURCES += io_endpoint_unittest.cc
|
||||
run_unittests_SOURCES += io_fetch_unittest.cc
|
||||
run_unittests_SOURCES += io_socket_unittest.cc
|
||||
run_unittests_SOURCES += io_service_unittest.cc
|
||||
run_unittests_SOURCES += interval_timer_unittest.cc
|
||||
@ -26,15 +28,18 @@ run_unittests_SOURCES += recursive_query_unittest.cc
|
||||
run_unittests_SOURCES += udp_endpoint_unittest.cc
|
||||
run_unittests_SOURCES += udp_query_unittest.cc
|
||||
run_unittests_SOURCES += udp_socket_unittest.cc
|
||||
run_unittests_SOURCES += run_unittests.cc
|
||||
|
||||
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
|
||||
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(LOG4CXX_LDFLAGS)
|
||||
run_unittests_LDADD = $(GTEST_LDADD)
|
||||
|
||||
run_unittests_LDADD = $(GTEST_LDADD)
|
||||
run_unittests_LDADD += $(SQLITE_LIBS)
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
|
||||
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
|
||||
|
||||
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(LOG4CXX_LDFLAGS)
|
||||
|
||||
# Note: the ordering matters: -Wno-... must follow -Wextra (defined in
|
||||
# B10_CXXFLAGS)
|
||||
run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
|
||||
|
189
src/lib/asiolink/tests/io_fetch_unittest.cc
Normal file
189
src/lib/asiolink/tests/io_fetch_unittest.cc
Normal file
@ -0,0 +1,189 @@
|
||||
// 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 <gtest/gtest.h>
|
||||
#include <boost/bind.hpp>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <dns/buffer.h>
|
||||
#include <dns/question.h>
|
||||
#include <dns/message.h>
|
||||
#include <dns/messagerenderer.h>
|
||||
#include <dns/opcode.h>
|
||||
#include <dns/name.h>
|
||||
#include <dns/rcode.h>
|
||||
|
||||
#include <asiolink/io_fetch.h>
|
||||
#include <asiolink/io_completion_cb.h>
|
||||
#include <asiolink/io_service.h>
|
||||
|
||||
using namespace asio;
|
||||
using namespace isc::dns;
|
||||
using asio::ip::udp;
|
||||
|
||||
namespace asiolink {
|
||||
|
||||
const asio::ip::address TEST_HOST(asio::ip::address::from_string("127.0.0.1"));
|
||||
const uint16_t TEST_PORT(5301);
|
||||
// FIXME Shouldn't we send something that is real message?
|
||||
const char TEST_DATA[] = "TEST DATA";
|
||||
|
||||
/// \brief Test fixture for the asiolink::IOFetch.
|
||||
class IOFetchTest : public virtual ::testing::Test, public virtual IOFetch::Callback
|
||||
{
|
||||
public:
|
||||
IOService service_; ///< Service to run the query
|
||||
IOFetch::Result expected_; ///< Expected result of the callback
|
||||
bool run_; ///< Did the callback run already?
|
||||
Question question_; ///< What to ask
|
||||
OutputBufferPtr buff_; ///< Buffer to hold result
|
||||
IOFetch udp_fetch_; ///< For UDP query test
|
||||
//IOFetch tcp_fetch_; ///< For TCP query test
|
||||
|
||||
// The next member is the buffer iin which the "server" (implemented by the
|
||||
// response handler method) receives the question sent by the fetch object.
|
||||
char server_buff_[512]; ///< Server buffer
|
||||
|
||||
/// \brief Constructor
|
||||
IOFetchTest() :
|
||||
service_(),
|
||||
expected_(IOFetch::NOTSET),
|
||||
run_(false),
|
||||
question_(Name("example.net"), RRClass::IN(), RRType::A()),
|
||||
buff_(new OutputBuffer(512)),
|
||||
udp_fetch_(IPPROTO_UDP, service_, question_, IOAddress(TEST_HOST),
|
||||
TEST_PORT, buff_, this, 100)
|
||||
// tcp_fetch_(service_, question_, IOAddress(TEST_HOST), TEST_PORT,
|
||||
// buff_, this, 100, IPPROTO_UDP)
|
||||
{ }
|
||||
|
||||
/// \brief Fetch completion callback
|
||||
///
|
||||
/// This is the callback's operator() method which is called when the fetch
|
||||
/// is complete. Check that the data received is the wire format of the
|
||||
/// question, then send back an arbitrary response.
|
||||
void operator()(IOFetch::Result result) {
|
||||
EXPECT_EQ(expected_, result); // Check correct result returned
|
||||
EXPECT_FALSE(run_); // Check it is run only once
|
||||
run_ = true; // Note success
|
||||
service_.stop(); // ... and exit run loop
|
||||
}
|
||||
|
||||
/// \brief Response handler, pretending to be remote DNS server
|
||||
///
|
||||
/// This checks that the data sent is what we expected to receive, and
|
||||
/// sends back a test answer.
|
||||
void respond(udp::endpoint* remote, udp::socket* socket,
|
||||
asio::error_code ec = asio::error_code(), size_t length = 0) {
|
||||
|
||||
// Construct the data buffer for question we expect to receive.
|
||||
OutputBuffer msgbuf(512);
|
||||
Message msg(Message::RENDER);
|
||||
msg.setQid(0);
|
||||
msg.setOpcode(Opcode::QUERY());
|
||||
msg.setRcode(Rcode::NOERROR());
|
||||
msg.setHeaderFlag(Message::HEADERFLAG_RD);
|
||||
msg.addQuestion(question_);
|
||||
MessageRenderer renderer(msgbuf);
|
||||
msg.toWire(renderer);
|
||||
|
||||
// The QID in the incoming data is random so set it to 0 for the
|
||||
// data comparison check. (It was set to 0 when the buffer containing
|
||||
// the expected data was constructed above.)
|
||||
server_buff_[0] = server_buff_[1] = 0;
|
||||
|
||||
// Check that lengths are identical.
|
||||
EXPECT_EQ(msgbuf.getLength(), length);
|
||||
EXPECT_TRUE(memcmp(msgbuf.getData(), server_buff_, length) == 0);
|
||||
|
||||
// ... and return a message back.
|
||||
socket->send_to(asio::buffer(TEST_DATA, sizeof TEST_DATA), *remote);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Test that when we run the query and stop it after it was run,
|
||||
/// it returns "stopped" correctly.
|
||||
///
|
||||
/// That is why stop() is posted to the service_ as well instead
|
||||
/// of calling it.
|
||||
TEST_F(IOFetchTest, UdpStop) {
|
||||
expected_ = IOFetch::STOPPED;
|
||||
|
||||
// Post the query
|
||||
service_.get_io_service().post(udp_fetch_);
|
||||
|
||||
// Post query_.stop() (yes, the boost::bind thing is just
|
||||
// query_.stop()).
|
||||
service_.get_io_service().post(
|
||||
boost::bind(&IOFetch::stop, udp_fetch_, IOFetch::STOPPED));
|
||||
|
||||
// Run both of them. run() returns when everything in the I/O service
|
||||
// queue has completed.
|
||||
service_.run();
|
||||
EXPECT_TRUE(run_);
|
||||
}
|
||||
|
||||
// Test that when we queue the query to service_ and call stop() before it gets
|
||||
// executed, it acts sanely as well (eg. has the same result as running stop()
|
||||
// after - calls the callback).
|
||||
TEST_F(IOFetchTest, UdpPrematureStop) {
|
||||
expected_ = IOFetch::STOPPED;
|
||||
|
||||
// Stop before it is started
|
||||
udp_fetch_.stop();
|
||||
service_.get_io_service().post(udp_fetch_);
|
||||
|
||||
service_.run();
|
||||
EXPECT_TRUE(run_);
|
||||
}
|
||||
|
||||
// Test that it will timeout when no answer arrives.
|
||||
TEST_F(IOFetchTest, UdpTimeout) {
|
||||
expected_ = IOFetch::TIME_OUT;
|
||||
|
||||
service_.get_io_service().post(udp_fetch_);
|
||||
service_.run();
|
||||
EXPECT_TRUE(run_);
|
||||
}
|
||||
|
||||
// Test that it will succeed when we fake an answer and stores the same data we
|
||||
// send. This is done through a real socket on the loopback address.
|
||||
TEST_F(IOFetchTest, UdpReceive) {
|
||||
expected_ = IOFetch::SUCCESS;
|
||||
|
||||
udp::socket socket(service_.get_io_service(), udp::v4());
|
||||
socket.set_option(socket_base::reuse_address(true));
|
||||
socket.bind(udp::endpoint(TEST_HOST, TEST_PORT));
|
||||
|
||||
udp::endpoint remote;
|
||||
socket.async_receive_from(asio::buffer(server_buff_, sizeof(server_buff_)),
|
||||
remote,
|
||||
boost::bind(&IOFetchTest::respond, this, &remote, &socket, _1, _2));
|
||||
service_.get_io_service().post(udp_fetch_);
|
||||
service_.run();
|
||||
|
||||
socket.close();
|
||||
|
||||
EXPECT_TRUE(run_);
|
||||
ASSERT_EQ(sizeof TEST_DATA, buff_->getLength());
|
||||
EXPECT_EQ(0, memcmp(TEST_DATA, buff_->getData(), sizeof TEST_DATA));
|
||||
}
|
||||
|
||||
} // namespace asiolink
|
@ -15,6 +15,8 @@
|
||||
#include <config.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <asiolink/io_socket.h>
|
||||
|
||||
|
@ -52,14 +52,12 @@
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <asiolink/io_completion_cb.h>
|
||||
#include <asiolink/io_service.h>
|
||||
#include <asiolink/udp_endpoint.h>
|
||||
#include <asiolink/udp_socket.h>
|
||||
|
||||
using namespace asio;
|
||||
using namespace asiolink;
|
||||
using asio::ip::udp;
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
@ -77,7 +75,7 @@ const char INBOUND_DATA[] = "Returned data from server to client";
|
||||
/// and the operator() method is called when when an asynchronous I/O
|
||||
/// completes. The arguments to the completion callback are stored for later
|
||||
/// retrieval.
|
||||
class UDPCallback : public IOCompletionCallback {
|
||||
class UDPCallback {
|
||||
public:
|
||||
|
||||
struct PrivateData {
|
||||
@ -187,33 +185,38 @@ private:
|
||||
TEST(UDPSocket, SequenceTest) {
|
||||
|
||||
// Common objects.
|
||||
IOAddress server_address(SERVER_ADDRESS); // Address of target server
|
||||
UDPEndpoint endpoint(server_address, SERVER_PORT); // Endpoint of target server
|
||||
IOService service; // Service object for async control
|
||||
|
||||
// Server
|
||||
IOAddress server_address(SERVER_ADDRESS); // Address of target server
|
||||
UDPCallback server_cb("Server"); // Server callback
|
||||
UDPEndpoint server_endpoint( // Endpoint describing server
|
||||
server_address, SERVER_PORT);
|
||||
UDPEndpoint server_remote_endpoint; // Address where server received message from
|
||||
|
||||
// The client - the UDPSocket being tested
|
||||
UDPSocket client(service); // Socket under test
|
||||
UDPSocket<UDPCallback> client(service);// Socket under test
|
||||
UDPCallback client_cb("Client"); // Async I/O callback function
|
||||
UDPEndpoint client_remote_endpoint; // Where client receives message from
|
||||
size_t client_cumulative = 0; // Cumulative data received
|
||||
|
||||
// The server - with which the client communicates. For convenience, we
|
||||
// use the same io_service, and use the endpoint object created for
|
||||
// the client to send to as the endpoint object in the constructor.
|
||||
UDPCallback server_cb("Server");
|
||||
udp::socket server(service.get_io_service(), endpoint.getASIOEndpoint());
|
||||
asio::ip::udp::socket server(service.get_io_service(),
|
||||
server_endpoint.getASIOEndpoint());
|
||||
server.set_option(socket_base::reuse_address(true));
|
||||
|
||||
// Assertion to ensure that the server buffer is large enough
|
||||
char data[UDPSocket::MAX_SIZE];
|
||||
char data[UDPSocket<UDPCallback>::MAX_SIZE];
|
||||
ASSERT_GT(sizeof(data), sizeof(OUTBOUND_DATA));
|
||||
|
||||
// Open the client socket - the operation should be synchronous
|
||||
EXPECT_FALSE(client.open(&endpoint, client_cb));
|
||||
EXPECT_FALSE(client.open(&server_endpoint, client_cb));
|
||||
|
||||
// Issue read on the server. Completion callback should not have run.
|
||||
server_cb.setCalled(false);
|
||||
server_cb.setCode(42); // Answer to Life, the Universe and Everything!
|
||||
UDPEndpoint server_remote_endpoint;
|
||||
server.async_receive_from(buffer(data, sizeof(data)),
|
||||
server_remote_endpoint.getASIOEndpoint(), server_cb);
|
||||
EXPECT_FALSE(server_cb.getCalled());
|
||||
@ -222,7 +225,7 @@ TEST(UDPSocket, SequenceTest) {
|
||||
// be called until we call the io_service.run() method.
|
||||
client_cb.setCalled(false);
|
||||
client_cb.setCode(7); // Arbitrary number
|
||||
client.asyncSend(OUTBOUND_DATA, sizeof(OUTBOUND_DATA), &endpoint, client_cb);
|
||||
client.asyncSend(OUTBOUND_DATA, sizeof(OUTBOUND_DATA), &server_endpoint, client_cb);
|
||||
EXPECT_FALSE(client_cb.getCalled());
|
||||
|
||||
// Execute the two callbacks.
|
||||
@ -244,7 +247,6 @@ TEST(UDPSocket, SequenceTest) {
|
||||
client_cb.setLength(12345); // Arbitrary number
|
||||
client_cb.setCalled(false);
|
||||
client_cb.setCode(32); // Arbitrary number
|
||||
UDPEndpoint client_remote_endpoint; // To receive address of remote system
|
||||
client.asyncReceive(data, sizeof(data), client_cumulative,
|
||||
&client_remote_endpoint, client_cb);
|
||||
|
||||
@ -256,8 +258,8 @@ TEST(UDPSocket, SequenceTest) {
|
||||
server_remote_endpoint.getASIOEndpoint(), server_cb);
|
||||
|
||||
// Expect two callbacks to run
|
||||
service.run_one();
|
||||
service.run_one();
|
||||
service.get_io_service().poll();
|
||||
//service.run_one();
|
||||
|
||||
EXPECT_TRUE(client_cb.getCalled());
|
||||
EXPECT_EQ(0, client_cb.getCode());
|
||||
|
@ -20,10 +20,10 @@
|
||||
|
||||
#include <log/dummylog.h>
|
||||
|
||||
#include <asiolink/io_completion_cb.h>
|
||||
#include <asiolink/udp_endpoint.h>
|
||||
#include <asiolink/udp_socket.h>
|
||||
|
||||
#include <asiolink/udp_server.h>
|
||||
#include <asiolink/udp_socket.h>
|
||||
|
||||
using namespace asio;
|
||||
using asio::ip::udp;
|
||||
@ -203,7 +203,17 @@ UDPServer::operator()(error_code ec, size_t length) {
|
||||
// that would quickly generate an IOMessage object without
|
||||
// all these calls to "new".)
|
||||
data_->peer_.reset(new UDPEndpoint(*data_->sender_));
|
||||
data_->iosock_.reset(new UDPSocket(*data_->socket_));
|
||||
|
||||
// The TCP socket class has been extended with asynchronous functions
|
||||
// and takes as a template parameter a completion callback class. As
|
||||
// TCPServer does not use these extended functions (only those defined
|
||||
// in the IOSocket base class) - but needs a TCPSocket to get hold of
|
||||
// the underlying Boost TCP socket - use "IOCompletionCallback" -
|
||||
// a basic callback class: it is not used but provides the appropriate
|
||||
// signature.
|
||||
data_->iosock_.reset(
|
||||
new UDPSocket<IOCompletionCallback>(*data_->socket_));
|
||||
|
||||
data_->io_message_.reset(new IOMessage(data_->data_.get(),
|
||||
data_->bytes_, *data_->iosock_, *data_->peer_));
|
||||
|
||||
|
@ -1,131 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#include <config.h>
|
||||
#include <iostream>
|
||||
#include <unistd.h> // for some IPC/network system calls
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <asio/deadline_timer.hpp>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
|
||||
#include <dns/buffer.h>
|
||||
#include <dns/message.h>
|
||||
#include <dns/messagerenderer.h>
|
||||
#include <log/dummylog.h>
|
||||
#include <dns/opcode.h>
|
||||
#include <dns/rcode.h>
|
||||
|
||||
#include <coroutine.h>
|
||||
#include <asiolink/asiolink.h>
|
||||
|
||||
using namespace asio;
|
||||
using asio::ip::udp;
|
||||
|
||||
using namespace std;
|
||||
using namespace isc::dns;
|
||||
|
||||
namespace asiolink {
|
||||
|
||||
// Constructor - create socket on the fly
|
||||
|
||||
UDPSocket::UDPSocket(IOService& service) :
|
||||
socket_ptr_(new asio::ip::udp::socket(service.get_io_service())),
|
||||
socket_(*socket_ptr_)
|
||||
{
|
||||
}
|
||||
|
||||
// Destructor
|
||||
|
||||
UDPSocket::~UDPSocket()
|
||||
{
|
||||
delete socket_ptr_;
|
||||
}
|
||||
|
||||
// Open the socket. Throws an error on failure
|
||||
// TODO: Make the open more resolient
|
||||
|
||||
bool
|
||||
UDPSocket::open(const IOEndpoint* endpoint, IOCompletionCallback&) {
|
||||
if (endpoint->getFamily() == AF_INET) {
|
||||
socket_.open(asio::ip::udp::v4());
|
||||
}
|
||||
else {
|
||||
socket_.open(asio::ip::udp::v6());
|
||||
}
|
||||
|
||||
// Ensure it can send and receive 4K buffers.
|
||||
socket_.set_option(asio::socket_base::send_buffer_size(MAX_SIZE));
|
||||
socket_.set_option(asio::socket_base::receive_buffer_size(MAX_SIZE));
|
||||
;
|
||||
// Allow reuse of an existing port/address
|
||||
socket_.set_option(asio::socket_base::reuse_address(true));
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
// Send a message.
|
||||
|
||||
void
|
||||
UDPSocket::asyncSend(const void* data, size_t length,
|
||||
const IOEndpoint* endpoint, IOCompletionCallback& callback)
|
||||
{
|
||||
// Upconverting. Not nice, but we have the problem that in the abstract
|
||||
// layer we are given an IOEndpoint. For UDP code it is a UDPEndpoint
|
||||
// and for TCP code a TCPEndpoint. However the member that we are
|
||||
// after - the asio endpoint - is different for UPD and TCP and there is
|
||||
// no common ancestor. Hence the promotion here.
|
||||
assert(endpoint->getProtocol() == IPPROTO_UDP);
|
||||
const UDPEndpoint* udp_endpoint = static_cast<const UDPEndpoint*>(endpoint);
|
||||
|
||||
socket_.async_send_to(buffer(data, length), udp_endpoint->getASIOEndpoint(),
|
||||
callback);
|
||||
}
|
||||
|
||||
// Receive a message. Note that the "cumulative" argument is ignored - every UDP
|
||||
// receive is put into the buffer beginning at the start - there is no concept
|
||||
// receiving a subsequent part of a message.
|
||||
|
||||
void
|
||||
UDPSocket::asyncReceive(void* data, size_t length, size_t, IOEndpoint* endpoint,
|
||||
IOCompletionCallback& callback)
|
||||
{
|
||||
// Upconvert the endpoint again.
|
||||
assert(endpoint->getProtocol() == IPPROTO_UDP);
|
||||
UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
|
||||
|
||||
socket_.async_receive_from(buffer(data, length),
|
||||
udp_endpoint->getASIOEndpoint(), callback);
|
||||
}
|
||||
|
||||
// Cancel I/O on the socket
|
||||
void
|
||||
UDPSocket::cancel() {
|
||||
socket_.cancel();
|
||||
}
|
||||
|
||||
// Close the socket down
|
||||
|
||||
void
|
||||
UDPSocket::close() {
|
||||
socket_.close();
|
||||
}
|
||||
|
||||
} // namespace asiolink
|
@ -19,9 +19,19 @@
|
||||
#error "asio.hpp must be included before including this, see asiolink.h as to why"
|
||||
#endif
|
||||
|
||||
#include <log/dummylog.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h> // for some IPC/network system calls
|
||||
|
||||
#include <cstddef>
|
||||
#include <asiolink/io_socket.h>
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <asiolink/io_asio_socket.h>
|
||||
#include <asiolink/io_endpoint.h>
|
||||
#include <asiolink/io_service.h>
|
||||
#include <asiolink/udp_endpoint.h>
|
||||
|
||||
namespace asiolink {
|
||||
|
||||
@ -29,7 +39,10 @@ namespace asiolink {
|
||||
/// \c IOSocket that represents a UDP socket.
|
||||
///
|
||||
/// Other notes about \c TCPSocket applies to this class, too.
|
||||
class UDPSocket : public IOSocket {
|
||||
///
|
||||
/// \param C Callback type
|
||||
template <typename C>
|
||||
class UDPSocket : public IOAsioSocket<C> {
|
||||
private:
|
||||
/// \brief Class is non-copyable
|
||||
UDPSocket(const UDPSocket&);
|
||||
@ -42,15 +55,17 @@ public:
|
||||
|
||||
/// \brief Constructor from an ASIO UDP socket.
|
||||
///
|
||||
/// \param socket The ASIO representation of the UDP socket.
|
||||
UDPSocket(asio::ip::udp::socket& socket) :
|
||||
socket_ptr_(NULL), socket_(socket)
|
||||
{}
|
||||
/// \param socket The ASIO representation of the UDP socket. It
|
||||
/// is assumed that the caller will open and close the socket, so
|
||||
/// these operations are a no-op for that socket.
|
||||
UDPSocket(asio::ip::udp::socket& socket);
|
||||
|
||||
/// \brief Constructor
|
||||
///
|
||||
/// Used when the UDPSocket is being asked to manage its own internal
|
||||
/// socket.
|
||||
/// socket. It is assumed that open() and close() will not be used.
|
||||
///
|
||||
/// \param service I/O Service object used to manage the socket.
|
||||
UDPSocket(IOService& service);
|
||||
|
||||
/// \brief Destructor
|
||||
@ -70,7 +85,7 @@ public:
|
||||
/// \param callback Unused.
|
||||
///
|
||||
/// \return false to indicate that the "operation" completed synchronously.
|
||||
virtual bool open(const IOEndpoint* endpoint, IOCompletionCallback&);
|
||||
virtual bool open(const IOEndpoint* endpoint, C&);
|
||||
|
||||
/// \brief Send Asynchronously
|
||||
///
|
||||
@ -83,7 +98,7 @@ public:
|
||||
/// \param endpoint Target of the send
|
||||
/// \param callback Callback object.
|
||||
virtual void asyncSend(const void* data, size_t length,
|
||||
const IOEndpoint* endpoint, IOCompletionCallback& callback);
|
||||
const IOEndpoint* endpoint, C& callback);
|
||||
|
||||
/// \brief Receive Asynchronously
|
||||
///
|
||||
@ -99,7 +114,7 @@ public:
|
||||
/// \param endpoint Source of the communication
|
||||
/// \param callback Callback object
|
||||
virtual void asyncReceive(void* data, size_t length, size_t cumulative,
|
||||
IOEndpoint* endpoint, IOCompletionCallback& callback);
|
||||
IOEndpoint* endpoint, C& callback);
|
||||
|
||||
/// \brief Checks if the data received is complete.
|
||||
///
|
||||
@ -130,9 +145,133 @@ private:
|
||||
// Two variables to hold the socket - a socket and a pointer to it. This
|
||||
// handles the case where a socket is passed to the UDPSocket on
|
||||
// construction, or where it is asked to manage its own socket.
|
||||
asio::ip::udp::socket* socket_ptr_; ///< Pointer to the socket
|
||||
asio::ip::udp::socket* socket_ptr_; ///< Pointer to own socket
|
||||
asio::ip::udp::socket& socket_; ///< Socket
|
||||
bool isopen_; ///< true when socket is open
|
||||
};
|
||||
|
||||
} // namespace asiolink
|
||||
// Constructor - caller manages socket
|
||||
|
||||
template <typename C>
|
||||
UDPSocket<C>::UDPSocket(asio::ip::udp::socket& socket) :
|
||||
socket_ptr_(NULL), socket_(socket), isopen_(true)
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor - create socket on the fly
|
||||
|
||||
template <typename C>
|
||||
UDPSocket<C>::UDPSocket(IOService& service) :
|
||||
socket_ptr_(new asio::ip::udp::socket(service.get_io_service())),
|
||||
socket_(*socket_ptr_), isopen_(false)
|
||||
{
|
||||
}
|
||||
|
||||
// Destructor. Only delete the socket if we are managing it.
|
||||
|
||||
template <typename C>
|
||||
UDPSocket<C>::~UDPSocket()
|
||||
{
|
||||
delete socket_ptr_;
|
||||
}
|
||||
|
||||
// Open the socket. Throws an error on failure
|
||||
// TODO: Make the open more resilient
|
||||
|
||||
template <typename C> bool
|
||||
UDPSocket<C>::open(const IOEndpoint* endpoint, C&) {
|
||||
|
||||
// Ignore opens on already-open socket. Don't throw a failure because
|
||||
// of uncertainties as to what precedes whan when using asynchronous I/O.
|
||||
// At also allows us a treat a passed-in socket as a self-managed socket.
|
||||
|
||||
if (!isopen_) {
|
||||
if (endpoint->getFamily() == AF_INET) {
|
||||
socket_.open(asio::ip::udp::v4());
|
||||
}
|
||||
else {
|
||||
socket_.open(asio::ip::udp::v6());
|
||||
}
|
||||
isopen_ = true;
|
||||
|
||||
// Ensure it can send and receive 4K buffers.
|
||||
socket_.set_option(asio::socket_base::send_buffer_size(MAX_SIZE));
|
||||
socket_.set_option(asio::socket_base::receive_buffer_size(MAX_SIZE));
|
||||
;
|
||||
// Allow reuse of an existing port/address
|
||||
socket_.set_option(asio::socket_base::reuse_address(true));
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
// Send a message. Should never do this if the socket is not open, so throw
|
||||
// an exception if this is the case.
|
||||
|
||||
template <typename C> void
|
||||
UDPSocket<C>::asyncSend(const void* data, size_t length,
|
||||
const IOEndpoint* endpoint, C& callback)
|
||||
{
|
||||
if (isopen_) {
|
||||
|
||||
// Upconvert to a UDPEndpoint. We need to do this because although
|
||||
// IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
|
||||
// doing cont contain a method for getting at the underlying endpoint
|
||||
// type - those are in the derived class and the two classes differ on
|
||||
// return type.
|
||||
|
||||
assert(endpoint->getProtocol() == IPPROTO_UDP);
|
||||
const UDPEndpoint* udp_endpoint =
|
||||
static_cast<const UDPEndpoint*>(endpoint);
|
||||
socket_.async_send_to(buffer(data, length),
|
||||
udp_endpoint->getASIOEndpoint(), callback);
|
||||
} else {
|
||||
isc_throw(SocketNotOpen,
|
||||
"attempt to send on a UDP socket that is not open");
|
||||
}
|
||||
}
|
||||
|
||||
// Receive a message. Note that the "cumulative" argument is ignored - every UDP
|
||||
// receive is put into the buffer beginning at the start - there is no concept
|
||||
// receiving a subsequent part of a message. Same critera as before concerning
|
||||
// the need for the socket to be open.
|
||||
|
||||
template <typename C> void
|
||||
UDPSocket<C>::asyncReceive(void* data, size_t length, size_t,
|
||||
IOEndpoint* endpoint, C& callback)
|
||||
{
|
||||
if (isopen_) {
|
||||
|
||||
// Upconvert the endpoint again.
|
||||
assert(endpoint->getProtocol() == IPPROTO_UDP);
|
||||
UDPEndpoint* udp_endpoint = static_cast<UDPEndpoint*>(endpoint);
|
||||
|
||||
socket_.async_receive_from(buffer(data, length),
|
||||
udp_endpoint->getASIOEndpoint(), callback);
|
||||
} else {
|
||||
isc_throw(SocketNotOpen,
|
||||
"attempt to receive from a UDP socket that is not open");
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel I/O on the socket. No-op if the socket is not open.
|
||||
template <typename C> void
|
||||
UDPSocket<C>::cancel() {
|
||||
if (isopen_) {
|
||||
socket_.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
// Close the socket down. Can only do this if the socket is open and we are
|
||||
// managing it ourself.
|
||||
|
||||
template <typename C> void
|
||||
UDPSocket<C>::close() {
|
||||
if (isopen_ && socket_ptr_) {
|
||||
socket_.close();
|
||||
isopen_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace asiolink
|
||||
|
||||
#endif // __UDP_SOCKET_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user