LCOV - code coverage report
Current view: top level - eventdispatcher - tcp_client.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 90 1.1 %
Date: 2022-06-18 10:10:36 Functions: 2 14 14.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2012-2022  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 along
      17             : // with this program; if not, write to the Free Software Foundation, Inc.,
      18             : // 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
      36             : //
      37             : #include    <snaplogger/message.h>
      38             : 
      39             : 
      40             : // C
      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 by the \p address object.
      73             :  *
      74             :  * \exception invalid_parameter
      75             :  * This exception is raised if the \p port parameter is out of range or the
      76             :  * IP address is an empty string or otherwise an invalid address.
      77             :  *
      78             :  * \exception runtime_error
      79             :  * This exception is raised if the client cannot create the socket or it
      80             :  * cannot connect to the server.
      81             :  *
      82             :  * \param[in] address  The address of the server to connect to. It must be valid.
      83             :  */
      84           0 : tcp_client::tcp_client(addr::addr const & address)
      85           0 :     : f_address(address)
      86             : {
      87           0 :     if(f_address.is_default())
      88             :     {
      89           0 :         throw invalid_parameter("the default address is not valid for a client socket");
      90             :     }
      91           0 :     if(f_address.get_protocol() != IPPROTO_TCP)
      92             :     {
      93           0 :         throw invalid_parameter("the address presents a protocol other than the expected TCP");
      94             :     }
      95             : 
      96           0 :     f_socket.reset(address.create_socket(0));
      97           0 :     if(f_socket < 0)
      98             :     {
      99           0 :         int const e(errno);
     100           0 :         SNAP_LOG_FATAL
     101           0 :             << "socket() failed to create a socket descriptor (errno: "
     102             :             << e
     103             :             << " -- "
     104           0 :             << strerror(e)
     105             :             << ")"
     106             :             << SNAP_LOG_SEND;
     107           0 :         throw runtime_error("could not create socket for client");
     108             :     }
     109             : 
     110           0 :     if(f_address.connect(f_socket.get()) != 0)
     111             :     {
     112           0 :         int const e(errno);
     113           0 :         std::stringstream ss;
     114           0 :         ss << "tcp_client() -- failed to connect() socket (errno: "
     115             :            << e
     116             :            << " -- "
     117           0 :            << strerror(e)
     118           0 :            << ")";
     119           0 :         SNAP_LOG_FATAL
     120             :             << ss
     121             :             << SNAP_LOG_SEND;
     122           0 :         throw runtime_error(ss.str());
     123             :     }
     124           0 : }
     125             : 
     126             : /** \brief Clean up the TCP client object.
     127             :  *
     128             :  * This function cleans up the TCP client object by closing the attached socket.
     129             :  *
     130             :  * \note
     131             :  * DO NOT use the shutdown() call since we may end up forking and using
     132             :  * that connection in the child.
     133             :  */
     134           0 : tcp_client::~tcp_client()
     135             : {
     136           0 : }
     137             : 
     138             : /** \brief Get the socket descriptor.
     139             :  *
     140             :  * This function returns the TCP client socket descriptor. This can be
     141             :  * used to change the descriptor behavior (i.e. make it non-blocking for
     142             :  * example.)
     143             :  *
     144             :  * \return The socket descriptor.
     145             :  */
     146           0 : int tcp_client::get_socket() const
     147             : {
     148           0 :     return f_socket.get();
     149             : }
     150             : 
     151             : /** \brief Get the TCP client port.
     152             :  *
     153             :  * This function returns the port used when creating the TCP client.
     154             :  * Note that this is the port the server is listening to and not the port
     155             :  * the TCP client is currently connected to.
     156             :  *
     157             :  * \return The TCP client port.
     158             :  */
     159           0 : int tcp_client::get_port() const
     160             : {
     161           0 :     return f_address.get_port();
     162             : }
     163             : 
     164             : /** \brief Get the TCP server address.
     165             :  *
     166             :  * This function returns the address used when creating the TCP address.
     167             :  * Note that this is the address of the server where the client is connected
     168             :  * and not the address where the client is running (although it may be the
     169             :  * same.)
     170             :  *
     171             :  * Use the get_client_addr() function to retrieve the client's TCP address.
     172             :  *
     173             :  * \return The TCP server address.
     174             :  */
     175           0 : std::string tcp_client::get_addr() const
     176             : {
     177           0 :     return f_address.to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_ONLY);
     178             : }
     179             : 
     180             : /** \brief Get the TCP server address.
     181             :  *
     182             :  * This function returns a copy of the address as specified in the contructor.
     183             :  *
     184             :  * \return The TCP server address/
     185             :  */
     186           0 : addr::addr tcp_client::get_address() const
     187             : {
     188           0 :     return f_address;
     189             : }
     190             : 
     191             : /** \brief Get the TCP client port.
     192             :  *
     193             :  * This function retrieve the port of the client (used on your computer).
     194             :  * This is retrieved from the socket using the getsockname() function.
     195             :  *
     196             :  * \return The port or -1 if it cannot be determined.
     197             :  *
     198             :  * \sa get_client_addr()
     199             :  * \sa get_client_address()
     200             :  */
     201           0 : int tcp_client::get_client_port() const
     202             : {
     203           0 :     struct sockaddr_in6 addr;
     204           0 :     struct sockaddr *a(reinterpret_cast<struct sockaddr *>(&addr));
     205           0 :     socklen_t len(sizeof(addr));
     206           0 :     int const r(getsockname(f_socket.get(), a, &len));
     207           0 :     if(r != 0)
     208             :     {
     209           0 :         return -1;
     210             :     }
     211             : 
     212             :     // Note: I know the port is at the exact same location in both
     213             :     //       structures in Linux but it could change on other Unices
     214             :     //
     215           0 :     if(a->sa_family == AF_INET
     216           0 :     && len >= sizeof(sockaddr_in))
     217             :     {
     218             :         // IPv4
     219           0 :         return reinterpret_cast<sockaddr_in const *>(a)->sin_port;
     220             :     }
     221             : 
     222           0 :     if(a->sa_family == AF_INET6
     223           0 :     && len >= sizeof(sockaddr_in6))
     224             :     {
     225             :         // IPv6
     226           0 :         return addr.sin6_port;
     227             :     }
     228             : 
     229           0 :     return -1;
     230             : }
     231             : 
     232             : /** \brief Get the TCP client address.
     233             :  *
     234             :  * This function retrieve the IP address of the client (your computer).
     235             :  * This is retrieved from the socket using the getsockname() function.
     236             :  *
     237             :  * \exception runtime_error
     238             :  * The function raises this exception if the address we retrieve doesn't
     239             :  * match its type properly;
     240             :  *
     241             :  * \return The IP address as a string.
     242             :  *
     243             :  * \sa get_client_port()
     244             :  * \sa get_client_address()
     245             :  */
     246           0 : std::string tcp_client::get_client_addr() const
     247             : {
     248           0 :     sockaddr_in6 addr;
     249           0 :     sockaddr *a(reinterpret_cast<sockaddr *>(&addr));
     250           0 :     socklen_t len(sizeof(addr));
     251           0 :     int const r(getsockname(f_socket.get(), a, &len));
     252           0 :     if(r != 0)
     253             :     {
     254           0 :         throw runtime_error("address not available");
     255             :     }
     256             : 
     257           0 :     char buf[BUFSIZ];
     258           0 :     switch(a->sa_family)
     259             :     {
     260           0 :     case AF_INET:
     261           0 :         if(len < sizeof(sockaddr_in))
     262             :         {
     263           0 :             throw runtime_error("address size incompatible (AF_INET)");
     264             :         }
     265           0 :         inet_ntop(
     266             :               AF_INET
     267           0 :             , &reinterpret_cast<sockaddr_in *>(a)->sin_addr
     268             :             , buf
     269             :             , sizeof(buf));
     270           0 :         break;
     271             : 
     272           0 :     case AF_INET6:
     273           0 :         if(len < sizeof(sockaddr_in6))
     274             :         {
     275           0 :             throw runtime_error("address size incompatible (AF_INET6)");
     276             :         }
     277           0 :         inet_ntop(AF_INET6, &addr.sin6_addr, buf, sizeof(buf));
     278           0 :         break;
     279             : 
     280           0 :     default:
     281           0 :         throw runtime_error("unknown address family");
     282             : 
     283             :     }
     284             : 
     285           0 :     return buf;
     286             : }
     287             : 
     288             : /** \brief Get the TCP client address.
     289             :  *
     290             :  * This function retrieve the IP address of the client (your computer).
     291             :  * This is retrieved from the socket using the getsockname() function.
     292             :  *
     293             :  * \exception runtime_error
     294             :  * The function raises this exception if the address we retrieve doesn't
     295             :  * match its type properly;
     296             :  *
     297             :  * \return The IP address as a string.
     298             :  *
     299             :  * \sa get_client_port()
     300             :  * \sa get_client_address()
     301             :  */
     302           0 : addr::addr tcp_client::get_client_address() const
     303             : {
     304           0 :     addr::addr a;
     305           0 :     a.set_from_socket(f_socket.get(), true);
     306           0 :     return a;
     307             : }
     308             : 
     309             : /** \brief Read data from the socket.
     310             :  *
     311             :  * A TCP socket is a stream type of socket and one can read data from it
     312             :  * as if it were a regular file. This function reads \p size bytes and
     313             :  * returns. The function returns early if the server closes the connection.
     314             :  *
     315             :  * If your socket is blocking, \p size should be exactly what you are
     316             :  * expecting or this function will block forever or until the server
     317             :  * closes the connection.
     318             :  *
     319             :  * The function returns -1 if an error occurs. The error is available in
     320             :  * errno as expected in the POSIX interface.
     321             :  *
     322             :  * \param[in,out] buf  The buffer where the data is read.
     323             :  * \param[in] size  The size of the buffer.
     324             :  *
     325             :  * \return The number of bytes read from the socket, or -1 on errors.
     326             :  */
     327           0 : int tcp_client::read(char *buf, size_t size)
     328             : {
     329           0 :     return static_cast<int>(::read(f_socket.get(), buf, size));
     330             : }
     331             : 
     332             : 
     333             : /** \brief Read one line.
     334             :  *
     335             :  * This function reads one line from the current location up to the next
     336             :  * '\\n' character. We do not have any special handling of the '\\r'
     337             :  * character.
     338             :  *
     339             :  * The function may return 0 in which case the server closed the connection.
     340             :  *
     341             :  * \param[out] line  The resulting line read from the server.
     342             :  *
     343             :  * \return The number of bytes read from the socket, or -1 on errors.
     344             :  *         If the function returns 0 or more, then the \p line parameter
     345             :  *         represents the characters read on the network.
     346             :  */
     347           0 : int tcp_client::read_line(std::string& line)
     348             : {
     349           0 :     line.clear();
     350           0 :     int len(0);
     351             :     for(;;)
     352             :     {
     353           0 :         char c;
     354           0 :         int r(read(&c, sizeof(c)));
     355           0 :         if(r <= 0)
     356             :         {
     357           0 :             return len == 0 && r < 0 ? -1 : len;
     358             :         }
     359           0 :         if(c == '\n')
     360             :         {
     361           0 :             return len;
     362             :         }
     363           0 :         ++len;
     364           0 :         line += c;
     365           0 :     }
     366             : }
     367             : 
     368             : 
     369             : /** \brief Write data to the socket.
     370             :  *
     371             :  * A TCP socket is a stream type of socket and one can write data to it
     372             :  * as if it were a regular file. This function writes \p size bytes to
     373             :  * the socket and then returns. This function returns early if the server
     374             :  * closes the connection.
     375             :  *
     376             :  * If your socket is not blocking, less than \p size bytes may be written
     377             :  * to the socket. In that case you are responsible for calling the function
     378             :  * again to write the remainder of the buffer until the function returns
     379             :  * a number of bytes written equal to \p size.
     380             :  *
     381             :  * The function returns -1 if an error occurs. The error is available in
     382             :  * errno as expected in the POSIX interface.
     383             :  *
     384             :  * \param[in] buf  The buffer with the data to send over the socket.
     385             :  * \param[in] size  The number of bytes in buffer to send over the socket.
     386             :  *
     387             :  * \return The number of bytes that were actually accepted by the socket
     388             :  * or -1 if an error occurs.
     389             :  */
     390           0 : int tcp_client::write(const char *buf, size_t size)
     391             : {
     392           0 :     return static_cast<int>(::write(f_socket.get(), buf, size));
     393             : }
     394             : 
     395             : 
     396             : 
     397           6 : } // namespace ed
     398             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13