Fix 'occurred' typo
[libt2n] / src / connection.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 <string>
24#include <sstream>
25#include <iostream>
26#include <stdexcept>
27
28#include <netinet/in.h>
29
30#include "connection.hxx"
31
32namespace libt2n
33{
34
35connection::~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
48void 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
58connection::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*/
79bool 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*/
98unsigned 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
117void 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
150void 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*/
169void 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*/
178void 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*/
192std::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*/
204void 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}