xdrpp
RFC4506 XDR compiler and message library
srpc.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 
3 #ifndef _XDRPP_SRPC_H_HEADER_INCLUDED_
4 #define _XDRPP_SRPC_H_HEADER_INCLUDED_ 1
5 
6 //! \file srpc.h Simple synchronous RPC functions.
7 
8 #include <xdrpp/exception.h>
9 #include <xdrpp/server.h>
10 
11 namespace xdr {
12 
13 extern bool xdr_trace_client;
14 
15 msg_ptr read_message(sock_t s);
16 void write_message(sock_t s, const msg_ptr &m);
17 
18 void prepare_call(uint32_t prog, uint32_t vers, uint32_t proc, rpc_msg &hdr);
19 template<typename P> inline void
20 prepare_call(rpc_msg &hdr)
21 {
22  prepare_call(P::interface_type::program, P::interface_type::version,
23  P::proc, hdr);
24 }
25 
26 
27 //! Synchronous file descriptor demultiplexer.
29  const sock_t s_;
30 
31  static void moveret(pointer<xdr_void> &) {}
32  template<typename T> static T &&moveret(T &t) { return std::move(t); }
33 
34  //static xdr_void arg_tuple() { return xdr_void{}; }
35  template<typename T> const T &arg_tuple(const T &t) { return t; }
36  template<typename...T> std::tuple<const T &...> arg_tuple(const T &...t) {
37  return std::make_tuple(std::cref(t)...);
38  }
39 
40 public:
41  synchronous_client_base(sock_t s) : s_(s) {}
42  synchronous_client_base(const synchronous_client_base &c) : s_(c.s_) {}
43 
44  template<typename P, typename...A> typename std::conditional<
45  std::is_void<typename P::res_type>::value, void,
46  std::unique_ptr<typename P::res_type>>::type
47  invoke(const A &...a) {
48  rpc_msg hdr;
49  prepare_call<P>(hdr);
50  uint32_t xid = hdr.xid;
51 
52  if (xdr_trace_client) {
53  std::string s = "CALL ";
54  s += P::proc_name();
55  s += " -> [xid " + std::to_string(xid) + "]";
56  std::clog << xdr_to_string(std::tie(a...), s.c_str());
57  }
58  write_message(s_, xdr_to_msg(hdr, a...));
59  msg_ptr m = read_message(s_);
60 
61  xdr_get g(m);
62  archive(g, hdr);
63  check_call_hdr(hdr);
64  if (hdr.xid != xid)
65  throw xdr_runtime_error("synchronous_client: unexpected xid");
66 
68  archive(g, r.activate());
69  g.done();
70  if (xdr_trace_client) {
71  std::string s = "REPLY ";
72  s += P::proc_name();
73  s += " <- [xid " + std::to_string(xid) + "]";
74  std::clog << xdr_to_string(*r, s.c_str());
75  }
76  return moveret(r);
77  }
78 
79  // because _xdr_client expects a pointer type
80  synchronous_client_base *operator->() { return this; }
81 };
82 
83 //! Create an RPC client from an interface type and connected stream
84 //! socket. Note that the file descriptor is not closed afterwards
85 //! (as you may wish to use different interfaces over the same file
86 //! descriptor). A simple example looks like this:
87 //!
88 //! \code
89 //! unique_fd fd = tcp_connect_rpc(argc > 2 ? argv[2] : nullptr,
90 //! MyProg1::program, MyProg1::version);
91 //! srpc_client<MyProg1> c{fd.get()};
92 //! unique_ptr<big_string> result = c.hello(5);
93 //! \endcode
94 template<typename T> using srpc_client =
95  typename T::template _xdr_client<synchronous_client_base>;
96 
97 
98 template<typename T, typename Session, typename Interface>
99 class srpc_service : public service_base {
100  template<typename P, typename A> typename
101  std::enable_if<std::is_same<void, typename P::res_type>::value,
102  const xdr_void *>::type
103  dispatch1(Session *s, A &a) {
104  static xdr_void v;
105  dispatch_with_session<P>(server_, s, std::move(a));
106  return &v;
107  }
108  template<typename P, typename A> typename
109  std::enable_if<!std::is_same<void, typename P::res_type>::value,
110  std::unique_ptr<typename P::res_type>>::type
111  dispatch1(Session *s, A &a) {
112  return dispatch_with_session<P>(server_, s, std::move(a));
113  }
114 
115 public:
116  T &server_;
117 
118  srpc_service(T &server)
119  : service_base(Interface::program, Interface::version), server_(server) {}
120 
121  void process(void *session, rpc_msg &hdr, xdr_get &g, cb_t reply) override {
122  if (!check_call(hdr))
123  reply(nullptr);
124  if (!Interface::call_dispatch(*this, hdr.body.cbody().proc,
125  static_cast<Session *>(session),
126  hdr, g, std::move(reply)))
127  reply(rpc_accepted_error_msg(hdr.xid, PROC_UNAVAIL));
128  }
129 
130  template<typename P>
131  void dispatch(Session *session, rpc_msg &hdr, xdr_get &g, cb_t reply) {
133  if (!decode_arg(g, arg))
134  return reply(rpc_accepted_error_msg(hdr.xid, GARBAGE_ARGS));
135 
136  if (xdr_trace_server) {
137  std::string s = "CALL ";
138  s += P::proc_name();
139  s += " <- [xid " + std::to_string(hdr.xid) + "]";
140  std::clog << xdr_to_string(arg, s.c_str());
141  }
142 
143  auto res = this->template dispatch1<P>(session, arg);
144 
145  if (xdr_trace_server) {
146  std::string s = "REPLY ";
147  s += P::proc_name();
148  s += " -> [xid " + std::to_string(hdr.xid) + "]";
149  std::clog << xdr_to_string(*res, s.c_str());
150  }
151 
152  reply(xdr_to_msg(rpc_success_hdr(hdr.xid), *res));
153  }
154 };
155 
156 //! Attach RPC services to a single, connected stream socket. No
157 //! procedures will be implemented by the RPC server until interface
158 //! objects are reigstered with \c register_server.
159 class srpc_server : public rpc_server_base {
160  const sock_t s_;
161  bool close_on_destruction_;
162 
163 public:
164  srpc_server(sock_t s, bool close_on_destruction = true)
165  : s_(s), close_on_destruction_(close_on_destruction) {}
166  ~srpc_server() { if (close_on_destruction_) close(s_); }
167 
168  //! Add objects implementing RPC program interfaces to the server.
169  template<typename T, typename Interface = typename T::rpc_interface_type>
170  void register_service(T &t) {
171  register_service_base(new srpc_service<T, void, Interface>(t));
172  }
173 
174  //! Start serving requests. (Loops until an exception.)
175  void run();
176 };
177 
178 template<typename Session = void,
179  typename SessionAllocator = session_allocator<Session>>
180 using srpc_tcp_listener =
182 
183 }
184 
185 #endif // !_XDRPP_SRPC_H_HEADER_INCLUDED_
Structure that gets marshalled as an RPC success header.
Definition: server.h:27
std::tuple<> xdr_void
Placehoder type representing void values marshaled as 0 bytes.
Definition: types.h:812
void register_service(T &t)
Add objects implementing RPC program interfaces to the server.
Definition: srpc.h:170
Most of the xdrpp library is encapsulated in the xdr namespace.
Definition: arpc.cc:4
Archive type for unmarshaling from a buffer.
Definition: marshal.h:130
msg_ptr xdr_to_msg(const Args &...args)
Marshal one or a series of XDR types into a newly allocated buffer referenced xdr::msg_ptr.
Definition: marshal.h:231
Attach RPC services to a single, connected stream socket.
Definition: srpc.h:159
Exceptions raised by RPC calls.
Generic class of XDR unmarshaling errors.
Definition: types.h:41
Optional data (represented with pointer notation in XDR source).
Definition: types.h:536
Classes for implementing RPC servers.
Trivial session allocator that just calls new and delete.
Definition: server.h:176
Synchronous file descriptor demultiplexer.
Definition: srpc.h:28
Abstract away the type of a socket (for windows).
Definition: socket.h:28
void check_call_hdr(const rpc_msg &hdr)
Check that an RPC header precedes a result.
Definition: rpc_msg.cc:115
typename detail::wrap_transparent_ptr_helper< T >::type wrap_transparent_ptr
Wrap xdr::transparent_ptr around each type in a tuple to generate a new tuple type.
Definition: server.h:118
std::string xdr_to_string(const T &t, const char *name=nullptr, int indent=0)
Return a std::string containing a pretty-printed version an XDR data type.
Definition: printer.h:162
typename T::template _xdr_client< synchronous_client_base > srpc_client
Create an RPC client from an interface type and connected stream socket.
Definition: srpc.h:95