Replace socket_handler::fill_buffer() recursion with loop (#8389)
[libt2n] / src / connection.cpp
CommitLineData
19facd85
TJ
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*/
a11e19b7
GE
22
23#include <string>
24#include <sstream>
a7170401 25#include <iostream>
af84dfb5 26#include <stdexcept>
a11e19b7 27
8104c8f7
GE
28#include <netinet/in.h>
29
a11e19b7
GE
30#include "connection.hxx"
31
32namespace libt2n
33{
34
a64066eb
GE
35connection::~connection()
36{
56f3994d
TJ
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 }
a64066eb
GE
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
487afb79 57/// get the number of bytes being available as next complete packet
a11e19b7
GE
58connection::packet_size_indicator connection::bytes_available()
59{
a11e19b7 60 // no size information -> no packet
8104c8f7 61 if (buffer.size() < sizeof(packet_size_indicator))
644c4d26 62 return 0;
a11e19b7 63
8104c8f7 64 packet_size_indicator psize=ntohl(*((packet_size_indicator*)(buffer.data())));
a11e19b7
GE
65
66 // enough data for one packet in buffer?
8104c8f7 67 if (buffer.size() < sizeof(packet_size_indicator)+psize)
644c4d26 68 return 0;
a11e19b7
GE
69
70 // ok, full packet there
644c4d26 71 return psize;
a11e19b7
GE
72}
73
94247295
GE
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*/
a11e19b7
GE
79bool connection::get_packet(std::string& data)
80{
81 packet_size_indicator psize;
82
83 if ((psize=bytes_available()))
84 {
8104c8f7
GE
85 data.assign(buffer,sizeof(packet_size_indicator),psize);
86 buffer.erase(0,sizeof(packet_size_indicator)+psize);
a11e19b7
GE
87 return true;
88 }
89 else
90 return false;
91}
92
b2ba0928
GE
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
af84dfb5
GE
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
94247295 149/// send a blob to the peer
a11e19b7
GE
150void connection::write(const std::string& data)
151{
152 // prepend packet size to data
8104c8f7 153 packet_size_indicator psize=htonl(data.size());
a11e19b7 154 std::string send_data(data);
644c4d26 155 send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
a11e19b7
GE
156
157 real_write(send_data);
158}
159
a64066eb
GE
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{
a64066eb
GE
171 callbacks[event].push_back(func);
172}
173
a63e08b8 174/** @brief an event has occurred, execute the callbacks that are registered for this event
a64066eb 175
a63e08b8 176 @param event event type that has occurred
a64066eb
GE
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
af84dfb5
GE
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}
a64066eb 216
a11e19b7 217}