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