32dda77bd908907ee7776ee650f8200178c0aee8
[libt2n] / src / server.cpp
1 /***************************************************************************
2  *   Copyright (C) 2006 by Gerd v. Egidy                                   *
3  *   gve@intra2net.com                                                     *
4  *                                                                         *
5  *   This library is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU Lesser General Public License version   *
7  *   2.1 as published by the Free Software Foundation.                     *
8  *                                                                         *
9  *   This library is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU Lesser General Public License for more details.                   *
13  *                                                                         *
14  *   You should have received a copy of the GNU Lesser General Public      *
15  *   License along with this program; if not, write to the                 *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19
20 #include <sstream>
21 #include <stdexcept>
22
23 #include <boost/bind.hpp>
24
25 #include "server.hxx"
26 #include "log.hxx"
27
28 namespace libt2n
29 {
30
31 server_connection::server_connection(int _timeout)
32     : connection()
33 {
34     set_timeout(_timeout);
35     reset_timeout();
36     connection_id=0;
37     my_server=0;
38 }
39
40 /// get pointer to logging stream, returns NULL if no logging needed
41 std::ostream* server_connection::get_logstream(log_level_values level)
42 {
43     if (my_server != NULL)
44     {
45         std::ostream* ostr=my_server->get_logstream(level);
46         if (ostr != NULL)
47             (*ostr) << "connection " << get_id() << ": ";
48         return ostr;
49     }
50     else
51         return NULL;
52 }
53
54 /// check if timeout is expired, close connection if so
55 void server_connection::check_timeout()
56 {
57     if (timeout != -1 && last_action_time+timeout < time(NULL))
58     {
59         LOGSTREAM(debug,"timeout on connection " << connection_id << ", closing");
60         this->close();
61     }
62 }
63
64 /// reset the timeout, e.g. if something is received
65 void server_connection::reset_timeout()
66 {
67     last_action_time=time(NULL);
68 }
69
70 void server_connection::add_callback(callback_event_type event, const boost::function<void ()>& func)
71 {
72     if (event == new_connection)
73         throw std::logic_error("new_connection callback not allowed for server_connections");
74
75     connection::add_callback(event,func);
76 }
77
78 server::server()
79     : callbacks(__events_end)
80 {
81     set_default_timeout(30);
82     set_logging(NULL,none);
83     next_id=1;
84 }
85
86 server::~server()
87 {
88     std::map<unsigned int, server_connection*>::iterator ie=connections.end();
89     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
90         delete i->second;
91 }
92
93 /** @brief add a callback
94
95     @param event event the function will be called at
96     @param func functor (see boost function) that will be called
97
98     @note use boost::bind to bind to member functions like this:
99         s.add_callback(new_connection,bind(&my_class::func_to_call_back, boost::ref(*this), _1));
100 */
101 void server::add_callback(callback_event_type event, const boost::function<void (unsigned int)>& func)
102 {
103     callbacks[event].push_back(func);
104
105     // add callback to all existing connections
106     if (event != new_connection)
107     {
108         std::map<unsigned int, server_connection*>::iterator ie=connections.end();
109         for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
110             i->second->add_callback(event,bind(func, i->first));
111     }
112 }
113
114 void server::do_callbacks(callback_event_type event, unsigned int conn_id)
115 {
116     std::list<boost::function<void (unsigned int)> >::iterator i,ie=callbacks[event].end();
117     for (i=callbacks[event].begin(); i != ie; i++)
118         (*i)(conn_id);
119 }
120
121 int server::add_connection(server_connection* newconn)
122 {
123     unsigned int cid=next_id++;
124     newconn->set_id(cid);
125     newconn->set_server(this);
126     connections[cid]=newconn;
127
128     // add all callbacks
129     for(int e=connection_closed; e != __events_end; e++)
130     {
131         std::list<boost::function<void (unsigned int)> >::iterator i,ie=callbacks[e].end();
132         for (i=callbacks[e].begin(); i != ie; i++)
133             newconn->add_callback(static_cast<callback_event_type>(e),bind(*i,cid));
134     }
135
136     LOGSTREAM(debug,"new connection accepted, id: " << cid);
137
138     do_callbacks(new_connection,cid);
139
140     return cid;
141 }
142
143 /// activate logging to the given stream. everything above the given level is logged.
144 void server::set_logging(std::ostream *_logstream, log_level_values _log_level)
145 {
146     log_level=_log_level;
147     logstream=_logstream;
148 }
149
150 /**
151     @brief Gets a connection by id
152     
153     @param conn_id Connection ID
154     
155     @retval Pointer to connection object
156 */
157 server_connection* server::get_connection(unsigned int conn_id)
158 {
159     std::map<unsigned int, server_connection*>::iterator p=connections.find(conn_id);
160     if (p==connections.end())
161         return NULL;
162     else
163         return p->second;
164 }
165
166 /// check for timeouts, remove closed connections. don't forget to call this from time to time.
167 void server::cleanup()
168 {
169     std::map<unsigned int, server_connection*>::iterator ie=connections.end();
170     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
171         i->second->check_timeout();
172
173     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie;)
174     {
175         if (i->second->is_closed() && !i->second->packet_available())
176         {
177             // closed and no usable data in buffer -> remove
178             LOGSTREAM(debug,"removing connection " << i->first << " because it is closed and no more data waiting");
179
180             delete i->second;
181             connections.erase(i);
182             i=connections.begin();
183             ie=connections.end();
184         }
185         else
186             i++;
187     }
188 }
189
190 /** @brief get a complete data packet from any client. The packet is removed from the
191             connection buffer.
192     @param[out] data the data package
193     @param[out] conn_id the connection id we got this packet from
194     @retval true if packet found
195 */
196 bool server::get_packet(std::string& data, unsigned int& conn_id)
197 {
198     // todo: this is somehow unfair: the first connections in the map get checked more
199     // often than the others and can thus block them out
200
201     std::map<unsigned int, server_connection*>::iterator ie=connections.end();
202     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
203         if (i->second->get_packet(data))
204         {
205             LOGSTREAM(debug,"got packet (" << data.size() << " bytes) from connection " << i->first);
206
207             conn_id=i->first;
208             return true;
209         }
210
211     return false;
212 }
213
214 /// get pointer to logging stream, returns NULL if no logging needed
215 std::ostream* server::get_logstream(log_level_values level)
216 {
217     if (logstream && log_level >= level)
218         return logstream;
219     else
220         return NULL;
221 }
222 };