libt2n: (tomj) documented code problems; have to find out if this is the source of...
[libt2n] / src / connection.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 <string>
21 #include <sstream>
22 #include <iostream>
23 #include <stdexcept>
24
25 #include <netinet/in.h>
26
27 #include "connection.hxx"
28
29 namespace libt2n
30 {
31
32 connection::~connection()
33 {
34     // we want the connection_closed callbacks to be called before
35     // FIXME: Functios is virtual
36     close();
37
38     do_callbacks(connection_deleted);
39 }
40
41 void connection::close()
42 {
43     if (!is_closed())
44     {
45         closed=true;
46         do_callbacks(connection_closed);
47     }
48 }
49
50 /// get the number of bytes being available as next complete packet
51 connection::packet_size_indicator connection::bytes_available()
52 {
53     // no size information -> no packet
54     if (buffer.size() < sizeof(packet_size_indicator))
55         return 0;
56
57     packet_size_indicator psize=ntohl(*((packet_size_indicator*)(buffer.data())));
58
59     // enough data for one packet in buffer?
60     if (buffer.size() < sizeof(packet_size_indicator)+psize)
61         return 0;
62
63     // ok, full packet there
64     return psize;
65 }
66
67 /** @brief read a complete data packet from the buffer. The packet is removed from the
68             connection buffer.
69     @param[out] data the data package
70     @retval true if packet found
71 */
72 bool connection::get_packet(std::string& data)
73 {
74     packet_size_indicator psize;
75
76     if ((psize=bytes_available()))
77     {
78         data.assign(buffer,sizeof(packet_size_indicator),psize);
79         buffer.erase(0,sizeof(packet_size_indicator)+psize);
80         return true;
81     }
82     else
83         return false;
84 }
85
86 /** @brief get (maybe incomplete) data of the next packet from the buffer. Does not remove the data
87             from the connection buffer.
88     @param[out] data the data package
89     @retval full size of the packet when it will be complete
90 */
91 unsigned int connection::peek_packet(std::string& data)
92 {
93     // no size information -> no packet
94     if (buffer.size() < sizeof(packet_size_indicator))
95         return 0;
96
97     packet_size_indicator psize=ntohl(*((packet_size_indicator*)(buffer.data())));
98
99     // not the full data available?
100     packet_size_indicator currsize=psize;
101     if (buffer.size() < currsize+sizeof(packet_size_indicator))
102         currsize=buffer.size()-sizeof(packet_size_indicator);
103
104     data.assign(buffer,sizeof(packet_size_indicator),currsize);
105
106     return psize;
107 }
108
109 /// remove all data from buffer that is not a complete packet
110 void connection::remove_incomplete_packets()
111 {
112     std::string::size_type p=0;
113     std::string::size_type end=buffer.size();
114
115     while (p < end)
116     {
117         // not enough space for size information -> no packet
118         if (p+sizeof(packet_size_indicator) > end)
119             break;
120
121         packet_size_indicator psize=ntohl(*((packet_size_indicator*)(buffer.data()+p)));
122
123         if (p+sizeof(packet_size_indicator)+psize > end)
124         {
125             // incomplete packet
126             break;
127         }
128         else
129         {
130             // move p to where the next packet will start
131             p+=sizeof(packet_size_indicator)+psize;
132         }
133     }
134
135     if (p < end)
136     {
137         // incomplete packets there, remove them
138         buffer.erase(p);
139     }
140 }
141
142 /// send a blob to the peer
143 void connection::write(const std::string& data)
144 {
145     // prepend packet size to data
146     packet_size_indicator psize=htonl(data.size());
147     std::string send_data(data);
148     send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
149
150     real_write(send_data);
151 }
152
153 /** @brief add a callback
154
155     @param event event the function will be called at
156     @param func functor (see boost function) that will be called
157
158     @note use boost::bind to bind to member functions and parameters like this:
159         17 is a fixed parameter that is always added to the call
160         c.add_callback(connection_closed,bind(&my_class::func_to_call_back, boost::ref(*this), 17));
161 */
162 void connection::add_callback(callback_event_type event, const boost::function<void ()>& func)
163 {
164     callbacks[event].push_back(func);
165 }
166
167 /** @brief an event has occured, execute the callbacks that are registered for this event
168
169     @param event event type that has occured
170 */
171 void connection::do_callbacks(callback_event_type event)
172 {
173     std::list<boost::function<void ()> >::iterator i,ie=callbacks[event].end();
174     for (i=callbacks[event].begin(); i != ie; i++)
175         (*i)();
176 }
177
178 /** @brief get the callbacks in place for one event
179
180     @param event event the callbacks should be registered for
181     @return std::list of functors (boost::function) with the callbacks
182
183     @note if you want to get the callbacks for all events, loop from 0 to __events_end 
184 */
185 std::list<boost::function<void ()> > connection::get_callback_list(callback_event_type event)
186 {
187     return callbacks[event];
188 }
189
190 /** @brief reopen a already closed connection, removes incomplete packets from the buffer
191
192     @note Only call when the connection is closed.
193
194     @note Justs cares about the data of connection, reconnecting has to be
195           done in a derived class.
196 */
197 void connection::reopen()
198 {
199     if (!is_closed())
200         throw std::logic_error("connection::reopen() called with connection still open");
201
202     closed=false;
203
204     // incomplete buffer data is worthless with a new connection
205     remove_incomplete_packets();
206
207     do_callbacks(new_connection);
208 }
209
210 }