LCOV - code coverage report
Current view: top level - eventdispatcher - tcp_client.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 100 1.0 %
Date: 2021-09-19 09:06:58 Functions: 2 12 16.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2012-2021  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 Construct 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_port(port)
      88           0 :     , f_addr(addr)
      89             : {
      90           0 :     if(f_port < 0 || f_port >= 65536)
      91             :     {
      92           0 :         throw event_dispatcher_invalid_parameter("invalid port for a client socket");
      93             :     }
      94           0 :     if(f_addr.empty())
      95             :     {
      96           0 :         throw event_dispatcher_invalid_parameter("an empty address is not valid for a client socket");
      97             :     }
      98             : 
      99           0 :     addrinfo hints;
     100           0 :     memset(&hints, 0, sizeof(hints));
     101           0 :     hints.ai_family = AF_UNSPEC;
     102           0 :     hints.ai_socktype = SOCK_STREAM;
     103           0 :     hints.ai_protocol = IPPROTO_TCP;
     104           0 :     std::string const port_str(std::to_string(f_port));
     105           0 :     addrinfo * addrinfo(nullptr);
     106           0 :     int const r(getaddrinfo(addr.c_str(), port_str.c_str(), &hints, &addrinfo));
     107           0 :     raii_addrinfo_t addr_info(addrinfo);
     108           0 :     if(r != 0
     109           0 :     || addrinfo == nullptr)
     110             :     {
     111           0 :         int const e(errno);
     112           0 :         std::string err("getaddrinfo() failed to parse the address or port \"");
     113           0 :         err += addr;
     114           0 :         err += ":";
     115           0 :         err += port_str;
     116           0 :         err += "\" strings (errno: ";
     117           0 :         err += std::to_string(e);
     118           0 :         err += " -- ";
     119           0 :         err += strerror(e);
     120           0 :         err += ")";
     121           0 :         SNAP_LOG_FATAL << err << SNAP_LOG_SEND;
     122           0 :         throw event_dispatcher_runtime_error(err);
     123             :     }
     124             : 
     125           0 :     f_socket.reset(socket(addr_info.get()->ai_family, SOCK_STREAM, IPPROTO_TCP));
     126           0 :     if(f_socket < 0)
     127             :     {
     128           0 :         int const e(errno);
     129           0 :         SNAP_LOG_FATAL
     130           0 :             << "socket() failed to create a socket descriptor (errno: "
     131             :             << e
     132             :             << " -- "
     133           0 :             << strerror(e)
     134             :             << ")"
     135             :             << SNAP_LOG_SEND;
     136           0 :         throw event_dispatcher_runtime_error("could not create socket for client");
     137             :     }
     138             : 
     139           0 :     if(connect(f_socket.get(), addr_info.get()->ai_addr, addr_info.get()->ai_addrlen) < 0)
     140             :     {
     141           0 :         int const e(errno);
     142           0 :         SNAP_LOG_FATAL
     143           0 :             << "connect() failed to connect a socket (errno: "
     144             :             << e
     145             :             << " -- "
     146           0 :             << strerror(e)
     147             :             << ")"
     148             :             << SNAP_LOG_SEND;
     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 : }
     164             : 
     165             : /** \brief Get the socket descriptor.
     166             :  *
     167             :  * This function returns the TCP client socket descriptor. This can be
     168             :  * used to change the descriptor behavior (i.e. make it non-blocking for
     169             :  * example.)
     170             :  *
     171             :  * \return The socket descriptor.
     172             :  */
     173           0 : int tcp_client::get_socket() const
     174             : {
     175           0 :     return f_socket.get();
     176             : }
     177             : 
     178             : /** \brief Get the TCP client port.
     179             :  *
     180             :  * This function returns the port used when creating the TCP client.
     181             :  * Note that this is the port the server is listening to and not the port
     182             :  * the TCP client is currently connected to.
     183             :  *
     184             :  * \return The TCP client port.
     185             :  */
     186           0 : int tcp_client::get_port() const
     187             : {
     188           0 :     return f_port;
     189             : }
     190             : 
     191             : /** \brief Get the TCP server address.
     192             :  *
     193             :  * This function returns the address used when creating the TCP address as is.
     194             :  * Note that this is the address of the server where the client is connected
     195             :  * and not the address where the client is running (although it may be the
     196             :  * same.)
     197             :  *
     198             :  * Use the get_client_addr() function to retrieve the client's TCP address.
     199             :  *
     200             :  * \return The TCP client address.
     201             :  */
     202           0 : std::string tcp_client::get_addr() const
     203             : {
     204           0 :     return f_addr;
     205             : }
     206             : 
     207             : /** \brief Get the TCP client port.
     208             :  *
     209             :  * This function retrieve the port of the client (used on your computer).
     210             :  * This is retrieved from the socket using the getsockname() function.
     211             :  *
     212             :  * \return The port or -1 if it cannot be determined.
     213             :  */
     214           0 : int tcp_client::get_client_port() const
     215             : {
     216           0 :     struct sockaddr addr;
     217           0 :     socklen_t len(sizeof(addr));
     218           0 :     int r(getsockname(f_socket.get(), &addr, &len));
     219           0 :     if(r != 0)
     220             :     {
     221           0 :         return -1;
     222             :     }
     223             :     // Note: I know the port is at the exact same location in both
     224             :     //       structures in Linux but it could change on other Unices
     225           0 :     if(addr.sa_family == AF_INET)
     226             :     {
     227             :         // IPv4
     228           0 :         return reinterpret_cast<sockaddr_in *>(&addr)->sin_port;
     229             :     }
     230           0 :     if(addr.sa_family == AF_INET6)
     231             :     {
     232             :         // IPv6
     233           0 :         return reinterpret_cast<sockaddr_in6 *>(&addr)->sin6_port;
     234             :     }
     235           0 :     return -1;
     236             : }
     237             : 
     238             : /** \brief Get the TCP client address.
     239             :  *
     240             :  * This function retrieve the IP address of the client (your computer).
     241             :  * This is retrieved from the socket using the getsockname() function.
     242             :  *
     243             :  * \return The IP address as a string.
     244             :  */
     245           0 : std::string tcp_client::get_client_addr() const
     246             : {
     247           0 :     struct sockaddr addr;
     248           0 :     socklen_t len(sizeof(addr));
     249           0 :     int const r(getsockname(f_socket.get(), &addr, &len));
     250           0 :     if(r != 0)
     251             :     {
     252           0 :         throw event_dispatcher_runtime_error("address not available");
     253             :     }
     254           0 :     char buf[BUFSIZ];
     255           0 :     switch(addr.sa_family)
     256             :     {
     257           0 :     case AF_INET:
     258           0 :         if(len < sizeof(struct sockaddr_in))
     259             :         {
     260           0 :             throw event_dispatcher_runtime_error("address size incompatible (AF_INET)");
     261             :         }
     262           0 :         inet_ntop(AF_INET, &reinterpret_cast<struct sockaddr_in *>(&addr)->sin_addr, buf, sizeof(buf));
     263           0 :         break;
     264             : 
     265           0 :     case AF_INET6:
     266           0 :         if(len < sizeof(struct sockaddr_in6))
     267             :         {
     268           0 :             throw event_dispatcher_runtime_error("address size incompatible (AF_INET6)");
     269             :         }
     270           0 :         inet_ntop(AF_INET6, &reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_addr, buf, sizeof(buf));
     271           0 :         break;
     272             : 
     273           0 :     default:
     274           0 :         throw event_dispatcher_runtime_error("unknown address family");
     275             : 
     276             :     }
     277           0 :     return buf;
     278             : }
     279             : 
     280             : /** \brief Read data from the socket.
     281             :  *
     282             :  * A TCP socket is a stream type of socket and one can read data from it
     283             :  * as if it were a regular file. This function reads \p size bytes and
     284             :  * returns. The function returns early if the server closes the connection.
     285             :  *
     286             :  * If your socket is blocking, \p size should be exactly what you are
     287             :  * expecting or this function will block forever or until the server
     288             :  * closes the connection.
     289             :  *
     290             :  * The function returns -1 if an error occurs. The error is available in
     291             :  * errno as expected in the POSIX interface.
     292             :  *
     293             :  * \param[in,out] buf  The buffer where the data is read.
     294             :  * \param[in] size  The size of the buffer.
     295             :  *
     296             :  * \return The number of bytes read from the socket, or -1 on errors.
     297             :  */
     298           0 : int tcp_client::read(char *buf, size_t size)
     299             : {
     300           0 :     return static_cast<int>(::read(f_socket.get(), buf, size));
     301             : }
     302             : 
     303             : 
     304             : /** \brief Read one line.
     305             :  *
     306             :  * This function reads one line from the current location up to the next
     307             :  * '\\n' character. We do not have any special handling of the '\\r'
     308             :  * character.
     309             :  *
     310             :  * The function may return 0 in which case the server closed the connection.
     311             :  *
     312             :  * \param[out] line  The resulting line read from the server.
     313             :  *
     314             :  * \return The number of bytes read from the socket, or -1 on errors.
     315             :  *         If the function returns 0 or more, then the \p line parameter
     316             :  *         represents the characters read on the network.
     317             :  */
     318           0 : int tcp_client::read_line(std::string& line)
     319             : {
     320           0 :     line.clear();
     321           0 :     int len(0);
     322             :     for(;;)
     323             :     {
     324           0 :         char c;
     325           0 :         int r(read(&c, sizeof(c)));
     326           0 :         if(r <= 0)
     327             :         {
     328           0 :             return len == 0 && r < 0 ? -1 : len;
     329             :         }
     330           0 :         if(c == '\n')
     331             :         {
     332           0 :             return len;
     333             :         }
     334           0 :         ++len;
     335           0 :         line += c;
     336           0 :     }
     337             : }
     338             : 
     339             : 
     340             : /** \brief Write data to the socket.
     341             :  *
     342             :  * A TCP socket is a stream type of socket and one can write data to it
     343             :  * as if it were a regular file. This function writes \p size bytes to
     344             :  * the socket and then returns. This function returns early if the server
     345             :  * closes the connection.
     346             :  *
     347             :  * If your socket is not blocking, less than \p size bytes may be written
     348             :  * to the socket. In that case you are responsible for calling the function
     349             :  * again to write the remainder of the buffer until the function returns
     350             :  * a number of bytes written equal to \p size.
     351             :  *
     352             :  * The function returns -1 if an error occurs. The error is available in
     353             :  * errno as expected in the POSIX interface.
     354             :  *
     355             :  * \param[in] buf  The buffer with the data to send over the socket.
     356             :  * \param[in] size  The number of bytes in buffer to send over the socket.
     357             :  *
     358             :  * \return The number of bytes that were actually accepted by the socket
     359             :  * or -1 if an error occurs.
     360             :  */
     361           0 : int tcp_client::write(const char *buf, size_t size)
     362             : {
     363           0 :     return static_cast<int>(::write(f_socket.get(), buf, size));
     364             : }
     365             : 
     366             : 
     367             : 
     368           6 : } // namespace ed
     369             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13