LCOV - code coverage report
Current view: top level - eventdispatcher - tcp_client.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 98 1.0 %
Date: 2019-08-10 01:48:51 Functions: 2 12 16.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2012-2019  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/eventdispatcher
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program; if not, write to the Free Software
      18             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19             : 
      20             : /** \file
      21             :  * \brief Event dispatch class.
      22             :  *
      23             :  * Class used to handle events.
      24             :  */
      25             : 
      26             : 
      27             : // self
      28             : //
      29             : #include    "eventdispatcher/tcp_client.h"
      30             : 
      31             : #include    "eventdispatcher/exception.h"
      32             : #include    "eventdispatcher/utils.h"
      33             : 
      34             : 
      35             : // snaplogger lib
      36             : //
      37             : #include    <snaplogger/message.h>
      38             : 
      39             : 
      40             : //// C lib
      41             : ////
      42             : #include    <netdb.h>
      43             : #include    <arpa/inet.h>
      44             : #include    <string.h>
      45             : 
      46             : 
      47             : // last include
      48             : //
      49             : #include    <snapdev/poison.h>
      50             : 
      51             : 
      52             : 
      53             : namespace ed
      54             : {
      55             : 
      56             : 
      57             : 
      58             : /** \class tcp_client
      59             :  * \brief Create a client socket and connect to a server.
      60             :  *
      61             :  * This class is a client socket implementation used to connect to a server.
      62             :  * The server is expected to be running at the time the client is created
      63             :  * otherwise it fails connecting.
      64             :  *
      65             :  * This class is not appropriate to connect to a server that may come and go
      66             :  * over time.
      67             :  */
      68             : 
      69             : /** \brief Contruct a tcp_client object.
      70             :  *
      71             :  * The tcp_client constructor initializes a TCP client object by connecting
      72             :  * to the specified server. The server is defined with the \p addr and
      73             :  * \p port specified as parameters.
      74             :  *
      75             :  * \exception tcp_client_server_parameter_error
      76             :  * This exception is raised if the \p port parameter is out of range or the
      77             :  * IP address is an empty string or otherwise an invalid address.
      78             :  *
      79             :  * \exception tcp_client_server_runtime_error
      80             :  * This exception is raised if the client cannot create the socket or it
      81             :  * cannot connect to the server.
      82             :  *
      83             :  * \param[in] addr  The address of the server to connect to. It must be valid.
      84             :  * \param[in] port  The port the server is listening on.
      85             :  */
      86           0 : tcp_client::tcp_client(std::string const & addr, int port)
      87             :     : f_socket(-1)
      88             :     , f_port(port)
      89           0 :     , f_addr(addr)
      90             : {
      91           0 :     if(f_port < 0 || f_port >= 65536)
      92             :     {
      93           0 :         throw event_dispatcher_invalid_parameter("invalid port for a client socket");
      94             :     }
      95           0 :     if(f_addr.empty())
      96             :     {
      97           0 :         throw event_dispatcher_invalid_parameter("an empty address is not valid for a client socket");
      98             :     }
      99             : 
     100             :     addrinfo hints;
     101           0 :     memset(&hints, 0, sizeof(hints));
     102           0 :     hints.ai_family = AF_UNSPEC;
     103           0 :     hints.ai_socktype = SOCK_STREAM;
     104           0 :     hints.ai_protocol = IPPROTO_TCP;
     105           0 :     std::string const port_str(std::to_string(f_port));
     106           0 :     addrinfo * addrinfo(nullptr);
     107           0 :     int const r(getaddrinfo(addr.c_str(), port_str.c_str(), &hints, &addrinfo));
     108           0 :     raii_addrinfo_t addr_info(addrinfo);
     109           0 :     if(r != 0
     110           0 :     || addrinfo == nullptr)
     111             :     {
     112           0 :         int const e(errno);
     113           0 :         std::string err("getaddrinfo() failed to parse the address or port \"");
     114           0 :         err += addr;
     115           0 :         err += ":";
     116           0 :         err += port_str;
     117           0 :         err += "\" strings (errno: ";
     118           0 :         err += std::to_string(e);
     119           0 :         err += " -- ";
     120           0 :         err += strerror(e);
     121           0 :         err += ")";
     122           0 :         SNAP_LOG_FATAL << err;
     123           0 :         throw event_dispatcher_runtime_error(err);
     124             :     }
     125             : 
     126           0 :     f_socket = socket(addr_info.get()->ai_family, SOCK_STREAM, IPPROTO_TCP);
     127           0 :     if(f_socket < 0)
     128             :     {
     129           0 :         int const e(errno);
     130             :         SNAP_LOG_FATAL
     131           0 :             << "socket() failed to create a socket descriptor (errno: "
     132           0 :             << e
     133           0 :             << " -- "
     134           0 :             << strerror(e)
     135           0 :             << ")";
     136           0 :         throw event_dispatcher_runtime_error("could not create socket for client");
     137             :     }
     138             : 
     139           0 :     if(connect(f_socket, addr_info.get()->ai_addr, addr_info.get()->ai_addrlen) < 0)
     140             :     {
     141           0 :         int const e(errno);
     142             :         SNAP_LOG_FATAL
     143           0 :             << "connect() failed to connect a socket (errno: "
     144           0 :             << e
     145           0 :             << " -- "
     146           0 :             << strerror(e)
     147           0 :             << ")";
     148           0 :         close(f_socket);
     149           0 :         throw event_dispatcher_runtime_error("could not connect client socket to \"" + f_addr + "\"");
     150             :     }
     151           0 : }
     152             : 
     153             : /** \brief Clean up the TCP client object.
     154             :  *
     155             :  * This function cleans up the TCP client object by closing the attached socket.
     156             :  *
     157             :  * \note
     158             :  * DO NOT use the shutdown() call since we may end up forking and using
     159             :  * that connection in the child.
     160             :  */
     161           0 : tcp_client::~tcp_client()
     162             : {
     163           0 :     close(f_socket);
     164           0 : }
     165             : 
     166             : /** \brief Get the socket descriptor.
     167             :  *
     168             :  * This function returns the TCP client socket descriptor. This can be
     169             :  * used to change the descriptor behavior (i.e. make it non-blocking for
     170             :  * example.)
     171             :  *
     172             :  * \return The socket descriptor.
     173             :  */
     174           0 : int tcp_client::get_socket() const
     175             : {
     176           0 :     return f_socket;
     177             : }
     178             : 
     179             : /** \brief Get the TCP client port.
     180             :  *
     181             :  * This function returns the port used when creating the TCP client.
     182             :  * Note that this is the port the server is listening to and not the port
     183             :  * the TCP client is currently connected to.
     184             :  *
     185             :  * \return The TCP client port.
     186             :  */
     187           0 : int tcp_client::get_port() const
     188             : {
     189           0 :     return f_port;
     190             : }
     191             : 
     192             : /** \brief Get the TCP server address.
     193             :  *
     194             :  * This function returns the address used when creating the TCP address as is.
     195             :  * Note that this is the address of the server where the client is connected
     196             :  * and not the address where the client is running (although it may be the
     197             :  * same.)
     198             :  *
     199             :  * Use the get_client_addr() function to retrieve the client's TCP address.
     200             :  *
     201             :  * \return The TCP client address.
     202             :  */
     203           0 : std::string tcp_client::get_addr() const
     204             : {
     205           0 :     return f_addr;
     206             : }
     207             : 
     208             : /** \brief Get the TCP client port.
     209             :  *
     210             :  * This function retrieve the port of the client (used on your computer).
     211             :  * This is retrieved from the socket using the getsockname() function.
     212             :  *
     213             :  * \return The port or -1 if it cannot be determined.
     214             :  */
     215           0 : int tcp_client::get_client_port() const
     216             : {
     217             :     struct sockaddr addr;
     218           0 :     socklen_t len(sizeof(addr));
     219           0 :     int r(getsockname(f_socket, &addr, &len));
     220           0 :     if(r != 0)
     221             :     {
     222           0 :         return -1;
     223             :     }
     224             :     // Note: I know the port is at the exact same location in both
     225             :     //       structures in Linux but it could change on other Unices
     226           0 :     if(addr.sa_family == AF_INET)
     227             :     {
     228             :         // IPv4
     229           0 :         return reinterpret_cast<sockaddr_in *>(&addr)->sin_port;
     230             :     }
     231           0 :     if(addr.sa_family == AF_INET6)
     232             :     {
     233             :         // IPv6
     234           0 :         return reinterpret_cast<sockaddr_in6 *>(&addr)->sin6_port;
     235             :     }
     236           0 :     return -1;
     237             : }
     238             : 
     239             : /** \brief Get the TCP client address.
     240             :  *
     241             :  * This function retrieve the IP address of the client (your computer).
     242             :  * This is retrieved from the socket using the getsockname() function.
     243             :  *
     244             :  * \return The IP address as a string.
     245             :  */
     246           0 : std::string tcp_client::get_client_addr() const
     247             : {
     248             :     struct sockaddr addr;
     249           0 :     socklen_t len(sizeof(addr));
     250           0 :     int const r(getsockname(f_socket, &addr, &len));
     251           0 :     if(r != 0)
     252             :     {
     253           0 :         throw event_dispatcher_runtime_error("address not available");
     254             :     }
     255             :     char buf[BUFSIZ];
     256           0 :     switch(addr.sa_family)
     257             :     {
     258             :     case AF_INET:
     259           0 :         if(len < sizeof(struct sockaddr_in))
     260             :         {
     261           0 :             throw event_dispatcher_runtime_error("address size incompatible (AF_INET)");
     262             :         }
     263           0 :         inet_ntop(AF_INET, &reinterpret_cast<struct sockaddr_in *>(&addr)->sin_addr, buf, sizeof(buf));
     264           0 :         break;
     265             : 
     266             :     case AF_INET6:
     267           0 :         if(len < sizeof(struct sockaddr_in6))
     268             :         {
     269           0 :             throw event_dispatcher_runtime_error("address size incompatible (AF_INET6)");
     270             :         }
     271           0 :         inet_ntop(AF_INET6, &reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_addr, buf, sizeof(buf));
     272           0 :         break;
     273             : 
     274             :     default:
     275           0 :         throw event_dispatcher_runtime_error("unknown address family");
     276             : 
     277             :     }
     278           0 :     return buf;
     279             : }
     280             : 
     281             : /** \brief Read data from the socket.
     282             :  *
     283             :  * A TCP socket is a stream type of socket and one can read data from it
     284             :  * as if it were a regular file. This function reads \p size bytes and
     285             :  * returns. The function returns early if the server closes the connection.
     286             :  *
     287             :  * If your socket is blocking, \p size should be exactly what you are
     288             :  * expecting or this function will block forever or until the server
     289             :  * closes the connection.
     290             :  *
     291             :  * The function returns -1 if an error occurs. The error is available in
     292             :  * errno as expected in the POSIX interface.
     293             :  *
     294             :  * \param[in,out] buf  The buffer where the data is read.
     295             :  * \param[in] size  The size of the buffer.
     296             :  *
     297             :  * \return The number of bytes read from the socket, or -1 on errors.
     298             :  */
     299           0 : int tcp_client::read(char *buf, size_t size)
     300             : {
     301           0 :     return static_cast<int>(::read(f_socket, buf, size));
     302             : }
     303             : 
     304             : 
     305             : /** \brief Read one line.
     306             :  *
     307             :  * This function reads one line from the current location up to the next
     308             :  * '\\n' character. We do not have any special handling of the '\\r'
     309             :  * character.
     310             :  *
     311             :  * The function may return 0 in which case the server closed the connection.
     312             :  *
     313             :  * \param[out] line  The resulting line read from the server.
     314             :  *
     315             :  * \return The number of bytes read from the socket, or -1 on errors.
     316             :  *         If the function returns 0 or more, then the \p line parameter
     317             :  *         represents the characters read on the network.
     318             :  */
     319           0 : int tcp_client::read_line(std::string& line)
     320             : {
     321           0 :     line.clear();
     322           0 :     int len(0);
     323           0 :     for(;;)
     324             :     {
     325             :         char c;
     326           0 :         int r(read(&c, sizeof(c)));
     327           0 :         if(r <= 0)
     328             :         {
     329           0 :             return len == 0 && r < 0 ? -1 : len;
     330             :         }
     331           0 :         if(c == '\n')
     332             :         {
     333           0 :             return len;
     334             :         }
     335           0 :         ++len;
     336           0 :         line += c;
     337             :     }
     338             : }
     339             : 
     340             : 
     341             : /** \brief Write data to the socket.
     342             :  *
     343             :  * A TCP socket is a stream type of socket and one can write data to it
     344             :  * as if it were a regular file. This function writes \p size bytes to
     345             :  * the socket and then returns. This function returns early if the server
     346             :  * closes the connection.
     347             :  *
     348             :  * If your socket is not blocking, less than \p size bytes may be written
     349             :  * to the socket. In that case you are responsible for calling the function
     350             :  * again to write the remainder of the buffer until the function returns
     351             :  * a number of bytes written equal to \p size.
     352             :  *
     353             :  * The function returns -1 if an error occurs. The error is available in
     354             :  * errno as expected in the POSIX interface.
     355             :  *
     356             :  * \param[in] buf  The buffer with the data to send over the socket.
     357             :  * \param[in] size  The number of bytes in buffer to send over the socket.
     358             :  *
     359             :  * \return The number of bytes that were actually accepted by the socket
     360             :  * or -1 if an error occurs.
     361             :  */
     362           0 : int tcp_client::write(const char *buf, size_t size)
     363             : {
     364           0 :     return static_cast<int>(::write(f_socket, buf, size));
     365             : }
     366             : 
     367             : 
     368             : 
     369           6 : } // namespace tcp_client_server
     370             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.12