1db24a4dca7d6ca4d74f00ef29c04fd3afe88402
[libt2n] / src / command_server.cpp
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 */
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
44 using namespace std;
45
46 namespace libt2n
47 {
48
49 command_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  */
59 command_server::~command_server()
60 {
61 }
62
63 /// send a hello message to a new connection
64 void 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
84 void 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
106     if (!res.has_exception())
107     {
108         std::ostream* ostr;
109         if ((ostr=s.get_logstream(fulldebug))!=NULL)
110         {
111             (*ostr) << "decoded packet data: " << std::endl;
112             boost::archive::xml_oarchive xo(*ostr);
113             xo << BOOST_SERIALIZATION_NVP(ccont);
114         }
115
116         command* cmd=cast_command(ccont.get_command());
117
118         if (cmd)
119         {
120             try
121             {
122                 res.set_result((*cmd)());
123             }
124             catch (t2n_exception &e)
125                 { res.set_exception(e.clone()); }
126         }
127         else
128         {
129             std::ostringstream msg;
130             if (ccont.get_command()!=NULL)
131                 msg << "illegal command of type " << typeid(ccont.get_command()).name() << " called";
132             else
133                 msg << "NULL command called";
134             res.set_exception(new t2n_command_error(msg.str()));
135         }
136     }
137
138     std::ostringstream ofs;
139     boost::archive::binary_oarchive oa(ofs);
140
141     try
142     {
143         oa << res;
144     }
145     catch(boost::archive::archive_exception &e)
146     {
147         std::ostringstream msg;
148         msg << "archive_exception while serializing on server-side, "
149                "code " << e.code << " (" << e.what() << ")";
150         res.set_exception(new t2n_serialization_error(msg.str()));
151         oa << res;
152     }
153
154     std::ostream* ostr;
155     if ((ostr=s.get_logstream(fulldebug))!=NULL)
156     {
157         (*ostr) << "returning result, decoded data: " << std::endl;
158         boost::archive::xml_oarchive xo(*ostr);
159         xo << BOOST_SERIALIZATION_NVP(res);
160     }
161
162     conn->write(ofs.str());
163 }
164
165 /** @brief handle incoming commands
166     @param[in,out] usec_timeout wait until new data is found, max timeout usecs.
167             -1: wait endless, 0: instant return
168     @param[out] usec_timeout_remaining microseconds from the timeout that were not used
169 */
170 void command_server::handle(long long usec_timeout, long long* usec_timeout_remaining)
171 {
172     guard_handle++;
173
174     try
175     {
176         if (s.fill_buffer(usec_timeout,usec_timeout_remaining))
177         {
178             std::string packet;
179             unsigned int conn_id = 0;
180
181             while (s.get_packet(packet,conn_id))
182             {
183                 server_connection* conn=s.get_connection(conn_id);
184                 if (!conn)
185                     EXCEPTIONSTREAM(error,logic_error,"illegal connection id " << conn_id << " received");
186                 try
187                     { handle_packet(packet,conn); }
188                 catch (t2n_transfer_error &e)
189                 {
190                     // shut down a connection with transfer errors (usually write errors)
191                     conn->close();
192                 }
193             }
194         }
195     }
196     catch(...)
197     {
198         guard_handle--;
199         throw;
200     }
201     guard_handle--;
202
203     // don't call cleanup on re-entered handle-calls
204     if (guard_handle == 0)
205         s.cleanup();
206 }
207
208 }