Skip to content
This repository was archived by the owner on Jul 6, 2024. It is now read-only.

Commit 69c3721

Browse files
committed
refactor: Sync and Async Code Re-use
Attempt to abstract sync and async requests by writing an abstraction over the flow of data that encompasses a request. The requests fill out a `RequestData` instance which then gets used as the source of future transformations. The login chain is a prime example of this. We define a series of steps that are expressed as a series of sequences which will finally produce the expected outcome. The Session now produces a `Sequence` implementation which needs to be driven by a client. Unfortunately, due to lack of Async Trait support, the async implementation is not as efficient as it could be. Attempt was made to test the code on nightly, but ran into this bug rust-lang/rust#113108
1 parent ad71f48 commit 69c3721

28 files changed

+1277
-924
lines changed

Cargo.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "proton-api-rs"
33
authors = ["Leander Beernaert <[email protected]>"]
4-
version = "0.10.2"
4+
version = "0.11.0"
55
edition = "2021"
66
license = "AGPL-3.0-only"
77
description = "Unofficial implemention of proton REST API in rust"
@@ -30,6 +30,7 @@ ureq = {version="2.6", optional=true, features=["socks-proxy", "socks"]}
3030
default = []
3131
http-ureq = ["dep:ureq"]
3232
http-reqwest = ["dep:reqwest"]
33+
async-traits =[]
3334

3435
[dependencies.reqwest]
3536
version = "0.11"
@@ -40,7 +41,6 @@ optional = true
4041
[dev-dependencies]
4142
env_logger = "0.10"
4243
tokio = {version ="1", features = ["full"]}
43-
httpmock = "0.6"
4444
go-gpa-server = {path= "go-gpa-server"}
4545

4646
[[example]]
@@ -53,5 +53,5 @@ required-features = ["http-ureq"]
5353

5454
[[test]]
5555
name = "session"
56-
required-features = ["http-ureq"]
56+
required-features = ["http-ureq", "http-reqwest"]
5757

examples/user_id.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
1-
use proton_api_rs::{http, ping_async};
1+
use proton_api_rs::domain::SecretString;
2+
use proton_api_rs::http::Sequence;
3+
use proton_api_rs::{http, ping};
24
use proton_api_rs::{Session, SessionType};
35
pub use tokio;
46
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
57

