Fix 'occurred' typo
[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
32namespace libt2n
33{
34
35server_connection::server_connection(int _timeout)
36 : connection_id(0)
37 , my_server(NULL)
38 , connection()
39{
40 set_timeout(_timeout);
41 reset_timeout();
42}
43
44/**
45 * Destructor
46 */
47server_connection::~server_connection()
48{
49}
50
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)
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;
63}
64
65/// check if timeout is expired, close connection if so
66void server_connection::check_timeout()
67{
68 if (timeout != -1 && last_action_time+timeout < time(NULL))
69 {
70 LOGSTREAM(debug,"timeout on connection " << connection_id << ", closing");
71 this->close();
72 }
73}
74
75/// reset the timeout, e.g. if something is received
76void server_connection::reset_timeout()
77{
78 last_action_time=time(NULL);
79}
80
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*/
86void server_connection::add_callback(callback_event_type event, const boost::function<void ()>& func)
87{
88 if (event == new_connection)
89 throw std::logic_error("new_connection callback not allowed for server_connections");
90
91 connection::add_callback(event,func);
92}
93
94server::server()
95 : callbacks(__events_end)
96{
97 set_default_timeout(30);
98 set_logging(NULL,none);
99 next_id=1;
100}
101
102server::~server()
103{
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++)
106 delete i->second;
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();
119}
120
121/** @brief add a callback
122
123 @param event event the function will be called at
124 @param func functor (see boost::function) that will be called
125
126 @note use boost::bind to bind to member functions like this:
127 s.add_callback(new_connection,boost::bind(&my_class::func_to_call_back, boost::ref(*this), _1));
128*/
129void server::add_callback(callback_event_type event, const boost::function<void (unsigned int)>& func)
130{
131 callbacks[event].push_back(func);
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++)
138 i->second->add_callback(event,boost::bind(func, i->first));
139 }
140}
141
142
143/** @brief an event occurred, call all server-side callbacks
144
145 @param event event that occurred
146 @param conn_id connection-id parameter that will be given to the callback-function
147*/
148void server::do_callbacks(callback_event_type event, unsigned int conn_id)
149{
150 std::list<boost::function<void (unsigned int)> >::iterator i,ie=callbacks[event].end();
151 for (i=callbacks[event].begin(); i != ie; i++)
152 (*i)(conn_id);
153}
154
155/// add a new connection to the server
156unsigned int server::add_connection(server_connection* newconn)
157{
158 unsigned int cid=next_id++;
159 newconn->set_id(cid);
160 newconn->set_server(this);
161 connections[cid]=newconn;
162
163 // add all callbacks except new_connection
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
171 LOGSTREAM(debug,"new connection accepted, id: " << cid);
172
173 do_callbacks(new_connection,cid);
174
175 return cid;
176}
177
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
185/**
186 @brief Gets a connection by id
187
188 @param conn_id Connection ID
189
190 @retval Pointer to connection object
191*/
192server_connection* server::get_connection(unsigned int conn_id)
193{
194 std::map<unsigned int, server_connection*>::iterator p=connections.find(conn_id);
195 if (p==connections.end())
196 return NULL;
197 else
198 return p->second;
199}
200
201/// check for timeouts, remove closed connections. don't forget to call this from time to time.
202void server::cleanup()
203{
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++)
206 i->second->check_timeout();
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
213 LOGSTREAM(debug,"removing connection " << i->first << " because it is closed and no more data waiting");
214
215 delete i->second;
216 connections.erase(i);
217 i=connections.begin();
218 ie=connections.end();
219 }
220 else
221 i++;
222 }
223}
224
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*/
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
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 {
240 LOGSTREAM(debug,"got packet (" << data.size() << " bytes) from connection " << i->first);
241
242 conn_id=i->first;
243 return true;
244 }
245
246 return false;
247}
248
249/// get pointer to logging stream, returns NULL if no logging needed
250std::ostream* server::get_logstream(log_level_values level)
251{
252 if (logstream && log_level >= level)
253 return logstream;
254 else
255 return NULL;
256}
257};