Adding DOCUMENTATION flag to the CMakeLists.txt
[libt2n] / src / server.cpp
... / ...
CommitLineData
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*/
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
33namespace libt2n
34{
35
36server_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 */
48server_connection::~server_connection()
49{
50}
51
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)
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
67void 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
77void 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*/
87void 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
95server::server()
96 : callbacks(__events_end)
97{
98 set_default_timeout(30);
99 set_logging(NULL,none);
100 next_id=1;
101}
102
103server::~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 */
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();
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*/
130void 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*/
149void 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
157unsigned 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.
180void 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*/
193server_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.
203void 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*/
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
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
251std::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};