Commit | Line | Data |
---|---|---|
19facd85 TJ |
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 | */ | |
7087e187 GE |
22 | |
23 | #include <string> | |
24 | #include <sstream> | |
25 | ||
26 | #include <boost/archive/binary_oarchive.hpp> | |
27 | #include <boost/archive/binary_iarchive.hpp> | |
28 | #include <boost/archive/xml_oarchive.hpp> | |
29 | #include <boost/archive/xml_iarchive.hpp> | |
30 | #include <boost/serialization/serialization.hpp> | |
7087e187 | 31 | |
8104c8f7 GE |
32 | #include <boost/bind.hpp> |
33 | ||
7087e187 GE |
34 | #include "command_client.hxx" |
35 | ||
8104c8f7 GE |
36 | #ifdef HAVE_CONFIG_H |
37 | #include <config.h> | |
38 | #endif | |
39 | ||
7087e187 GE |
40 | using namespace std; |
41 | ||
42 | namespace libt2n | |
43 | { | |
44 | ||
696c95c2 TJ |
45 | /** |
46 | * Constructor | |
47 | * @param _c connection for this command. Ownership of the pointer is outside. | |
48 | * @param _command_timeout_usec timeout until the command has to be completed | |
49 | * @param _hello_timeout_usec timeout until hello has to be received | |
50 | */ | |
fb3345ad | 51 | command_client::command_client(client_connection* _c, long long _command_timeout_usec, long long _hello_timeout_usec) |
8104c8f7 | 52 | : c(_c) |
696c95c2 | 53 | , constructorException(NULL) |
8104c8f7 | 54 | { |
45a2ebc9 GE |
55 | command_timeout_usec=_command_timeout_usec; |
56 | hello_timeout_usec=_hello_timeout_usec; | |
57 | ||
8104c8f7 | 58 | // for reconnects |
fb3345ad | 59 | c->add_callback(new_connection,bind(&command_client::read_hello, boost::ref(*this))); |
8104c8f7 | 60 | |
fb3345ad | 61 | // don't expect hello from an always closed connection (like dummy_client_connection) |
b5922184 GE |
62 | if (!is_connection_closed()) |
63 | { | |
64 | try | |
65 | { | |
66 | read_hello(); | |
67 | } | |
68 | catch (t2n_communication_error &e) | |
69 | { | |
70 | c->close(); | |
71 | ||
72 | // store a copy of the exception that you can find out details about the error later | |
696c95c2 | 73 | constructorException = e.clone(); |
b5922184 | 74 | } |
b5922184 | 75 | } |
8104c8f7 GE |
76 | } |
77 | ||
696c95c2 TJ |
78 | /** |
79 | * Destructor | |
80 | */ | |
81 | command_client::~command_client() | |
82 | { | |
83 | if (constructorException) | |
84 | { | |
85 | delete constructorException; | |
86 | constructorException = NULL; | |
87 | } | |
88 | } | |
89 | ||
af84dfb5 | 90 | /** @brief replace the connection currently in use with a new one |
fb3345ad | 91 | @param _c pointer to the new connection |
af84dfb5 GE |
92 | |
93 | @note the old connection must still be valid when this method is called, | |
94 | it can safely be deleted after this method returned | |
95 | ||
96 | @note all callbacks registered on the old connection will be copied over | |
97 | to the new one | |
98 | */ | |
fb3345ad | 99 | void command_client::replace_connection(client_connection* _c) |
af84dfb5 GE |
100 | { |
101 | // copy all callbacks registered on the old connection | |
102 | for(callback_event_type e=static_cast<callback_event_type>(0); | |
103 | e < __events_end; | |
104 | e=static_cast<callback_event_type>(static_cast<int>(e)+1)) | |
105 | { | |
fb3345ad | 106 | list<boost::function<void ()> > evcb=c->get_callback_list(e); |
af84dfb5 GE |
107 | |
108 | for (list<boost::function<void ()> >::iterator i=evcb.begin(); i != evcb.end(); i++) | |
fb3345ad | 109 | _c->add_callback(e,*i); |
af84dfb5 GE |
110 | } |
111 | ||
112 | // replace the connection | |
113 | c=_c; | |
114 | ||
115 | read_hello(); | |
116 | } | |
117 | ||
487afb79 GE |
118 | /** @brief return a complete packet |
119 | @param usec_timeout maximum microseconds to wait until the packet is complete | |
120 | @retval packet data as std::string | |
121 | ||
122 | @note throws a t2n_transfer_error if the timeout is exceeded | |
123 | */ | |
45a2ebc9 | 124 | std::string command_client::read_packet(const long long &usec_timeout) |
8104c8f7 | 125 | { |
8104c8f7 | 126 | string resultpacket; |
45a2ebc9 GE |
127 | bool got_packet=false; |
128 | long long my_timeout=usec_timeout; | |
fb3345ad GE |
129 | while(!(got_packet=c->get_packet(resultpacket)) && my_timeout > 0 && !c->is_closed()) |
130 | c->fill_buffer(my_timeout,&my_timeout); | |
8104c8f7 | 131 | |
45a2ebc9 GE |
132 | if (!got_packet) |
133 | throw t2n_transfer_error("timeout exceeded"); | |
134 | ||
135 | return resultpacket; | |
136 | } | |
137 | ||
487afb79 GE |
138 | /** @brief read and check the hello message at the beginning of a connection |
139 | ||
140 | @note throws exceptions if something went wrong | |
141 | */ | |
45a2ebc9 GE |
142 | void command_client::read_hello() |
143 | { | |
b2ba0928 GE |
144 | string resultpacket; |
145 | bool got_packet=false; | |
146 | long long my_timeout=hello_timeout_usec; | |
fb3345ad | 147 | while(!(got_packet=c->get_packet(resultpacket)) && my_timeout > 0 && !c->is_closed()) |
b2ba0928 | 148 | { |
fb3345ad | 149 | c->fill_buffer(my_timeout,&my_timeout); |
b2ba0928 | 150 | |
fb3345ad | 151 | c->peek_packet(resultpacket); |
b2ba0928 GE |
152 | check_hello(resultpacket); // will throw before timeout if wrong data received |
153 | } | |
154 | ||
155 | if (!got_packet) | |
156 | throw t2n_transfer_error("timeout exceeded"); | |
157 | ||
158 | if (!check_hello(resultpacket)) | |
159 | throw t2n_version_mismatch("illegal hello received (incomplete): "+resultpacket); | |
160 | } | |
161 | ||
487afb79 GE |
162 | /** @brief check if a hello message is valid |
163 | @param hellostr std::string with the message to check | |
164 | @retval true if the hello is good and complete | |
165 | ||
166 | @note you can check incomplete hellos. you will get a false return value | |
167 | but no exception. throws exceptions as soon as something is wrong. | |
168 | */ | |
169 | bool command_client::check_hello(const std::string& hellostr) | |
b2ba0928 GE |
170 | { |
171 | istringstream hello(hellostr); | |
8104c8f7 | 172 | |
b2ba0928 GE |
173 | char chk; |
174 | ||
175 | if (hello.read(&chk,1)) | |
176 | { | |
177 | if (chk != 'T') | |
178 | throw t2n_version_mismatch("illegal hello received (T2N)"); | |
179 | } | |
180 | else | |
181 | return false; | |
182 | ||
183 | if (hello.read(&chk,1)) | |
184 | { | |
185 | if (chk != '2') | |
186 | throw t2n_version_mismatch("illegal hello received (T2N)"); | |
187 | } | |
188 | else | |
189 | return false; | |
190 | ||
191 | if (hello.read(&chk,1)) | |
192 | { | |
193 | if (chk != 'N') | |
194 | throw t2n_version_mismatch("illegal hello received (T2N)"); | |
195 | } | |
196 | else | |
197 | return false; | |
198 | ||
199 | if (hello.read(&chk,1)) | |
200 | { | |
201 | if (chk != 'v') | |
202 | throw t2n_version_mismatch("illegal hello received (T2N)"); | |
203 | } | |
204 | else | |
205 | return false; | |
8104c8f7 GE |
206 | |
207 | int prot_version; | |
b2ba0928 GE |
208 | if (hello >> prot_version) |
209 | { | |
210 | if (prot_version != PROTOCOL_VERSION) | |
211 | throw t2n_version_mismatch("not compatible with the server protocol version"); | |
212 | } | |
213 | else | |
214 | return false; | |
215 | ||
216 | if (hello.read(&chk,1)) | |
217 | { | |
218 | if (chk != ';') | |
219 | throw t2n_version_mismatch("illegal hello received (1. ;)"); | |
220 | } | |
221 | else | |
222 | return false; | |
8104c8f7 | 223 | |
b2ba0928 GE |
224 | unsigned int hbo; |
225 | if (hello.read((char*)&hbo,sizeof(hbo))) | |
226 | { | |
227 | if (hbo != 1) | |
228 | throw t2n_version_mismatch("host byte order not matching"); | |
229 | } | |
230 | else | |
231 | return false; | |
8104c8f7 | 232 | |
b2ba0928 GE |
233 | if (hello.read(&chk,1)) |
234 | { | |
235 | if (chk != ';') | |
236 | throw t2n_version_mismatch("illegal hello received (2. ;)"); | |
237 | } | |
238 | else | |
239 | return false; | |
8104c8f7 | 240 | |
b2ba0928 | 241 | return true; |
8104c8f7 GE |
242 | } |
243 | ||
487afb79 GE |
244 | /** @brief send a command to the server and store the result |
245 | @param cmd pointer to a command-object | |
246 | @param[out] res result container to store the result in | |
247 | ||
248 | @note you can check incomplete hellos. you will get a false return value | |
249 | but no exception. throws exceptions as soon as something is wrong. | |
250 | */ | |
7087e187 GE |
251 | void command_client::send_command(command* cmd, result_container &res) |
252 | { | |
253 | ostringstream ofs; | |
254 | command_container cc(cmd); | |
255 | boost::archive::binary_oarchive oa(ofs); | |
256 | ||
b5922184 GE |
257 | if (is_connection_closed()) |
258 | throw t2n_transfer_error("connection to server is closed"); | |
259 | ||
45a2ebc9 GE |
260 | try |
261 | { | |
262 | oa << cc; | |
263 | } | |
264 | catch(boost::archive::archive_exception &e) | |
265 | { | |
266 | ostringstream msg; | |
267 | msg << "archive_exception while serializing on client-side, code " << e.code << " (" << e.what() << ")"; | |
268 | throw t2n_serialization_error(msg.str()); | |
269 | } | |
7087e187 | 270 | |
d535333f | 271 | std::ostream* ostr; |
fb3345ad | 272 | if ((ostr=c->get_logstream(fulldebug))!=NULL) |
d535333f GE |
273 | { |
274 | (*ostr) << "sending command, decoded data: " << std::endl; | |
275 | boost::archive::xml_oarchive xo(*ostr); | |
276 | xo << BOOST_SERIALIZATION_NVP(cc); | |
696c95c2 | 277 | } |
d535333f | 278 | |
fb3345ad | 279 | c->write(ofs.str()); |
7087e187 | 280 | |
45a2ebc9 | 281 | istringstream ifs(read_packet(command_timeout_usec)); |
7087e187 GE |
282 | boost::archive::binary_iarchive ia(ifs); |
283 | ||
45a2ebc9 GE |
284 | try |
285 | { | |
286 | ia >> res; | |
287 | } | |
288 | catch(boost::archive::archive_exception &e) | |
289 | { | |
290 | ostringstream msg; | |
291 | msg << "archive_exception while deserializing on client-side, code " << e.code << " (" << e.what() << ")"; | |
292 | throw t2n_serialization_error(msg.str()); | |
293 | } | |
d535333f | 294 | |
fb3345ad | 295 | if ((ostr=c->get_logstream(fulldebug))!=NULL) |
d535333f GE |
296 | { |
297 | (*ostr) << "received result, decoded data: " << std::endl; | |
298 | boost::archive::xml_oarchive xo(*ostr); | |
299 | xo << BOOST_SERIALIZATION_NVP(res); | |
300 | } | |
7087e187 GE |
301 | } |
302 | ||
303 | } |