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