1 /*=============================================================================
2                               pstream
3 ===============================================================================
4 
5    Client XML transport for Xmlrpc-c based on a very simple byte
6    stream.
7 
8    The protocol we use is the "packet socket" protocol, which
9    is an Xmlrpc-c invention.  It is an almost trivial representation of
10    a sequence of packets on a byte stream.
11 
12    A transport object talks to exactly one server over its lifetime.
13 
14    You can create a pstream transport from any file descriptor from which
15    you can read and write a bidirectional character stream.  Typically,
16    it's a TCP socket.
17 
18    This transport is synchronous only.  It does not provide a working
19    'start' method.  You have at most one outstanding RPC and wait for
20    it to complete.
21 
22    By Bryan Henderson 07.05.12.
23 
24    Contributed to the public domain by its author.
25 =============================================================================*/
26 
27 #include <memory>
28 
29 using namespace std;
30 
31 #include "xmlrpc-c/girerr.hpp"
32 using girerr::throwf;
33 #include "xmlrpc-c/packetsocket.hpp"
34 
35 #include "xmlrpc-c/client_transport.hpp"
36 
37 typedef xmlrpc_c::clientXmlTransport_pstream::BrokenConnectionEx
38     BrokenConnectionEx;
39 
40 namespace xmlrpc_c {
41 
42 struct clientXmlTransport_pstream::constrOpt_impl {
43 
44     constrOpt_impl();
45 
46     struct {
47         int         fd;
48         bool        useBrokenConnEx;
49     } value;
50     struct {
51         bool fd;
52         bool useBrokenConnEx;
53     } present;
54 };
55 
56 
57 
constrOpt_impl()58 clientXmlTransport_pstream::constrOpt_impl::constrOpt_impl() {
59 
60     this->present.fd              = false;
61     this->present.useBrokenConnEx = false;
62 }
63 
64 
65 
66 #define DEFINE_OPTION_SETTER(OPTION_NAME, TYPE) \
67 clientXmlTransport_pstream::constrOpt & \
68 clientXmlTransport_pstream::constrOpt::OPTION_NAME(TYPE const& arg) { \
69     this->implP->value.OPTION_NAME = arg; \
70     this->implP->present.OPTION_NAME = true; \
71     return *this; \
72 }
73 
74 DEFINE_OPTION_SETTER(fd, xmlrpc_socket);
75 DEFINE_OPTION_SETTER(useBrokenConnEx, bool);
76 
77 #undef DEFINE_OPTION_SETTER
78 
79 
80 
constrOpt()81 clientXmlTransport_pstream::constrOpt::constrOpt() {
82 
83     this->implP = new clientXmlTransport_pstream::constrOpt_impl();
84 }
85 
86 
87 
~constrOpt()88 clientXmlTransport_pstream::constrOpt::~constrOpt() {
89 
90     delete(this->implP);
91 }
92 
93 
94 
constrOpt(constrOpt & arg)95 clientXmlTransport_pstream::constrOpt::constrOpt(constrOpt& arg) {
96 
97     this->implP = new clientXmlTransport_pstream::constrOpt_impl(*arg.implP);
98 }
99 
100 
101 
102 class clientXmlTransport_pstream_impl {
103 
104 public:
105     clientXmlTransport_pstream_impl(
106         clientXmlTransport_pstream::constrOpt_impl const& opt);
107 
108     ~clientXmlTransport_pstream_impl();
109 
110     void
111     call(xmlrpc_c::carriageParm * const  carriageParmP,
112          std::string              const& callXml,
113          std::string *            const  responseXmlP);
114 
115 private:
116     packetSocket * packetSocketP;
117 
118     bool usingBrokenConnEx;
119         // We're throwing a Broken Connection object when something fails
120         // because the connection to the server is broken.  When this is false,
121         // we throw an ordinary error when that happens.
122 
123     void
124     sendCall(std::string const& callXml);
125 
126     void
127     recvResp(std::string * const responseXmlP);
128 };
129 
130 
131 
clientXmlTransport_pstream_impl(clientXmlTransport_pstream::constrOpt_impl const & opt)132 clientXmlTransport_pstream_impl::clientXmlTransport_pstream_impl(
133     clientXmlTransport_pstream::constrOpt_impl const& opt) {
134 
135     if (!opt.present.fd)
136         throwf("You must provide a 'fd' constructor option.");
137 
138     auto_ptr<packetSocket> packetSocketAP;
139 
140     try {
141         auto_ptr<packetSocket> p(new packetSocket(opt.value.fd));
142         packetSocketAP = p;
143     } catch (exception const& e) {
144         throwf("Unable to create packet socket out of file descriptor %d.  %s",
145                opt.value.fd, e.what());
146     }
147 
148     if (opt.present.useBrokenConnEx)
149         this->usingBrokenConnEx = opt.value.useBrokenConnEx;
150     else
151         this->usingBrokenConnEx = false;
152 
153     this->packetSocketP = packetSocketAP.release();
154 }
155 
156 
157 
clientXmlTransport_pstream(constrOpt const & optExt)158 clientXmlTransport_pstream::clientXmlTransport_pstream(
159     constrOpt const& optExt) :
160 
161     implP(new clientXmlTransport_pstream_impl(*optExt.implP))
162 {}
163 
164 
165 
~clientXmlTransport_pstream_impl()166 clientXmlTransport_pstream_impl::~clientXmlTransport_pstream_impl() {
167 
168     delete(this->packetSocketP);
169 }
170 
171 
172 
~clientXmlTransport_pstream()173 clientXmlTransport_pstream::~clientXmlTransport_pstream() {
174 
175     delete(this->implP);
176 }
177 
178 
179 
180 void  // private
sendCall(string const & callXml)181 clientXmlTransport_pstream_impl::sendCall(string const& callXml) {
182 /*----------------------------------------------------------------------------
183    Send the text 'callXml' down the pipe as a packet which is the RPC call.
184 -----------------------------------------------------------------------------*/
185     packetPtr const callPacketP(new packet(callXml.c_str(), callXml.length()));
186 
187     try {
188         bool brokenConn;
189 
190         this->packetSocketP->writeWait(callPacketP, &brokenConn);
191 
192         if (brokenConn) {
193             if (this->usingBrokenConnEx)
194                 throw BrokenConnectionEx();
195             else
196                 throwf("Server hung up or connection broke");
197         }
198     } catch (exception const& e) {
199         throwf("Failed to write the call to the packet socket.  %s", e.what());
200     }
201 }
202 
203 
204 
205 void
recvResp(string * const responseXmlP)206 clientXmlTransport_pstream_impl::recvResp(string * const responseXmlP) {
207 /*----------------------------------------------------------------------------
208    Receive a packet which is the RPC response and return its contents
209    as the text *responseXmlP.
210 -----------------------------------------------------------------------------*/
211     packetPtr responsePacketP;
212 
213     try {
214         bool eof;
215         this->packetSocketP->readWait(&eof, &responsePacketP);
216 
217         if (eof) {
218             if (this->usingBrokenConnEx)
219                 throw BrokenConnectionEx();
220             else
221                 throwf("The other end closed the socket before sending "
222                        "the response.");
223         }
224     } catch (exception const& e) {
225         throwf("We sent the call, but couldn't get the response.  %s",
226                e.what());
227     }
228     *responseXmlP =
229         string(reinterpret_cast<char *>(responsePacketP->getBytes()),
230                responsePacketP->getLength());
231 }
232 
233 
234 
235 void
call(carriageParm * const carriageParmP,string const & callXml,string * const responseXmlP)236 clientXmlTransport_pstream_impl::call(
237     carriageParm * const  carriageParmP,
238     string         const& callXml,
239     string *       const  responseXmlP) {
240 
241     carriageParm_pstream * const carriageParmPstreamP(
242         dynamic_cast<carriageParm_pstream *>(carriageParmP));
243 
244     if (carriageParmPstreamP == NULL)
245         throwf("Pstream client XML transport called with carriage "
246                "parameter object not of class carriageParm_pstream");
247 
248     this->sendCall(callXml);
249 
250     this->recvResp(responseXmlP);
251 }
252 
253 
254 
255 void
call(carriageParm * const carriageParmP,string const & callXml,string * const responseXmlP)256 clientXmlTransport_pstream::call(
257     carriageParm * const  carriageParmP,
258     string         const& callXml,
259     string *       const  responseXmlP) {
260 
261     this->implP->call(carriageParmP, callXml, responseXmlP);
262 }
263 
264 } // namespace
265