├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── brpc-build ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── brpc-protoc-plugin ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── brpc_generator.cpp │ └── main.rs ├── brpc-sys ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── ffi.cpp │ ├── ffi.rs │ ├── lib.rs │ ├── zero_copy.cpp │ └── zero_copy.rs ├── examples ├── Cargo.toml ├── build.rs └── echo │ ├── client.rs │ ├── echo.proto │ └── server.rs ├── logo.svg ├── overview.svg └── src ├── channel.rs ├── controller.rs ├── error.rs ├── lib.rs └── server.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *.rs.bk 4 | .vscode 5 | .o 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | rust: 4 | - stable 5 | matrix: 6 | include: 7 | - os: linux 8 | compiler: gcc 9 | dist: xenial 10 | - os: linux 11 | compiler: gcc 12 | dist: bionic 13 | - os: linux 14 | compiler: clang 15 | dist: xenial 16 | - os: linux 17 | compiler: clang 18 | dist: bionic 19 | - name: clippy and fmt 20 | rust: stable 21 | before_script: 22 | - rustup component add clippy 23 | - rustup component add rustfmt 24 | script: 25 | - cargo fmt --all -- --check `find src -iname "*.rs"` 26 | - cargo clippy 27 | before_install: 28 | - sudo apt-get update 29 | - sudo apt-get install -y libgflags-dev libleveldb-dev libssl-dev 30 | - sudo apt-get install -y libprotobuf-dev libprotoc-dev protobuf-compiler 31 | - | 32 | set -eo pipefail 33 | curl -LO https://brpc-rs.s3-us-west-1.amazonaws.com/brpc_prebuilt/brpc-0.9.6_x86_64_${TRAVIS_DIST}.deb 34 | sudo dpkg -i brpc-0.9.6_x86_64_${TRAVIS_DIST}.deb 35 | script: 36 | - | 37 | set -eo pipefail 38 | cargo build -p brpc-protoc-plugin --release 39 | sudo cp target/release/protoc-gen-brpc /usr/local/bin 40 | cargo build -p examples --release 41 | - cargo test --lib --release 42 | notifications: 43 | email: 44 | on_success: never 45 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "brpc-rs" 3 | version = "0.1.0" 4 | authors = ["Yiming Jing "] 5 | edition = "2018" 6 | license = "Apache-2.0" 7 | description = "Apache BRPC library for Rust" 8 | keywords = ["rpc","rust"] 9 | categories = ["network-programming"] 10 | homepage = "https://github.com/mesalock-linux/brpc-rs" 11 | documentation = "https://mesalock-linux.github.io/brpc-rs-docs/brpc_rs/index.html" 12 | repository = "https://github.com/mesalock-linux/brpc-rs" 13 | readme = "README.md" 14 | 15 | [badges] 16 | travis-ci = { repository = "mesalock-linux/brpc-rs", branch = "master" } 17 | 18 | [dependencies] 19 | brpc-sys = { path = "brpc-sys", version = "0.1.0" } 20 | libc = "0.2.60" 21 | 22 | [workspace] 23 | members = ["brpc-sys", "brpc-build", "brpc-protoc-plugin", "examples"] 24 | 25 | [profile.release] 26 | opt-level = 3 27 | debug = false 28 | rpath = false 29 | lto = true 30 | debug-assertions = false 31 | codegen-units = 1 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

brpc-rs: Apache BRPC library for Rust

