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
|
use std::str::FromStr;
use anyhow::Context;
use anyhow::Error;
use anyhow::Result;
use actix_web::{get, web, App, HttpServer, Responder};
use derive_more::{Display, Error};
#[derive(Clone)]
struct Endpoints {
hello_service_endpoint: String,
hello_service_port: u16,
world_service_endpoint: String,
world_service_port: u16,
}
#[derive(Debug, Display, Error)]
#[display(fmt = "error: {}", inner)]
struct AppError {
inner: anyhow::Error,
}
impl actix_web::error::ResponseError for AppError {}
#[get("/")]
async fn joiner(state: web::Data<Endpoints>) -> impl Responder {
async fn inner(state: web::Data<Endpoints>) -> Result<impl Responder> {
let hello = {
let url = reqwest::Url::parse(&format!("http://{host}:{port}/",
host = state.hello_service_endpoint,
port = state.hello_service_port))?;
reqwest::get(url)
};
let world = {
let url = reqwest::Url::parse(&format!("http://{host}:{port}/",
host = state.world_service_endpoint,
port = state.world_service_port))?;
reqwest::get(url)
};
let (hello, world) = tokio::try_join!(hello, world)?;
let (hello, world) = tokio::try_join!(hello.text(), world.text())?;
Ok(format!("{} {}", hello, world)) as Result<String>
}
inner(state).await.map_err(|inner| AppError { inner })
}
#[actix_web::main]
async fn main() -> Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
let bind = std::env::var("HOST").expect("environment: HOST variable not set");
let port = std::env::var("PORT")
.as_ref()
.map(|p| u16::from_str(p))
.expect("environment: HOST variable not set")
.context("Failed to parse port as integer")?;
let hello_service = std::env::var("HELLO_SERVICE")
.as_ref()
.map(std::ops::Deref::deref)
.map(parse_endpoint)
.expect("environment: HELLO_SERVICE variable not set")?;
let world_service = std::env::var("WORLD_SERVICE")
.as_ref()
.map(std::ops::Deref::deref)
.map(parse_endpoint)
.expect("environment: WORLD_SERVICE variable not set")?;
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(Endpoints {
hello_service_endpoint: hello_service.0.clone(),
hello_service_port: hello_service.1,
world_service_endpoint: world_service.0.clone(),
world_service_port: world_service.1,
}))
.service(joiner)
})
.bind(format!("{}:{}", bind, port))?
.run()
.await
.map_err(anyhow::Error::from)
}
fn parse_endpoint(s: &str) -> Result<(String, u16)> {
let v = s.split(':').collect::<Vec<_>>();
if v.len() != 2 {
anyhow::bail!("Expected host:port, got: '{}'", s)
}
u16::from_str(v[1])
.map(|port| (v[0].to_string(), port))
.map_err(Error::from)
}
|