Line data Source code
1 : // Copyright (c) 2012-2024 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 Implementation of the Snap Communicator class. 22 : * 23 : * This class wraps the C poll() interface in a C++ object with many types 24 : * of objects: 25 : * 26 : * \li Server Connections; for software that want to offer a port to 27 : * which clients can connect to; the server will call accept() 28 : * once a new client connection is ready; this results in a 29 : * Server/Client connection object 30 : * \li Client Connections; for software that want to connect to 31 : * a server; these expect the IP address and port to connect to 32 : * \li Server/Client Connections; for the server when it accepts a new 33 : * connection; in this case the server gets a socket from accept() 34 : * and creates one of these objects to handle the connection 35 : * 36 : * Using the poll() function is the easiest and allows us to listen 37 : * on pretty much any number of sockets (on my server it is limited 38 : * at 16,768 and frankly over 1,000 we probably will start to have 39 : * real slowness issues on small VPN servers.) 40 : */ 41 : 42 : // self 43 : // 44 : #include "eventdispatcher/tcp_server_client_message_connection.h" 45 : 46 : #include "eventdispatcher/exception.h" 47 : 48 : 49 : // snaplogger 50 : // 51 : #include <snaplogger/message.h> 52 : 53 : 54 : // C++ 55 : // 56 : #include <cstring> 57 : 58 : 59 : // C 60 : // 61 : #include <arpa/inet.h> 62 : #include <sys/socket.h> 63 : 64 : 65 : // last include 66 : // 67 : #include <snapdev/poison.h> 68 : 69 : 70 : 71 : namespace ed 72 : { 73 : 74 : 75 : 76 : /** \brief Initializes a client to read messages from a socket. 77 : * 78 : * This implementation creates a message in/out client. 79 : * This is the most useful client in our Snap! Communicator 80 : * as it directly sends and receives messages. 81 : * 82 : * \param[in] client The client representing the in/out socket. 83 : */ 84 0 : tcp_server_client_message_connection::tcp_server_client_message_connection(tcp_bio_client::pointer_t client) 85 0 : : tcp_server_client_buffer_connection(client) 86 : { 87 : // TODO: somehow the port seems wrong (i.e. all connections return the same port) 88 : // I changed this to do the getpeername() at the time you call the 89 : // get_remote_address() and I use the addr::addr get_from_socket() 90 : // so now it may work? I need to test it again 91 : 92 : // make sure the socket is defined and well 93 : // 94 0 : int const socket(client->get_socket()); 95 0 : if(socket < 0) 96 : { 97 0 : SNAP_LOG_ERROR 98 : << "called with a closed client connection." 99 : << SNAP_LOG_SEND; 100 0 : throw std::runtime_error("tcp_server_client_message_connection() called with a closed client connection."); 101 : } 102 0 : } 103 : 104 : 105 : /** \brief Process a line (string) just received. 106 : * 107 : * The function parses the line as a message and then calls the 108 : * process_message() function if the line was valid. 109 : * 110 : * \param[in] line The line of text that was just read. 111 : */ 112 0 : void tcp_server_client_message_connection::process_line(std::string const & line) 113 : { 114 : // empty lines should not occur, but just in case, just ignore 115 0 : if(line.empty()) 116 : { 117 0 : return; 118 : } 119 : 120 0 : message msg; 121 0 : if(msg.from_message(line)) 122 : { 123 0 : dispatch_message(msg); 124 : } 125 : else 126 : { 127 : // TODO: what to do here? This could because the version changed 128 : // and the messages are not compatible anymore. 129 : // 130 0 : SNAP_LOG_ERROR 131 : << "process_line() was asked to process an invalid message (" 132 : << line 133 : << ")" 134 : << SNAP_LOG_SEND; 135 : } 136 0 : } 137 : 138 : 139 : /** \brief Send a message. 140 : * 141 : * This function sends a message to the client on the other side 142 : * of this connection. 143 : * 144 : * \exception event_dispatcher_runtime_error 145 : * This function throws this exception if the write() to the pipe 146 : * fails to write the entire message. This should only happen if 147 : * the pipe gets severed. 148 : * 149 : * \param[in] msg The message to be processed. 150 : * \param[in] cache Whether to cache the message if there is no connection. 151 : * (Ignore because a client socket has to be there until 152 : * closed and then it can't be reopened by the server.) 153 : * 154 : * \return Always true. If an error occurs the function throws. 155 : */ 156 0 : bool tcp_server_client_message_connection::send_message( 157 : message & msg 158 : , bool cache) 159 : { 160 0 : snapdev::NOT_USED(cache); 161 : 162 : // transform the message to a string and write to the socket 163 : // the writing is asynchronous so the message is saved in a cache 164 : // and transferred only later when the run() loop is hit again 165 : // 166 0 : std::string buf(msg.to_message()); 167 0 : buf += '\n'; 168 0 : return write(buf.c_str(), buf.length()) == static_cast<ssize_t>(buf.length()); 169 0 : } 170 : 171 : 172 : /** \brief Retrieve the remote address information. 173 : * 174 : * This function can be used to retrieve the remove address and port 175 : * information as was specified on the constructor. These can be used 176 : * to find this specific connection at a later time or create another 177 : * connection. 178 : * 179 : * For example, you may get 192.168.2.17:4040. 180 : * 181 : * The function works even after the socket gets closed as we save 182 : * the remote address and port in a string just after the connection 183 : * was established. 184 : * 185 : * \warning 186 : * This function returns BOTH: the address and the port. 187 : * 188 : * \note 189 : * These parameters are the same as what was passed to the constructor, 190 : * only both will have been converted to numbers. So for example when 191 : * you used "localhost", here you get "::1" or "127.0.0.1" for the 192 : * address. 193 : * 194 : * \return The remote host address and connection port. 195 : */ 196 0 : addr::addr tcp_server_client_message_connection::get_remote_address() 197 : { 198 0 : if(f_remote_address.is_default()) 199 : { 200 0 : int const s(get_socket()); 201 0 : if(s >= 0) 202 : { 203 0 : f_remote_address.set_from_socket(s, true); 204 : } 205 : } 206 : 207 0 : return f_remote_address; 208 : } 209 : 210 : 211 : } // namespace ed 212 : // vim: ts=4 sw=4 et