Fix 'occurred' typo
[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"
ac7fdc22
GE
31
32namespace libt2n
33{
34
a7170401 35server_connection::server_connection(int _timeout)
56f3994d
TJ
36 : connection_id(0)
37 , my_server(NULL)
38 , connection()
a7170401
GE
39{
40 set_timeout(_timeout);
41 reset_timeout();
56f3994d
TJ
42}
43
44/**
45 * Destructor
46 */
47server_connection::~server_connection()
48{
a7170401
GE
49}
50
a7170401
GE
51/// get pointer to logging stream, returns NULL if no logging needed
52std::ostream* server_connection::get_logstream(log_level_values level)
53{
54 if (my_server != NULL)
d535333f
GE
55 {
56 std::ostream* ostr=my_server->get_logstream(level);
57 if (ostr != NULL)
58 (*ostr) << "connection " << get_id() << ": ";
59 return ostr;
60 }
61 else
62 return NULL;
a7170401
GE
63}
64
65/// check if timeout is expired, close connection if so
a11e19b7 66void server_connection::check_timeout()
ac7fdc22 67{
a7170401
GE
68 if (timeout != -1 && last_action_time+timeout < time(NULL))
69 {
70 LOGSTREAM(debug,"timeout on connection " << connection_id << ", closing");
ac7fdc22 71 this->close();
a7170401 72 }
ac7fdc22
GE
73}
74
a7170401 75/// reset the timeout, e.g. if something is received
a11e19b7 76void server_connection::reset_timeout()
ac7fdc22
GE
77{
78 last_action_time=time(NULL);
79}
80
487afb79
GE
81/** @brief add a callback to one connection
82
83 @param event event the function will be called at
84 @param func functor (see boost::function) that will be called
85*/
6cda58a6
GE
86void server_connection::add_callback(callback_event_type event, const boost::function<void ()>& func)
87{
88 if (event == new_connection)
04d86ba4 89 throw std::logic_error("new_connection callback not allowed for server_connections");
6cda58a6 90
a64066eb 91 connection::add_callback(event,func);
6cda58a6
GE
92}
93
a7170401 94server::server()
28cb45a5 95 : callbacks(__events_end)
a7170401
GE
96{
97 set_default_timeout(30);
98 set_logging(NULL,none);
99 next_id=1;
100}
101
ac7fdc22
GE
102server::~server()
103{
a11e19b7
GE
104 std::map<unsigned int, server_connection*>::iterator ie=connections.end();
105 for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
ac7fdc22 106 delete i->second;
56f3994d
TJ
107
108 connections.clear();
109}
110
111/**
112 * Close all open connections
113 */
114void server::close()
115{
116 std::map<unsigned int, server_connection*>::iterator ie=connections.end();
117 for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; ++i)
118 i->second->close();
ac7fdc22
GE
119}
120
28cb45a5
GE
121/** @brief add a callback
122
123 @param event event the function will be called at
487afb79 124 @param func functor (see boost::function) that will be called
6cda58a6 125
e1d0794d 126 @note use boost::bind to bind to member functions like this:
487afb79 127 s.add_callback(new_connection,boost::bind(&my_class::func_to_call_back, boost::ref(*this), _1));
28cb45a5 128*/
6cda58a6 129void server::add_callback(callback_event_type event, const boost::function<void (unsigned int)>& func)
28cb45a5
GE
130{
131 callbacks[event].push_back(func);
6cda58a6
GE
132
133 // add callback to all existing connections
134 if (event != new_connection)
135 {
136 std::map<unsigned int, server_connection*>::iterator ie=connections.end();
137 for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
487afb79 138 i->second->add_callback(event,boost::bind(func, i->first));
6cda58a6 139 }
28cb45a5
GE
140}
141
487afb79 142
a63e08b8 143/** @brief an event occurred, call all server-side callbacks
487afb79 144
a63e08b8 145 @param event event that occurred
487afb79
GE
146 @param conn_id connection-id parameter that will be given to the callback-function
147*/
6cda58a6 148void server::do_callbacks(callback_event_type event, unsigned int conn_id)
28cb45a5 149{
6cda58a6 150 std::list<boost::function<void (unsigned int)> >::iterator i,ie=callbacks[event].end();
28cb45a5 151 for (i=callbacks[event].begin(); i != ie; i++)
6cda58a6 152 (*i)(conn_id);
28cb45a5
GE
153}
154
487afb79 155/// add a new connection to the server
56f3994d 156unsigned int server::add_connection(server_connection* newconn)
04e6b271 157{
aa499d20
GE
158 unsigned int cid=next_id++;
159 newconn->set_id(cid);
160 newconn->set_server(this);
161 connections[cid]=newconn;
a7170401 162
af84dfb5 163 // add all callbacks except new_connection
6cda58a6
GE
164 for(int e=connection_closed; e != __events_end; e++)
165 {
166 std::list<boost::function<void (unsigned int)> >::iterator i,ie=callbacks[e].end();
167 for (i=callbacks[e].begin(); i != ie; i++)
168 newconn->add_callback(static_cast<callback_event_type>(e),bind(*i,cid));
169 }
170
a7170401
GE
171 LOGSTREAM(debug,"new connection accepted, id: " << cid);
172
6cda58a6 173 do_callbacks(new_connection,cid);
28cb45a5 174
aa499d20 175 return cid;
04e6b271
GE
176}
177
a7170401
GE
178/// activate logging to the given stream. everything above the given level is logged.
179void server::set_logging(std::ostream *_logstream, log_level_values _log_level)
180{
181 log_level=_log_level;
182 logstream=_logstream;
183}
184
59adb9e2 185/**
94247295 186 @brief Gets a connection by id
59adb9e2 187
94247295 188 @param conn_id Connection ID
59adb9e2 189
94247295 190 @retval Pointer to connection object
59adb9e2 191*/
a11e19b7 192server_connection* server::get_connection(unsigned int conn_id)
ac7fdc22 193{
a11e19b7 194 std::map<unsigned int, server_connection*>::iterator p=connections.find(conn_id);
ac7fdc22
GE
195 if (p==connections.end())
196 return NULL;
197 else
198 return p->second;
199}
200
94247295 201/// check for timeouts, remove closed connections. don't forget to call this from time to time.
a11e19b7 202void server::cleanup()
ac7fdc22 203{
a11e19b7
GE
204 std::map<unsigned int, server_connection*>::iterator ie=connections.end();
205 for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
ac7fdc22 206 i->second->check_timeout();
a11e19b7
GE
207
208 for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie;)
209 {
210 if (i->second->is_closed() && !i->second->packet_available())
211 {
212 // closed and no usable data in buffer -> remove
38de59c2 213 LOGSTREAM(debug,"removing connection " << i->first << " because it is closed and no more data waiting");
a7170401 214
a11e19b7
GE
215 delete i->second;
216 connections.erase(i);
217 i=connections.begin();
218 ie=connections.end();
219 }
220 else
221 i++;
222 }
ac7fdc22
GE
223}
224
a7170401
GE
225/** @brief get a complete data packet from any client. The packet is removed from the
226 connection buffer.
227 @param[out] data the data package
228 @param[out] conn_id the connection id we got this packet from
229 @retval true if packet found
230*/
ac7fdc22
GE
231bool server::get_packet(std::string& data, unsigned int& conn_id)
232{
233 // todo: this is somehow unfair: the first connections in the map get checked more
234 // often than the others and can thus block them out
235
a11e19b7
GE
236 std::map<unsigned int, server_connection*>::iterator ie=connections.end();
237 for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
238 if (i->second->get_packet(data))
239 {
a7170401
GE
240 LOGSTREAM(debug,"got packet (" << data.size() << " bytes) from connection " << i->first);
241
a11e19b7 242 conn_id=i->first;
ac7fdc22 243 return true;
a11e19b7
GE
244 }
245
246 return false;
ac7fdc22
GE
247}
248
a7170401
GE
249/// get pointer to logging stream, returns NULL if no logging needed
250std::ostream* server::get_logstream(log_level_values level)
ac7fdc22 251{
d535333f 252 if (logstream && log_level >= level)
a7170401 253 return logstream;
d535333f
GE
254 else
255 return NULL;
ac7fdc22 256}
ac7fdc22 257};