Switch time() calls to monotonic clock calls (#7597)
[libt2n] / src / server.cpp
CommitLineData
19facd85
TJ
1/*
2Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
3
4The software in this package is distributed under the GNU General
5Public License version 2 (with a special exception described below).
6
7A copy of GNU General Public License (GPL) is included in this distribution,
8in the file COPYING.GPL.
9
10As a special exception, if other files instantiate templates or use macros
11or inline functions from this file, or you compile this file and link it
12with other works to produce a work based on this file, this file
13does not by itself cause the resulting work to be covered
14by the GNU General Public License.
15
16However the source code for this file must still be made available
17in accordance with section (3) of the GNU General Public License.
18
19This exception does not invalidate any other reasons why a work based
20on this file might be covered by the GNU General Public License.
21*/
ac7fdc22 22
aa499d20 23#include <sstream>
04d86ba4 24#include <stdexcept>
f3823a13 25#include <time.h>
aa499d20 26
6cda58a6
GE
27#include <boost/bind.hpp>
28
ac7fdc22 29#include "server.hxx"
a7170401 30#include "log.hxx"
0a531de6 31#include "monotonic_clock.hxx"
ac7fdc22
GE
32
33namespace libt2n
34{
35
a7170401 36server_connection::server_connection(int _timeout)
56f3994d
TJ
37 : connection_id(0)
38 , my_server(NULL)
39 , connection()
a7170401
GE
40{
41 set_timeout(_timeout);
42 reset_timeout();
56f3994d
TJ
43}
44
45/**
46 * Destructor
47 */
48server_connection::~server_connection()
49{
a7170401
GE
50}
51
a7170401
GE
52/// get pointer to logging stream, returns NULL if no logging needed
53std::ostream* server_connection::get_logstream(log_level_values level)
54{
55 if (my_server != NULL)
d535333f
GE
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;
a7170401
GE
64}
65
66/// check if timeout is expired, close connection if so
a11e19b7 67void server_connection::check_timeout()
ac7fdc22 68{
0a531de6 69 if (timeout != -1 && last_action_time+timeout < monotonic_clock_gettime_sec())
a7170401
GE
70 {
71 LOGSTREAM(debug,"timeout on connection " << connection_id << ", closing");
ac7fdc22 72 this->close();
a7170401 73 }
ac7fdc22
GE
74}
75
a7170401 76/// reset the timeout, e.g. if something is received
a11e19b7 77void server_connection::reset_timeout()
ac7fdc22 78{
0a531de6 79 last_action_time=monotonic_clock_gettime_sec();
ac7fdc22
GE
80}
81
487afb79
GE
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*/
6cda58a6
GE
87void server_connection::add_callback(callback_event_type event, const boost::function<void ()>& func)
88{
89 if (event == new_connection)
04d86ba4 90 throw std::logic_error("new_connection callback not allowed for server_connections");
6cda58a6 91
a64066eb 92 connection::add_callback(event,func);
6cda58a6
GE
93}
94
a7170401 95server::server()
28cb45a5 96 : callbacks(__events_end)
a7170401
GE
97{
98 set_default_timeout(30);
99 set_logging(NULL,none);
100 next_id=1;
101}
102
ac7fdc22
GE
103server::~server()
104{
a11e19b7
GE
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++)
ac7fdc22 107 delete i->second;
56f3994d
TJ
108
109 connections.clear();
110}
111
112/**
113 * Close all open connections
114 */
115void 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();
ac7fdc22
GE
120}
121
28cb45a5
GE
122/** @brief add a callback
123
124 @param event event the function will be called at
487afb79 125 @param func functor (see boost::function) that will be called
6cda58a6 126
e1d0794d 127 @note use boost::bind to bind to member functions like this:
487afb79 128 s.add_callback(new_connection,boost::bind(&my_class::func_to_call_back, boost::ref(*this), _1));
28cb45a5 129*/
6cda58a6 130void server::add_callback(callback_event_type event, const boost::function<void (unsigned int)>& func)
28cb45a5
GE
131{
132 callbacks[event].push_back(func);
6cda58a6
GE
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++)
487afb79 139 i->second->add_callback(event,boost::bind(func, i->first));
6cda58a6 140 }
28cb45a5
GE
141}
142
487afb79 143
a63e08b8 144/** @brief an event occurred, call all server-side callbacks
487afb79 145
a63e08b8 146 @param event event that occurred
487afb79
GE
147 @param conn_id connection-id parameter that will be given to the callback-function
148*/
6cda58a6 149void server::do_callbacks(callback_event_type event, unsigned int conn_id)
28cb45a5 150{
6cda58a6 151 std::list<boost::function<void (unsigned int)> >::iterator i,ie=callbacks[event].end();
28cb45a5 152 for (i=callbacks[event].begin(); i != ie; i++)
6cda58a6 153 (*i)(conn_id);
28cb45a5
GE
154}
155
487afb79 156/// add a new connection to the server
56f3994d 157unsigned int server::add_connection(server_connection* newconn)
04e6b271 158{
aa499d20
GE
159 unsigned int cid=next_id++;
160 newconn->set_id(cid);
161 newconn->set_server(this);
162 connections[cid]=newconn;
a7170401 163
af84dfb5 164 // add all callbacks except new_connection
6cda58a6
GE
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
a7170401
GE
172 LOGSTREAM(debug,"new connection accepted, id: " << cid);
173
6cda58a6 174 do_callbacks(new_connection,cid);
28cb45a5 175
aa499d20 176 return cid;
04e6b271
GE
177}
178
a7170401
GE
179/// activate logging to the given stream. everything above the given level is logged.
180void server::set_logging(std::ostream *_logstream, log_level_values _log_level)
181{
182 log_level=_log_level;
183 logstream=_logstream;
184}
185
59adb9e2 186/**
94247295 187 @brief Gets a connection by id
59adb9e2 188
94247295 189 @param conn_id Connection ID
59adb9e2 190
94247295 191 @retval Pointer to connection object
59adb9e2 192*/
a11e19b7 193server_connection* server::get_connection(unsigned int conn_id)
ac7fdc22 194{
a11e19b7 195 std::map<unsigned int, server_connection*>::iterator p=connections.find(conn_id);
ac7fdc22
GE
196 if (p==connections.end())
197 return NULL;
198 else
199 return p->second;
200}
201
94247295 202/// check for timeouts, remove closed connections. don't forget to call this from time to time.
a11e19b7 203void server::cleanup()
ac7fdc22 204{
a11e19b7
GE
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++)
ac7fdc22 207 i->second->check_timeout();
a11e19b7
GE
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
38de59c2 214 LOGSTREAM(debug,"removing connection " << i->first << " because it is closed and no more data waiting");
a7170401 215
a11e19b7
GE
216 delete i->second;
217 connections.erase(i);
218 i=connections.begin();
219 ie=connections.end();
220 }
221 else
222 i++;
223 }
ac7fdc22
GE
224}
225
a7170401
GE
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*/
ac7fdc22
GE
232bool 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
a11e19b7
GE
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 {
a7170401
GE
241 LOGSTREAM(debug,"got packet (" << data.size() << " bytes) from connection " << i->first);
242
a11e19b7 243 conn_id=i->first;
ac7fdc22 244 return true;
a11e19b7
GE
245 }
246
247 return false;
ac7fdc22
GE
248}
249
a7170401
GE
250/// get pointer to logging stream, returns NULL if no logging needed
251std::ostream* server::get_logstream(log_level_values level)
ac7fdc22 252{
d535333f 253 if (logstream && log_level >= level)
a7170401 254 return logstream;
d535333f
GE
255 else
256 return NULL;
ac7fdc22 257}
ac7fdc22 258};