Skip to content

Commit d035ba3

Browse files
authored
Merge pull request #56 from betagouv/only_force
remove front keepalive, better timeout, fix empty body ...
2 parents 13948bd + c43baa2 commit d035ba3

File tree

4 files changed

+97
-26
lines changed

4 files changed

+97
-26
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ futures = "0"
1111
futures-core = "0"
1212
actix-web = { version="3", features = ["openssl"] }
1313
actix-rt = "1"
14+
actix-http = "*"
1415
bytes = "0"
1516
docopt = "1"
1617
serde = { version = "1", features = ["derive"] }

src/proxy.rs

+82-23
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use log::error;
1616
use sodiumoxide::crypto::secretstream::xchacha20poly1305::{ABYTES, HEADERBYTES};
1717
use std::time::Duration;
1818

19-
const TIMEOUT_DURATION: Duration = Duration::from_secs(60 * 60);
19+
const CONNECT_TIMEOUT: Duration = Duration::from_secs(1);
20+
const RESPONSE_TIMEOUT: Duration = Duration::from_secs(5);
21+
const UPLOAD_TIMEOUT: Duration = Duration::from_secs(3 * 60);
2022

2123
static FORWARD_REQUEST_HEADERS_TO_REMOVE: [header::HeaderName; 4] = [
2224
// Connection settings (keepalived) must not be resend
@@ -82,7 +84,8 @@ async fn forward(
8284

8385
let mut forwarded_req = client
8486
.request_from(put_url.as_str(), req.head())
85-
.timeout(TIMEOUT_DURATION);
87+
.force_close()
88+
.timeout(UPLOAD_TIMEOUT);
8689

8790
let forward_length: Option<usize> = content_length(req.headers()).map(|content_length| {
8891
if config.noop {
@@ -96,7 +99,7 @@ async fn forward(
9699
forwarded_req.headers_mut().remove(header);
97100
}
98101

99-
let stream_to_send: Box<dyn Stream<Item = _> + Unpin> = if config.noop {
102+
let stream: Box<dyn Stream<Item = _> + Unpin> = if config.noop {
100103
Box::new(payload)
101104
} else {
102105
Box::new(Encoder::new(
@@ -106,23 +109,27 @@ async fn forward(
106109
))
107110
};
108111

109-
let mut res = if let Some(length) = forward_length {
112+
let req_copy = req.clone();
113+
let stream_to_send = stream.map_err(move |e| {
114+
error!("forward error with stream {:?}, {:?}", e, req_copy);
115+
Error::from(e)
116+
});
117+
118+
let res_e = if let Some(length) = forward_length {
110119
forwarded_req
111-
.send_body(SizedStream::new(
112-
length as u64,
113-
stream_to_send.map_err(Error::from),
114-
))
120+
.send_body(SizedStream::new(length as u64, stream_to_send))
115121
.await
116-
.map_err(Error::from)?
117122
} else {
118-
forwarded_req
119-
.send_stream(stream_to_send.map_err(Error::from))
120-
.await
121-
.map_err(Error::from)?
123+
forwarded_req.send_stream(stream_to_send).await
122124
};
123125

126+
let mut res = res_e.map_err(|e| {
127+
error!("forward fwk error {:?}, {:?}", e, req);
128+
Error::from(e)
129+
})?;
130+
124131
if res.status().is_client_error() || res.status().is_server_error() {
125-
error!("forward error {:?} {:?}", req, res);
132+
error!("forward status error {:?} {:?}", req, res);
126133
}
127134

128135
let mut client_resp = HttpResponse::build(res.status());
@@ -148,16 +155,19 @@ async fn fetch(
148155

149156
let mut fetch_req = client
150157
.request_from(get_url.as_str(), req.head())
151-
.timeout(TIMEOUT_DURATION);
158+
.force_close();
152159

153160
for header in &FETCH_REQUEST_HEADERS_TO_REMOVE {
154161
fetch_req.headers_mut().remove(header);
155162
}
156163

157-
let res = fetch_req.send_body(body).await.map_err(Error::from)?;
164+
let res = fetch_req.send_body(body).await.map_err(|e| {
165+
error!("fetch fwk error {:?}, {:?}", e, req);
166+
Error::from(e)
167+
})?;
158168

159169
if res.status().is_client_error() || res.status().is_server_error() {
160-
error!("fetch error {:?} {:?}", req, res);
170+
error!("fetch status error {:?} {:?}", req, res);
161171
}
162172

163173
let mut client_resp = HttpResponse::build(res.status());
@@ -204,9 +214,7 @@ async fn simple_proxy(
204214
) -> Result<HttpResponse, Error> {
205215
let url = config.create_url(&req.uri());
206216

207-
let mut proxied_req = client
208-
.request_from(url.as_str(), req.head())
209-
.timeout(TIMEOUT_DURATION);
217+
let mut proxied_req = client.request_from(url.as_str(), req.head()).force_close();
210218

211219
for header in &FETCH_REQUEST_HEADERS_TO_REMOVE {
212220
proxied_req.headers_mut().remove(header);
@@ -215,10 +223,13 @@ async fn simple_proxy(
215223
proxied_req
216224
.send_stream(payload)
217225
.await
218-
.map_err(Error::from)
226+
.map_err(|e| {
227+
error!("simple proxy fwk error {:?}, {:?}", e, req);
228+
Error::from(e)
229+
})
219230
.map(|res| {
220231
if res.status().is_client_error() || res.status().is_server_error() {
221-
error!("simple proxy error {:?} {:?}", req, res);
232+
error!("simple proxy status error {:?} {:?}", req, res);
222233
}
223234

224235
let mut client_resp = HttpResponse::build(res.status());
@@ -243,6 +254,10 @@ fn content_length(headers: &HeaderMap) -> Option<usize> {
243254
}
244255

245256
fn encrypted_content_length(clear_length: usize, chunk_size: usize) -> usize {
257+
if clear_length == 0 {
258+
return 0;
259+
}
260+
246261
let nb_chunk = clear_length / chunk_size;
247262
let remainder = clear_length % chunk_size;
248263

@@ -254,6 +269,10 @@ fn encrypted_content_length(clear_length: usize, chunk_size: usize) -> usize {
254269
}
255270

256271
fn decrypted_content_length(encrypted_length: usize, decipher: DecipherType) -> usize {
272+
if encrypted_length == 0 {
273+
return 0;
274+
}
275+
257276
match decipher {
258277
DecipherType::Encrypted { chunk_size } => {
259278
// encrypted = header_ds + header_crypto + n ( abytes + chunk ) + a (abytes + remainder)
@@ -285,9 +304,20 @@ pub async fn main(config: Config) -> std::io::Result<()> {
285304
let address = config.address.unwrap();
286305
let max_conn = config.max_connections;
287306

307+
use actix_http;
308+
288309
HttpServer::new(move || {
289310
App::new()
290-
.data(actix_web::client::Client::new())
311+
.data(
312+
actix_web::client::ClientBuilder::new()
313+
.connector(
314+
actix_web::client::Connector::new()
315+
.timeout(CONNECT_TIMEOUT) // max time to connect to remote host including dns name resolution
316+
.finish(),
317+
)
318+
.timeout(RESPONSE_TIMEOUT) // the total time before a response must be received
319+
.finish(),
320+
)
291321
.data(config.clone())
292322
.wrap(middleware::Logger::default())
293323
.service(web::resource("/ping").guard(guard::Get()).to(ping))
@@ -296,6 +326,7 @@ pub async fn main(config: Config) -> std::io::Result<()> {
296326
.default_service(web::route().to(simple_proxy))
297327
})
298328
.max_connections(max_conn)
329+
.keep_alive(actix_http::KeepAlive::Disabled)
299330
.bind_uds("/tmp/actix-uds.socket")?
300331
.bind(address)?
301332
.run()
@@ -306,6 +337,22 @@ pub async fn main(config: Config) -> std::io::Result<()> {
306337
mod tests {
307338
use super::*;
308339

340+
#[test]
341+
fn test_decrypt_content_length_from_0() {
342+
let original_length = 0;
343+
let chunk_size = 16;
344+
let encrypted_length = 0;
345+
346+
let decrypted_length = decrypted_content_length(
347+
encrypted_length,
348+
DecipherType::Encrypted {
349+
chunk_size: chunk_size,
350+
},
351+
);
352+
353+
assert_eq!(original_length, decrypted_length);
354+
}
355+
309356
#[test]
310357
fn test_decrypt_content_length_without_remainder() {
311358
let original_length = 32;
@@ -354,6 +401,18 @@ mod tests {
354401
assert_eq!(original_length, decrypted_length);
355402
}
356403

404+
#[test]
405+
fn test_encrypted_content_length_from_0() {
406+
let original_length = 0;
407+
let chunk_size = 16;
408+
let encrypted_length = 0;
409+
410+
assert_eq!(
411+
encrypted_length,
412+
encrypted_content_length(original_length, chunk_size)
413+
);
414+
}
415+
357416
#[test]
358417
fn test_encrypted_content_length_without_remainder() {
359418
let original_length = 32;

tests/fixtures/server-static/server.js

+13-3
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,20 @@ let last_put_headers = {};
1212
app.put('*', function(req, res) {
1313
last_put_headers = req.headers;
1414

15-
req.pipe(fs.createWriteStream(__dirname + '/uploads/' +req.url));
15+
writeStream = fs.createWriteStream(__dirname + '/uploads/' +req.url);
1616

17-
res.writeHead(200, {'Content-Type': 'text/plain'});
18-
res.end('OK!');
17+
req.pipe(writeStream);
18+
19+
// After all the data is saved, respond Ok
20+
req.on('end', function () {
21+
res.writeHead(200, {"content-type":"text/html"});
22+
res.end('Ok!');
23+
});
24+
25+
// This is here incase any errors occur
26+
writeStream.on('error', function (err) {
27+
console.log(err);
28+
});
1929
});
2030

2131
// Add extra latency to all requests

0 commit comments

Comments
 (0)