%PDF- %PDF-
| Direktori : /backups/router/usr/local/include/kea/dhcp_ddns/ |
| Current File : //backups/router/usr/local/include/kea/dhcp_ddns/ncr_io.h |
// Copyright (C) 2013-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef NCR_IO_H
#define NCR_IO_H
/// @file ncr_io.h
/// @brief This file defines abstract classes for exchanging NameChangeRequests.
///
/// These classes are used for sending and receiving requests to update DNS
/// information for FQDNs embodied as NameChangeRequests (aka NCRs). Ultimately,
/// NCRs must move through the following three layers in order to implement
/// DHCP-DDNS:
///
/// * Application layer - the business layer which needs to
/// transport NameChangeRequests, and is unaware of the means by which
/// they are transported.
///
/// * NameChangeRequest layer - This is the layer which acts as the
/// intermediary between the Application layer and the IO layer. It must
/// be able to move NameChangeRequests to the IO layer as raw data and move
/// raw data from the IO layer in the Application layer as
/// NameChangeRequests.
///
/// * IO layer - the low-level layer that is directly responsible for
/// sending and receiving data asynchronously and is supplied through
/// other libraries. This layer is largely unaware of the nature of the
/// data being transmitted. In other words, it doesn't know beans about
/// NCRs.
///
/// The abstract classes defined here implement the latter, middle layer,
/// the NameChangeRequest layer. There are two types of participants in this
/// middle ground:
///
/// * listeners - Receive NCRs from one or more sources. The DHCP-DDNS
/// application, (aka D2), is a listener. Listeners are embodied by the
/// class, NameChangeListener.
///
/// * senders - sends NCRs to a given target. DHCP servers are senders.
/// Senders are embodied by the class, NameChangeSender.
///
/// These two classes present a public interface for asynchronous
/// communications that is independent of the IO layer mechanisms. While the
/// type and details of the IO mechanism are not relevant to either class, it
/// is presumed to use isc::asiolink library for asynchronous event processing.
#include <asiolink/io_address.h>
#include <asiolink/io_service.h>
#include <dhcp_ddns/ncr_msg.h>
#include <exceptions/exceptions.h>
#include <boost/scoped_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <deque>
#include <mutex>
namespace isc {
namespace dhcp_ddns {
/// @brief Defines the list of socket protocols supported.
/// Currently only UDP is implemented.
/// @todo TCP is intended to be implemented prior 1.0 release.
/// @todo Give some thought to an ANY protocol which might try
/// first as UDP then as TCP, etc.
enum NameChangeProtocol {
NCR_UDP,
NCR_TCP
};
/// @brief Function which converts text labels to @ref NameChangeProtocol enums.
///
/// @param protocol_str text to convert to an enum.
/// Valid string values: "UDP", "TCP"
///
/// @return NameChangeProtocol value which maps to the given string.
///
/// @throw isc::BadValue if given a string value which does not map to an
/// enum value.
extern NameChangeProtocol stringToNcrProtocol(const std::string& protocol_str);
/// @brief Function which converts @ref NameChangeProtocol enums to text labels.
///
/// @param protocol enum value to convert to label
///
/// @return std:string containing the text label if the value is valid, or
/// "UNKNOWN" if not.
extern std::string ncrProtocolToString(NameChangeProtocol protocol);
/// @brief Exception thrown if an NcrListenerError encounters a general error.
class NcrListenerError : public isc::Exception {
public:
NcrListenerError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Exception thrown if an error occurs during IO source open.
class NcrListenerOpenError : public isc::Exception {
public:
NcrListenerOpenError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Exception thrown if an error occurs initiating an IO receive.
class NcrListenerReceiveError : public isc::Exception {
public:
NcrListenerReceiveError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Abstract interface for receiving NameChangeRequests.
///
/// NameChangeListener provides the means to:
/// - Supply a callback to invoke upon receipt of an NCR or a listening
/// error
/// - Start listening using a given IOService instance to process events
/// - Stop listening
///
/// It implements the high level logic flow to listen until a request arrives,
/// invoke the implementation's handler and return to listening for the next
/// request.
///
/// It provides virtual methods that allow derivations supply implementations
/// to open the appropriate IO source, perform a listen, and close the IO
/// source.
///
/// The overall design is based on a callback chain. The listener's caller (the
/// application) supplies an "application" layer callback through which it will
/// receive inbound NameChangeRequests. The listener derivation will supply
/// its own callback to the IO layer to process receive events from its IO
/// source. This is referred to as the NameChangeRequest completion handler.
/// It is through this handler that the NameChangeRequest layer gains access
/// to the low level IO read service results. It is expected to assemble
/// NameChangeRequests from the inbound data and forward them to the
/// application layer by invoking the application layer callback registered
/// with the listener.
///
/// The application layer callback is structured around a nested class,
/// RequestReceiveHandler. It consists of single, abstract operator() which
/// accepts a result code and a pointer to a NameChangeRequest as parameters.
/// In order to receive inbound NCRs, a caller implements a derivation of the
/// RequestReceiveHandler and supplies an instance of this derivation to the
/// NameChangeListener constructor. This "registers" the handler with the
/// listener.
///
/// To begin listening, the caller invokes the listener's startListener()
/// method, passing in an IOService instance. This in turn will pass the
/// IOService into the virtual method, open(). The open method is where the
/// listener derivation performs the steps necessary to prepare its IO source
/// for reception (e.g. opening a socket, connecting to a database).
///
/// Assuming the open is successful, startListener will call receiveNext, to
/// initiate an asynchronous receive. This method calls the virtual method,
/// doReceive(). The listener derivation uses doReceive to instigate an IO
/// layer asynchronous receive passing in its IO layer callback to
/// handle receive events from the IO source.
///
/// As stated earlier, the derivation's NameChangeRequest completion handler
/// MUST invoke the application layer handler registered with the listener.
/// This is done by passing in either a success status and a populated
/// NameChangeRequest or an error status and an empty request into the
/// listener's invokeRecvHandler method. This is the mechanism by which the
/// listener's caller is handed inbound NCRs.
class NameChangeListener {
public:
/// @brief Defines the outcome of an asynchronous NCR receive
enum Result {
SUCCESS,
TIME_OUT,
STOPPED,
ERROR
};
/// @brief Abstract class for defining application layer receive callbacks.
///
/// Applications which will receive NameChangeRequests must provide a
/// derivation of this class to the listener constructor in order to
/// receive NameChangeRequests.
class RequestReceiveHandler : public boost::enable_shared_from_this<RequestReceiveHandler> {
public:
/// @brief Function operator implementing a NCR receive callback.
///
/// This method allows the application to receive the inbound
/// NameChangeRequests. It is intended to function as a hand off of
/// information and should probably not be time-consuming.
///
/// @param result contains that receive outcome status.
/// @param ncr is a pointer to the newly received NameChangeRequest if
/// result is NameChangeListener::SUCCESS. It is indeterminate other
/// wise.
///
/// @throw This method MUST NOT throw.
virtual void operator()(const Result result,
NameChangeRequestPtr& ncr) = 0;
virtual ~RequestReceiveHandler() {
}
};
/// @brief Defines a smart pointer to an instance of a request receive handler.
typedef boost::shared_ptr<RequestReceiveHandler> RequestReceiveHandlerPtr;
/// @brief Constructor
///
/// @param recv_handler is a pointer the application layer handler to be
/// invoked each time a NCR is received or a receive error occurs.
NameChangeListener(RequestReceiveHandlerPtr recv_handler);
/// @brief Destructor
virtual ~NameChangeListener() {
};
/// @brief Prepares the IO for reception and initiates the first receive.
///
/// Calls the derivation's open implementation to initialize the IO layer
/// source for receiving inbound requests. If successful, it starts the
/// first asynchronous read by receiveNext.
///
/// @param io_service is the IOService that will handle IO event processing.
///
/// @throw NcrListenError if the listener is already "listening" or
/// in the event the open or doReceive methods fail.
void startListening(const isc::asiolink::IOServicePtr& io_service);
/// @brief Closes the IO source and stops listen logic.
///
/// Calls the derivation's implementation of close and marks the state
/// as not listening.
void stopListening();
protected:
/// @brief Initiates an asynchronous receive
///
/// Sets context information to indicate that IO is in progress and invokes
/// the derivation's asynchronous receive method, doReceive. Note doReceive
/// should not be called outside this method to ensure context information
/// integrity.
///
/// @throw Derivation's doReceive method may throw isc::Exception upon
/// error.
void receiveNext();
/// @brief Calls the NCR receive handler registered with the listener.
///
/// This is the hook by which the listener's caller's NCR receive handler
/// is called. This method MUST be invoked by the derivation's
/// implementation of doReceive.
///
/// NOTE:
/// The handler invoked by this method MUST NOT THROW. The handler is
/// at application level and should trap and handle any errors at
/// that level, rather than throw exceptions. If an error has occurred
/// prior to invoking the handler, it will be expressed in terms a failed
/// result being passed to the handler, not a throw. Therefore any
/// exceptions at the handler level are application issues and should be
/// dealt with at that level.
///
/// This method does wrap the handler invocation within a try-catch
/// block as a fail-safe. The exception will be logged but the
/// receive logic will continue. What this implies is that continued
/// operation may or may not succeed as the application has violated
/// the interface contract.
///
/// @param result contains that receive outcome status.
/// @param ncr is a pointer to the newly received NameChangeRequest if
/// result is NameChangeListener::SUCCESS. It is indeterminate other
/// wise.
void invokeRecvHandler(const Result result, NameChangeRequestPtr& ncr);
/// @brief Abstract method which opens the IO source for reception.
///
/// The derivation uses this method to perform the steps needed to
/// prepare the IO source to receive requests.
///
/// @param io_service is the IOService that process IO events.
///
/// @throw If the implementation encounters an error it MUST
/// throw it as an isc::Exception or derivative.
virtual void open(const isc::asiolink::IOServicePtr& io_service) = 0;
/// @brief Abstract method which closes the IO source.
///
/// The derivation uses this method to perform the steps needed to
/// "close" the IO source.
///
/// @throw If the implementation encounters an error it MUST
/// throw it as an isc::Exception or derivative.
virtual void close() = 0;
/// @brief Initiates an IO layer asynchronous read.
///
/// The derivation uses this method to perform the steps needed to
/// initiate an asynchronous read of the IO source with the
/// derivation's IO layer handler as the IO completion callback.
///
/// @throw If the implementation encounters an error it MUST
/// throw it as an isc::Exception or derivative.
virtual void doReceive() = 0;
public:
/// @brief Returns true if the listener is listening, false otherwise.
///
/// A true value indicates that the IO source has been opened successfully,
/// and that receive loop logic is active. This implies that closing the
/// IO source will interrupt that operation, resulting in a callback
/// invocation.
///
/// @return The listening mode.
bool amListening() const {
return (listening_);
}
/// @brief Returns true if the listener has an IO call in progress.
///
/// A true value indicates that the listener has an asynchronous IO in
/// progress which will complete at some point in the future. Completion
/// of the call will invoke the registered callback. It is important to
/// understand that the listener and its related objects should not be
/// deleted while there is an IO call pending. This can result in the
/// IO service attempting to invoke methods on objects that are no longer
/// valid.
///
/// @return The IO pending flag.
bool isIoPending() const {
return (io_pending_);
}
private:
/// @brief Sets the listening indicator to the given value.
///
/// Note, this method is private as it is used the base class is solely
/// responsible for managing the state.
///
/// @param value is the new value to assign to the indicator.
void setListening(bool value) {
listening_ = value;
}
/// @brief Indicates if the listener is in listening mode.
bool listening_;
/// @brief Indicates that listener has an async IO pending completion.
bool io_pending_;
/// @brief Application level NCR receive completion handler.
RequestReceiveHandlerPtr recv_handler_;
};
/// @brief Defines a smart pointer to an instance of a listener.
typedef boost::shared_ptr<NameChangeListener> NameChangeListenerPtr;
/// @brief Thrown when a NameChangeSender encounters an error.
class NcrSenderError : public isc::Exception {
public:
NcrSenderError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Exception thrown if an error occurs during IO source open.
class NcrSenderOpenError : public isc::Exception {
public:
NcrSenderOpenError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Exception thrown if an error occurs initiating an IO send.
class NcrSenderQueueFull : public isc::Exception {
public:
NcrSenderQueueFull(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Exception thrown if an error occurs initiating an IO send.
class NcrSenderSendError : public isc::Exception {
public:
NcrSenderSendError(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Abstract interface for sending NameChangeRequests.
///
/// NameChangeSender provides the means to:
/// - Supply a callback to invoke upon completing the delivery of an NCR or a
/// send error
/// - Start sending using a given IOService instance to process events
/// - Queue NCRs for delivery
/// - Stop sending
///
/// It implements the high level logic flow to queue requests for delivery,
/// and ship them one at a time, waiting for the send to complete prior to
/// sending the next request in the queue. If a send fails, the request
/// will remain at the front of queue and the send will be retried
/// endlessly unless the caller dequeues the request. Note, it is presumed that
/// a send failure is some form of IO error such as loss of connectivity and
/// not a message content error. It should not be possible to queue an invalid
/// message.
///
/// It should be noted that once a request is placed onto the send queue it
/// will remain there until one of three things occur:
/// * It is successfully delivered
/// * @c NameChangeSender::skipNext() is called
/// * @c NameChangeSender::clearSendQueue() is called
///
/// The queue contents are preserved across start and stop listening
/// transitions. This is to provide for error recovery without losing
/// undelivered requests.
/// It provides virtual methods so derivations may supply implementations to
/// open the appropriate IO sink, perform a send, and close the IO sink.
///
/// The overall design is based on a callback chain. The sender's caller (the
/// application) supplies an "application" layer callback through which it will
/// be given send completion notifications. The sender derivation will employ
/// its own callback at the IO layer to process send events from its IO sink.
/// This callback is expected to forward the outcome of each asynchronous send
/// to the application layer by invoking the application layer callback
/// registered with the sender.
///
/// The application layer callback is structured around a nested class,
/// RequestSendHandler. It consists of single, abstract operator() which
/// accepts a result code and a pointer to a NameChangeRequest as parameters.
/// In order to receive send completion notifications, a caller implements a
/// derivation of the RequestSendHandler and supplies an instance of this
/// derivation to the NameChangeSender constructor. This "registers" the
/// handler with the sender.
///
/// To begin sending, the caller invokes the listener's startSending()
/// method, passing in an IOService instance. This in turn will pass the
/// IOService into the virtual method, open(). The open method is where the
/// sender derivation performs the steps necessary to prepare its IO sink for
/// output (e.g. opening a socket, connecting to a database). At this point,
/// the sender is ready to send messages.
///
/// In order to send a request, the application layer invokes the sender
/// method, sendRequest(), passing in the NameChangeRequest to send. This
/// method places the request onto the back of the send queue, and then invokes
/// the sender method, sendNext().
///
/// If there is already a send in progress when sendNext() is called, the method
/// will return immediately rather than initiate the next send. This is to
/// ensure that sends are processed sequentially.
///
/// If there is not a send in progress and the send queue is not empty,
/// the sendNext method will pass the NCR at the front of the send queue into
/// the virtual doSend() method.
///
/// The sender derivation uses this doSend() method to instigate an IO layer
/// asynchronous send with its IO layer callback to handle send events from its
/// IO sink.
///
/// As stated earlier, the derivation's IO layer callback MUST invoke the
/// application layer handler registered with the sender. This is done by
/// passing in a status indicating the outcome of the send into the sender's
/// invokeSendHandler method. This is the mechanism by which the sender's
/// caller is handed outbound notifications.
/// After invoking the application layer handler, the invokeSendHandler method
/// will call the sendNext() method to initiate the next send. This ensures
/// that requests continue to dequeue and ship.
///
class NameChangeSender {
public:
/// @brief Defines the type used for the request send queue.
typedef std::deque<NameChangeRequestPtr> SendQueue;
/// @brief Defines a default maximum number of entries in the send queue.
static const size_t MAX_QUEUE_DEFAULT = 1024;
/// @brief Defines the outcome of an asynchronous NCR send.
enum Result {
SUCCESS,
TIME_OUT,
STOPPED,
ERROR
};
/// @brief Abstract class for defining application layer send callbacks.
///
/// Applications which will send NameChangeRequests must provide a
/// derivation of this class to the sender constructor in order to
/// receive send outcome notifications.
class RequestSendHandler : public boost::enable_shared_from_this<RequestSendHandler> {
public:
/// @brief Function operator implementing a NCR send callback.
///
/// This method allows the application to receive the outcome of
/// each send. It is intended to function as a hand off of information
/// and should probably not be time-consuming.
///
/// @param result contains that send outcome status.
/// @param ncr is a pointer to the NameChangeRequest that was
/// delivered (or attempted).
///
/// @throw This method MUST NOT throw.
virtual void operator ()(const Result result,
NameChangeRequestPtr& ncr) = 0;
virtual ~RequestSendHandler() {
}
};
/// @brief Defines a smart pointer to an instance of a request send handler.
typedef boost::shared_ptr<RequestSendHandler> RequestSendHandlerPtr;
/// @brief Constructor
///
/// @param send_handler is a pointer the application layer handler to be
/// invoked each time a NCR send attempt completes.
/// @param send_queue_max is the maximum number of entries allowed in the
/// send queue. Once the maximum number is reached, all calls to
/// sendRequest will fail with an exception.
NameChangeSender(RequestSendHandlerPtr send_handler,
size_t send_queue_max = MAX_QUEUE_DEFAULT);
/// @brief Destructor
virtual ~NameChangeSender() {
}
/// @brief Prepares the IO for transmission.
///
/// Calls the derivation's open implementation to initialize the IO layer
/// sink for sending outbound requests.
///
/// @param io_service is the IOService that will handle IO event processing.
///
/// @throw NcrSenderError if the sender is already "sending" or
/// NcrSenderOpenError if the open fails.
void startSending(const isc::asiolink::IOServicePtr& io_service);
/// @brief Closes the IO sink and stops send logic.
///
/// Calls the derivation's implementation of close and marks the state
/// as not sending.
void stopSending();
/// @brief Queues the given request to be sent.
///
/// The given request is placed at the back of the send queue and then
/// sendNext is invoked.
///
/// @param ncr is the NameChangeRequest to send.
///
/// @throw NcrSenderError if the sender is not in sending state or
/// the request is empty; NcrSenderQueueFull if the send queue has reached
/// capacity.
void sendRequest(NameChangeRequestPtr& ncr);
/// @brief Move all queued requests from a given sender into the send queue
///
/// Moves all of the entries in the given sender's queue and places them
/// into send queue. This provides a mechanism of reassigning queued
/// messages from one sender to another. This is useful for dealing with
/// dynamic configuration changes.
///
/// @param source_sender from whom the queued messages will be taken
///
/// @throw NcrSenderError if either sender is in send mode, if the number of
/// messages in the source sender's queue is larger than this sender's
/// maximum queue size, or if this sender's queue is not empty.
void assumeQueue(NameChangeSender& source_sender);
/// @brief Returns a file descriptor suitable for use with select
///
/// The value returned is an open file descriptor which can be used with
/// select() system call to monitor the sender for IO events. This allows
/// NameChangeSenders to be used in applications which use select, rather
/// than IOService to wait for IO events to occur.
///
/// @warning Attempting other use of this value may lead to unpredictable
/// behavior in the sender.
///
/// @return Returns an "open" file descriptor
///
/// @throw NcrSenderError if the sender is not in send mode,
virtual int getSelectFd() = 0;
/// @brief Returns whether or not the sender has IO ready to process.
///
/// @return true if the sender has at IO ready, false otherwise.
virtual bool ioReady() = 0;
private:
/// @brief Prepares the IO for transmission in a thread safe context.
///
/// @param io_service is the IOService that will handle IO event processing.
void startSendingInternal(const isc::asiolink::IOServicePtr& io_service);
/// @brief Queues the given request to be sent in a thread safe context.
///
/// @param ncr is the NameChangeRequest to send.
///
/// @throw NcrSenderQueueFull if the send queue has reached capacity.
void sendRequestInternal(NameChangeRequestPtr& ncr);
/// @brief Move all queued requests from a given sender into the send queue
/// in a thread safe context.
///
/// @param source_sender from whom the queued messages will be taken
///
/// @throw NcrSenderError if this sender's queue is not empty.
void assumeQueueInternal(NameChangeSender& source_sender);
/// @brief Calls the NCR send completion handler registered with the
/// sender in a thread safe context.
///
/// @param result contains that send outcome status.
void invokeSendHandlerInternal(const NameChangeSender::Result result);
/// @brief Removes the request at the front of the send queue in a thread
/// safe context.
void skipNextInternal();
/// @brief Returns the number of entries currently in the send queue in a
/// thread safe context.
///
/// @return the queue size.
size_t getQueueSizeInternal() const;
/// @brief Returns the entry at a given position in the queue in a thread
/// safe context.
///
/// @return Pointer reference to the queue entry.
///
/// @throw NcrSenderError if the given index is beyond the
/// end of the queue.
const NameChangeRequestPtr& peekAtInternal(const size_t index) const;
protected:
/// @brief Dequeues and sends the next request on the send queue in a thread
/// safe context.
///
/// If there is already a send in progress just return. If there is not
/// a send in progress and the send queue is not empty the grab the next
/// message on the front of the queue and call doSend().
void sendNext();
/// @brief Calls the NCR send completion handler registered with the
/// sender.
///
/// This is the hook by which the sender's caller's NCR send completion
/// handler is called. This method MUST be invoked by the derivation's
/// implementation of doSend. Note that if the send was a success, the
/// entry at the front of the queue is removed from the queue.
/// If not we leave it there so we can retry it. After we invoke the
/// handler we clear the pending ncr value and queue up the next send.
///
/// NOTE:
/// The handler invoked by this method MUST NOT THROW. The handler is
/// application level logic and should trap and handle any errors at
/// that level, rather than throw exceptions. If IO errors have occurred
/// prior to invoking the handler, they are expressed in terms a failed
/// result being passed to the handler. Therefore any exceptions at the
/// handler level are application issues and should be dealt with at that
/// level.
///
/// This method does wrap the handler invocation within a try-catch
/// block as a fail-safe. The exception will be logged but the
/// send logic will continue. What this implies is that continued
/// operation may or may not succeed as the application has violated
/// the interface contract.
///
/// @param result contains that send outcome status.
void invokeSendHandler(const NameChangeSender::Result result);
/// @brief Abstract method which opens the IO sink for transmission.
///
/// The derivation uses this method to perform the steps needed to
/// prepare the IO sink to send requests.
///
/// @param io_service is the IOService that process IO events.
///
/// @throw If the implementation encounters an error it MUST
/// throw it as an isc::Exception or derivative.
virtual void open(const isc::asiolink::IOServicePtr& io_service) = 0;
/// @brief Abstract method which closes the IO sink.
///
/// The derivation uses this method to perform the steps needed to
/// "close" the IO sink.
///
/// @throw If the implementation encounters an error it MUST
/// throw it as an isc::Exception or derivative.
virtual void close() = 0;
/// @brief Initiates an IO layer asynchronous send
///
/// The derivation uses this method to perform the steps needed to
/// initiate an asynchronous send through the IO sink of the given NCR.
///
/// @param ncr is a pointer to the NameChangeRequest to send.
/// derivation's IO layer handler as the IO completion callback.
///
/// @throw If the implementation encounters an error it MUST
/// throw it as an isc::Exception or derivative.
virtual void doSend(NameChangeRequestPtr& ncr) = 0;
public:
/// @brief Removes the request at the front of the send queue
///
/// This method can be used to avoid further retries of a failed
/// send. It is provided primarily as a just-in-case measure. Since
/// a failed send results in the same request being retried continuously
/// this method makes it possible to remove that entry, causing the
/// subsequent entry in the queue to be attempted on the next send.
/// It is presumed that sends will only fail due to some sort of
/// communications issue. In the unlikely event that a request is
/// somehow tainted and causes an send failure based on its content,
/// this method provides a means to remove the message.
void skipNext();
/// @brief Flushes all entries in the send queue
///
/// This method can be used to discard all of the NCRs currently in the
/// the send queue. Note it may not be called while the sender is in
/// the sending state.
///
/// @throw NcrSenderError if called and sender is in sending state.
void clearSendQueue();
/// @brief Returns true if the sender is in send mode, false otherwise.
///
/// A true value indicates that the IO sink has been opened successfully,
/// and that send loop logic is active.
///
/// @return The send mode.
bool amSending() const {
return (sending_);
}
/// @brief Returns true when a send is in progress.
///
/// A true value indicates that a request is actively in the process of
/// being delivered.
///
/// @return The send in progress flag.
bool isSendInProgress() const;
/// @brief Returns the maximum number of entries allowed in the send queue.
///
/// @return The queue maximum size.
size_t getQueueMaxSize() const {
return (send_queue_max_);
}
/// @brief Sets the maximum queue size to the given value.
///
/// Sets the maximum number of entries allowed in the queue to the
/// the given value.
///
/// @param new_max the new value to use as the maximum
///
/// @throw NcrSenderError if the value is less than one.
void setQueueMaxSize(const size_t new_max);
/// @brief Returns the number of entries currently in the send queue.
///
/// @return The queue size.
size_t getQueueSize() const;
/// @brief Returns the entry at a given position in the queue.
///
/// Note that the entry is not removed from the queue.
///
/// @param index the index of the entry in the queue to fetch.
/// Valid values are 0 (front of the queue) to (queue size - 1).
///
/// @return Pointer reference to the queue entry.
///
/// @throw NcrSenderError if the given index is beyond the
/// end of the queue.
const NameChangeRequestPtr& peekAt(const size_t index) const;
/// @brief Processes sender IO events
///
/// Executes at most one ready handler on the sender's IO service. If
/// no handlers are ready it returns immediately.
///
/// @warning - Running all ready handlers, in theory, could process all
/// messages currently queued.
///
/// NameChangeSender daisy chains requests together in its completion
/// by one message completion's handler initiating the next message's send.
/// When using UDP, a send immediately marks its event handler as ready
/// to run. If this occurs inside a call to ioservice::poll() or run(),
/// that event will also be run. If that handler calls UDP send then
/// that send's handler will be marked ready and executed and so on. If
/// there were 1000 messages in the queue then all them would be sent from
/// within the context of one call to runReadyIO().
/// By running only one handler at time, we ensure that NCR IO activity
/// doesn't starve other processing. It is unclear how much of a real
/// threat this poses but for now it is best to err on the side of caution.
virtual void runReadyIO();
protected:
/// @brief Returns a reference to the send queue.
///
/// @return The send queue.
SendQueue& getSendQueue() {
return (send_queue_);
}
private:
/// @brief Sets the sending indicator to the given value.
///
/// Note, this method is private as it is used the base class is solely
/// responsible for managing the state.
///
/// @param value is the new value to assign to the indicator.
void setSending(bool value) {
sending_ = value;
}
protected:
/// @brief Pointer to the IOService currently being used by the sender.
/// @note We need to remember the io_service but we receive it by
/// reference. Use a raw pointer to store it. This value should never be
/// exposed and is only valid while in send mode.
asiolink::IOServicePtr io_service_;
private:
/// @brief Boolean indicator which tracks sending status.
bool sending_;
/// @brief A pointer to registered send completion handler.
RequestSendHandlerPtr send_handler_;
/// @brief Maximum number of entries permitted in the send queue.
size_t send_queue_max_;
/// @brief Queue of the requests waiting to be sent.
SendQueue send_queue_;
/// @brief Pointer to the request which is in the process of being sent.
NameChangeRequestPtr ncr_to_send_;
/// @brief The mutex used to protect internal state.
const boost::scoped_ptr<std::mutex> mutex_;
};
/// @brief Defines a smart pointer to an instance of a sender.
typedef boost::shared_ptr<NameChangeSender> NameChangeSenderPtr;
} // namespace dhcp_ddns
} // namespace isc
#endif