ez-rpc.h (11756B)
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors 2 // Licensed under the MIT License: 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 #pragma once 23 24 #include "rpc.h" 25 #include "message.h" 26 27 CAPNP_BEGIN_HEADER 28 29 struct sockaddr; 30 31 namespace kj { class AsyncIoProvider; class LowLevelAsyncIoProvider; } 32 33 namespace capnp { 34 35 class EzRpcContext; 36 37 class EzRpcClient { 38 // Super-simple interface for setting up a Cap'n Proto RPC client. Example: 39 // 40 // # Cap'n Proto schema 41 // interface Adder { 42 // add @0 (left :Int32, right :Int32) -> (value :Int32); 43 // } 44 // 45 // // C++ client 46 // int main() { 47 // capnp::EzRpcClient client("localhost:3456"); 48 // Adder::Client adder = client.getMain<Adder>(); 49 // auto request = adder.addRequest(); 50 // request.setLeft(12); 51 // request.setRight(34); 52 // auto response = request.send().wait(client.getWaitScope()); 53 // assert(response.getValue() == 46); 54 // return 0; 55 // } 56 // 57 // // C++ server 58 // class AdderImpl final: public Adder::Server { 59 // public: 60 // kj::Promise<void> add(AddContext context) override { 61 // auto params = context.getParams(); 62 // context.getResults().setValue(params.getLeft() + params.getRight()); 63 // return kj::READY_NOW; 64 // } 65 // }; 66 // 67 // int main() { 68 // capnp::EzRpcServer server(kj::heap<AdderImpl>(), "*:3456"); 69 // kj::NEVER_DONE.wait(server.getWaitScope()); 70 // } 71 // 72 // This interface is easy, but it hides a lot of useful features available from the lower-level 73 // classes: 74 // - The server can only export a small set of public, singleton capabilities under well-known 75 // string names. This is fine for transient services where no state needs to be kept between 76 // connections, but hides the power of Cap'n Proto when it comes to long-lived resources. 77 // - EzRpcClient/EzRpcServer automatically set up a `kj::EventLoop` and make it current for the 78 // thread. Only one `kj::EventLoop` can exist per thread, so you cannot use these interfaces 79 // if you wish to set up your own event loop. (However, you can safely create multiple 80 // EzRpcClient / EzRpcServer objects in a single thread; they will make sure to make no more 81 // than one EventLoop.) 82 // - These classes only support simple two-party connections, not multilateral VatNetworks. 83 // - These classes only support communication over a raw, unencrypted socket. If you want to 84 // build on an abstract stream (perhaps one which supports encryption), you must use the 85 // lower-level interfaces. 86 // 87 // Some of these restrictions will probably be lifted in future versions, but some things will 88 // always require using the low-level interfaces directly. If you are interested in working 89 // at a lower level, start by looking at these interfaces: 90 // - `kj::setupAsyncIo()` in `kj/async-io.h`. 91 // - `RpcSystem` in `capnp/rpc.h`. 92 // - `TwoPartyVatNetwork` in `capnp/rpc-twoparty.h`. 93 94 public: 95 explicit EzRpcClient(kj::StringPtr serverAddress, uint defaultPort = 0, 96 ReaderOptions readerOpts = ReaderOptions()); 97 // Construct a new EzRpcClient and connect to the given address. The connection is formed in 98 // the background -- if it fails, calls to capabilities returned by importCap() will fail with an 99 // appropriate exception. 100 // 101 // `defaultPort` is the IP port number to use if `serverAddress` does not include it explicitly. 102 // If unspecified, the port is required in `serverAddress`. 103 // 104 // The address is parsed by `kj::Network` in `kj/async-io.h`. See that interface for more info 105 // on the address format, but basically it's what you'd expect. 106 // 107 // `readerOpts` is the ReaderOptions structure used to read each incoming message on the 108 // connection. Setting this may be necessary if you need to receive very large individual 109 // messages or messages. However, it is recommended that you instead think about how to change 110 // your protocol to send large data blobs in multiple small chunks -- this is much better for 111 // both security and performance. See `ReaderOptions` in `message.h` for more details. 112 113 EzRpcClient(const struct sockaddr* serverAddress, uint addrSize, 114 ReaderOptions readerOpts = ReaderOptions()); 115 // Like the above constructor, but connects to an already-resolved socket address. Any address 116 // format supported by `kj::Network` in `kj/async-io.h` is accepted. 117 118 explicit EzRpcClient(int socketFd, ReaderOptions readerOpts = ReaderOptions()); 119 // Create a client on top of an already-connected socket. 120 // `readerOpts` acts as in the first constructor. 121 122 ~EzRpcClient() noexcept(false); 123 124 template <typename Type> 125 typename Type::Client getMain(); 126 Capability::Client getMain(); 127 // Get the server's main (aka "bootstrap") interface. 128 129 template <typename Type> 130 typename Type::Client importCap(kj::StringPtr name) CAPNP_DEPRECATED( 131 "Change your server to export a main interface, then use getMain() instead."); 132 Capability::Client importCap(kj::StringPtr name) CAPNP_DEPRECATED( 133 "Change your server to export a main interface, then use getMain() instead."); 134 // ** DEPRECATED ** 135 // 136 // Ask the sever for the capability with the given name. You may specify a type to automatically 137 // down-cast to that type. It is up to you to specify the correct expected type. 138 // 139 // Named interfaces are deprecated. The new preferred usage pattern is for the server to export 140 // a "main" interface which itself has methods for getting any other interfaces. 141 142 kj::WaitScope& getWaitScope(); 143 // Get the `WaitScope` for the client's `EventLoop`, which allows you to synchronously wait on 144 // promises. 145 146 kj::AsyncIoProvider& getIoProvider(); 147 // Get the underlying AsyncIoProvider set up by the RPC system. This is useful if you want 148 // to do some non-RPC I/O in asynchronous fashion. 149 150 kj::LowLevelAsyncIoProvider& getLowLevelIoProvider(); 151 // Get the underlying LowLevelAsyncIoProvider set up by the RPC system. This is useful if you 152 // want to do some non-RPC I/O in asynchronous fashion. 153 154 private: 155 struct Impl; 156 kj::Own<Impl> impl; 157 }; 158 159 class EzRpcServer { 160 // The server counterpart to `EzRpcClient`. See `EzRpcClient` for an example. 161 162 public: 163 explicit EzRpcServer(Capability::Client mainInterface, kj::StringPtr bindAddress, 164 uint defaultPort = 0, ReaderOptions readerOpts = ReaderOptions()); 165 // Construct a new `EzRpcServer` that binds to the given address. An address of "*" means to 166 // bind to all local addresses. 167 // 168 // `defaultPort` is the IP port number to use if `serverAddress` does not include it explicitly. 169 // If unspecified, a port is chosen automatically, and you must call getPort() to find out what 170 // it is. 171 // 172 // The address is parsed by `kj::Network` in `kj/async-io.h`. See that interface for more info 173 // on the address format, but basically it's what you'd expect. 174 // 175 // The server might not begin listening immediately, especially if `bindAddress` needs to be 176 // resolved. If you need to wait until the server is definitely up, wait on the promise returned 177 // by `getPort()`. 178 // 179 // `readerOpts` is the ReaderOptions structure used to read each incoming message on the 180 // connection. Setting this may be necessary if you need to receive very large individual 181 // messages or messages. However, it is recommended that you instead think about how to change 182 // your protocol to send large data blobs in multiple small chunks -- this is much better for 183 // both security and performance. See `ReaderOptions` in `message.h` for more details. 184 185 EzRpcServer(Capability::Client mainInterface, struct sockaddr* bindAddress, uint addrSize, 186 ReaderOptions readerOpts = ReaderOptions()); 187 // Like the above constructor, but binds to an already-resolved socket address. Any address 188 // format supported by `kj::Network` in `kj/async-io.h` is accepted. 189 190 EzRpcServer(Capability::Client mainInterface, int socketFd, uint port, 191 ReaderOptions readerOpts = ReaderOptions()); 192 // Create a server on top of an already-listening socket (i.e. one on which accept() may be 193 // called). `port` is returned by `getPort()` -- it serves no other purpose. 194 // `readerOpts` acts as in the other two above constructors. 195 196 explicit EzRpcServer(kj::StringPtr bindAddress, uint defaultPort = 0, 197 ReaderOptions readerOpts = ReaderOptions()) 198 CAPNP_DEPRECATED("Please specify a main interface for your server."); 199 EzRpcServer(struct sockaddr* bindAddress, uint addrSize, 200 ReaderOptions readerOpts = ReaderOptions()) 201 CAPNP_DEPRECATED("Please specify a main interface for your server."); 202 EzRpcServer(int socketFd, uint port, ReaderOptions readerOpts = ReaderOptions()) 203 CAPNP_DEPRECATED("Please specify a main interface for your server."); 204 205 ~EzRpcServer() noexcept(false); 206 207 void exportCap(kj::StringPtr name, Capability::Client cap); 208 // Export a capability publicly under the given name, so that clients can import it. 209 // 210 // Keep in mind that you can implicitly convert `kj::Own<MyType::Server>&&` to 211 // `Capability::Client`, so it's typical to pass something like 212 // `kj::heap<MyImplementation>(<constructor params>)` as the second parameter. 213 214 kj::Promise<uint> getPort(); 215 // Get the IP port number on which this server is listening. This promise won't resolve until 216 // the server is actually listening. If the address was not an IP address (e.g. it was a Unix 217 // domain socket) then getPort() resolves to zero. 218 219 kj::WaitScope& getWaitScope(); 220 // Get the `WaitScope` for the client's `EventLoop`, which allows you to synchronously wait on 221 // promises. 222 223 kj::AsyncIoProvider& getIoProvider(); 224 // Get the underlying AsyncIoProvider set up by the RPC system. This is useful if you want 225 // to do some non-RPC I/O in asynchronous fashion. 226 227 kj::LowLevelAsyncIoProvider& getLowLevelIoProvider(); 228 // Get the underlying LowLevelAsyncIoProvider set up by the RPC system. This is useful if you 229 // want to do some non-RPC I/O in asynchronous fashion. 230 231 private: 232 struct Impl; 233 kj::Own<Impl> impl; 234 }; 235 236 // ======================================================================================= 237 // inline implementation details 238 239 template <typename Type> 240 inline typename Type::Client EzRpcClient::getMain() { 241 return getMain().castAs<Type>(); 242 } 243 244 template <typename Type> 245 inline typename Type::Client EzRpcClient::importCap(kj::StringPtr name) { 246 return importCap(name).castAs<Type>(); 247 } 248 249 } // namespace capnp 250 251 CAPNP_END_HEADER