1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
// Copyright 2018 Google LLC // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. //! *Disclaimer*: This is not an official Google product. //! //! tarpc is an RPC framework for rust with a focus on ease of use. Defining a //! service can be done in just a few lines of code, and most of the boilerplate of //! writing a server is taken care of for you. //! //! [Documentation](https://docs.rs/crate/tarpc/) //! //! ## What is an RPC framework? //! "RPC" stands for "Remote Procedure Call," a function call where the work of //! producing the return value is being done somewhere else. When an rpc function is //! invoked, behind the scenes the function contacts some other process somewhere //! and asks them to evaluate the function instead. The original function then //! returns the value produced by the other process. //! //! RPC frameworks are a fundamental building block of most microservices-oriented //! architectures. Two well-known ones are [gRPC](http://www.grpc.io) and //! [Cap'n Proto](https://capnproto.org/). //! //! tarpc differentiates itself from other RPC frameworks by defining the schema in code, //! rather than in a separate language such as .proto. This means there's no separate compilation //! process, and no context switching between different languages. //! //! Some other features of tarpc: //! - Pluggable transport: any type impling `Stream<Item = Request> + Sink<Response>` can be //! used as a transport to connect the client and server. //! - `Send + 'static` optional: if the transport doesn't require it, neither does tarpc! //! - Cascading cancellation: dropping a request will send a cancellation message to the server. //! The server will cease any unfinished work on the request, subsequently cancelling any of its //! own requests, repeating for the entire chain of transitive dependencies. //! - Configurable deadlines and deadline propagation: request deadlines default to 10s if //! unspecified. The server will automatically cease work when the deadline has passed. Any //! requests sent by the server that use the request context will propagate the request deadline. //! For example, if a server is handling a request with a 10s deadline, does 2s of work, then //! sends a request to another server, that server will see an 8s deadline. //! - Serde serialization: enabling the `serde1` Cargo feature will make service requests and //! responses `Serialize + Deserialize`. It's entirely optional, though: in-memory transports can //! be used, as well, so the price of serialization doesn't have to be paid when it's not needed. //! //! ## Usage //! Add to your `Cargo.toml` dependencies: //! //! ```toml //! tarpc = "0.23.0" //! ``` //! //! The `tarpc::service` attribute expands to a collection of items that form an rpc service. //! These generated types make it easy and ergonomic to write servers with less boilerplate. //! Simply implement the generated service trait, and you're off to the races! //! //! ## Example //! //! This example uses [tokio](https://tokio.rs), so add the following dependencies to //! your `Cargo.toml`: //! //! ```toml //! futures = "0.3" //! tarpc = { version = "0.23.0", features = ["tokio1"] } //! tokio = "0.3" //! ``` //! //! In the following example, we use an in-process channel for communication between //! client and server. In real code, you will likely communicate over the network. //! For a more real-world example, see [example-service](example-service). //! //! First, let's set up the dependencies and service definition. //! //! ```rust //! # extern crate futures; //! //! use futures::{ //! future::{self, Ready}, //! prelude::*, //! }; //! use tarpc::{ //! client, context, //! server::{self, Handler}, //! }; //! use std::io; //! //! // This is the service definition. It looks a lot like a trait definition. //! // It defines one RPC, hello, which takes one arg, name, and returns a String. //! #[tarpc::service] //! trait World { //! /// Returns a greeting for name. //! async fn hello(name: String) -> String; //! } //! ``` //! //! This service definition generates a trait called `World`. Next we need to //! implement it for our Server struct. //! //! ```rust //! # extern crate futures; //! # use futures::{ //! # future::{self, Ready}, //! # prelude::*, //! # }; //! # use tarpc::{ //! # client, context, //! # server::{self, Handler}, //! # }; //! # use std::io; //! # // This is the service definition. It looks a lot like a trait definition. //! # // It defines one RPC, hello, which takes one arg, name, and returns a String. //! # #[tarpc::service] //! # trait World { //! # /// Returns a greeting for name. //! # async fn hello(name: String) -> String; //! # } //! // This is the type that implements the generated World trait. It is the business logic //! // and is used to start the server. //! #[derive(Clone)] //! struct HelloServer; //! //! impl World for HelloServer { //! // Each defined rpc generates two items in the trait, a fn that serves the RPC, and //! // an associated type representing the future output by the fn. //! //! type HelloFut = Ready<String>; //! //! fn hello(self, _: context::Context, name: String) -> Self::HelloFut { //! future::ready(format!("Hello, {}!", name)) //! } //! } //! ``` //! //! Lastly let's write our `main` that will start the server. While this example uses an //! [in-process channel](rpc::transport::channel), tarpc also ships a generic [`serde_transport`] //! behind the `serde-transport` feature, with additional [TCP](serde_transport::tcp) functionality //! available behind the `tcp` feature. //! //! ```rust //! # extern crate futures; //! # use futures::{ //! # future::{self, Ready}, //! # prelude::*, //! # }; //! # use tarpc::{ //! # client, context, //! # server::{self, Handler}, //! # }; //! # use std::io; //! # // This is the service definition. It looks a lot like a trait definition. //! # // It defines one RPC, hello, which takes one arg, name, and returns a String. //! # #[tarpc::service] //! # trait World { //! # /// Returns a greeting for name. //! # async fn hello(name: String) -> String; //! # } //! # // This is the type that implements the generated World trait. It is the business logic //! # // and is used to start the server. //! # #[derive(Clone)] //! # struct HelloServer; //! # impl World for HelloServer { //! # // Each defined rpc generates two items in the trait, a fn that serves the RPC, and //! # // an associated type representing the future output by the fn. //! # type HelloFut = Ready<String>; //! # fn hello(self, _: context::Context, name: String) -> Self::HelloFut { //! # future::ready(format!("Hello, {}!", name)) //! # } //! # } //! #[tokio::main] //! async fn main() -> io::Result<()> { //! let (client_transport, server_transport) = tarpc::transport::channel::unbounded(); //! //! let server = server::new(server::Config::default()) //! // incoming() takes a stream of transports such as would be returned by //! // TcpListener::incoming (but a stream instead of an iterator). //! .incoming(stream::once(future::ready(server_transport))) //! .respond_with(HelloServer.serve()); //! //! tokio::spawn(server); //! //! // WorldClient is generated by the macro. It has a constructor `new` that takes a config and //! // any Transport as input //! let mut client = WorldClient::new(client::Config::default(), client_transport).spawn()?; //! //! // The client has an RPC method for each RPC defined in the annotated trait. It takes the same //! // args as defined, with the addition of a Context, which is always the first arg. The Context //! // specifies a deadline and trace information which can be helpful in debugging requests. //! let hello = client.hello(context::current(), "Stim".to_string()).await?; //! //! println!("{}", hello); //! //! Ok(()) //! } //! ``` //! //! ## Service Documentation //! //! Use `cargo doc` as you normally would to see the documentation created for all //! items expanded by a `service!` invocation. #![deny(missing_docs)] #![allow(clippy::type_complexity)] #![cfg_attr(docsrs, feature(doc_cfg))] pub mod rpc; pub use rpc::*; #[cfg(feature = "serde-transport")] #[cfg_attr(docsrs, doc(cfg(feature = "serde-transport")))] pub mod serde_transport; pub mod trace; /// The main macro that creates RPC services. /// /// Rpc methods are specified, mirroring trait syntax: /// /// ``` /// #[tarpc::service] /// trait Service { /// /// Say hello /// async fn hello(name: String) -> String; /// } /// ``` /// /// Attributes can be attached to each rpc. These attributes /// will then be attached to the generated service traits' /// corresponding `fn`s, as well as to the client stubs' RPCs. /// /// The following items are expanded in the enclosing module: /// /// * `trait Service` -- defines the RPC service. /// * `fn serve` -- turns a service impl into a request handler. /// * `Client` -- a client stub with a fn for each RPC. /// * `fn new_stub` -- creates a new Client stub. pub use tarpc_plugins::service; /// A utility macro that can be used for RPC server implementations. /// /// Syntactic sugar to make using async functions in the server implementation /// easier. It does this by rewriting code like this, which would normally not /// compile because async functions are disallowed in trait implementations: /// /// ```rust /// # use tarpc::context; /// # use std::net::SocketAddr; /// #[tarpc::service] /// trait World { /// async fn hello(name: String) -> String; /// } /// /// #[derive(Clone)] /// struct HelloServer(SocketAddr); /// /// #[tarpc::server] /// impl World for HelloServer { /// async fn hello(self, _: context::Context, name: String) -> String { /// format!("Hello, {}! You are connected from {:?}.", name, self.0) /// } /// } /// ``` /// /// Into code like this, which matches the service trait definition: /// /// ```rust /// # use tarpc::context; /// # use std::pin::Pin; /// # use futures::Future; /// # use std::net::SocketAddr; /// #[derive(Clone)] /// struct HelloServer(SocketAddr); /// /// #[tarpc::service] /// trait World { /// async fn hello(name: String) -> String; /// } /// /// impl World for HelloServer { /// type HelloFut = Pin<Box<dyn Future<Output = String> + Send>>; /// /// fn hello(self, _: context::Context, name: String) -> Pin<Box<dyn Future<Output = String> /// + Send>> { /// Box::pin(async move { /// format!("Hello, {}! You are connected from {:?}.", name, self.0) /// }) /// } /// } /// ``` /// /// Note that this won't touch functions unless they have been annotated with /// `async`, meaning that this should not break existing code. pub use tarpc_plugins::server;