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