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