3 | 4 | [![Build Status](https://travis-ci.com/mesalock-linux/brpc-rs.svg?token=jQ7Xyo9mbqzpz1GRwbzf&branch=master)](https://travis-ci.com/mesalock-linux/brpc-rs) 5 | [![Crate](https://img.shields.io/crates/v/brpc-rs.svg)](https://crates.io/crates/brpc-rs) 6 | [![Documentation](https://docs.rs/brpc-rs/badge.svg)](https://mesalock-linux.github.io/brpc-rs-docs/brpc_rs/index.html) 7 | 8 | [Apache BRPC](https://github.com/apache/incubator-brpc) is an industrial-grade 9 | RPC framework for building reliable and high-performance services. `brpc-rs` enables BRPC clients and servers implemented in the Rust programming language. 10 | 11 | `brpc-rs` is part of the [MesaLock Linux](https://github.com/mesalock-linux) 12 | Project. 13 | 14 | ## Status 15 | 16 | This project is currently a prototype under active development. Many APIs are 17 | missing; the provided APIs are not guaranteed to be stable until 1.0. 18 | 19 | 20 | ## Repository structure 21 | + Project root 22 | + src 23 | - [brpc-rs](https://crates.io/crates/brpc-rs) crate, Rust APIs for 24 | `brpc-rs` 25 | + brpc-build 26 | - [brpc-build](https://crates.io/crates/brpc-build) crate, build-time 27 | code generator 28 | + brpc-protoc-plugin 29 | - [brpc-protoc-plugin](https://crates.io/crates/brpc-protoc-plugin) 30 | crate, plugin for Google Protobuf compiler 31 | + brpc-sys 32 | - [brpc-sys](https://crates.io/crates/brpc-sys) crate, FFI bindings to 33 | Apache BRPC 34 | + examples 35 | - examples 36 | 37 | This graph illustrates how the crates work together: 38 |

39 | 40 | ## Quickstart 41 | 42 | ### Prerequisites 43 | 44 | First **build** and **install** Apache BRPC. Instructions can be found in 45 | [getting_started.md](https://github.com/apache/incubator-brpc/blob/master/docs/cn/getting_started.md). 46 | 47 | Alternatively, you may use the prebuilt deb packages of Apache BRPC 0.9.6 for 48 | Ubuntu 49 | [16.04](https://brpc-rs.s3-us-west-1.amazonaws.com/brpc_prebuilt/brpc-0.9.6_x86_64_xenial.deb)/[18.04](https://brpc-rs.s3-us-west-1.amazonaws.com/brpc_prebuilt/brpc-0.9.6_x86_64_bionic.deb). 50 | These are NOT official packages. 51 | 52 | Make sure these dependencies are already installed: 53 | 54 | ``` 55 | $ sudo apt-get install libprotobuf-dev libprotoc-dev protobuf-compiler 56 | $ sudo apt-get install libssl-dev libgflags-dev libleveldb-dev 57 | ``` 58 | 59 | Install `brpc-protoc-plugin` from crates.io. 60 | 61 | ```shell 62 | $ cargo install brpc-protoc-plugin 63 | ``` 64 | 65 | Now we are ready to start a `brpc-rs` project. 66 | 67 | ### Cargo.toml 68 | 69 | Let's create a small crate, `echo_service`, that includes an `echo_client` and 70 | an `echo_server`. 71 | 72 | ```shell 73 | $ cargo new echo_service && cd echo_service 74 | ``` 75 | 76 | In `Cargo.toml` , add `brpc-rs`, `prost` and `bytes` to the `[dependencies]` 77 | section; add `brpc-build` to the `[build-dependencies]` section. For example, 78 | 79 | ```toml 80 | [build-dependencies] 81 | brpc-build = "0.1.0" 82 | 83 | [dependencies] 84 | brpc-rs = "0.1.0" 85 | prost = "0.5.0" 86 | bytes = "0.4.12" 87 | ``` 88 | 89 | Define two binaries: `echo_client` and `echo_server` in `Cargo.toml` 90 | 91 | ```toml 92 | [[bin]] 93 | name = "echo_client" 94 | path = "src/client.rs" 95 | 96 | [[bin]] 97 | name = "echo_server" 98 | path = "src/server.rs" 99 | ``` 100 | 101 | ### build.rs 102 | 103 | Put a protobuf file `echo.proto` in `src`. This file defines `EchoRequest`, 104 | `EchoResponse` and `EchoService`. 105 | 106 | ```protobuf 107 | syntax="proto2"; 108 | package example; 109 | message EchoRequest { 110 | required string message = 1; 111 | }; 112 | message EchoResponse { 113 | required string message = 1; 114 | }; 115 | service EchoService { 116 | rpc echo(EchoRequest) returns (EchoResponse); 117 | }; 118 | ``` 119 | 120 | 121 | Add a line `build = "build.rs"` in the `[package]` section in `Cargo.toml`. 122 | Then, create a file called `build.rs` to generate bindings from 123 | `src/echo.proto`. 124 | 125 | ```rust 126 | fn main() { 127 | brpc_build::compile_protos(&["src/echo.proto"], 128 | &["src"]).unwrap(); 129 | } 130 | ``` 131 | 132 | Note the `package` name in `echo.proto` is `example`. So `build.rs` would generate two files named `example.rs` and `example.brpc.rs`. 133 | 134 | 135 | ### src/server.rs 136 | 137 | Next let's implement the echo server. Create `src/server.rs` as follows: 138 | 139 | ```rust 140 | use brpc_rs::{Server, ServerOptions, ServiceOwnership}; 141 | 142 | pub mod echo { 143 | include!(concat!(env!("OUT_DIR"), "/example.rs")); 144 | include!(concat!(env!("OUT_DIR"), "/example.brpc.rs")); 145 | } 146 | 147 | fn main() { 148 | let mut service = echo::EchoService::new(); 149 | service.set_echo_handler(&mut move |request, mut response| { 150 | response.message = request.message.clone(); 151 | Ok(()) 152 | }); 153 | 154 | let mut server = Server::new(); 155 | let mut options = ServerOptions::new(); 156 | options.set_idle_timeout_ms(1000); 157 | server 158 | .add_service(&service, ServiceOwnership::ServerDoesntOwnService) 159 | .expect("Failed to add service"); 160 | server.start(50000, &options).expect("Failed to start service"); 161 | server.run(); // Run until CTRL-C 162 | } 163 | ``` 164 | 165 | Because `EchoService` defines a function called `echo()` in `echo.proto`, the 166 | `brpc-protoc-plugin` generates the Rust definition of `set_echo_handler()` for 167 | `EchoService`. `set_echo_handler()` accepts a closure which handles 168 | `EchoRequest` sent from clients and returns an `EchoResponse` with the same 169 | message. The remaining lines create a server that listens at `0.0.0.0:50000`. 170 | 171 | 172 | ### src/client.rs 173 | 174 | ```rust 175 | use brpc_rs::{Channel, ChannelOptions}; 176 | 177 | pub mod echo { 178 | include!(concat!(env!("OUT_DIR"), "/example.rs")); 179 | include!(concat!(env!("OUT_DIR"), "/example.brpc.rs")); 180 | } 181 | 182 | fn main() { 183 | let mut options = ChannelOptions::new(); 184 | options.set_timeout_ms(100); 185 | let addr = "127.0.0.1:50000".parse().expect("Invalid socket address"); 186 | let ch = Channel::with_options(&addr, &options); 187 | let client = echo::EchoServiceStub::with_channel(&ch); 188 | let request = echo::EchoRequest { 189 | message: "hello".to_owned(), 190 | }; 191 | match client.echo(&request) { 192 | Ok(r) => println!("Response: {:?}", r), 193 | Err(e) => eprintln!("Error: {:?}", e), 194 | } 195 | } 196 | ``` 197 | 198 | The client first creates a `Channel` and initializes a `service_stub` with that 199 | channel. The client then calls `service_stub.echo()` to send a request.. 200 | 201 | 202 | ### Running the client and server 203 | 204 | ```shell 205 | $ cargo run --bin echo_server & 206 | $ cargo run --bin echo_client 207 | Response: EchoResponse { message: "hello" } 208 | ``` 209 | 210 | 211 | ## Maintainer 212 | 213 | * Yiming Jing `` [@kevinis](https://github.com/kevinis) 214 | 215 | ## License 216 | `brpc-rs` is provided under Apache License, Version 2.0. For a copy, see the 217 | LICENSE file. 218 | -------------------------------------------------------------------------------- /brpc-build/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "brpc-build" 3 | version = "0.1.0" 4 | authors = ["Yiming Jing "] 5 | edition = "2018" 6 | license = "Apache-2.0" 7 | description = "Build-time code generator for brpc-rs" 8 | documentation = "https://mesalock-linux.github.io/brpc-rs-docs/brpc_build/index.html" 9 | keywords = ["rpc","rust"] 10 | categories = ["network-programming"] 11 | homepage = "https://github.com/mesalock-linux/brpc-rs" 12 | repository = "https://github.com/mesalock-linux/brpc-rs" 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | prost-build = "0.5.0" 17 | cc = "1.0.38" 18 | -------------------------------------------------------------------------------- /brpc-build/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /brpc-build/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //! `brpc-build` compiles `.proto` files for `brpc-rs`. 15 | //! 16 | //! `brpc-build` is designed to be used for build-time code generation as part of 17 | //! a Cargo build-script. 18 | 19 | #![deny(warnings)] 20 | 21 | use std::path::{Path, PathBuf}; 22 | use std::{env, io, path, process}; 23 | 24 | /// Compile .proto files into Rust files during a Cargo build. 25 | /// 26 | /// The generated `.rs` files will be written to the Cargo `OUT_DIR` directory, 27 | /// suitable for use with the `include!` macro. 28 | /// 29 | /// This function should be called in a project's `build.rs`. 30 | /// 31 | /// # Arguments 32 | /// 33 | /// **`protos`** - Paths to `.proto` files to compile. Any transitively 34 | /// [imported][3] `.proto` files will automatically be included. 35 | /// 36 | /// **`includes`** - Paths to directories in which to search for imports. 37 | /// Directories will be searched in order. The `.proto` files passed in 38 | /// **`protos`** must be found in one of the provided include directories. 39 | /// 40 | /// It's expected that this function call be `unwrap`ed in a `build.rs`; there 41 | /// is typically no reason to gracefully recover from errors during a build. 42 | /// 43 | /// # Example `build.rs` 44 | /// 45 | /// ```norun 46 | /// fn main() { 47 | /// brpc_build::compile_protos(&["src/echo.proto",], 48 | /// &["src"]).unwrap(); 49 | /// } 50 | /// ``` 51 | pub fn compile_protos

(protos: &[P], includes: &[P]) -> io::Result<()> 52 | where 53 | P: AsRef, 54 | { 55 | let out_dir_path: path::PathBuf = env::var_os("OUT_DIR") 56 | .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "OUT_DIR env var is not set")) 57 | .map(Into::into)?; 58 | let out_dir = out_dir_path.as_os_str(); 59 | 60 | let brpc_plugin_path = find_in_path("protoc-gen-brpc").ok_or(io::Error::new( 61 | io::ErrorKind::NotFound, 62 | "protoc-gen-brpc not found in PATH", 63 | ))?; 64 | 65 | // Step 0 66 | let _ = prost_build::compile_protos(protos, includes)?; 67 | 68 | // Step 1 69 | let mut cmd = process::Command::new("protoc"); 70 | for include in includes { 71 | cmd.arg("-I").arg(include.as_ref()); 72 | } 73 | for proto in protos { 74 | cmd.arg(proto.as_ref()); 75 | } 76 | cmd.arg(&format!( 77 | "--plugin=protoc-gen=brpc={}", 78 | brpc_plugin_path.to_string_lossy() 79 | )); 80 | cmd.arg("--brpc_out").arg(&out_dir); 81 | 82 | let output = cmd.output()?; 83 | if !output.status.success() { 84 | return Err(io::Error::new( 85 | io::ErrorKind::Other, 86 | format!( 87 | "protoc failed in the first pass: {}", 88 | String::from_utf8_lossy(&output.stderr) 89 | ), 90 | )); 91 | } 92 | 93 | // Step 2 94 | let mut cmd = process::Command::new("protoc"); 95 | let current_dir = out_dir_path.to_path_buf(); 96 | cmd.arg("-I").arg(out_dir_path.to_path_buf()); 97 | for proto in protos { 98 | let f = proto 99 | .as_ref() 100 | .file_name() 101 | .ok_or(io::Error::new(io::ErrorKind::Other, "Invalid file name"))?; 102 | cmd.arg(current_dir.join(f)); 103 | } 104 | cmd.arg("--cpp_out").arg(out_dir_path.to_path_buf()); 105 | let output = cmd.output()?; 106 | if !output.status.success() { 107 | return Err(io::Error::new( 108 | io::ErrorKind::Other, 109 | format!( 110 | "protoc failed in the second pass: {}", 111 | String::from_utf8_lossy(&output.stderr) 112 | ), 113 | )); 114 | } 115 | 116 | // Step 3 117 | let mut builder = cc::Build::new(); 118 | for proto in protos { 119 | let mut cc_to_build = out_dir_path.to_path_buf(); 120 | let f = proto 121 | .as_ref() 122 | .file_name() 123 | .ok_or(io::Error::new(io::ErrorKind::Other, "Invalid file name"))?; 124 | cc_to_build.push(f); 125 | cc_to_build.set_extension("brpc.cc"); 126 | builder.file(&cc_to_build); 127 | 128 | let mut cc_to_build = out_dir_path.to_path_buf(); 129 | let f = proto 130 | .as_ref() 131 | .file_name() 132 | .ok_or(io::Error::new(io::ErrorKind::Other, "Invalid file name"))?; 133 | cc_to_build.push(f); 134 | cc_to_build.set_extension("pb.cc"); 135 | builder.file(&cc_to_build); 136 | } 137 | 138 | builder.cpp(true).flag("-std=c++11").warnings(false); 139 | builder.compile("brpc_service"); 140 | println!("cargo:rustc-link-lib=static=brpc_service"); 141 | 142 | println!("cargo:rustc-link-lib=brpc"); 143 | println!("cargo:rustc-link-lib=protobuf"); 144 | println!("cargo:rustc-link-lib=gflags"); 145 | println!("cargo:rustc-link-lib=leveldb"); 146 | println!("cargo:rustc-link-lib=ssl"); 147 | println!("cargo:rustc-link-lib=crypto"); 148 | 149 | Ok(()) 150 | } 151 | 152 | // find executable file in $PATH (%PATH% in windows) 153 | fn find_in_path>(exe: E) -> Option { 154 | env::var_os("PATH").and_then(|paths| { 155 | env::split_paths(&paths) 156 | .filter_map(|dir| { 157 | let full_path = dir.join(&exe); 158 | if full_path.is_file() { 159 | Some(full_path) 160 | } else { 161 | None 162 | } 163 | }) 164 | .next() 165 | }) 166 | } 167 | -------------------------------------------------------------------------------- /brpc-protoc-plugin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "brpc-protoc-plugin" 3 | version = "0.1.0" 4 | authors = ["Yiming Jing "] 5 | edition = "2018" 6 | license = "Apache-2.0" 7 | description = "Protobuf-compiler plugin for brpc-rs" 8 | keywords = ["rpc","rust"] 9 | categories = ["network-programming"] 10 | homepage = "https://github.com/mesalock-linux/brpc-rs" 11 | repository = "https://github.com/mesalock-linux/brpc-rs" 12 | readme = "README.md" 13 | build = "build.rs" 14 | 15 | [build-dependencies] 16 | cc = "1.0.38" 17 | 18 | [dependencies] 19 | libc = "0.2.60" 20 | 21 | [[bin]] 22 | name = "protoc-gen-brpc" 23 | path = "src/main.rs" 24 | -------------------------------------------------------------------------------- /brpc-protoc-plugin/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /brpc-protoc-plugin/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | fn main() { 15 | let mut builder = cc::Build::new(); 16 | builder 17 | .cpp(true) 18 | .file("src/brpc_generator.cpp") 19 | .flag("-std=c++11") 20 | .warnings(false); 21 | 22 | builder.compile("plugin"); 23 | println!("cargo:rustc-link-lib=protobuf"); 24 | println!("cargo:rustc-link-lib=protoc"); 25 | } 26 | -------------------------------------------------------------------------------- /brpc-protoc-plugin/src/brpc_generator.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | inline bool HasSuffixString(const std::string &str, const std::string &suffix) { 22 | return str.size() >= suffix.size() && 23 | str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; 24 | } 25 | 26 | inline std::string StripSuffixString(const std::string &str, 27 | const std::string &suffix) { 28 | if (HasSuffixString(str, suffix)) { 29 | return str.substr(0, str.size() - suffix.size()); 30 | } else { 31 | return str; 32 | } 33 | } 34 | 35 | inline std::string StripBeforeSlashFromRight(const std::string &str) { 36 | size_t pos = str.rfind('/', str.length()); 37 | if (pos != std::string::npos) { 38 | return str.substr(pos + 1, str.size() - 1); 39 | } 40 | return str; 41 | } 42 | 43 | namespace brpc_rs { 44 | class BrpcToProtobuf : public google::protobuf::compiler::CodeGenerator { 45 | public: 46 | bool Generate(const google::protobuf::FileDescriptor *file, 47 | const std::string ¶meter, 48 | google::protobuf::compiler::GeneratorContext *, 49 | std::string *error) const; 50 | }; 51 | 52 | bool BrpcToProtobuf::Generate(const google::protobuf::FileDescriptor *file, 53 | const std::string ¶meter, 54 | google::protobuf::compiler::GeneratorContext *ctx, 55 | std::string *error) const { 56 | 57 | std::string base_name = StripSuffixString(file->name(), ".proto"); 58 | std::string proto_file_name = file->name(); 59 | std::string cc_file_name = base_name + ".brpc.cc"; 60 | std::string rust_file_name = file->package() + ".brpc.rs"; 61 | std::string include_file_name = 62 | StripBeforeSlashFromRight(base_name) + ".pb.h"; 63 | 64 | /* Generate *.proto */ 65 | 66 | google::protobuf::io::ZeroCopyOutputStream *proto_file = 67 | ctx->Open(proto_file_name); 68 | google::protobuf::io::Printer proto_printer(proto_file, '$'); 69 | proto_printer.Print("syntax=\"proto2\";\n"); 70 | proto_printer.Print("package $package_name$;\n\n", "package_name", 71 | file->package()); 72 | 73 | proto_printer.Print("option cc_generic_services = true;\n\n" 74 | "message HttpRequest {};\n" 75 | "message HttpResponse {};\n\n"); 76 | 77 | for (int i = 0; i < file->service_count(); ++i) { 78 | const google::protobuf::ServiceDescriptor *service = file->service(i); 79 | proto_printer.Print("service $service_name$ {\n", "service_name", 80 | service->name()); 81 | for (int j = 0; j < service->method_count(); ++j) { 82 | const google::protobuf::MethodDescriptor *method = service->method(j); 83 | proto_printer.Print( 84 | " rpc $method_name$(HttpRequest) returns (HttpResponse);\n", 85 | "method_name", method->name()); 86 | } 87 | proto_printer.Print("}\n"); 88 | } 89 | 90 | /* Generate *.brpc.cc */ 91 | 92 | google::protobuf::io::ZeroCopyOutputStream *ffi_file = 93 | ctx->Open(cc_file_name); 94 | google::protobuf::io::Printer cpp_printer(ffi_file, '$'); 95 | cpp_printer.Print("#include \"$header_name$\"\n", "header_name", 96 | include_file_name); 97 | cpp_printer.Print("#include \n" 98 | "#include \n" 99 | "#include \n" 100 | "#include \n\n" 101 | "#include \n\n"); 102 | 103 | cpp_printer.Print( 104 | "namespace butil {\n" 105 | "class ZeroCopyBuf {\n" 106 | "public:\n" 107 | " explicit ZeroCopyBuf(const IOBuf &buf)\n" 108 | " : _block_start(NULL), _block_end(NULL), _total_len(0), _buf(&buf),\n" 109 | " _stream(buf) {\n" 110 | " char *block_end = NULL;\n" 111 | " int block_len;\n" 112 | " if (_block_start == NULL) {\n" 113 | " const void *start_ptr = NULL;\n" 114 | " _stream.Next(reinterpret_cast(&start_ptr), " 115 | "&block_len);\n" 116 | " _block_start = (char *)start_ptr;\n" 117 | " _block_end = _block_start + block_len;\n" 118 | " }\n" 119 | "}\n" 120 | " uint64_t Remaining() const;\n" 121 | " bool Bytes(const void **data, int *size);\n" 122 | " bool Advance(int count);\n" 123 | "\n" 124 | "private:\n" 125 | " char *_block_start;\n" 126 | " char *_block_end;\n" 127 | " uint64_t _total_len;\n" 128 | " const IOBuf *_buf;\n" 129 | " IOBufAsZeroCopyInputStream _stream;\n" 130 | "};\n" 131 | "\n" 132 | "class ZeroCopyBufMut {\n" 133 | "public:\n" 134 | " explicit ZeroCopyBufMut(IOBuf &buf)\n" 135 | " : _block_start(NULL), _block_end(NULL), _total_len(0), " 136 | "_stream(&buf) {\n" 137 | " char *block_end = NULL;\n" 138 | " int block_len;\n" 139 | " if (_block_start == NULL) {\n" 140 | " _stream.Next(reinterpret_cast(&_block_start), " 141 | "&block_len);\n" 142 | " _block_end = _block_start + block_len;\n" 143 | " }\n" 144 | " }\n" 145 | " uint64_t RemainingMut() const;\n" 146 | " bool BytesMut(void **data, int *size);\n" 147 | " bool AdvanceMut(size_t count);\n\n" 148 | "private:\n" 149 | " char *_block_start;\n" 150 | " char *_block_end;\n" 151 | " uint64_t _total_len;\n" 152 | " IOBufAsZeroCopyOutputStream _stream;\n" 153 | "};\n" 154 | "\n" 155 | "} // namespace butil\n\n\n"); 156 | 157 | // typedefs 158 | for (int i = 0; i < file->service_count(); ++i) { 159 | const google::protobuf::ServiceDescriptor *service = file->service(i); 160 | cpp_printer.Print("typedef void *brpc_$service_name$_service_t;\n" 161 | "typedef void *brpc_$service_name$_service_handler_t;\n" 162 | "typedef $package_name$::$service_name$_Stub " 163 | "*brpc_$service_name$_stub_t;\n", 164 | "service_name", service->name(), "package_name", 165 | file->package()); 166 | } 167 | 168 | cpp_printer.Print("\n\n"); 169 | 170 | // namespace $package_name$ 171 | cpp_printer.Print("namespace $package_name$ {\n\n", "package_name", 172 | file->package()); 173 | for (int i = 0; i < file->service_count(); ++i) { 174 | const google::protobuf::ServiceDescriptor *service = file->service(i); 175 | cpp_printer.Print("class $service_name$Impl: public $service_name$ {\n" 176 | "public:\n" 177 | " $service_name$Impl() {\n", 178 | "service_name", service->name()); 179 | for (int j = 0; j < service->method_count(); ++j) { 180 | const google::protobuf::MethodDescriptor *method = service->method(j); 181 | cpp_printer.Print(" $method_name$_trampoline = NULL;\n" 182 | " $method_name$_closure_ptr = NULL;\n", 183 | "method_name", method->name()); 184 | } 185 | cpp_printer.Print(" };\n" 186 | " virtual ~$service_name$Impl(){};\n", 187 | "service_name", service->name()); 188 | 189 | for (int j = 0; j < service->method_count(); ++j) { 190 | const google::protobuf::MethodDescriptor *method = service->method(j); 191 | cpp_printer.Print( 192 | " void $method_name$(google::protobuf::RpcController *cntl_base,\n" 193 | " const HttpRequest *,\n" 194 | " HttpResponse *, \n" 195 | " google::protobuf::Closure *done) {\n" 196 | " brpc::ClosureGuard done_guard(done);\n" 197 | " brpc::Controller *cntl = \n" 198 | " static_cast(cntl_base);\n" 199 | " cntl->http_response()\n" 200 | " .set_content_type(\"application/octet-stream\");\n" 201 | " butil::IOBuf &request_buf = cntl->request_attachment();\n" 202 | " butil::IOBuf &response_buf = cntl->response_attachment();\n" 203 | " butil::ZeroCopyBuf zc_request(request_buf);\n" 204 | " butil::ZeroCopyBufMut zc_response(response_buf);\n" 205 | " if (0 != $method_name$_trampoline(\n" 206 | " $method_name$_closure_ptr,\n" 207 | " zc_request,\n" 208 | " zc_response)) {\n" 209 | " cntl->SetFailed(brpc::EINTERNAL, \"brpc-rs controller " 210 | "failed\");\n" 211 | " }\n" 212 | " }\n" 213 | "\n" 214 | " int (*$method_name$_trampoline)(\n" 215 | " void *,\n" 216 | " butil::ZeroCopyBuf &,\n" 217 | " butil::ZeroCopyBufMut &);\n" 218 | " void *$method_name$_closure_ptr;\n\n", 219 | "method_name", method->name()); 220 | } 221 | cpp_printer.Print("};\n"); // Service class ends 222 | } 223 | 224 | cpp_printer.Print("} // namespace $package_name$\n\n", "package_name", 225 | file->package()); 226 | 227 | cpp_printer.Print("extern \"C\" {\n"); 228 | 229 | for (int i = 0; i < file->service_count(); ++i) { 230 | const google::protobuf::ServiceDescriptor *service = file->service(i); 231 | cpp_printer.Print( 232 | "brpc_$service_name$_service_t brpc_$service_name$_new() {\n" 233 | " return new $package_name$::$service_name$Impl;\n" 234 | "}\n" 235 | "\n" 236 | "void brpc_$service_name$_destroy(\n" 237 | " brpc_$service_name$_service_t service\n" 238 | ") {\n" 239 | " $package_name$::$service_name$Impl *service_ptr = \n" 240 | " static_cast<$package_name$::$service_name$Impl *>(service);\n" 241 | " delete service_ptr;\n" 242 | "}\n" 243 | "brpc_$service_name$_stub_t brpc_$service_name$Stub_with_channel(\n" 244 | " brpc::Channel *ch" 245 | ") {\n" 246 | " return new $package_name$::$service_name$_Stub(ch);\n" 247 | "}\n" 248 | "void brpc_$service_name$Stub_destroy(\n" 249 | " brpc_$service_name$_stub_t stub\n" 250 | ") {\n" 251 | " $package_name$::$service_name$_Stub *stub_ptr = \n" 252 | " static_cast<$package_name$::$service_name$_Stub *>(stub);\n" 253 | " delete stub_ptr;\n" 254 | "}\n" 255 | "\n", 256 | "package_name", file->package(), "service_name", service->name()); 257 | 258 | for (int j = 0; j < service->method_count(); ++j) { 259 | const google::protobuf::MethodDescriptor *method = service->method(j); 260 | 261 | cpp_printer.Print( 262 | "void brpc_$service_name$_$method_name$_set_handler(\n" 263 | " brpc_$service_name$_service_t service,\n" 264 | " void *rust_closure_ptr,\n" 265 | " int (*trampoline)(void *, butil::ZeroCopyBuf &, \n" 266 | " butil::ZeroCopyBufMut &))\n" 267 | "{\n" 268 | " $package_name$::$service_name$Impl *service_ptr = \n" 269 | " static_cast<$package_name$::$service_name$Impl *>(service);\n" 270 | " service_ptr->$method_name$_trampoline = trampoline;\n" 271 | " service_ptr->$method_name$_closure_ptr = rust_closure_ptr;\n" 272 | "}\n", 273 | "method_name", method->name(), "service_name", service->name(), 274 | "package_name", file->package()); 275 | 276 | cpp_printer.Print( 277 | "void brpc_$service_name$Stub_$method_name$(\n" 278 | " brpc_$service_name$_stub_t stub,\n" 279 | " brpc::Controller *cntl) {\n" 280 | " $package_name$::$service_name$_Stub *stub_ptr = \n" 281 | " static_cast<$package_name$::$service_name$_Stub *>(stub);\n" 282 | " stub_ptr->$method_name$(cntl, NULL, NULL, NULL);\n" 283 | "}\n", 284 | "method_name", method->name(), "service_name", service->name(), 285 | "package_name", file->package()); 286 | } 287 | } 288 | 289 | cpp_printer.Print("}\n"); // extern "C" 290 | 291 | /* Generate [proto_name].rs */ 292 | google::protobuf::io::ZeroCopyOutputStream *rust_file = 293 | ctx->Open(rust_file_name); 294 | google::protobuf::io::Printer rs_printer(rust_file, '$'); 295 | 296 | rs_printer.Print( 297 | "use std::os::raw::{c_int, c_void};\n\n" 298 | "use brpc_rs::{BrpcError, BrpcResult, Channel, Controller, Service};\n" 299 | "use brpc_rs::internal::ffi::{BrpcChannel, BrpcController};\n" 300 | "use brpc_rs::internal::zero_copy::{ZeroCopyBuf, ZeroCopyBufMut};\n" 301 | "use brpc_rs::internal::zero_copy::{BrpcZeroCopyBuf, " 302 | "BrpcZeroCopyBufMut};\n" 303 | "use prost::Message; // Trait\n\n"); 304 | 305 | for (int i = 0; i < file->service_count(); ++i) { 306 | const google::protobuf::ServiceDescriptor *service = file->service(i); 307 | 308 | rs_printer.Print( 309 | "pub enum Brpc$service_name$ {}\n" 310 | "pub enum Brpc$service_name$Stub {}\n" 311 | "pub struct $service_name$ { inner: *mut Brpc$service_name$ }\n" 312 | "pub struct $service_name$Stub { inner: *mut Brpc$service_name$Stub }\n" 313 | "\n" 314 | "impl Service for $service_name$ {\n" 315 | " fn get_service_ptr(&self) -> *mut c_void {\n" 316 | " self.inner as *mut c_void\n" 317 | " }\n" 318 | "}\n" 319 | "\n" 320 | "impl Default for $service_name$ {\n" 321 | " fn default() -> Self {\n" 322 | " Self::new()\n" 323 | " }\n" 324 | "}\n" 325 | "\n" 326 | "impl Drop for $service_name$ {\n" 327 | " fn drop(&mut self) {\n" 328 | " unsafe {\n" 329 | " brpc_$service_name$_destroy(self.inner);\n" 330 | " }\n" 331 | " }\n" 332 | "}\n" 333 | "\n" 334 | "impl $service_name$ {\n" 335 | " pub fn new() -> $service_name$ {\n" 336 | " $service_name$ { inner: unsafe { brpc_$service_name$_new() } " 337 | "}\n" 338 | " }\n" 339 | "\n\n", 340 | "service_name", service->name()); 341 | 342 | for (int j = 0; j < service->method_count(); ++j) { 343 | const google::protobuf::MethodDescriptor *method = service->method(j); 344 | 345 | const google::protobuf::Descriptor *input = method->input_type(); 346 | const google::protobuf::Descriptor *output = method->output_type(); 347 | 348 | rs_printer.Print(" pub fn set_$method_name$_handler(" 349 | "&mut self, rust_fn: &mut F)\n", 350 | "method_name", method->name()); 351 | rs_printer.Print( 352 | " where\n" 353 | " F: FnMut(&$input$, &mut $output$) -> " 354 | "BrpcResult<()> + Send + Sync + 'static {\n" 355 | " unsafe extern \"C\" fn trampoline(\n" 356 | " data: *mut c_void,\n" 357 | " zc_req: *mut c_void, // function parameter 1\n" 358 | " zc_resp: *mut c_void, // function parameter 2\n" 359 | " ) -> c_int\n" 360 | " where F: FnMut(&$input$, &mut $output$) -> " 361 | "BrpcResult<()> + Send + Sync + 'static {\n" 362 | " let buf = ZeroCopyBuf::from_raw_ptr(\n" 363 | " zc_req as *mut BrpcZeroCopyBuf);\n" 364 | " let mut buf_mut = ZeroCopyBufMut::from_raw_ptr(\n" 365 | " zc_resp as *mut BrpcZeroCopyBufMut);\n" 366 | " let closure: &mut F = &mut *(data as *mut F);\n" 367 | " let mut response = $output$::default();\n" 368 | " let request = match " 369 | "$input$::decode_length_delimited(buf) {\n" 370 | " Ok(r) => r,\n" 371 | " Err(_e) => return -1, \n" 372 | " };\n" 373 | " match (*closure)(&request, &mut response) {\n" 374 | " Ok(r) => r,\n" 375 | " Err(_e) => return -1, \n" 376 | " };\n" 377 | " match response.encode_length_delimited(&mut buf_mut) {\n" 378 | " Ok(r) => r,\n" 379 | " Err(_e) => return -1, \n" 380 | " };\n" 381 | " 0\n" 382 | " }\n", 383 | "input", input->name(), "output", output->name()); 384 | rs_printer.Print( 385 | " let rust_fn_ptr = rust_fn as *mut F as *mut c_void;\n" 386 | " unsafe { " 387 | "brpc_$service_name$_$method_name$_set_handler(self.inner, " 388 | "rust_fn_ptr, trampoline::) };\n" 389 | " }\n", 390 | "service_name", service->name(), "method_name", method->name()); 391 | } 392 | rs_printer.Print("}\n\n"); 393 | 394 | // ServiceStub functions 395 | rs_printer.Print( 396 | "impl Drop for $service_name$Stub {\n" 397 | " fn drop(&mut self) {\n" 398 | " unsafe {\n" 399 | " brpc_$service_name$Stub_destroy(self.inner);\n" 400 | " }\n" 401 | " }\n" 402 | "}\n\n" 403 | "impl $service_name$Stub {\n" 404 | " pub fn with_channel(ch: &Channel) -> $service_name$Stub {\n" 405 | " $service_name$Stub { \n" 406 | " inner: unsafe{ " 407 | "brpc_$service_name$Stub_with_channel(ch.inner) }\n" 408 | " }\n" 409 | " }\n" 410 | "\n\n", 411 | "service_name", service->name()); 412 | 413 | for (int j = 0; j < service->method_count(); ++j) { 414 | const google::protobuf::MethodDescriptor *method = service->method(j); 415 | const google::protobuf::Descriptor *input = method->input_type(); 416 | const google::protobuf::Descriptor *output = method->output_type(); 417 | 418 | rs_printer.Print(" pub fn $method_name$(&self, request: &$input$) -> " 419 | "BrpcResult<$output$> {\n", 420 | "method_name", method->name(), "input", input->name(), 421 | "output", output->name()); 422 | rs_printer.Print( 423 | " let cntl = Controller::new();\n" 424 | " let mut request_buf = unsafe { ZeroCopyBufMut::from_iobuf(\n" 425 | " cntl.request_attachment()\n" 426 | " ) };\n" 427 | " request.encode_length_delimited(&mut " 428 | "request_buf).map_err(|_| BrpcError::ESERIALIZE)?;\n" 429 | " unsafe { brpc_$service_name$Stub_$method_name$(self.inner, " 430 | "cntl.inner) };\n" 431 | " if cntl.failed() { return Err(cntl.error()); }\n" 432 | " let response_buf = unsafe { ZeroCopyBuf::from_iobuf(\n" 433 | " cntl.response_attachment()\n" 434 | " ) };\n" 435 | " let response = " 436 | "$output$::decode_length_delimited(response_buf).map_err(|_| " 437 | "BrpcError::EDESERIALIZE)?;\n" 438 | " Ok(response)\n" 439 | " }\n", 440 | "service_name", service->name(), "method_name", method->name(), 441 | "output", output->name()); 442 | rs_printer.Print("}\n\n"); 443 | } 444 | } 445 | 446 | rs_printer.Print("type Trampoline = unsafe extern \"C\" fn(*mut c_void, *mut " 447 | "c_void, *mut c_void) -> c_int;\n\n"); 448 | 449 | rs_printer.Print("extern \"C\" {\n"); 450 | for (int i = 0; i < file->service_count(); ++i) { 451 | const google::protobuf::ServiceDescriptor *service = file->service(i); 452 | 453 | rs_printer.Print( 454 | "fn brpc_$service_name$_new() -> *mut Brpc$service_name$;\n" 455 | "fn brpc_$service_name$_destroy(service: *mut Brpc$service_name$);\n" 456 | "fn brpc_$service_name$Stub_with_channel(ch: *mut BrpcChannel) -> *mut " 457 | "Brpc$service_name$Stub;\n" 458 | "fn brpc_$service_name$Stub_destroy(service: *mut " 459 | "Brpc$service_name$Stub);\n", 460 | "service_name", service->name()); 461 | 462 | for (int j = 0; j < service->method_count(); ++j) { 463 | const google::protobuf::MethodDescriptor *method = service->method(j); 464 | rs_printer.Print("fn brpc_$service_name$_$method_name$_set_handler(\n" 465 | " service: *mut Brpc$service_name$,\n" 466 | " closure: *mut c_void,\n" 467 | " t: Trampoline\n" 468 | ");\n" 469 | "\n" 470 | "fn brpc_$service_name$Stub_$method_name$(\n" 471 | " stub: *mut Brpc$service_name$Stub,\n" 472 | " cntl: *mut BrpcController\n" 473 | ");\n" 474 | "\n", 475 | "service_name", service->name(), "method_name", 476 | method->name()); 477 | } 478 | } 479 | 480 | rs_printer.Print("}\n\n\n"); // extern "C" 481 | return true; 482 | } 483 | 484 | } // namespace brpc_rs 485 | 486 | extern "C" { 487 | int cpp_main(int argc, char *argv[]) { 488 | ::brpc_rs::BrpcToProtobuf brpc_generator; 489 | return google::protobuf::compiler::PluginMain(argc, argv, &brpc_generator); 490 | } 491 | } 492 | -------------------------------------------------------------------------------- /brpc-protoc-plugin/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | use libc::{c_char, c_int}; 15 | use std::{env, ffi}; 16 | 17 | extern "C" { 18 | fn cpp_main(argv: c_int, argv: *const *const c_char) -> c_int; 19 | } 20 | 21 | fn main() { 22 | let args = env::args() 23 | .map(|arg| ffi::CString::new(arg).unwrap()) 24 | .collect::>(); 25 | let c_args = args 26 | .iter() 27 | .map(|arg| arg.as_ptr()) 28 | .collect::>(); 29 | unsafe { 30 | let _ = cpp_main(c_args.len() as c_int, c_args.as_ptr()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /brpc-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "brpc-sys" 3 | version = "0.1.0" 4 | authors = ["Yiming Jing "] 5 | edition = "2018" 6 | license = "Apache-2.0" 7 | description = "The Rust FFI bindings of Apache BRPC" 8 | documentation = "https://mesalock-linux.github.io/brpc-rs-docs/brpc_sys/index.html" 9 | keywords = ["rpc","rust"] 10 | categories = ["network-programming"] 11 | homepage = "https://github.com/mesalock-linux/brpc-rs" 12 | repository = "https://github.com/mesalock-linux/brpc-rs" 13 | readme = "README.md" 14 | build = "build.rs" 15 | 16 | [build-dependencies] 17 | cc = "1.0" 18 | 19 | [dependencies] 20 | libc = "0.2" 21 | cc = "1.0.38" 22 | bytes = "0.4.12" 23 | -------------------------------------------------------------------------------- /brpc-sys/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /brpc-sys/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | fn main() { 15 | let mut builder = cc::Build::new(); 16 | builder 17 | .cpp(true) 18 | .file("src/ffi.cpp") 19 | .file("src/zero_copy.cpp") 20 | .flag("-std=c++11") 21 | .flag_if_supported("-Wno-everything") 22 | .warnings(false); 23 | 24 | builder.compile("brpc_ffi"); 25 | println!("cargo:rustc-link-lib=static=brpc_ffi"); 26 | 27 | println!("cargo:rustc-link-lib=brpc"); 28 | println!("cargo:rustc-link-lib=protobuf"); 29 | println!("cargo:rustc-link-lib=gflags"); 30 | println!("cargo:rustc-link-lib=leveldb"); 31 | println!("cargo:rustc-link-lib=ssl"); 32 | println!("cargo:rustc-link-lib=crypto"); 33 | } 34 | -------------------------------------------------------------------------------- /brpc-sys/src/ffi.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include 15 | #include 16 | 17 | extern "C" { 18 | size_t iobuf_size(butil::IOBuf &buf) { return buf.size(); } 19 | bool brpc_is_asked_to_quit(void) { return brpc::IsAskedToQuit(); } 20 | } 21 | 22 | // brpc::Server 23 | extern "C" { 24 | brpc::Server *brpc_server_new() { return new brpc::Server; } 25 | 26 | void brpc_server_destroy(brpc::Server *server) { delete server; } 27 | 28 | int brpc_server_add_service(brpc::Server *server, 29 | ::google::protobuf::Service *service, 30 | brpc::ServiceOwnership ownership) { 31 | return server->AddService(service, ownership); 32 | } 33 | 34 | int brpc_server_start(brpc::Server *server, int port, 35 | brpc::ServerOptions *options) { 36 | return server->Start(port, options); 37 | } 38 | 39 | void brpc_server_run_until_asked_to_quit(brpc::Server *server) { 40 | return server->RunUntilAskedToQuit(); 41 | } 42 | 43 | // brpc::ServerOptions 44 | brpc::ServerOptions *brpc_server_options_new() { 45 | return new brpc::ServerOptions; 46 | } 47 | 48 | void brpc_server_options_destroy(brpc::ServerOptions *options) { 49 | delete options; 50 | } 51 | 52 | void brpc_server_options_set_idle_timeout_ms(brpc::ServerOptions *options, 53 | int timeout) { 54 | options->idle_timeout_sec = timeout; 55 | } 56 | } // extern "C" brpc::Server 57 | 58 | // brpc::Channel 59 | extern "C" { 60 | brpc::Channel *brpc_channel_new() { return new brpc::Channel; } 61 | 62 | void brpc_channel_destroy(brpc::Channel *channel) { delete channel; } 63 | 64 | int brpc_channel_init(brpc::Channel *channel, const char *server_addr_and_port, 65 | const brpc::ChannelOptions *options) { 66 | return channel->Init(server_addr_and_port, options); 67 | } 68 | 69 | // brpc::ChannelOptions 70 | brpc::ChannelOptions *brpc_channel_options_new() { 71 | brpc::ChannelOptions *ptr = new brpc::ChannelOptions; 72 | ptr->protocol = "http"; 73 | return ptr; 74 | } 75 | 76 | void brpc_channel_options_destroy(brpc::ChannelOptions *options) { 77 | delete options; 78 | } 79 | 80 | void brpc_channel_options_set_timeout_ms(brpc::ChannelOptions *options, 81 | int timeout) { 82 | options->timeout_ms = timeout; 83 | } 84 | 85 | void brpc_channel_options_set_max_retry(brpc::ChannelOptions *options, 86 | int max_retry) { 87 | options->max_retry = max_retry; 88 | } 89 | 90 | } // extern "C" brpc::Channel 91 | 92 | // brpc::Controller 93 | extern "C" { 94 | brpc::Controller *brpc_controller_new() { return new brpc::Controller; } 95 | 96 | void brpc_controller_destroy(brpc::Controller *cntl) { delete cntl; } 97 | 98 | bool brpc_controller_failed(brpc::Controller *cntl) { return cntl->Failed(); } 99 | 100 | int brpc_controller_error_code(brpc::Controller *cntl) { 101 | return cntl->ErrorCode(); 102 | } 103 | 104 | void brpc_controller_set_failed(brpc::Controller *cntl, int code) { 105 | cntl->SetFailed(code, "brpc-rs controller failed"); 106 | } 107 | 108 | butil::IOBuf &brpc_controller_get_request_attachment(brpc::Controller *cntl) { 109 | return cntl->request_attachment(); 110 | } 111 | 112 | butil::IOBuf &brpc_controller_get_response_attachment(brpc::Controller *cntl) { 113 | return cntl->response_attachment(); 114 | } 115 | } -------------------------------------------------------------------------------- /brpc-sys/src/ffi.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | use std::os::raw::{c_char, c_int, c_void}; 15 | 16 | pub enum BrpcServer {} // brpc::Server 17 | pub enum BrpcServerOptions {} // brpc::ServerOptions 18 | 19 | pub enum BrpcChannel {} // brpc::Channel 20 | pub enum BrpcChannelOptions {} // brpc::ChannelOptions 21 | 22 | pub enum BrpcController {} // brpc::Controller 23 | pub enum BrpcIOBuf {} // butil::IOBuf 24 | 25 | #[allow(dead_code)] 26 | extern "C" { 27 | pub fn brpc_is_asked_to_quit() -> c_int; 28 | pub fn brpc_server_new() -> *mut BrpcServer; 29 | pub fn brpc_server_destroy(server: *mut BrpcServer); 30 | pub fn brpc_server_add_service( 31 | server: *mut BrpcServer, 32 | service: *mut c_void, 33 | ownership: c_int, 34 | ) -> c_int; 35 | pub fn brpc_server_start( 36 | server: *mut BrpcServer, 37 | port: c_int, 38 | opt: *const BrpcServerOptions, 39 | ) -> c_int; 40 | pub fn brpc_server_run_until_asked_to_quit(server: *mut BrpcServer); 41 | 42 | pub fn brpc_server_options_new() -> *mut BrpcServerOptions; 43 | pub fn brpc_server_options_destroy(server_options: *mut BrpcServerOptions); 44 | pub fn brpc_server_options_set_idle_timeout_ms( 45 | server_options: *mut BrpcServerOptions, 46 | timeout: c_int, 47 | ); 48 | 49 | pub fn brpc_channel_new() -> *mut BrpcChannel; 50 | pub fn brpc_channel_destroy(channel: *mut BrpcChannel); 51 | pub fn brpc_channel_init( 52 | channel: *mut BrpcChannel, 53 | server_addr_and_port: *const c_char, 54 | options: *const BrpcChannelOptions, 55 | ) -> c_int; 56 | 57 | pub fn brpc_channel_options_new() -> *mut BrpcChannelOptions; 58 | pub fn brpc_channel_options_destroy(channel_options: *mut BrpcChannelOptions); 59 | 60 | pub fn brpc_channel_options_set_timeout_ms( 61 | channel_options: *mut BrpcChannelOptions, 62 | timeout: c_int, 63 | ); 64 | pub fn brpc_channel_options_set_max_retry( 65 | channel_options: *mut BrpcChannelOptions, 66 | max_retry: c_int, 67 | ); 68 | pub fn brpc_controller_new() -> *mut BrpcController; 69 | pub fn brpc_controller_destroy(cntl: *mut BrpcController); 70 | pub fn brpc_controller_failed(cntl: *mut BrpcController) -> c_int; 71 | pub fn brpc_controller_error_code(cntl: *mut BrpcController) -> c_int; 72 | pub fn brpc_controller_set_failed(cntl: *mut BrpcController, code: c_int); 73 | pub fn brpc_controller_get_request_attachment(cntl: *mut BrpcController) -> *mut BrpcIOBuf; 74 | pub fn brpc_controller_get_response_attachment(cntl: *mut BrpcController) -> *mut BrpcIOBuf; 75 | } 76 | -------------------------------------------------------------------------------- /brpc-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | pub mod ffi; 15 | pub mod zero_copy; 16 | -------------------------------------------------------------------------------- /brpc-sys/src/zero_copy.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #include 15 | #include 16 | 17 | namespace butil { 18 | class ZeroCopyBuf { 19 | public: 20 | explicit ZeroCopyBuf(const IOBuf &buf) 21 | : _block_start(NULL), _block_end(NULL), _total_len(0), _buf(&buf), 22 | _stream(buf) { 23 | char *block_end = NULL; 24 | int block_len; 25 | if (_block_start == NULL) { 26 | const void *start_ptr = NULL; 27 | _stream.Next(reinterpret_cast(&start_ptr), &block_len); 28 | _block_start = (char *)start_ptr; 29 | _block_end = _block_start + block_len; 30 | } 31 | } 32 | uint64_t Remaining() const; 33 | bool Bytes(const void **data, int *size); 34 | bool Advance(int count); 35 | 36 | private: 37 | char *_block_start; 38 | char *_block_end; 39 | uint64_t _total_len; 40 | const IOBuf *_buf; 41 | IOBufAsZeroCopyInputStream _stream; 42 | }; 43 | 44 | uint64_t ZeroCopyBuf::Remaining() const { return _buf->length() - _total_len; } 45 | 46 | bool ZeroCopyBuf::Bytes(const void **data, int *size) { 47 | *data = _block_start; 48 | *size = _block_end - _block_start; 49 | return true; 50 | } 51 | 52 | bool ZeroCopyBuf::Advance(int count) { 53 | if (count == 0) { 54 | return true; 55 | } 56 | size_t nc = 0; 57 | while (nc < count && Remaining() != 0) { 58 | const size_t block_size = _block_end - _block_start; 59 | const size_t to_skip = std::min(block_size, count - nc); 60 | _block_start += to_skip; 61 | _total_len += to_skip; 62 | nc += to_skip; 63 | if (_block_start == _block_end) { 64 | int block_len; 65 | _stream.Next(reinterpret_cast(_block_start), &block_len); 66 | _block_end = _block_start + block_len; 67 | } 68 | } 69 | return true; 70 | } 71 | 72 | class ZeroCopyBufMut { 73 | public: 74 | explicit ZeroCopyBufMut(IOBuf &buf) 75 | : _block_start(NULL), _block_end(NULL), _total_len(0), _buf(&buf), 76 | _stream(&buf) { 77 | char *block_end = NULL; 78 | int block_len; 79 | if (_block_start == NULL) { 80 | _stream.Next(reinterpret_cast(&_block_start), &block_len); 81 | _block_end = _block_start + block_len; 82 | } 83 | } 84 | uint64_t RemainingMut() const; 85 | bool BytesMut(void **data, int *size); 86 | bool AdvanceMut(size_t count); 87 | 88 | private: 89 | char *_block_start; 90 | char *_block_end; 91 | uint64_t _total_len; 92 | IOBuf *_buf; 93 | IOBufAsZeroCopyOutputStream _stream; 94 | }; 95 | 96 | uint64_t ZeroCopyBufMut::RemainingMut() const { 97 | return UINT64_MAX - _total_len; 98 | } 99 | 100 | bool ZeroCopyBufMut::BytesMut(void **data, int *size) { 101 | *data = _block_start; 102 | *size = _block_end - _block_start; 103 | return true; 104 | } 105 | 106 | bool ZeroCopyBufMut::AdvanceMut(size_t count) { 107 | if (count == 0) { 108 | return true; 109 | } 110 | if (count > RemainingMut()) { 111 | return false; 112 | } 113 | size_t nc = 0; 114 | while (nc < count && RemainingMut() != 0) { 115 | const size_t block_size = _block_end - _block_start; 116 | const size_t to_skip = std::min(block_size, count - nc); 117 | _block_start += to_skip; 118 | _total_len += to_skip; 119 | nc += to_skip; 120 | if (_block_start == _block_end) { 121 | int block_len; 122 | _stream.Next(reinterpret_cast(_block_start), &block_len); 123 | _block_end = _block_start + block_len; 124 | } 125 | } 126 | return true; 127 | } 128 | 129 | } // namespace butil 130 | 131 | // ZeroCopyBuf and ZeroCopyBufMut 132 | extern "C" { 133 | 134 | butil::ZeroCopyBuf *zero_copy_buf_new(butil::IOBuf &iobuf) { 135 | return new butil::ZeroCopyBuf(iobuf); 136 | } 137 | 138 | butil::ZeroCopyBufMut *zero_copy_buf_mut_new(butil::IOBuf &iobuf) { 139 | return new butil::ZeroCopyBufMut(iobuf); 140 | } 141 | 142 | uint64_t zero_copy_buf_remaining(butil::ZeroCopyBuf *zc) { 143 | return zc->Remaining(); 144 | } 145 | 146 | bool zero_copy_buf_bytes(butil::ZeroCopyBuf *zc, const void **data, int *size) { 147 | return zc->Bytes(data, size); 148 | } 149 | 150 | bool zero_copy_buf_advance(butil::ZeroCopyBuf *zc, int count) { 151 | return zc->Advance(count); 152 | } 153 | 154 | uint64_t zero_copy_buf_mut_remaining(butil::ZeroCopyBufMut *zc) { 155 | return zc->RemainingMut(); 156 | } 157 | 158 | bool zero_copy_buf_mut_bytes(butil::ZeroCopyBufMut *zc, void **data, 159 | int *size) { 160 | return zc->BytesMut(data, size); 161 | } 162 | 163 | bool zero_copy_buf_mut_advance(butil::ZeroCopyBufMut *zc, int count) { 164 | return zc->AdvanceMut(count); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /brpc-sys/src/zero_copy.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | use crate::ffi::BrpcIOBuf; 15 | use bytes::{Buf, BufMut}; 16 | use std::os::raw::{c_int, c_ulonglong, c_void}; // traits 17 | 18 | pub enum BrpcZeroCopyBuf {} 19 | pub enum BrpcZeroCopyBufMut {} 20 | 21 | pub struct ZeroCopyBuf { 22 | inner: *mut BrpcZeroCopyBuf, 23 | } 24 | 25 | impl ZeroCopyBuf { 26 | pub unsafe fn from_iobuf(ptr: *mut BrpcIOBuf) -> Self { 27 | ZeroCopyBuf { 28 | inner: zero_copy_buf_new(ptr), 29 | } 30 | } 31 | 32 | pub unsafe fn from_raw_ptr(ptr: *mut BrpcZeroCopyBuf) -> Self { 33 | ZeroCopyBuf { inner: ptr } 34 | } 35 | } 36 | 37 | impl Buf for ZeroCopyBuf { 38 | fn remaining(&self) -> usize { 39 | unsafe { zero_copy_buf_remaining(self.inner) as usize } 40 | } 41 | 42 | fn bytes(&self) -> &[u8] { 43 | let mut buf_ptr: *const c_void = std::ptr::null_mut(); 44 | let mut size: c_int = 0; 45 | unsafe { 46 | let _ = zero_copy_buf_bytes( 47 | self.inner, 48 | &mut buf_ptr as *mut *const c_void, 49 | &mut size as *mut c_int, 50 | ); 51 | std::slice::from_raw_parts(buf_ptr as *const u8, size as usize) 52 | } 53 | } 54 | 55 | fn advance(&mut self, cnt: usize) { 56 | // Panic if zero_copy_buf_advance() failed. 57 | unsafe { 58 | assert_eq!(1, zero_copy_buf_advance(self.inner, cnt as c_int)); 59 | } 60 | } 61 | } 62 | 63 | pub struct ZeroCopyBufMut { 64 | inner: *mut BrpcZeroCopyBufMut, 65 | } 66 | 67 | impl ZeroCopyBufMut { 68 | pub unsafe fn from_iobuf(ptr: *mut BrpcIOBuf) -> Self { 69 | ZeroCopyBufMut { 70 | inner: zero_copy_buf_mut_new(ptr), 71 | } 72 | } 73 | 74 | pub unsafe fn from_raw_ptr(ptr: *mut BrpcZeroCopyBufMut) -> Self { 75 | ZeroCopyBufMut { inner: ptr } 76 | } 77 | } 78 | 79 | impl BufMut for ZeroCopyBufMut { 80 | fn remaining_mut(&self) -> usize { 81 | unsafe { zero_copy_buf_mut_remaining(self.inner) as usize } 82 | } 83 | 84 | unsafe fn bytes_mut(&mut self) -> &mut [u8] { 85 | let mut buf_ptr: *mut c_void = std::ptr::null_mut(); 86 | let mut size: c_int = 0; 87 | let _ = zero_copy_buf_mut_bytes( 88 | self.inner, 89 | &mut buf_ptr as *mut *mut c_void, 90 | &mut size as *mut c_int, 91 | ); 92 | std::slice::from_raw_parts_mut(buf_ptr as *mut u8, size as usize) 93 | } 94 | 95 | unsafe fn advance_mut(&mut self, cnt: usize) { 96 | // Panic if zero_copy_buf_advance() failed. 97 | assert_eq!(1, zero_copy_buf_mut_advance(self.inner, cnt as c_int)); 98 | } 99 | } 100 | 101 | extern "C" { 102 | pub fn zero_copy_buf_new(iobuf: *mut BrpcIOBuf) -> *mut BrpcZeroCopyBuf; 103 | pub fn zero_copy_buf_mut_new(iobuf: *mut BrpcIOBuf) -> *mut BrpcZeroCopyBufMut; 104 | 105 | pub fn zero_copy_buf_remaining(zc: *mut BrpcZeroCopyBuf) -> c_ulonglong; 106 | pub fn zero_copy_buf_bytes( 107 | zc: *mut BrpcZeroCopyBuf, 108 | data: *mut *const c_void, 109 | size: *mut c_int, 110 | ) -> c_int; 111 | pub fn zero_copy_buf_advance(zc: *mut BrpcZeroCopyBuf, count: c_int) -> c_int; 112 | 113 | pub fn zero_copy_buf_mut_remaining(zc: *mut BrpcZeroCopyBufMut) -> c_ulonglong; 114 | pub fn zero_copy_buf_mut_bytes( 115 | zc: *mut BrpcZeroCopyBufMut, 116 | data: *mut *mut c_void, 117 | size: *mut c_int, 118 | ) -> c_int; 119 | pub fn zero_copy_buf_mut_advance(zc: *mut BrpcZeroCopyBufMut, count: c_int) -> c_int; 120 | } 121 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples" 3 | version = "0.1.0-alpha" 4 | authors = ["Yiming Jing "] 5 | edition = "2018" 6 | license = "Apache-2.0" 7 | build = "build.rs" 8 | 9 | [build-dependencies] 10 | brpc-build = { path = "../brpc-build", version = "0.1.0" } 11 | 12 | [dependencies] 13 | brpc-rs = { path = "..", version = "0.1.0" } 14 | prost = "0.5.0" 15 | bytes = "0.4.12" 16 | 17 | [[bin]] 18 | name = "echo_client" 19 | path = "echo/client.rs" 20 | 21 | [[bin]] 22 | name = "echo_server" 23 | path = "echo/server.rs" 24 | -------------------------------------------------------------------------------- /examples/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | fn main() -> std::io::Result<()> { 15 | brpc_build::compile_protos(&["echo/echo.proto"], &["echo"]) 16 | } 17 | -------------------------------------------------------------------------------- /examples/echo/client.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | use brpc_rs::{Channel, ChannelOptions}; 15 | 16 | pub mod echo { 17 | include!(concat!(env!("OUT_DIR"), "/example.rs")); 18 | include!(concat!(env!("OUT_DIR"), "/example.brpc.rs")); 19 | } 20 | 21 | fn main() { 22 | let mut options = ChannelOptions::new(); 23 | options.set_timeout_ms(100); 24 | let addr = "127.0.0.1:50000".parse().expect("Invalid socket address"); 25 | let ch = Channel::with_options(&addr, &options); 26 | let client = echo::EchoServiceStub::with_channel(&ch); 27 | let request = echo::EchoRequest { 28 | message: "hello".to_owned(), 29 | }; 30 | match client.echo(&request) { 31 | Ok(r) => println!("Response: {:?}", r), 32 | Err(e) => eprintln!("Error: {:?}", e), 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/echo/echo.proto: -------------------------------------------------------------------------------- 1 | syntax="proto2"; 2 | package example; 3 | 4 | message EchoRequest { 5 | required string message = 1; 6 | }; 7 | 8 | message EchoResponse { 9 | required string message = 1; 10 | }; 11 | 12 | service EchoService { 13 | rpc echo(EchoRequest) returns (EchoResponse); 14 | }; 15 | -------------------------------------------------------------------------------- /examples/echo/server.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | use brpc_rs::{Server, ServerOptions, ServiceOwnership}; 15 | 16 | pub mod echo { 17 | include!(concat!(env!("OUT_DIR"), "/example.rs")); 18 | include!(concat!(env!("OUT_DIR"), "/example.brpc.rs")); 19 | } 20 | 21 | fn main() { 22 | let mut service = echo::EchoService::new(); 23 | service.set_echo_handler(&mut move |request, mut response| { 24 | response.message = request.message.clone(); 25 | Ok(()) 26 | }); 27 | 28 | let mut server = Server::new(); 29 | let mut options = ServerOptions::new(); 30 | options.set_idle_timeout_ms(1000); 31 | server 32 | .add_service(&service, ServiceOwnership::ServerDoesntOwnService) 33 | .expect("Failed to add service"); 34 | server 35 | .start(50000, &options) 36 | .expect("Failed to start service"); 37 | server.run(); // Run until CTRL-C 38 | } 39 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /overview.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/channel.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | use brpc_sys::ffi::{self, BrpcChannel, BrpcChannelOptions}; 15 | use libc::c_int; 16 | use std::ffi::CString; 17 | use std::net::SocketAddr; 18 | 19 | /// A `Channel` provides a connection to a BRPC server on a specified host and 20 | /// port and is used when creating a client stub 21 | pub struct Channel { 22 | pub inner: *mut BrpcChannel, // brpc_channel_t in ffi.cpp 23 | } 24 | 25 | impl Channel { 26 | /// Make a `Channel` with the provided socker address and `ChannelOptions`. 27 | pub fn with_options(sockaddr: &SocketAddr, options: &ChannelOptions) -> Self { 28 | let inner = unsafe { ffi::brpc_channel_new() }; 29 | // safe to unwrap() because format!(sockaddr) does NOT contains \0 30 | let server_addr_and_port = CString::new(format!("{}", sockaddr)).unwrap(); 31 | let server_addr_and_port_ptr = server_addr_and_port.as_c_str().as_ptr(); 32 | assert!( 33 | 0 == unsafe { ffi::brpc_channel_init(inner, server_addr_and_port_ptr, options.inner) } 34 | ); 35 | Channel { inner } 36 | } 37 | } 38 | 39 | impl Drop for Channel { 40 | fn drop(&mut self) { 41 | unsafe { 42 | ffi::brpc_channel_destroy(self.inner); 43 | } 44 | } 45 | } 46 | 47 | /// Options for a `Channel` 48 | pub struct ChannelOptions { 49 | #[doc(hidden)] 50 | pub inner: *mut BrpcChannelOptions, 51 | } 52 | 53 | impl Default for ChannelOptions { 54 | fn default() -> Self { 55 | Self::new() 56 | } 57 | } 58 | 59 | impl ChannelOptions { 60 | /// Make a `ChannelOptions` with default values. 61 | pub fn new() -> Self { 62 | ChannelOptions { 63 | inner: unsafe { ffi::brpc_channel_options_new() }, 64 | } 65 | } 66 | 67 | /// Set max duration of RPC in milliseconds over this Channel. -1 means wait 68 | /// indefinitely. 69 | pub fn set_timeout_ms(&mut self, timeout: i32) { 70 | unsafe { ffi::brpc_channel_options_set_timeout_ms(self.inner, timeout as c_int) } 71 | } 72 | 73 | /// Set retry limit for RPC over this channel. <=0 means no retry. 74 | pub fn set_max_retry(&mut self, timeout: i32) { 75 | unsafe { ffi::brpc_channel_options_set_max_retry(self.inner, timeout as c_int) } 76 | } 77 | } 78 | 79 | impl Drop for ChannelOptions { 80 | fn drop(&mut self) { 81 | unsafe { 82 | ffi::brpc_channel_options_destroy(self.inner); 83 | } 84 | } 85 | } 86 | 87 | #[cfg(test)] 88 | mod tests { 89 | use super::*; 90 | use std::ptr; 91 | 92 | #[test] 93 | fn channel_options_new() { 94 | let opt = ChannelOptions::new(); 95 | assert_ne!(opt.inner, ptr::null_mut()); 96 | } 97 | 98 | #[test] 99 | fn channel_options_set_timeout() { 100 | let mut opt = ChannelOptions::new(); 101 | opt.set_timeout_ms(0); 102 | } 103 | 104 | #[test] 105 | fn channel_options_set_max_retry() { 106 | let mut opt = ChannelOptions::new(); 107 | opt.set_max_retry(0); 108 | } 109 | 110 | #[test] 111 | fn channel_new_with_options() { 112 | let opt = ChannelOptions::new(); 113 | let addr = "127.0.0.1:50000".parse().unwrap(); 114 | let ch = Channel::with_options(&addr, &opt); 115 | assert_ne!(opt.inner, ptr::null_mut()); 116 | assert_ne!(ch.inner, ptr::null_mut()); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/controller.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | use crate::BrpcError; 15 | use brpc_sys::ffi::{self, BrpcController, BrpcIOBuf}; 16 | 17 | #[doc(hidden)] 18 | pub struct Controller { 19 | pub inner: *mut BrpcController, 20 | } 21 | 22 | impl Controller { 23 | pub fn new() -> Self { 24 | Controller { 25 | inner: unsafe { ffi::brpc_controller_new() }, 26 | } 27 | } 28 | 29 | pub fn failed(&self) -> bool { 30 | unsafe { 1 == ffi::brpc_controller_failed(self.inner) } 31 | } 32 | 33 | pub fn error(&self) -> BrpcError { 34 | let error_code = unsafe { ffi::brpc_controller_error_code(self.inner) }; 35 | BrpcError::from(error_code) 36 | } 37 | 38 | pub fn request_attachment(&self) -> *mut BrpcIOBuf { 39 | unsafe { ffi::brpc_controller_get_request_attachment(self.inner) } 40 | } 41 | 42 | pub fn response_attachment(&self) -> *mut BrpcIOBuf { 43 | unsafe { ffi::brpc_controller_get_response_attachment(self.inner) } 44 | } 45 | } 46 | 47 | impl Drop for Controller { 48 | fn drop(&mut self) { 49 | unsafe { 50 | ffi::brpc_controller_destroy(self.inner); 51 | } 52 | } 53 | } 54 | 55 | impl Default for Controller { 56 | fn default() -> Self { 57 | Self::new() 58 | } 59 | } 60 | 61 | #[cfg(test)] 62 | mod tests { 63 | use super::*; 64 | use std::ptr; 65 | 66 | #[test] 67 | fn controller_new() { 68 | let cntl = Controller::new(); 69 | assert_ne!(cntl.inner, ptr::null_mut()); 70 | } 71 | 72 | #[test] 73 | fn controller_failed() { 74 | let cntl = Controller::new(); 75 | assert_eq!(false, cntl.failed()); 76 | } 77 | 78 | #[test] 79 | fn controller_error_code() { 80 | let cntl = Controller::new(); 81 | assert_eq!(BrpcError::NOERROR, cntl.error()); 82 | } 83 | 84 | #[test] 85 | fn controller_get_request_attachment() { 86 | let cntl = Controller::new(); 87 | assert_ne!(cntl.request_attachment(), ptr::null_mut()); 88 | } 89 | 90 | #[test] 91 | fn controller_get_response_attachment() { 92 | let cntl = Controller::new(); 93 | assert_ne!(cntl.response_attachment(), ptr::null_mut()); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | #[repr(C)] 15 | #[derive(PartialEq, Clone, Debug)] 16 | #[allow(dead_code)] 17 | pub enum BrpcError { 18 | NOERROR = 0, // No error_code 19 | 20 | ENOSERVICE = 1001, // Service not found 21 | ENOMETHOD = 1002, // Method not found 22 | EREQUEST = 1003, // Bad Request 23 | ERPCAUTH = 1004, // Unauthorized, can't be called EAUTH 24 | // directly which is defined in MACOSX 25 | ETOOMANYFAILS = 1005, // Too many sub calls failed 26 | EPCHANFINISH = 1006, // [Internal] ParallelChannel finished 27 | EBACKUPREQUEST = 1007, // Sending backup request 28 | ERPCTIMEDOUT = 1008, // RPC call is timed out 29 | EFAILEDSOCKET = 1009, // Broken socket 30 | EHTTP = 1010, // Bad http call 31 | EOVERCROWDED = 1011, // The server is overcrowded 32 | ERTMPPUBLISHABLE = 1012, // RtmpRetryingClientStream is publishable 33 | ERTMPCREATESTREAM = 1013, // createStream was rejected by the RTMP server 34 | EEOF = 1014, // Got EOF 35 | EUNUSED = 1015, // The socket was not needed 36 | ESSL = 1016, // SSL related error 37 | EH2RUNOUTSTREAMS = 1017, // The H2 socket was run out of streams 38 | EREJECT = 1018, // The Request is rejected 39 | 40 | // Errno caused by server 41 | EINTERNAL = 2001, // Internal Server Error 42 | ERESPONSE = 2002, // Bad Response 43 | ELOGOFF = 2003, // Server is stopping 44 | ELIMIT = 2004, // Reached server's limit on resources 45 | ECLOSE = 2005, // Close socket initiatively 46 | EITP = 2006, // Failed Itp response 47 | 48 | // Errno caused by brpc-rs 49 | ESERIALIZE = 3001, // Prost serialization error 50 | EDESERIALIZE = 3002, // Prost deserialization error 51 | EFFI = 3003, // FFI error 52 | 53 | UNKNOWN = 0xffff, // Unknown error, 54 | } 55 | 56 | #[doc(hidden)] 57 | impl From for BrpcError { 58 | fn from(e: i32) -> BrpcError { 59 | match e { 60 | 0 => BrpcError::NOERROR, 61 | 1001 => BrpcError::ENOSERVICE, 62 | 1002 => BrpcError::ENOMETHOD, 63 | 1003 => BrpcError::EREQUEST, 64 | 1004 => BrpcError::ERPCAUTH, 65 | 1005 => BrpcError::ETOOMANYFAILS, 66 | 1006 => BrpcError::EPCHANFINISH, 67 | 1007 => BrpcError::EBACKUPREQUEST, 68 | 1008 => BrpcError::ERPCTIMEDOUT, 69 | 1009 => BrpcError::EFAILEDSOCKET, 70 | 1010 => BrpcError::EHTTP, 71 | 1011 => BrpcError::EOVERCROWDED, 72 | 1012 => BrpcError::ERTMPPUBLISHABLE, 73 | 1013 => BrpcError::ERTMPCREATESTREAM, 74 | 1014 => BrpcError::EEOF, 75 | 1015 => BrpcError::EUNUSED, 76 | 1016 => BrpcError::ESSL, 77 | 1017 => BrpcError::EH2RUNOUTSTREAMS, 78 | 1018 => BrpcError::EREJECT, 79 | 80 | 2001 => BrpcError::EINTERNAL, 81 | 2002 => BrpcError::ERESPONSE, 82 | 2003 => BrpcError::ELOGOFF, 83 | 2004 => BrpcError::ELIMIT, 84 | 2005 => BrpcError::ECLOSE, 85 | 2006 => BrpcError::EITP, 86 | 87 | 3001 => BrpcError::ESERIALIZE, 88 | 3002 => BrpcError::EDESERIALIZE, 89 | 3003 => BrpcError::EFFI, 90 | 91 | _ => BrpcError::UNKNOWN, 92 | } 93 | } 94 | } 95 | 96 | #[cfg(test)] 97 | mod tests { 98 | use super::*; 99 | 100 | #[test] 101 | fn from_i32() { 102 | assert_eq!(BrpcError::from(0), BrpcError::NOERROR); 103 | assert_eq!(BrpcError::from(1001), BrpcError::ENOSERVICE); 104 | assert_eq!(BrpcError::from(1002), BrpcError::ENOMETHOD); 105 | assert_eq!(BrpcError::from(1003), BrpcError::EREQUEST); 106 | assert_eq!(BrpcError::from(1004), BrpcError::ERPCAUTH); 107 | assert_eq!(BrpcError::from(1005), BrpcError::ETOOMANYFAILS); 108 | assert_eq!(BrpcError::from(1006), BrpcError::EPCHANFINISH); 109 | assert_eq!(BrpcError::from(1007), BrpcError::EBACKUPREQUEST); 110 | assert_eq!(BrpcError::from(1008), BrpcError::ERPCTIMEDOUT); 111 | assert_eq!(BrpcError::from(1009), BrpcError::EFAILEDSOCKET); 112 | assert_eq!(BrpcError::from(1010), BrpcError::EHTTP); 113 | assert_eq!(BrpcError::from(1011), BrpcError::EOVERCROWDED); 114 | assert_eq!(BrpcError::from(1012), BrpcError::ERTMPPUBLISHABLE); 115 | assert_eq!(BrpcError::from(1013), BrpcError::ERTMPCREATESTREAM); 116 | assert_eq!(BrpcError::from(1014), BrpcError::EEOF); 117 | assert_eq!(BrpcError::from(1015), BrpcError::EUNUSED); 118 | assert_eq!(BrpcError::from(1016), BrpcError::ESSL); 119 | assert_eq!(BrpcError::from(1017), BrpcError::EH2RUNOUTSTREAMS); 120 | assert_eq!(BrpcError::from(1018), BrpcError::EREJECT); 121 | assert_eq!(BrpcError::from(2001), BrpcError::EINTERNAL); 122 | assert_eq!(BrpcError::from(2002), BrpcError::ERESPONSE); 123 | assert_eq!(BrpcError::from(2003), BrpcError::ELOGOFF); 124 | assert_eq!(BrpcError::from(2004), BrpcError::ELIMIT); 125 | assert_eq!(BrpcError::from(2005), BrpcError::ECLOSE); 126 | assert_eq!(BrpcError::from(2006), BrpcError::EITP); 127 | assert_eq!(BrpcError::from(3001), BrpcError::ESERIALIZE); 128 | assert_eq!(BrpcError::from(3002), BrpcError::EDESERIALIZE); 129 | assert_eq!(BrpcError::from(3003), BrpcError::EFFI); 130 | assert_eq!(BrpcError::from(5678), BrpcError::UNKNOWN); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | //! [Apache BRPC](https://github.com/apache/incubator-brpc) is an 15 | //! industrial-grade RPC framework for building reliable and high-performance 16 | //! services. `brpc-rs` enables BRPC clients and servers implemented in the Rust 17 | //! programming language. 18 | //! 19 | //! ## Status 20 | //! This project is currently a prototype under active development. Many APIs 21 | //! are missing; the provided APIs are not guaranteed to be stable until 1.0. 22 | //! 23 | //! ## Prerequisites 24 | //! These dependencies are required for `brpc-rs` and `brpc-build` to work properly. 25 | //! 26 | //! * Apache BRPC: shared library and headers 27 | //! * libprotobuf-dev 28 | //! * libprotoc-dev 29 | //! * protobuf-compiler 30 | //! * libssl-dev 31 | //! * libgflags-dev 32 | //! * libleveldb-dev 33 | //! 34 | //! ## Quickstart 35 | //! Please refer to the latest 36 | //! [README.md](https://github.com/mesalock-linux/brpc-rs/blob/master/README.md). 37 | 38 | mod channel; 39 | mod controller; 40 | mod server; 41 | 42 | mod error; 43 | pub use error::BrpcError; 44 | 45 | #[doc(hidden)] 46 | pub type BrpcResult = Result; 47 | 48 | // for user code 49 | pub use channel::{Channel, ChannelOptions}; 50 | pub use controller::Controller; 51 | pub use server::{Server, ServerOptions, Service, ServiceOwnership}; 52 | 53 | // for protoc-generated code 54 | #[doc(hidden)] 55 | pub use brpc_sys as internal; 56 | -------------------------------------------------------------------------------- /src/server.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Baidu, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | use crate::{BrpcError, BrpcResult}; 15 | use brpc_sys::ffi::{self, BrpcServer, BrpcServerOptions}; 16 | use libc::{c_int, c_void}; 17 | 18 | #[repr(C)] 19 | /// Represent server's ownership of services. 20 | pub enum ServiceOwnership { 21 | ServerOwnsService = 0, 22 | ServerDoesntOwnService = 1, 23 | } 24 | 25 | #[doc(hidden)] 26 | pub trait Service { 27 | fn get_service_ptr(&self) -> *mut c_void; 28 | } 29 | 30 | /// A `Server` provides a BRPC server where multiple BRPC services can run. 31 | pub struct Server { 32 | inner: *mut BrpcServer, // brpc_server_t in ffi.cpp 33 | } 34 | 35 | impl Server { 36 | /// Create a new `Server` 37 | pub fn new() -> Self { 38 | Server { 39 | inner: unsafe { ffi::brpc_server_new() }, 40 | } 41 | } 42 | 43 | /// Add a `Service`. `ownership` represents server's ownership of services. 44 | /// If `ownership` is `SERVER_OWNS_SERVICE`, server deletes the service at 45 | /// destruction. To prevent the deletion, set ownership to 46 | /// `SERVER_DOESNT_OWN_SERVICE`. 47 | pub fn add_service( 48 | &mut self, 49 | service: &T, 50 | ownership: ServiceOwnership, 51 | ) -> BrpcResult<()> { 52 | let ret = unsafe { 53 | ffi::brpc_server_add_service(self.inner, service.get_service_ptr(), ownership as c_int) 54 | }; 55 | if ret == 0 { 56 | Ok(()) 57 | } else { 58 | Err(BrpcError::EINTERNAL) 59 | } 60 | } 61 | 62 | /// Config a `Server` with the provided TCP port and `ServerOptions`. 63 | pub fn start(&mut self, port: u16, opt: &ServerOptions) -> BrpcResult<()> { 64 | let ret = unsafe { ffi::brpc_server_start(self.inner, i32::from(port), opt.inner) }; 65 | if ret == 0 { 66 | Ok(()) 67 | } else { 68 | Err(BrpcError::EINTERNAL) 69 | } 70 | } 71 | 72 | /// Run a `Server` until asked to quit (e.g CTRL-C). 73 | pub fn run(&mut self) { 74 | unsafe { ffi::brpc_server_run_until_asked_to_quit(self.inner) }; 75 | } 76 | } 77 | 78 | impl Drop for Server { 79 | fn drop(&mut self) { 80 | unsafe { 81 | ffi::brpc_server_destroy(self.inner); 82 | } 83 | } 84 | } 85 | 86 | impl Default for Server { 87 | fn default() -> Self { 88 | Self::new() 89 | } 90 | } 91 | 92 | /// Options for a `Server` 93 | pub struct ServerOptions { 94 | #[doc(hidden)] 95 | pub(crate) inner: *mut BrpcServerOptions, // brpc_server_options_t in ffi.cpp 96 | } 97 | 98 | impl ServerOptions { 99 | /// Make a `ServerOptions` with default values. 100 | pub fn new() -> Self { 101 | ServerOptions { 102 | inner: unsafe { ffi::brpc_server_options_new() }, 103 | } 104 | } 105 | 106 | /// Notify user when there's no data for at least `idle_timeout_ms` 107 | /// milliseconds. The default value is -1. 108 | pub fn set_idle_timeout_ms(&mut self, timeout: i32) { 109 | unsafe { ffi::brpc_server_options_set_idle_timeout_ms(self.inner, timeout as c_int) } 110 | } 111 | } 112 | 113 | impl Drop for ServerOptions { 114 | fn drop(&mut self) { 115 | unsafe { 116 | ffi::brpc_server_options_destroy(self.inner); 117 | } 118 | } 119 | } 120 | 121 | impl Default for ServerOptions { 122 | fn default() -> Self { 123 | Self::new() 124 | } 125 | } 126 | 127 | #[cfg(test)] 128 | mod tests { 129 | use super::*; 130 | use std::ptr; 131 | 132 | struct NullService {} 133 | impl Service for NullService { 134 | fn get_service_ptr(&self) -> *mut c_void { 135 | ptr::null_mut() 136 | } 137 | } 138 | 139 | #[test] 140 | fn server_options_new() { 141 | let opt = ServerOptions::new(); 142 | assert_ne!(opt.inner, ptr::null_mut()); 143 | } 144 | 145 | #[test] 146 | fn server_options_set_idle_timeout_ms() { 147 | let mut opt = ServerOptions::new(); 148 | opt.set_idle_timeout_ms(0); 149 | } 150 | 151 | #[test] 152 | fn server_new() { 153 | let server = Server::new(); 154 | assert_ne!(server.inner, ptr::null_mut()); 155 | } 156 | 157 | #[test] 158 | fn server_add_null_service() { 159 | let service = NullService {}; 160 | let mut server = Server::new(); 161 | let ret = server.add_service(&service, ServiceOwnership::ServerDoesntOwnService); 162 | assert_eq!(false, ret.is_ok()); // NullService must fail to add 163 | } 164 | 165 | #[test] 166 | fn server_start_null_service() { 167 | let service = NullService {}; 168 | let mut server = Server::new(); 169 | let _ = server.add_service(&service, ServiceOwnership::ServerDoesntOwnService); 170 | 171 | let opt = ServerOptions::new(); 172 | let ret = server.start(50000, &opt); 173 | assert_eq!(true, ret.is_ok()); // NullService must fail to add 174 | } 175 | } 176 | --------------------------------------------------------------------------------