/include/socket.hh
C++ Header | 449 lines | 75 code | 56 blank | 318 comment | 4 complexity | 9a1f5c7a9289eee8f76041eaf1cdeea5 MD5 | raw file
- /*
- * socket.hh
- *
- * Copyright (c) 2012, Heiko Wundram.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of modelnine.org, protoproxy nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL HEIKO WUNDRAM BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * Created on: 31.01.2012
- * Author: Heiko Wundram <modelnine@modelnine.org>
- */
- #ifndef SOCKET_HH_
- #define SOCKET_HH_
- #include <sys/socket.h>
- #include <sys/types.h>
- #include <cstdarg>
- #include <cstddef>
- #include <poll.h>
- #include "logger.hh"
- #include "types.hh"
- namespace protoproxy {
- /**
- * Abstract base class representing a socket (file descriptor) and its
- * attached callbacks. This class implements no own functionality, but rather
- * requires derived classes to implement actual callbacks/functions on the
- * socket.
- */
- class socket :
- protected logger {
- /**
- * Reactor manages our internal state, and as such requires access to
- * internal data elements of the socket.
- */
- friend class reactor;
- /**
- * Proxy socket as a specialized subclass of socket may access internal
- * fields of the socket directly.
- */
- friend class proxy_socket;
- private:
- /**
- * Hidden methods.
- */
- socket(const socket& other);
- socket& operator =(const socket& other);
- /**
- * Boolean indicating whether this socket wants to be destroyed by the
- * reactor when it's attached to it and the reactor cleans up.
- */
- const bool m_destroy;
- /**
- * Reactor this socket is bound to. In case there is no reactor currently
- * bound which manages callbacks on the socket, this is NULL.
- */
- reactor* m_reactor;
- /**
- * Previous socket in reactor chain. Pointer is only valid when a reactor
- * is currently setup.
- */
- socket* m_prev;
- /**
- * Next socket in reactor chain. Pointer is only valid when a reactor is
- * currently set up.
- */
- socket* m_next;
- /**
- * File descriptor bound to this socket. In case the file descriptor is
- * unbound, this is -1, in case a file descriptor is bound and a reactor
- * is set up, this is an index into the reactors m_pollfds array, otherwise
- * it is the plain file descriptor of the socket.
- */
- int m_fd;
- /**
- * Local event mask for the socket, managed by the socket when it's bound
- * and unbound from a reactor. When the socket is bound, this mask gets
- * transferred to the reactor poll descriptor and remains in sync with
- * it.
- */
- short m_events;
- /**
- * Identifier used to identify the next timeout. This is a large integer
- * type which makes it improbable that timeout IDs will begin overlapping.
- */
- std::size_t m_nexttimeoutid;
- /**
- * Timeouts currently attached to this socket, as a time measured against
- * CLOCK_REALTIME. This is a singly linked list of socket timeouts,
- * ordered in ascending timeout and time of insertion. When there are no
- * timeouts pending, this is NULL.
- */
- socket_timeout* m_nexttimeout;
- /**
- * Pointer to m_next in the last timeout instance registered in the
- * timeout singly linked list starting with m_nexttimeout. This field is
- * only valid when timeouts are set up with m_nexttimeout.
- */
- socket_timeout** m_lasttimeout;
- /**
- * Handle attachment of this socket to a reactor. This callback is called
- * when the attachment process has been completed successfully. The default
- * implementation does nothing.
- */
- virtual void handle_attach();
- /**
- * Handle errors occuring on this socket. The default implementation of
- * this method removes the socket from the reactor and destroys it in
- * case the socket is destructible, otherwise it just detaches the
- * socket from the reactor. Error cannot be controlled, and will always
- * be delivered by the reactor.
- */
- virtual void handle_error();
- /**
- * Handle reads on this socket. The default implementation of this method
- * throws an exception. This is only delivered in case the event mask
- * requests to be informed of read events (want_read()).
- */
- virtual void handle_read();
- #if POLLRDBAND != POLLPRI
- /**
- * Handle priority reads on this socket. The default implementation of this
- * method throws an exception. This is only delivered in case the event
- * mask requests to be informed of priority read events
- * (want_priority_read()).
- */
- virtual void handle_priority_read();
- #endif
- /**
- * Handle high priority reads on this socket. The default implementation of
- * this method throws an exception. This is only delivered in case the
- * event mask requests to be informed of high priority read events
- * (want_high_priority_read()).
- */
- virtual void handle_high_priority_read();
- /**
- * Handle writes on this socket. The default implementation of this method
- * throws an exception. This is only delivered in case the event mask
- * requests to be informed of write events (want_write()).
- */
- virtual void handle_write();
- #if POLLWRBAND != POLLWRNORM
- /**
- * Handle priority writes on this socket. The default implementation of
- * this method throws an exception. This is only delivered in case the
- * event mask requests to be informed of priority write events
- * (want_priority_write()).
- */
- virtual void handle_priority_write();
- #endif
- /**
- * Handle hangup on this socket. The default implementation does nothing.
- * Hangup cannot be controlled, and will always be delivered when
- * signalled by the reactor.
- */
- virtual void handle_hangup();
- /**
- * Handle timeout signalled by the internal timeout parameter, processing
- * the socket. The default implementation of this method throws an
- * exception. The identifier passed as ID is the identifier that was
- * granted on timeout registration.
- * @param id Identifier of the timeout that's expired.
- */
- virtual void handle_timeout(std::size_t id);
- /**
- * Handle detachment of the socket from a reactor. The callback is called
- * when the detachment process has completed. The default implementation
- * does nothing. This callback is not called when the socket is detached
- * from the reactor in its destructor, or the socket is being destroyed
- * with a reactor still bound.
- */
- virtual void handle_detach();
- /**
- * Implementation of logging target for sockets. This dispatches the log
- * message to the reactor this socket is currently bound to (in case a
- * reactor is set up), otherwise it handles the message through a generic
- * logging channel.
- * @param level Level of message to log.
- * @param domain Domain of the log message.
- * @param msg Message to log.
- * @param args Arguments passed to message as a varargs set.
- */
- virtual void log(loglevel level, logdomain domain, const char* msg,
- va_list args) __attribute__((format(printf, 4, 0)));
- protected:
- /**
- * Initialize a new socket which is not bound to a reactor and has no
- * file descriptor bound to it. In case this socket is designed to be
- * used as a stack variable, pass destroy as false.
- * @param destroy Boolean indicating destruction by reactor.
- * @param defdomain Default domain of log messages for socket.
- */
- explicit socket(bool destroy = true, logdomain defdomain = SOCKET);
- /**
- * Create a new socket of the specified domain, type and protocol, which is
- * not bound to a reactor. In case this socket is designed to be used as
- * a stack variable, pass destroy as false.
- * @param domain Domain of socket to create.
- * @param type Type of socket to create.
- * @param protocol Protocol of socket to create.
- * @param destroy Boolean indicating destruction by reactor.
- * @param defdomain Default domain of log messages for socket.
- */
- socket(int domain, int type, int protocol = 0, bool destroy = true,
- logdomain defdomain = SOCKET);
- /**
- * Create a new socket bound to the passed open file descriptor (which must
- * be an integer), setting it up as appropriate. In case this socket is
- * designed to be used as a stack variable, pass destroy as false.
- * @param fd File descriptor to bind socket to.
- * @param defdomain Default domain of log messages for socket.
- */
- explicit socket(int fd, bool destroy = true, logdomain defdomain = SOCKET);
- /**
- * Fetch the reactor that is attached to this socket, returning a reference
- * to it.
- * @return Reference to bound reactor.
- */
- reactor& reactor_() const;
- /**
- * Set the relevant flags in the current event mask for this socket to
- * enable reads. It is an error to set flags on socket when it is not
- * opened.
- * @param read Whether to set or clear the read flag.
- */
- void want_read(bool read = true);
- #if POLLRDBAND != POLLPRI
- /**
- * Set the relevant flags in the current event mask for this socket to
- * enable priority reads. It is an error to set flags on socket when it is
- * not opened.
- * @param read Whether to set or clear the priority read flag.
- */
- void want_priority_read(bool read = true);
- #endif
- /**
- * Set the relevant flags in the current event mask for this socket to
- * enable high priority reads. It is an error to set flags on socket when
- * it is not opened.
- * @param read Whether to set or clear the high priority read flag.
- */
- void want_high_priority_read(bool read = true);
- /**
- * Set the relevant flags in the current event mask for this socket to
- * enable writes. It is an error to set flags on socket when
- * it is not opened.
- * @param write Whether to set or clear the write flag.
- */
- void want_write(bool write = true);
- #if POLLWRBAND != POLLWRNORM
- /**
- * Set the relevant flags in the current event mask for this socket to
- * enable priority writes. It is an error to set flags on socket when
- * it is not opened.
- * @param write Whether to set or clear the priority write flag.
- */
- void want_priority_write(bool write = true);
- #endif
- /**
- * Add a new timeout in the specified number of milli-seconds, to dispatch
- * on this socket. The return value indicates the timeout ID of the newly
- * attached timeout. Be aware that timeouts will only be triggered when the
- * socket is actually bound to a reactor. When passing zero or negative
- * timeouts, the timeout expires immediately when the reactor loop next
- * dispatches timeouts to this socket.
- * @param msecs Milli-seconds after which the timeout expires.
- * @return Identifier which represents this timeout instance.
- */
- std::size_t addtimeout(int msecs);
- /**
- * Cancel the timeout specified by the passed timeout ID. In case the
- * timeout has already expired, this method throws an exception. As
- * canceling a timeout needs to walk the entire tree, this is an expensive
- * operation.
- * @param timeoutid Timeout identifier to cancel.
- */
- void canceltimeout(std::size_t timeoutid);
- /**
- * Create a new socket of the specified domain, type and protocol. This
- * simply dispatches to the underlying operating system callbacks to create
- * the file descriptor. It is only valid to call create when the socket is
- * not created (open()) yet.
- * @param domain Domain of socket to create.
- * @param type Type of socket to create.
- * @param protocol Protocol of socket to create.
- */
- void create(int domain, int type, int protocol = 0);
- /**
- * Set specified socket option to the passed integer value, calling to
- * the base option setter.
- * @param level Level of socket option.
- * @param optname Option name.
- * @param val Value to set up.
- */
- void setsockopt(int level, int optname, const int& val);
- /**
- * Bind the current socket to the passed address, setting up the binding
- * as specified. It is only valid to bind a socket when the socket has
- * been created.
- * @param addr Address to bind to.
- * @param addrlen Length of address in address structure.
- */
- void bind(const sockaddr& addr, socklen_t addrlen);
- /**
- * Start listening on the current socket, with the specified backlog. It
- * is only valid to listen on a socket in case it has been created.
- * @param backlog Backlog to start listening with.
- */
- void listen(int backlog);
- /**
- * Accept an incoming connection on the currently open socket, returning
- * the file descriptor of the accepted connection as integer. The address
- * of the incoming connection is set up in the passed reference parameters.
- * @param addr Address of incoming connection.
- * @param addrlen Length of address.
- * @return File descriptor accepted on socket.
- */
- int accept(sockaddr& addr, socklen_t& addrlen);
- /**
- * Receive data from socket, returning the length of data actually received
- * on input. The actual length received is returned as argument. When data
- * can't be received because the operation would block, returns zero, if
- * the remote end disconnected the receiving channel, returns -1. All other
- * errors cause an exception.
- * @param buf Buffer to receive to.
- * @param len Length of space available in buffer.
- * @return Actual size read.
- */
- ssize_t read(void* buf, std::size_t len);
- /**
- * Write out the specified set of data to the socket, returning the actual
- * amount of length able to be written. In case no more data can be written
- * because the remote end has closed the channel, returns -1.
- * @param buf Buffer to send from.
- * @param len Length of data in buffer to send.
- * @return Actual size sent.
- */
- ssize_t write(const void* buf, std::size_t len);
- /**
- * Close the current socket, releasing any bound file descriptor and
- * resetting any remaining internal state for the socket. In case the
- * socket is currently not open, this raises an exception.
- */
- void close();
- public:
- /**
- * Clean up the socket, destroying any remaining internal state of the
- * socket in the process. This detaches the socket from a reactor in case
- * it is still bound to one, but will not call handle_detach() on the
- * socket to signal the detachment.
- */
- virtual ~socket();
- /**
- * Check whether socket is opened, returning a boolean indicating that a
- * descriptor is bound.
- * @return Flag indicating that the descriptor is bound.
- */
- bool open() const;
- /**
- * Check whether socket is currently in attached state, returning an
- * appropriate boolean.
- * @return Boolean indicating whether socket is attached.
- */
- bool attached() const;
- /**
- * Detach the current socket from a possibly attached reactor, clearing
- * out any remaining reactor bindings. This method gives strong exception
- * safety in that the reactor and socket state are unchanged in case it
- * fails, and the socket is completely detached in case it succeeds.
- */
- void detach();
- };
- }
- #endif /* SOCKET_HH_ */