Adding DOCUMENTATION flag to the CMakeLists.txt
[libt2n] / src / server.cpp
1 /*
2 Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
3
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
6
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
9
10 As a special exception, if other files instantiate templates or use macros
11 or inline functions from this file, or you compile this file and link it
12 with other works to produce a work based on this file, this file
13 does not by itself cause the resulting work to be covered
14 by the GNU General Public License.
15
16 However the source code for this file must still be made available
17 in accordance with section (3) of the GNU General Public License.
18
19 This exception does not invalidate any other reasons why a work based
20 on this file might be covered by the GNU General Public License.
21 */
22
23 #include <sstream>
24 #include <stdexcept>
25 #include <time.h>
26
27 #include <boost/bind.hpp>
28
29 #include "server.hxx"
30 #include "log.hxx"
31 #include "monotonic_clock.hxx"
32
33 namespace libt2n
34 {
35
36 server_connection::server_connection(int _timeout)
37     : connection_id(0)
38     , my_server(NULL)
39     , connection()
40 {
41     set_timeout(_timeout);
42     reset_timeout();
43 }
44
45 /**
46  * Destructor
47  */
48 server_connection::~server_connection()
49 {
50 }
51
52 /// get pointer to logging stream, returns NULL if no logging needed
53 std::ostream* server_connection::get_logstream(log_level_values level)
54 {
55     if (my_server != NULL)
56     {
57         std::ostream* ostr=my_server->get_logstream(level);
58         if (ostr != NULL)
59             (*ostr) << "connection " << get_id() << ": ";
60         return ostr;
61     }
62     else
63         return NULL;
64 }
65
66 /// check if timeout is expired, close connection if so
67 void server_connection::check_timeout()
68 {
69     if (timeout != -1 && last_action_time+timeout < monotonic_clock_gettime_sec())
70     {
71         LOGSTREAM(debug,"timeout on connection " << connection_id << ", closing");
72         this->close();
73     }
74 }
75
76 /// reset the timeout, e.g. if something is received
77 void server_connection::reset_timeout()
78 {
79     last_action_time=monotonic_clock_gettime_sec();
80 }
81
82 /** @brief add a callback to one connection
83
84     @param event event the function will be called at
85     @param func functor (see boost::function) that will be called
86 */
87 void server_connection::add_callback(callback_event_type event, const boost::function<void ()>& func)
88 {
89     if (event == new_connection)
90         throw std::logic_error("new_connection callback not allowed for server_connections");
91
92     connection::add_callback(event,func);
93 }
94
95 server::server()
96     : callbacks(__events_end)
97 {
98     set_default_timeout(30);
99     set_logging(NULL,none);
100     next_id=1;
101 }
102
103 server::~server()
104 {
105     std::map<unsigned int, server_connection*>::iterator ie=connections.end();
106     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
107         delete i->second;
108
109     connections.clear();
110 }
111
112 /**
113  * Close all open connections
114  */
115 void server::close()
116 {
117     std::map<unsigned int, server_connection*>::iterator ie=connections.end();
118     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; ++i)
119         i->second->close();
120 }
121
122 /** @brief add a callback
123
124     @param event event the function will be called at
125     @param func functor (see boost::function) that will be called
126
127     @note use boost::bind to bind to member functions like this:
128         s.add_callback(new_connection,boost::bind(&my_class::func_to_call_back, boost::ref(*this), _1));
129 */
130 void server::add_callback(callback_event_type event, const boost::function<void (unsigned int)>& func)
131 {
132     callbacks[event].push_back(func);
133
134     // add callback to all existing connections
135     if (event != new_connection)
136     {
137         std::map<unsigned int, server_connection*>::iterator ie=connections.end();
138         for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
139             i->second->add_callback(event,boost::bind(func, i->first));
140     }
141 }
142
143
144 /** @brief an event occurred, call all server-side callbacks
145
146     @param event event that occurred
147     @param conn_id connection-id parameter that will be given to the callback-function
148 */
149 void server::do_callbacks(callback_event_type event, unsigned int conn_id)
150 {
151     std::list<boost::function<void (unsigned int)> >::iterator i,ie=callbacks[event].end();
152     for (i=callbacks[event].begin(); i != ie; i++)
153         (*i)(conn_id);
154 }
155
156 /// add a new connection to the server
157 unsigned int server::add_connection(server_connection* newconn)
158 {
159     unsigned int cid=next_id++;
160     newconn->set_id(cid);
161     newconn->set_server(this);
162     connections[cid]=newconn;
163
164     // add all callbacks except new_connection
165     for(int e=connection_closed; e != __events_end; e++)
166     {
167         std::list<boost::function<void (unsigned int)> >::iterator i,ie=callbacks[e].end();
168         for (i=callbacks[e].begin(); i != ie; i++)
169             newconn->add_callback(static_cast<callback_event_type>(e),bind(*i,cid));
170     }
171
172     LOGSTREAM(debug,"new connection accepted, id: " << cid);
173
174     do_callbacks(new_connection,cid);
175
176     return cid;
177 }
178
179 /// activate logging to the given stream. everything above the given level is logged.
180 void server::set_logging(std::ostream *_logstream, log_level_values _log_level)
181 {
182     log_level=_log_level;
183     logstream=_logstream;
184 }
185
186 /**
187     @brief Gets a connection by id
188     
189     @param conn_id Connection ID
190     
191     @retval Pointer to connection object
192 */
193 server_connection* server::get_connection(unsigned int conn_id)
194 {
195     std::map<unsigned int, server_connection*>::iterator p=connections.find(conn_id);
196     if (p==connections.end())
197         return NULL;
198     else
199         return p->second;
200 }
201
202 /// check for timeouts, remove closed connections. don't forget to call this from time to time.
203 void server::cleanup()
204 {
205     std::map<unsigned int, server_connection*>::iterator ie=connections.end();
206     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
207         i->second->check_timeout();
208
209     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie;)
210     {
211         if (i->second->is_closed() && !i->second->packet_available())
212         {
213             // closed and no usable data in buffer -> remove
214             LOGSTREAM(debug,"removing connection " << i->first << " because it is closed and no more data waiting");
215
216             delete i->second;
217             connections.erase(i);
218             i=connections.begin();
219             ie=connections.end();
220         }
221         else
222             i++;
223     }
224 }
225
226 /** @brief get a complete data packet from any client. The packet is removed from the
227             connection buffer.
228     @param[out] data the data package
229     @param[out] conn_id the connection id we got this packet from
230     @retval true if packet found
231 */
232 bool server::get_packet(std::string& data, unsigned int& conn_id)
233 {
234     // todo: this is somehow unfair: the first connections in the map get checked more
235     // often than the others and can thus block them out
236
237     std::map<unsigned int, server_connection*>::iterator ie=connections.end();
238     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
239         if (i->second->get_packet(data))
240         {
241             LOGSTREAM(debug,"got packet (" << data.size() << " bytes) from connection " << i->first);
242
243             conn_id=i->first;
244             return true;
245         }
246
247     return false;
248 }
249
250 /// get pointer to logging stream, returns NULL if no logging needed
251 std::ostream* server::get_logstream(log_level_values level)
252 {
253     if (logstream && log_level >= level)
254         return logstream;
255     else
256         return NULL;
257 }
258 };