68
#[tokio::main(worker_threads = 1)]
79
async fn main() {
810
let user_email = std::env::var("PAPI_USER_EMAIL").unwrap();
9-
let user_password = std::env::var("PAPI_USER_PASSWORD").unwrap();
11+
let user_password = SecretString::new(std::env::var("PAPI_USER_PASSWORD").unwrap());
1012
let app_version = std::env::var("PAPI_APP_VERSION").unwrap();
1113

1214
let client = http::ClientBuilder::new()
1315
.app_version(&app_version)
1416
.build::<http::reqwest_client::ReqwestClient>()
1517
.unwrap();
1618

17-
ping_async(&client).await.unwrap();
19+
ping().do_async(&client).await.unwrap();
1820

19-
let session = match Session::login_async(&client, &user_email, &user_password, None, None)
21+
let session = match Session::login(&user_email, &user_password, None)
22+
.do_async(&client)
2023
.await
2124
.unwrap()
2225
{
2326
SessionType::Authenticated(c) => c,
2427

25-
SessionType::AwaitingTotp(mut t) => {
28+
SessionType::AwaitingTotp(t) => {
2629
let mut stdout = tokio::io::stdout();
2730
let mut line_reader = tokio::io::BufReader::new(tokio::io::stdin()).lines();
2831
let session = {
@@ -41,13 +44,12 @@ async fn main() {
4144

4245
let totp = line.trim_end_matches('\n');
4346

44-
match t.submit_totp_async(&client, totp).await {
47+
match t.submit_totp(totp).do_async(&client).await {
4548
Ok(ac) => {
4649
session = Some(ac);
4750
break;
4851
}
49-
Err((et, e)) => {
50-
t = et;
52+
Err(e) => {
5153
eprintln!("Failed to submit totp: {e}");
5254
continue;
5355
}
@@ -65,8 +67,8 @@ async fn main() {
6567
}
6668
};
6769

68-
let user = session.get_user_async(&client).await.unwrap();
70+
let user = session.get_user().do_async(&client).await.unwrap();
6971
println!("User ID is {}", user.id);
7072

71-
session.logout_async(&client).await.unwrap();
73+
session.logout().do_async(&client).await.unwrap();
7274
}

examples/user_id_sync.rs

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use proton_api_rs::clientv2::{ping, SessionType};
2+
use proton_api_rs::domain::SecretString;
3+
use proton_api_rs::http::Sequence;
24
use proton_api_rs::{http, Session};
35
use std::io::{BufRead, Write};
46

57
fn main() {
68
env_logger::init();
79

810
let user_email = std::env::var("PAPI_USER_EMAIL").unwrap();
9-
let user_password = std::env::var("PAPI_USER_PASSWORD").unwrap();
11+
let user_password = SecretString::new(std::env::var("PAPI_USER_PASSWORD").unwrap());
1012
let app_version = std::env::var("PAPI_APP_VERSION").unwrap();
1113

1214
let client = http::ClientBuilder::new()
@@ -15,12 +17,12 @@ fn main() {
1517
.build::<http::ureq_client::UReqClient>()
1618
.unwrap();
1719

18-
ping(&client).unwrap();
20+
ping().do_sync(&client).unwrap();
1921

20-
let login_result = Session::login(&client, &user_email, &user_password, None, None);
22+
let login_result = Session::login(&user_email, &user_password, None).do_sync(&client);
2123
let session = match login_result.unwrap() {
2224
SessionType::Authenticated(s) => s,
23-
SessionType::AwaitingTotp(mut t) => {
25+
SessionType::AwaitingTotp(t) => {
2426
let mut line_reader = std::io::BufReader::new(std::io::stdin());
2527
let session = {
2628
let mut session = None;
@@ -38,13 +40,12 @@ fn main() {
3840

3941
let totp = line.trim_end_matches('\n');
4042

41-
match t.submit_totp(&client, totp) {
43+
match t.submit_totp(totp).do_sync(&client) {
4244
Ok(ac) => {
4345
session = Some(ac);
4446
break;
4547
}
46-
Err((et, e)) => {
47-
t = et;
48+
Err(e) => {
4849
eprintln!("Failed to submit totp: {e}");
4950
continue;
5051
}
@@ -62,8 +63,8 @@ fn main() {
6263
}
6364
};
6465

65-
let user = session.get_user(&client).unwrap();
66+
let user = session.get_user().do_sync(&client).unwrap();
6667
println!("User ID is {}", user.id);
6768

68-
session.logout(&client).unwrap();
69+
session.logout().do_sync(&client).unwrap();
6970
}

go-gpa-server/build.rs

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ fn target_path_for_go_lib() -> (PathBuf, PathBuf) {
2727
fn build_go_lib(lib_path: &Path) {
2828
let mut command = Command::new("go");
2929

30+
#[cfg(any(target_os= "linux",target_os = "android"))]
3031
command.env("CGO_LDFLAGS", "-Wl,--build-id=none");
3132
command.arg("build");
3233
command.arg("-ldflags=-buildid=");

go-gpa-server/go/lib.go

+13
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ typedef const char cchar_t;
99
import "C"
1010
import (
1111
"sync"
12+
"time"
1213
"unsafe"
1314

1415
"github.com/ProtonMail/go-proton-api/server"
@@ -104,6 +105,18 @@ func gpaCreateUser(h int, cuser *C.cchar_t, cpassword *C.cchar_t, outUserID **C.
104105
return 0
105106
}
106107

108+
//export gpaSetAuthLife
109+
func gpaSetAuthLife(h int, seconds int) int {
110+
srv := alloc.resolve(h)
111+
if srv == nil {
112+
return -1
113+
}
114+
115+
srv.SetAuthLife(time.Duration(seconds) * time.Second)
116+
117+
return 0
118+
}
119+
107120
//export CStrFree
108121
func CStrFree(ptr *C.char) {
109122
C.free(unsafe.Pointer(ptr))

go-gpa-server/src/lib.rs

+10
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ impl Server {
6868
))
6969
}
7070
}
71+
72+
pub fn set_auth_timeout(&self, duration: std::time::Duration) -> Result<()> {
73+
unsafe {
74+
if go::gpaSetAuthLife(self.0, duration.as_secs() as i64) < 0 {
75+
return Err("Failed to set auth timeout".to_string());
76+
}
77+
78+
Ok(())
79+
}
80+
}
7181
}
7282

7383
impl Drop for Server {

go-srp/build.rs

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ fn target_path_for_go_lib(platform: Platform) -> (PathBuf, PathBuf) {
7474
fn build_go_lib(lib_path: &Path, platform: Platform) {
7575
let mut command = Command::new("go");
7676

77+
#[cfg(any(target_os= "linux",target_os = "android"))]
7778
command.env("CGO_LDFLAGS", "-Wl,--build-id=none");
7879
match platform {
7980
Platform::Desktop => {}

src/clientv2/client.rs

+5-25
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,10 @@
1-
use crate::http;
2-
use crate::http::Request;
1+
use crate::http::{Request, RequestDesc};
32
use crate::requests::{CaptchaRequest, Ping};
43

5-
pub fn ping<T: http::ClientSync>(client: &T) -> Result<(), http::Error> {
6-
Ping.execute_sync::<T>(client, &http::DefaultRequestFactory {})
4+
pub fn ping() -> impl Request {
5+
Ping.to_request()
76
}
87

9-
pub async fn ping_async<T: http::ClientAsync>(client: &T) -> Result<(), http::Error> {
10-
Ping.execute_async::<T>(client, &http::DefaultRequestFactory {})
11-
.await
12-
}
13-
14-
pub fn captcha_get<T: http::ClientSync>(
15-
client: &T,
16-
token: &str,
17-
force_web: bool,
18-
) -> Result<String, http::Error> {
19-
CaptchaRequest::new(token, force_web).execute_sync(client, &http::DefaultRequestFactory {})
20-
}
21-
22-
pub async fn captcha_get_async<T: http::ClientAsync>(
23-
client: &T,
24-
token: &str,
25-
force_web: bool,
26-
) -> Result<String, http::Error> {
27-
CaptchaRequest::new(token, force_web)
28-
.execute_async(client, &http::DefaultRequestFactory {})
29-
.await
8+
pub fn captcha_get(token: &str, force_web: bool) -> impl Request {
9+
CaptchaRequest::new(token, force_web).to_request()
3010
}

src/clientv2/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
mod client;
2-
mod request_repeater;
32
mod session;
43
mod totp;
54

65
pub use client::*;
7-
pub use request_repeater::*;
86
pub use session::*;
97
pub use totp::*;

0 commit comments

Comments
 (0)