libt2n: (gerd) add protocol version config, add server callbacks (not fully working...
[libt2n] / src / server.cpp
1 /***************************************************************************
2  *   Copyright (C) 2006 by Gerd v. Egidy                                   *
3  *   gve@intra2net.com                                                     *
4  *                                                                         *
5  *   This library is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU Lesser General Public License version   *
7  *   2.1 as published by the Free Software Foundation.                     *
8  *                                                                         *
9  *   This library is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU Lesser General Public License for more details.                   *
13  *                                                                         *
14  *   You should have received a copy of the GNU Lesser General Public      *
15  *   License along with this program; if not, write to the                 *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19
20 #include <sstream>
21
22 #include "server.hxx"
23 #include "log.hxx"
24
25 namespace libt2n
26 {
27
28 server_connection::server_connection(int _timeout)
29     : connection()
30 {
31     set_timeout(_timeout);
32     reset_timeout();
33     connection_id=0;
34     my_server=0;
35 }
36
37
38 /// get pointer to logging stream, returns NULL if no logging needed
39 std::ostream* server_connection::get_logstream(log_level_values level)
40 {
41     if (my_server != NULL)
42     {
43         std::ostream* ostr=my_server->get_logstream(level);
44         if (ostr != NULL)
45             (*ostr) << "connection " << get_id() << ": ";
46         return ostr;
47     }
48     else
49         return NULL;
50 }
51
52 /// check if timeout is expired, close connection if so
53 void server_connection::check_timeout()
54 {
55     if (timeout != -1 && last_action_time+timeout < time(NULL))
56     {
57         LOGSTREAM(debug,"timeout on connection " << connection_id << ", closing");
58         this->close();
59     }
60 }
61
62 /// reset the timeout, e.g. if something is received
63 void server_connection::reset_timeout()
64 {
65     last_action_time=time(NULL);
66 }
67
68 server::server()
69     : callbacks(__events_end)
70 {
71     set_default_timeout(30);
72     set_logging(NULL,none);
73     next_id=1;
74 }
75
76 server::~server()
77 {
78     std::map<unsigned int, server_connection*>::iterator ie=connections.end();
79     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
80         delete i->second;
81 }
82
83 /** @brief add a callback
84
85     @param event event the function will be called at
86     @param func functor (see boost function) that will be called
87 */
88 void server::add_callback(callback_event_type event, const boost::function<void (server_connection*)>& func)
89 {
90     callbacks[event].push_back(func);
91 }
92
93 void server::do_callbacks(callback_event_type event, server_connection* conn)
94 {
95     std::list<boost::function<void (server_connection*)> >::iterator i,ie=callbacks[event].end();
96     for (i=callbacks[event].begin(); i != ie; i++)
97         (*i)(conn);
98 }
99
100 int server::add_connection(server_connection* newconn)
101 {
102     unsigned int cid=next_id++;
103     newconn->set_id(cid);
104     newconn->set_server(this);
105     connections[cid]=newconn;
106
107     LOGSTREAM(debug,"new connection accepted, id: " << cid);
108
109     do_callbacks(new_connection,newconn);
110
111     return cid;
112 }
113
114 /// activate logging to the given stream. everything above the given level is logged.
115 void server::set_logging(std::ostream *_logstream, log_level_values _log_level)
116 {
117     log_level=_log_level;
118     logstream=_logstream;
119 }
120
121 /**
122     @brief Gets a connection by id
123     
124     @param conn_id Connection ID
125     
126     @retval Pointer to connection object
127 */
128 server_connection* server::get_connection(unsigned int conn_id)
129 {
130     std::map<unsigned int, server_connection*>::iterator p=connections.find(conn_id);
131     if (p==connections.end())
132         return NULL;
133     else
134         return p->second;
135 }
136
137 /// check for timeouts, remove closed connections. don't forget to call this from time to time.
138 void server::cleanup()
139 {
140     std::map<unsigned int, server_connection*>::iterator ie=connections.end();
141     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
142         i->second->check_timeout();
143
144     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie;)
145     {
146         if (i->second->is_closed() && !i->second->packet_available())
147         {
148             // closed and no usable data in buffer -> remove
149             LOGSTREAM(debug,"removing conneciton " << i->first << " because it is closed and no more data waiting");
150
151             delete i->second;
152             connections.erase(i);
153             i=connections.begin();
154             ie=connections.end();
155         }
156         else
157             i++;
158     }
159 }
160
161 /** @brief get a complete data packet from any client. The packet is removed from the
162             connection buffer.
163     @param[out] data the data package
164     @param[out] conn_id the connection id we got this packet from
165     @retval true if packet found
166 */
167 bool server::get_packet(std::string& data, unsigned int& conn_id)
168 {
169     // todo: this is somehow unfair: the first connections in the map get checked more
170     // often than the others and can thus block them out
171
172     std::map<unsigned int, server_connection*>::iterator ie=connections.end();
173     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
174         if (i->second->get_packet(data))
175         {
176             LOGSTREAM(debug,"got packet (" << data.size() << " bytes) from connection " << i->first);
177
178             conn_id=i->first;
179             return true;
180         }
181
182     return false;
183 }
184
185 /// get pointer to logging stream, returns NULL if no logging needed
186 std::ostream* server::get_logstream(log_level_values level)
187 {
188     if (logstream && log_level >= level)
189         return logstream;
190     else
191         return NULL;
192 }
193 };