YARP
Yet Another Robot Platform
XmlRpcCarrier.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
6 #include "XmlRpcCarrier.h"
7 
8 #include "XmlRpcLogComponent.h"
9 
11 #include <yarp/os/Name.h>
12 #include <yarp/os/NetType.h>
13 #include <yarp/os/Bottle.h>
14 #include <yarp/os/Route.h>
15 #include <yarp/os/SizedWriter.h>
16 #include <yarp/os/Contactable.h>
17 #include <yarp/os/Network.h>
18 
19 #include <XmlRpc.h>
20 
21 #include <cstdio>
22 
23 using namespace yarp::os;
24 using YarpXmlRpc::XmlRpcValue;
25 using YarpXmlRpc::XmlRpcClient;
26 using YarpXmlRpc::XmlRpcServerConnection;
27 
28 void toXmlRpcValue(Value& vin, XmlRpcValue& vout)
29 {
30  if (vin.isInt32()) {
31  vout = vin.asInt32();
32  } else if (vin.isFloat64()) {
33  vout = vin.asFloat64();
34  } else if (vin.isString()) {
35  vout = std::string(vin.asString());
36  } else if (vin.isVocab32()) {
37  vout = std::string("[") + std::string(vin.toString()) + "]";
38  } else if (vin.isList()) {
39  Bottle *bot = vin.asList();
40  bool struc = true;
41  int offset = 0;
42  std::string tag = bot->get(0).asString();
43  if (tag=="list") {
44  struc = false;
45  offset = 1;
46  } else if (tag=="dict") {
47  struc = true;
48  offset = 1;
49  } else {
50  // auto-detect
51  for (size_t i=0; i<bot->size(); i++) {
52  Value& vi = bot->get(i);
53  if (!vi.isList()) {
54  struc = false;
55  break;
56  }
57  if (vi.asList()->size()!=2) {
58  struc = false;
59  break;
60  }
61  }
62  }
63  if (struc) {
64  vout = XmlRpcValue();
65  for (size_t i=offset; i<bot->size(); i++) {
66  Bottle *boti = bot->get(i).asList();
67  XmlRpcValue& vouti=vout[std::string(boti->get(0).toString())]=XmlRpcValue();
68  toXmlRpcValue(boti->get(1),vouti);
69  }
70  } else {
71  vout = XmlRpcValue();
72  for (size_t i=offset; i<bot->size(); i++) {
73  XmlRpcValue& vouti = vout[i] = XmlRpcValue();
74  toXmlRpcValue(bot->get(i),vouti);
75  }
76  }
77  }
78 }
79 
81 {
82  Route route = proto.getRoute();
83  route.setFromName("rpc");
84  proto.setRoute(route);
85  return true;
86 }
87 
89 {
92  writer.write(sos);
93  sis.reset(sos.toString());
94  std::string header;
95  if (sender) {
96  header = sis.readLine();
97  }
98  std::string body = sis.readLine();
99  Value v;
100  if (header.length()>0 && header[0]=='q') {
101  body = "yarp.quit";
102  // XMLRPC does not need a quit message, this should get stripped
103  return false;
104  }
105  Bottle *bot = v.asList();
106  bot->fromString(body);
107  std::string methodName;
108  if (sender) {
109  methodName = bot->get(0).toString();
110  *bot = bot->tail();
111  }
112  XmlRpcValue args;
113  if (bot->size()==1) {
114  toXmlRpcValue(bot->get(0),args);
115  } else {
116  toXmlRpcValue(v,args);
117  }
118  std::string req;
119  if (sender) {
120  const Contact& addr = host.isValid()?host:proto.getStreams().getRemoteAddress();
121  XmlRpcClient c(addr.getHost().c_str(),(addr.getPort()>0)?addr.getPort():80);
122  c.generateRequest(methodName.c_str(),args);
123  req = c.getRequest();
124  } else {
125  XmlRpcServerConnection c(0, nullptr);
126  c.generateResponse(args.toXml());
127  req = c.getResponse();
128  }
129  int start = 0;
130  if (sender) {
131  if (req.length()<8) {
132  yCError(XMLRPCCARRIER, "XmlRpcCarrier fail");
133  return false;
134  }
135  for (char i : req) {
136  if (i == '\n') {
137  start++;
138  break;
139  }
140  start++;
141  }
142  if (!firstRound) {
143  Bytes b((char*)http.c_str(),http.length());
144  proto.os().write(b);
145  }
146  firstRound = false;
147  }
148  Bytes b((char*)req.c_str()+start,req.length()-start);
149  proto.os().write(b);
150 
151  return proto.os().isOk();
152 }
153 
154 
156 {
157  return write(proto,writer);
158 }
159 
160 
161 bool XmlRpcCarrier::shouldInterpretRosMessages(ConnectionState& proto)
162 {
163  // We need to set the interpretRos flag, which controls
164  // whether ROS-style admin messages are treated as
165  // admin messages or data messages in YARP.
166  // In the future, they should always be data messages.
167  // For now, they should be admin messages for all ports
168  // except ports tagged as corresponding to ros nodes.
169 
170  bool nodelike = false;
171  Contactable *port = proto.getContactable();
172  Property opt;
173  if (port) {
174  Property *pport = port->acquireProperties(true);
175  if (pport) {
176  opt = *pport;
177  }
178  port->releaseProperties(pport);
179  }
180  if (opt.check("node_like")) {
181  nodelike = true;
182  }
183 
184  Name n(proto.getRoute().getCarrierName() + "://test");
185  std::string rospass = n.getCarrierModifier("ros");
186  interpretRos = !nodelike;
187  if (rospass=="1"||rospass=="on") {
188  interpretRos = true;
189  }
190  if (rospass=="0"||rospass=="off") {
191  interpretRos = false;
192  }
193  return interpretRos;
194 }
195 
197 {
198  shouldInterpretRosMessages(proto);
199  std::string target = "POST /RPC2";
200  Name n(proto.getRoute().getCarrierName() + "://test");
201  std::string pathValue = n.getCarrierModifier("path");
202  if (pathValue!="") {
203  target = "POST /";
204  target += pathValue;
205  // on the wider web, we should provide real host names
206  host = NetworkBase::queryName(proto.getRoute().getToName());
207  }
208  target += " HTTP/1.1\n";
209  http = target;
210  Bytes b((char*)target.c_str(),target.length());
211  proto.os().write(b);
212  return true;
213 }
214 
215 
217 {
218  shouldInterpretRosMessages(proto);
219  sender = false;
220  auto* stream = new XmlRpcStream(proto.giveStreams(),
221  sender,
222  interpretRos);
223  if (stream == nullptr) {
224  return false;
225  }
226  proto.takeStreams(stream);
227  return true;
228 }
void toXmlRpcValue(Value &vin, XmlRpcValue &vout)
const yarp::os::LogComponent & XMLRPCCARRIER()
bool expectSenderSpecifier(yarp::os::ConnectionState &proto) override
Expect the name of the sending port.
bool reply(yarp::os::ConnectionState &proto, yarp::os::SizedWriter &writer) override
bool respondToHeader(yarp::os::ConnectionState &proto) override
Respond to the header.
bool write(yarp::os::ConnectionState &proto, yarp::os::SizedWriter &writer) override
Write a message.
bool sendHeader(yarp::os::ConnectionState &proto) override
Write a header appropriate to the carrier to the connection, followed by any carrier-specific data.
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:74
void fromString(const std::string &text)
Initializes bottle from a string.
Definition: Bottle.cpp:204
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:251
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:246
Bottle tail() const
Get all but the first element of a bottle.
Definition: Bottle.cpp:388
A simple abstraction for a block of bytes.
Definition: Bytes.h:25
The basic state of a connection - route, streams in use, etc.
virtual Contactable * getContactable() const =0
Get the port associated with the connection.
virtual TwoWayStream & getStreams()=0
Access the streams associated with the connection.
virtual const Route & getRoute() const =0
Get the route associated with this connection.
virtual TwoWayStream * giveStreams()=0
Take ownership of the streams associated with the connection.
virtual void takeStreams(TwoWayStream *streams)=0
Provide streams to be used with the connection.
OutputStream & os()
Shorthand for getOutputStream()
virtual void setRoute(const Route &route)=0
Set the route associated with this connection.
Represents how to reach a part of a YARP network.
Definition: Contact.h:36
bool isValid() const
Checks if a Contact is tagged as valid.
Definition: Contact.cpp:298
int getPort() const
Get the port number associated with this Contact for socket communication.
Definition: Contact.cpp:239
std::string getHost() const
Get the host name associated with this Contact for socket communication.
Definition: Contact.cpp:228
An abstract port.
Definition: Contactable.h:35
virtual void releaseProperties(Property *prop)=0
End access unstructured port properties.
virtual Property * acquireProperties(bool readOnly)=0
Access unstructured port properties.
std::string readLine(const char terminal='\n', bool *success=nullptr)
Read a block of text terminated with a specific marker (or EOF).
Definition: InputStream.cpp:54
Simple abstraction for a YARP port name.
Definition: Name.h:19
std::string getCarrierModifier(const char *mod, bool *hasModifier=nullptr)
Definition: Name.cpp:44
static Contact queryName(const std::string &name)
Find out information about a registered name.
Definition: Network.cpp:995
virtual bool isOk() const =0
Check if the stream is ok or in an error state.
virtual void write(char ch)
Write a single byte to the stream.
A class for storing options and configuration information.
Definition: Property.h:34
bool check(const std::string &key) const override
Check if there exists a property of the given name.
Definition: Property.cpp:1041
Information about a connection between two ports.
Definition: Route.h:29
const std::string & getToName() const
Get the destination of the route.
Definition: Route.cpp:103
const std::string & getCarrierName() const
Get the carrier type of the route.
Definition: Route.cpp:123
void setFromName(const std::string &fromName)
Set the source of the route.
Definition: Route.cpp:98
Minimal requirements for an efficient Writer.
Definition: SizedWriter.h:33
virtual void write(OutputStream &os)
Definition: SizedWriter.cpp:16
An InputStream that reads from a string.
An OutputStream that produces a string.
virtual const Contact & getRemoteAddress() const =0
Get the address of the remote side of the stream.
A single value (typically within a Bottle).
Definition: Value.h:45
virtual yarp::conf::float64_t asFloat64() const
Get 64-bit floating point value.
Definition: Value.cpp:222
virtual bool isString() const
Checks if value is a string.
Definition: Value.cpp:156
virtual std::int32_t asInt32() const
Get 32-bit integer value.
Definition: Value.cpp:204
virtual bool isList() const
Checks if value is a list.
Definition: Value.cpp:162
virtual Bottle * asList() const
Get list value.
Definition: Value.cpp:240
virtual bool isFloat64() const
Checks if value is a 64-bit floating point number.
Definition: Value.cpp:150
std::string toString() const override
Return a standard text representation of the content of the object.
Definition: Value.cpp:356
virtual bool isInt32() const
Checks if value is a 32-bit integer.
Definition: Value.cpp:132
virtual bool isVocab32() const
Checks if value is a vocabulary identifier.
Definition: Value.cpp:174
virtual std::string asString() const
Get string value.
Definition: Value.cpp:234
#define yCError(component,...)
Definition: LogComponent.h:154
An interface to the operating system, including Port based communication.
bool write(const ImageOf< PixelRgb > &src, const std::string &dest, image_fileformat format=FORMAT_PPM)
Definition: ImageFile.cpp:1098