687906708806630e59ddd8dca8e5045208a8e5fe
[libt2n] / src / connection.cpp
1 /*
2 Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
3
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
6
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
9
10 As a special exception, if other files instantiate templates or use macros
11 or inline functions from this file, or you compile this file and link it
12 with other works to produce a work based on this file, this file
13 does not by itself cause the resulting work to be covered
14 by the GNU General Public License.
15
16 However the source code for this file must still be made available
17 in accordance with section (3) of the GNU General Public License.
18
19 This exception does not invalidate any other reasons why a work based
20 on this file might be covered by the GNU General Public License.
21 */
22
23 #include <string>
24 #include <sstream>
25 #include <iostream>
26 #include <stdexcept>
27
28 #include <netinet/in.h>
29
30 #include "connection.hxx"
31
32 namespace libt2n
33 {
34
35 connection::~connection()
36 {
37     // Run close() manually since it's a virtual function
38     // and we are in the destructor.
39     if (!is_closed())
40     {
41         closed=true;
42         do_callbacks(connection_closed);
43     }
44
45     do_callbacks(connection_deleted);
46 }
47
48 void connection::close()
49 {
50     if (!is_closed())
51     {
52         closed=true;
53         do_callbacks(connection_closed);
54     }
55 }
56
57 /// get the number of bytes being available as next complete packet
58 connection::packet_size_indicator connection::bytes_available()
59 {
60     // no size information -> no packet
61     if (buffer.size() < sizeof(packet_size_indicator))
62         return 0;
63
64     packet_size_indicator psize=ntohl(*((packet_size_indicator*)(buffer.data())));
65
66     // enough data for one packet in buffer?
67     if (buffer.size() < sizeof(packet_size_indicator)+psize)
68         return 0;
69
70     // ok, full packet there
71     return psize;
72 }
73
74 /** @brief read a complete data packet from the buffer. The packet is removed from the
75             connection buffer.
76     @param[out] data the data package
77     @retval true if packet found
78 */
79 bool connection::get_packet(std::string& data)
80 {
81     packet_size_indicator psize;
82
83     if ((psize=bytes_available()))
84     {
85         data.assign(buffer,sizeof(packet_size_indicator),psize);
86         buffer.erase(0,sizeof(packet_size_indicator)+psize);
87         return true;
88     }
89     else
90         return false;
91 }
92
93 /** @brief get (maybe incomplete) data of the next packet from the buffer. Does not remove the data
94             from the connection buffer.
95     @param[out] data the data package
96     @retval full size of the packet when it will be complete
97 */
98 unsigned int connection::peek_packet(std::string& data)
99 {
100     // no size information -> no packet
101     if (buffer.size() < sizeof(packet_size_indicator))
102         return 0;
103
104     packet_size_indicator psize=ntohl(*((packet_size_indicator*)(buffer.data())));
105
106     // not the full data available?
107     packet_size_indicator currsize=psize;
108     if (buffer.size() < currsize+sizeof(packet_size_indicator))
109         currsize=buffer.size()-sizeof(packet_size_indicator);
110
111     data.assign(buffer,sizeof(packet_size_indicator),currsize);
112
113     return psize;
114 }
115
116 /// remove all data from buffer that is not a complete packet
117 void connection::remove_incomplete_packets()
118 {
119     std::string::size_type p=0;
120     std::string::size_type end=buffer.size();
121
122     while (p < end)
123     {
124         // not enough space for size information -> no packet
125         if (p+sizeof(packet_size_indicator) > end)
126             break;
127
128         packet_size_indicator psize=ntohl(*((packet_size_indicator*)(buffer.data()+p)));
129
130         if (p+sizeof(packet_size_indicator)+psize > end)
131         {
132             // incomplete packet
133             break;
134         }
135         else
136         {
137             // move p to where the next packet will start
138             p+=sizeof(packet_size_indicator)+psize;
139         }
140     }
141
142     if (p < end)
143     {
144         // incomplete packets there, remove them
145         buffer.erase(p);
146     }
147 }
148
149 /// send a blob to the peer
150 void connection::write(const std::string& data)
151 {
152     // prepend packet size to data
153     packet_size_indicator psize=htonl(data.size());
154     std::string send_data(data);
155     send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
156
157     real_write(send_data);
158 }
159
160 /** @brief add a callback
161
162     @param event event the function will be called at
163     @param func functor (see boost function) that will be called
164
165     @note use boost::bind to bind to member functions and parameters like this:
166         17 is a fixed parameter that is always added to the call
167         c.add_callback(connection_closed,bind(&my_class::func_to_call_back, boost::ref(*this), 17));
168 */
169 void connection::add_callback(callback_event_type event, const boost::function<void ()>& func)
170 {
171     callbacks[event].push_back(func);
172 }
173
174 /** @brief an event has occurred, execute the callbacks that are registered for this event
175
176     @param event event type that has occurred
177 */
178 void connection::do_callbacks(callback_event_type event)
179 {
180     std::list<boost::function<void ()> >::iterator i,ie=callbacks[event].end();
181     for (i=callbacks[event].begin(); i != ie; i++)
182         (*i)();
183 }
184
185 /** @brief get the callbacks in place for one event
186
187     @param event event the callbacks should be registered for
188     @return std::list of functors (boost::function) with the callbacks
189
190     @note if you want to get the callbacks for all events, loop from 0 to __events_end 
191 */
192 std::list<boost::function<void ()> > connection::get_callback_list(callback_event_type event)
193 {
194     return callbacks[event];
195 }
196
197 /** @brief reopen a already closed connection, removes incomplete packets from the buffer
198
199     @note Only call when the connection is closed.
200
201     @note Justs cares about the data of connection, reconnecting has to be
202           done in a derived class.
203 */
204 void connection::reopen()
205 {
206     if (!is_closed())
207         throw std::logic_error("connection::reopen() called with connection still open");
208
209     closed=false;
210
211     // incomplete buffer data is worthless with a new connection
212     remove_incomplete_packets();
213
214     do_callbacks(new_connection);
215 }
216
217 }