Change license from LGPL to GPL version 2 + linking exception. This fixes C++ templat...
[libt2n] / src / command_server.cpp
... / ...
CommitLineData
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*/
22
23#include <string>
24#include <sstream>
25#include <stdexcept>
26#include <iostream>
27
28#include <boost/archive/binary_oarchive.hpp>
29#include <boost/archive/binary_iarchive.hpp>
30#include <boost/archive/xml_oarchive.hpp>
31#include <boost/archive/xml_iarchive.hpp>
32#include <boost/serialization/serialization.hpp>
33
34#include <boost/bind.hpp>
35
36#include "command_server.hxx"
37#include "container.hxx"
38#include "log.hxx"
39
40#ifdef HAVE_CONFIG_H
41#include <config.h>
42#endif
43
44using namespace std;
45
46namespace libt2n
47{
48
49command_server::command_server(server& _s)
50 : s(_s), guard_handle(0)
51{
52 // register callback
53 s.add_callback(new_connection,bind(&command_server::send_hello, boost::ref(*this), _1));
54}
55
56/**
57 * Destructor
58 */
59command_server::~command_server()
60{
61}
62
63/// send a hello message to a new connection
64void command_server::send_hello(unsigned int conn_id)
65{
66 server_connection* sc=s.get_connection(conn_id);
67
68 if (!sc)
69 return; // connection not existing, so no hello
70
71 std::ostringstream hello;
72
73 hello << "T2Nv" << PROTOCOL_VERSION << ';';
74
75 int byteordercheck=1;
76 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
77
78 hello << ';';
79
80 sc->write(hello.str());
81}
82
83/// handle a command including deserialization and answering
84void command_server::handle_packet(const std::string& packet, server_connection* conn)
85{
86 OBJLOGSTREAM(s,debug,"handling packet from connection " << conn->get_id());
87
88 // deserialize packet
89 std::istringstream ifs(packet);
90 boost::archive::binary_iarchive ia(ifs);
91 command_container ccont;
92 result_container res;
93
94 try
95 {
96 ia >> ccont;
97 }
98 catch(boost::archive::archive_exception &e)
99 {
100 std::ostringstream msg;
101 msg << "archive_exception while deserializing on server-side, "
102 "code " << e.code << " (" << e.what() << ")";
103 res.set_exception(new t2n_serialization_error(msg.str()));
104 }
105 catch(...)
106 { throw; }
107
108 if (!res.has_exception())
109 {
110 std::ostream* ostr;
111 if ((ostr=s.get_logstream(fulldebug))!=NULL)
112 {
113 (*ostr) << "decoded packet data: " << std::endl;
114 boost::archive::xml_oarchive xo(*ostr);
115 xo << BOOST_SERIALIZATION_NVP(ccont);
116 }
117
118 command* cmd=cast_command(ccont.get_command());
119
120 if (cmd)
121 {
122 try
123 {
124 res.set_result((*cmd)());
125 }
126 catch (t2n_exception &e)
127 { res.set_exception(e.clone()); }
128 catch (...)
129 { throw; }
130 }
131 else
132 {
133 std::ostringstream msg;
134 if (ccont.get_command()!=NULL)
135 msg << "illegal command of type " << typeid(ccont.get_command()).name() << " called";
136 else
137 msg << "NULL command called";
138 res.set_exception(new t2n_command_error(msg.str()));
139 }
140 }
141
142 std::ostringstream ofs;
143 boost::archive::binary_oarchive oa(ofs);
144
145 try
146 {
147 oa << res;
148 }
149 catch(boost::archive::archive_exception &e)
150 {
151 std::ostringstream msg;
152 msg << "archive_exception while serializing on server-side, "
153 "code " << e.code << " (" << e.what() << ")";
154 res.set_exception(new t2n_serialization_error(msg.str()));
155 oa << res;
156 }
157 catch(...)
158 { throw; }
159
160 std::ostream* ostr;
161 if ((ostr=s.get_logstream(fulldebug))!=NULL)
162 {
163 (*ostr) << "returning result, decoded data: " << std::endl;
164 boost::archive::xml_oarchive xo(*ostr);
165 xo << BOOST_SERIALIZATION_NVP(res);
166 }
167
168 conn->write(ofs.str());
169}
170
171/** @brief handle incoming commands
172 @param[in,out] usec_timeout wait until new data is found, max timeout usecs.
173 -1: wait endless, 0: instant return
174 @param[out] usec_timeout_remaining microseconds from the timeout that were not used
175*/
176void command_server::handle(long long usec_timeout, long long* usec_timeout_remaining)
177{
178 guard_handle++;
179
180 try
181 {
182 if (s.fill_buffer(usec_timeout,usec_timeout_remaining))
183 {
184 std::string packet;
185 unsigned int conn_id = 0;
186
187 while (s.get_packet(packet,conn_id))
188 {
189 server_connection* conn=s.get_connection(conn_id);
190 if (!conn)
191 EXCEPTIONSTREAM(error,logic_error,"illegal connection id " << conn_id << " received");
192 try
193 { handle_packet(packet,conn); }
194 catch (t2n_transfer_error &e)
195 {
196 // shut down a connection with transfer errors (usually write errors)
197 conn->close();
198 }
199 catch(...)
200 { throw; }
201 }
202 }
203 }
204 catch(...)
205 {
206 guard_handle--;
207 throw;
208 }
209 guard_handle--;
210
211 // don't call cleanup on re-entered handle-calls
212 if (guard_handle == 0)
213 s.cleanup();
214}
215
216}