Refactored protobuf to own crate

This commit is contained in:
adel-signal 2024-06-21 13:09:41 -07:00 committed by GitHub
parent 49b4b8a16f
commit ee5b89d160
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 210 additions and 300 deletions

19
.dockerignore Normal file
View file

@ -0,0 +1,19 @@
out/
out-arm/
cscope.*
.DS_Store
.dir-locals.el
.cargo
.gradle/
*.code-workspace
**/.vscode
.idea
src/webrtc*
src/jar.list
.project
.classpath
org.eclipse.buildship.core.prefs
local.properties
*.tar.gz
.cargo
.spr.yml

View file

@ -61,6 +61,8 @@ jobs:
run: cargo clippy --package call_sim -- -D warnings
- name: Clippy (mrp)
run: cargo clippy --package mrp -- -D warnings
- name: Clippy (protobuf)
run: cargo clippy --package protobuf --features call_sim -- -D warnings
- name: Clippy (signaling_server)
run: cargo clippy -- -D warnings
working-directory: call_sim/docker/signaling_server

1
.gitignore vendored
View file

@ -17,3 +17,4 @@ local.properties
*.tar.gz
.cargo
target
.spr.yml

11
Cargo.lock generated
View file

@ -457,12 +457,12 @@ dependencies = [
"itertools 0.12.1",
"plotly",
"prost",
"protobuf",
"regex",
"relative-path",
"serde_json",
"tokio",
"tonic",
"tonic-build",
"tower",
]
@ -1904,6 +1904,14 @@ dependencies = [
"prost",
]
[[package]]
name = "protobuf"
version = "2.44.0"
dependencies = [
"prost-build",
"tonic-build",
]
[[package]]
name = "quote"
version = "1.0.35"
@ -2058,6 +2066,7 @@ dependencies = [
"num_enum 0.7.2",
"prost",
"prost-build",
"protobuf",
"rand",
"rand_chacha",
"regex-aot",

View file

@ -8,6 +8,7 @@ resolver = "2"
members = [
"call_sim",
"mrp",
"protobuf",
"src/rust",
]

View file

@ -46,7 +46,7 @@
<h2>Overview of licenses:</h2>
<ul class="licenses-overview">
<li><a href="#MIT">MIT License</a> (135)</li>
<li><a href="#AGPL-3.0">GNU Affero General Public License v3.0</a> (10)</li>
<li><a href="#AGPL-3.0">GNU Affero General Public License v3.0</a> (11)</li>
<li><a href="#Apache-2.0">Apache License 2.0</a> (5)</li>
<li><a href="#BSD-3-Clause">BSD 3-Clause &quot;New&quot; or &quot;Revised&quot; License</a> (4)</li>
<li><a href="#ISC">ISC License</a> (1)</li>
@ -735,6 +735,7 @@ For more information on this, and how to apply and follow the GNU AGPL, see
<li><a href="https://crates.io/crates/libsignal-core">libsignal-core 0.1.0</a></li>
<li><a href="https://crates.io/crates/mrp">mrp 2.44.0</a></li>
<li><a href="https://github.com/signalapp/partial-default">partial-default-derive 0.1.0</a></li>
<li><a href="https://crates.io/crates/protobuf">protobuf 2.44.0</a></li>
<li><a href="https://crates.io/crates/regex-aot">regex-aot 0.1.0</a></li>
<li><a href="https://crates.io/crates/ringrtc">ringrtc 2.44.0</a></li>
</ul>

View file

@ -669,7 +669,7 @@ For more information on this, and how to apply and follow the GNU AGPL, see
```
## libsignal-core 0.1.0, mrp 2.44.0, partial-default-derive 0.1.0, regex-aot 0.1.0, ringrtc 2.44.0
## libsignal-core 0.1.0, mrp 2.44.0, partial-default-derive 0.1.0, protobuf 2.44.0, regex-aot 0.1.0, ringrtc 2.44.0
```
GNU AFFERO GENERAL PUBLIC LICENSE

View file

@ -924,7 +924,7 @@ You should also get your employer (if you work as a programmer) or school, if an
<key>License</key>
<string>GNU Affero General Public License v3.0</string>
<key>Title</key>
<string>libsignal-core 0.1.0, mrp 2.44.0, partial-default-derive 0.1.0, regex-aot 0.1.0, ringrtc 2.44.0</string>
<string>libsignal-core 0.1.0, mrp 2.44.0, partial-default-derive 0.1.0, protobuf 2.44.0, regex-aot 0.1.0, ringrtc 2.44.0</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>

View file

@ -13,6 +13,7 @@ license = "AGPL-3.0-only"
[dependencies]
anyhow = "1.0.81"
bollard = "0.15.0"
protobuf = { path = "../protobuf", features = ["call_sim"] }
chrono = "0.4.35"
clap = { version = "4.5.3", features = ["derive"] }
futures-core = "0.3.30"
@ -27,6 +28,3 @@ serde_json = "1.0.114"
tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros", "time", "fs", "process"] }
tonic = "0.11.0"
tower = "0.4.13"
[build-dependencies]
tonic-build = "0.11.0"

View file

@ -1,11 +0,0 @@
//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
fn main() {
tonic_build::configure()
.build_server(false)
.compile(&["proto/call_sim.proto"], &["proto"])
.expect("Service protos are valid");
}

View file

@ -730,6 +730,14 @@ dependencies = [
"prost",
]
[[package]]
name = "protobuf"
version = "2.44.0"
dependencies = [
"prost-build",
"tonic-build",
]
[[package]]
name = "quote"
version = "1.0.35"
@ -879,13 +887,13 @@ dependencies = [
"futures-util",
"log",
"prost",
"protobuf",
"rand",
"serde",
"serde_json",
"tokio",
"tokio-stream",
"tonic",
"tonic-build",
]
[[package]]

View file

@ -14,6 +14,7 @@ license = "AGPL-3.0-only"
[dependencies]
async-stream = "0.3.5"
protobuf = { path = "../../../protobuf", features = ["call_sim"] }
env_logger = "0.11.3"
futures-core = "0.3.30"
futures-util = "0.3.30"
@ -25,6 +26,3 @@ serde_json = "1.0.114"
tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros", "signal", "sync", "time"] }
tokio-stream = "0.1.15"
tonic = "0.11.0"
[build-dependencies]
tonic-build = "0.11.0"

View file

@ -1,3 +1,5 @@
# syntax=docker/dockerfile:1.7-labs
#
# Copyright 2023 Signal Messenger, LLC
# SPDX-License-Identifier: AGPL-3.0-only
@ -11,26 +13,38 @@ RUN apt-get update \
&& apt-get install -y --no-install-recommends --no-install-suggests curl build-essential ca-certificates protobuf-compiler \
&& update-ca-certificates
SHELL [ "/bin/bash", "-c"]
# Install Rust.
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
WORKDIR /usr/src
# Create a stub version of the project to cache dependencies.
RUN cargo new signaling_server
WORKDIR /usr/src/signaling_server
COPY Cargo.toml Cargo.lock ./
# Do the initial stub build.
RUN cargo build --release
# Since protobuf is a workspace package, we have to stub the entire workspace
WORKDIR /usr/src/ringrtc
COPY --parents ./**/Cargo.toml ./**/Cargo.lock ./**/main.rs ./**/lib.rs ./**/bin/*.rs ./
RUN shopt -s globstar; shopt -s nullglob; \
for filename in ./**/main.rs; do \
truncate -s 0 "$filename"; \
done; \
for filename in ./**/lib.rs; do \
truncate -s 0 "$filename"; \
done; \
for filename in ./**/bin/*.rs; do \
truncate -s 0 "$filename"; \
done
# stub the signaling server, but remove the actual code first
RUN cd call_sim/docker && \
rm -rf signaling_server && \
cargo new signaling_server
COPY call_sim/docker/signaling_server/Cargo.* ./call_sim/docker/signaling_server/
RUN cd call_sim/docker/signaling_server && cargo build --release
# Copy the source and build the project normally.
COPY . .
COPY protobuf protobuf
RUN cd protobuf && cargo build --release
RUN cargo build --release
COPY call_sim/docker/signaling_server call_sim/docker/signaling_server
RUN cd call_sim/docker/signaling_server && cargo build --release
FROM ubuntu:22.04 AS run-stage
@ -38,7 +52,7 @@ RUN apt-get update \
&& apt upgrade -y \
&& rm -rf /var/lib/apt/lists/*
COPY --from=build-stage /usr/src/signaling_server/target/release/signaling_server /usr/local/bin/
COPY --from=build-stage /usr/src/ringrtc/call_sim/docker/signaling_server/target/release/signaling_server /usr/local/bin/
USER nobody:nogroup

View file

@ -1,9 +0,0 @@
//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
fn main() {
tonic_build::compile_protos("proto/call_sim.proto")
.unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e));
}

View file

@ -14,9 +14,10 @@ use tonic::{transport::Server, Request, Response, Status};
pub mod calling {
#![allow(clippy::derive_partial_eq_without_eq, clippy::redundant_async_block)]
tonic::include_proto!("calling");
protobuf::include_call_sim_proto!();
}
use calling::signaling_relay_server::{SignalingRelay, SignalingRelayServer};
use calling::{Empty, Registration, RelayMessage};

View file

@ -1,110 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
package calling;
message Empty {}
message Registration {
// From, the client that is registering.
string client = 1;
}
message CallMessage {
message Offer {
enum Type {
OFFER_AUDIO_CALL = 0;
OFFER_VIDEO_CALL = 1;
}
uint64 id = 1;
Type type = 2;
bytes opaque = 3;
}
message Answer {
uint64 id = 1;
bytes opaque = 2;
}
message IceUpdate {
uint64 id = 1;
bytes opaque = 2;
}
message Busy {
uint64 id = 1;
}
message Hangup {
enum Type {
HANGUP_NORMAL = 0;
HANGUP_ACCEPTED = 1;
HANGUP_DECLINED = 2;
HANGUP_BUSY = 3;
HANGUP_NEED_PERMISSION = 4;
}
uint64 id = 1;
Type type = 2;
uint32 deviceId = 3;
}
Offer offer = 1;
Answer answer = 2;
repeated IceUpdate iceUpdate = 3;
Busy busy = 4;
Hangup hangup = 5;
}
// The RelayMessage is essentially the message envelope, containing the actual
// call message along with other meta data.
message RelayMessage {
// From, the client that is sending the message.
string client = 1;
// The deviceId of the client that is sending the message.
uint32 deviceId = 2;
// The actual message we are passing through.
CallMessage callMessage = 3;
}
service SignalingRelay {
// The client will register with the server, and this is a "server-side streaming RPC".
rpc Register (Registration) returns (stream RelayMessage);
// After registering, the client can send messages, and this is a "Simple RPC".
rpc Send (RelayMessage) returns (Empty);
}
message CommandMessage {
enum Command {
START_AS_CALLER = 0;
START_AS_CALLEE = 1;
STOP = 2;
}
// To, the client that should receive the message.
string client = 1;
// The command to send.
Command command = 2;
}
message Event {
// The number of clients that are ready for the test. Increments with ready, decrements with done.
int32 readyCount = 1;
}
service TestManagement {
// Each test client will let the server know when it is ready (generally after it registers with the
// relay server, and thus be able to receive commands).
rpc Ready (Registration) returns (stream CommandMessage);
// The client can let the server (and test manager) know it is done (after stopping).
rpc Done (Registration) returns (Empty);
// A simplistic notification scheme to send updates to the test manager.
rpc Notification (Empty) returns (stream Event);
// The controller can send messages to specific clients in order to instruct them to do things.
rpc SendCommand (CommandMessage) returns (Empty);
}

View file

@ -43,8 +43,15 @@ pub async fn build_images() -> Result<()> {
println!("signaling-server:");
stdout().flush().await?;
let _ = Command::new("docker")
.current_dir("call_sim/docker/signaling_server")
.args(["build", "-t", "signaling-server", "-q", "."])
.args([
"build",
"-t",
"signaling-server",
"-q",
"-f",
"call_sim/docker/signaling_server/Dockerfile",
".",
])
.spawn()?
.wait()
.await?;

View file

@ -3,12 +3,10 @@
// SPDX-License-Identifier: AGPL-3.0-only
//
// Modules for the testing service, from protobufs compiled by tonic.
pub mod calling {
#![allow(clippy::derive_partial_eq_without_eq, clippy::enum_variant_names)]
tonic::include_proto!("calling");
protobuf::include_call_sim_proto!();
}
use anyhow::Result;
use calling::{
command_message::Command, test_management_client::TestManagementClient, CommandMessage, Empty,

27
protobuf/Cargo.toml Normal file
View file

@ -0,0 +1,27 @@
#
# Copyright 2024 Signal Messenger, LLC
# SPDX-License-Identifier: AGPL-3.0-only
#
[package]
name = "protobuf"
edition = "2021"
version.workspace = true
authors.workspace = true
license = "AGPL-3.0-only"
[lib]
name = "protobuf"
path = "src/lib.rs"
proc-macro = true
[features]
default = ["signaling"]
signaling = []
call_sim = ["signaling"]
[dependencies]
[build-dependencies]
prost-build = { version = "0.12.3" }
tonic-build = "0.11.0"

32
protobuf/build.rs Normal file
View file

@ -0,0 +1,32 @@
//
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
fn main() {
// Explicitly state that by depending on build.rs itself, as recommended.
println!("cargo:rerun-if-changed=build.rs");
if cfg!(feature = "signaling") {
let protos = [
"protobuf/group_call.proto",
"protobuf/rtp_data.proto",
"protobuf/signaling.proto",
];
prost_build::compile_protos(&protos, &["protobuf"]).expect("Protobufs are valid");
for proto in &protos {
println!("cargo:rerun-if-changed={}", proto);
}
}
if cfg!(feature = "call_sim") {
tonic_build::configure()
.build_client(true)
.build_server(true)
.build_transport(true)
.compile(&["protobuf/call_sim.proto"], &["protobuf"])
.expect("call_sim service protobufs are valid")
}
}

56
protobuf/src/lib.rs Normal file
View file

@ -0,0 +1,56 @@
//
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
use proc_macro::TokenStream;
/**
* This crate is a wrapper around prost and tonic, removing the need for copies of protobuf files
* and protobuf builds in build.rs. Note that dependent crates still need to add prost and tonic
* to their dependencies.
*
* Just like prost/tonic, we expose a include_xyz_proto macros so protobuf types are local to a
* crate. This allows crates to define traits on the types.
*
* We "curry" the protobuf code here. Since macros don't have eager evaluation, nested macros would
* be evaluated at the wrong point in compilation,
* i.e. `include!(concat!(env!("OUT_DIR"), "/group_call.rs"))` would look in the wrong OUT_DIR. So
* we save the proto code here during this package's compilation and emit it using a proc macro.
*/
#[cfg(feature = "signaling")]
const GROUP_PROTO: &str = include_str!(concat!(env!("OUT_DIR"), "/group_call.rs"));
#[cfg(feature = "signaling")]
const RTP_DATA_PROTO: &str = include_str!(concat!(env!("OUT_DIR"), "/rtp_data.rs"));
#[cfg(feature = "signaling")]
const SIGNALING_PROTO: &str = include_str!(concat!(env!("OUT_DIR"), "/signaling.rs"));
#[cfg(feature = "call_sim")]
const CALL_SIM_PROTO: &str = include_str!(concat!(env!("OUT_DIR"), "/calling.rs"));
#[cfg(feature = "signaling")]
#[proc_macro]
pub fn include_groupcall_proto(_input: TokenStream) -> TokenStream {
GROUP_PROTO.parse().unwrap()
}
#[cfg(feature = "signaling")]
#[proc_macro]
pub fn include_rtp_proto(_input: TokenStream) -> TokenStream {
RTP_DATA_PROTO.parse().unwrap()
}
#[cfg(feature = "signaling")]
#[proc_macro]
pub fn include_signaling_proto(_input: TokenStream) -> TokenStream {
SIGNALING_PROTO.parse().unwrap()
}
#[cfg(feature = "call_sim")]
#[proc_macro]
pub fn include_call_sim_proto(_input: TokenStream) -> TokenStream {
CALL_SIM_PROTO.parse().unwrap()
}

View file

@ -89,6 +89,7 @@ tokio = { version = "1.36.0", optional = true, features = ["rt-multi-thread"] }
tonic = { version = "0.11.0", optional = true }
tower = { version = "0.4.13", optional = true }
call_protobuf = { path = "../../protobuf", package = "protobuf"}
mrp = { path = "../../mrp" }
[target.'cfg(not(target_os="android"))'.dependencies]
@ -105,7 +106,7 @@ simnet = ["injectable_network"]
injectable_network = []
http = ["ureq", "rustls"]
check-all = ["electron", "jni"]
call_sim = ["native", "injectable_network", "bitvec", "chrono", "clap", "fern", "tokio", "tonic", "tower"]
call_sim = ["call_protobuf/call_sim", "native", "injectable_network", "bitvec", "chrono", "clap", "fern", "tokio", "tonic", "tower"]
[[test]]
name = "incoming"

View file

@ -8,20 +8,6 @@ use std::env::{self, VarError};
use std::fs;
use std::process::Command;
fn build_protos() {
let protos = [
"protobuf/group_call.proto",
"protobuf/rtp_data.proto",
"protobuf/signaling.proto",
];
prost_build::compile_protos(&protos, &["protobuf"]).expect("Protobufs are valid");
for proto in &protos {
println!("cargo:rerun-if-changed={}", proto);
}
}
// corresponds to PROJECT_DIR in bin/env.sh
fn project_dir() -> String {
format!("{}/../..", env::current_dir().unwrap().display())
@ -68,15 +54,6 @@ fn main() {
// Explicitly state that by depending on build.rs itself, as recommended.
println!("cargo:rerun-if-changed=build.rs");
build_protos();
if cfg!(feature = "call_sim") {
tonic_build::configure()
.build_server(false)
.compile(&["protobuf/call_sim.proto"], &["protobuf"])
.expect("call_sim protobufs are valid")
}
if cfg!(feature = "native") {
let webrtc_dir = if cfg!(feature = "prebuilt_webrtc") {
if let Err(e) = fs::create_dir_all(&out_dir) {

View file

@ -1,110 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
syntax = "proto3";
package calling;
message Empty {}
message Registration {
// From, the client that is registering.
string client = 1;
}
message CallMessage {
message Offer {
enum Type {
OFFER_AUDIO_CALL = 0;
OFFER_VIDEO_CALL = 1;
}
uint64 id = 1;
Type type = 2;
bytes opaque = 3;
}
message Answer {
uint64 id = 1;
bytes opaque = 2;
}
message IceUpdate {
uint64 id = 1;
bytes opaque = 2;
}
message Busy {
uint64 id = 1;
}
message Hangup {
enum Type {
HANGUP_NORMAL = 0;
HANGUP_ACCEPTED = 1;
HANGUP_DECLINED = 2;
HANGUP_BUSY = 3;
HANGUP_NEED_PERMISSION = 4;
}
uint64 id = 1;
Type type = 2;
uint32 deviceId = 3;
}
Offer offer = 1;
Answer answer = 2;
repeated IceUpdate iceUpdate = 3;
Busy busy = 4;
Hangup hangup = 5;
}
// The RelayMessage is essentially the message envelope, containing the actual
// call message along with other meta data.
message RelayMessage {
// From, the client that is sending the message.
string client = 1;
// The deviceId of the client that is sending the message.
uint32 deviceId = 2;
// The actual message we are passing through.
CallMessage callMessage = 3;
}
service SignalingRelay {
// The client will register with the server, and this is a "server-side streaming RPC".
rpc Register (Registration) returns (stream RelayMessage);
// After registering, the client can send messages, and this is a "Simple RPC".
rpc Send (RelayMessage) returns (Empty);
}
message CommandMessage {
enum Command {
START_AS_CALLER = 0;
START_AS_CALLEE = 1;
STOP = 2;
}
// To, the client that should receive the message.
string client = 1;
// The command to send.
Command command = 2;
}
message Event {
// The number of clients that are ready for the test. Increments with ready, decrements with done.
int32 readyCount = 1;
}
service TestManagement {
// Each test client will let the server know when it is ready (generally after it registers with the
// relay server, and thus be able to receive commands).
rpc Ready (Registration) returns (stream CommandMessage);
// The client can let the server (and test manager) know it is done (after stopping).
rpc Done (Registration) returns (Empty);
// A simplistic notification scheme to send updates to the test manager.
rpc Notification (Empty) returns (stream Event);
// The controller can send messages to specific clients in order to instruct them to do things.
rpc SendCommand (CommandMessage) returns (Empty);
}

View file

@ -25,7 +25,7 @@ use crate::{
// Modules for the testing service, from protobufs compiled by tonic.
pub mod calling {
#![allow(clippy::derive_partial_eq_without_eq, clippy::enum_variant_names)]
tonic::include_proto!("calling");
call_protobuf::include_call_sim_proto!();
}
use calling::test_management_client::TestManagementClient;
use calling::{command_message::Command, Registration};

View file

@ -22,7 +22,7 @@ use crate::endpoint::CallEndpoint;
// Modules for the calling service, from protobufs compiled by tonic.
pub mod calling {
#![allow(clippy::derive_partial_eq_without_eq, clippy::enum_variant_names)]
tonic::include_proto!("calling");
call_protobuf::include_call_sim_proto!();
}
use calling::signaling_relay_client::SignalingRelayClient;
use calling::{call_message, CallMessage, Registration, RelayMessage};

View file

@ -6,13 +6,13 @@
#![allow(clippy::derive_partial_eq_without_eq)]
pub mod group_call {
include!(concat!(env!("OUT_DIR"), "/group_call.rs"));
call_protobuf::include_groupcall_proto!();
}
pub mod rtp_data {
include!(concat!(env!("OUT_DIR"), "/rtp_data.rs"));
call_protobuf::include_rtp_proto!();
}
pub mod signaling {
include!(concat!(env!("OUT_DIR"), "/signaling.rs"));
call_protobuf::include_signaling_proto!();
}