Skip to content

Commit 500b7b3

Browse files
committed
feat: implement ohttp gateway middleware
Signed-off-by: Harsh Dev Pathak <hiddenHaze@proton.me>
1 parent 1b6cc38 commit 500b7b3

7 files changed

Lines changed: 407 additions & 16 deletions

File tree

Cargo-minimal.lock

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1824,9 +1824,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
18241824

18251825
[[package]]
18261826
name = "hyper"
1827-
version = "1.7.0"
1827+
version = "1.8.1"
18281828
source = "registry+https://github.com/rust-lang/crates.io-index"
1829-
checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
1829+
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
18301830
dependencies = [
18311831
"atomic-waker",
18321832
"bytes",
@@ -2651,8 +2651,12 @@ dependencies = [
26512651
"anyhow",
26522652
"axum",
26532653
"axum-server",
2654+
"bhttp",
2655+
"bitcoin-ohttp",
26542656
"clap",
26552657
"config",
2658+
"http-body-util",
2659+
"hyper",
26562660
"ohttp-relay",
26572661
"payjoin-directory",
26582662
"payjoin-test-utils",

Cargo-recent.lock

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1824,9 +1824,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
18241824

18251825
[[package]]
18261826
name = "hyper"
1827-
version = "1.7.0"
1827+
version = "1.8.1"
18281828
source = "registry+https://github.com/rust-lang/crates.io-index"
1829-
checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
1829+
checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11"
18301830
dependencies = [
18311831
"atomic-waker",
18321832
"bytes",
@@ -2651,8 +2651,12 @@ dependencies = [
26512651
"anyhow",
26522652
"axum",
26532653
"axum-server",
2654+
"bhttp",
2655+
"bitcoin-ohttp",
26542656
"clap",
26552657
"config",
2658+
"http-body-util",
2659+
"hyper",
26562660
"ohttp-relay",
26572661
"payjoin-directory",
26582662
"payjoin-test-utils",

payjoin-directory/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ fn init_tls_acceptor(cert_key: (Vec<u8>, Vec<u8>)) -> Result<tokio_rustls::TlsAc
6868
#[derive(Clone)]
6969
pub struct Service<D: Db> {
7070
db: D,
71-
ohttp: ohttp::Server,
71+
pub ohttp: ohttp::Server,
7272
metrics: Metrics,
7373
sentinel_tag: SentinelTag,
7474
}

payjoin-service/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ tokio-listener = { version = "0.5", features = ["axum08", "serde"] }
3737
tower = "0.5"
3838
tracing = "0.1"
3939
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
40+
http-body-util = "0.1"
41+
hyper = "1.8.1"
42+
ohttp = { package = "bitcoin-ohttp", version = "0.6" }
43+
bhttp = { version = "0.6.1", features = ["http"] }
4044

4145
[dev-dependencies]
4246
payjoin-test-utils = { version = "0.0.1" }

payjoin-service/src/lib.rs

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ use rand::Rng;
88
use tokio_listener::{Listener, SystemOptions, UserOptions};
99
use tower::Service;
1010
use tracing::info;
11+
pub mod ohttp;
12+
13+
use http_body_util::combinators::BoxBody;
14+
use hyper::body::Bytes;
15+
use hyper::{Request, StatusCode};
16+
use ohttp::{OhttpGatewayConfig, OhttpGatewayLayer};
17+
use tower::{ServiceBuilder, ServiceExt};
1118

1219
pub mod cli;
1320
pub mod config;
@@ -16,6 +23,7 @@ pub mod config;
1623
struct Services {
1724
directory: payjoin_directory::Service<payjoin_directory::FilesDb>,
1825
relay: ohttp_relay::Service,
26+
sentinel_tag: SentinelTag,
1927
}
2028

2129
pub async fn serve(config: Config) -> anyhow::Result<()> {
@@ -24,6 +32,7 @@ pub async fn serve(config: Config) -> anyhow::Result<()> {
2432
let services = Services {
2533
directory: init_directory(&config, sentinel_tag).await?,
2634
relay: ohttp_relay::Service::new(sentinel_tag).await,
35+
sentinel_tag,
2736
};
2837
let app = Router::new().fallback(route_request).with_state(services);
2938

@@ -56,6 +65,7 @@ pub async fn serve_manual_tls(
5665
let services = Services {
5766
directory: init_directory(&config, sentinel_tag).await?,
5867
relay: ohttp_relay::Service::new_with_roots(root_store, sentinel_tag).await,
68+
sentinel_tag,
5969
};
6070
let app = Router::new().fallback(route_request).with_state(services);
6171

@@ -119,22 +129,68 @@ fn init_ohttp_config(
119129
}
120130
}
121131

122-
async fn route_request(
123-
State(mut services): State<Services>,
124-
req: axum::extract::Request,
125-
) -> Response {
132+
async fn route_request(State(services): State<Services>, req: axum::extract::Request) -> Response {
126133
if is_relay_request(&req) {
127-
match services.relay.call(req).await {
134+
let mut relay = services.relay.clone();
135+
match relay.call(req).await {
128136
Ok(res) => res.into_response(),
129-
Err(e) => (axum::http::StatusCode::BAD_GATEWAY, e.to_string()).into_response(),
137+
Err(e) => (StatusCode::BAD_GATEWAY, e.to_string()).into_response(),
130138
}
131139
} else {
132-
// The directory service handles all other requests (including 404)
133-
match services.directory.call(req).await {
134-
Ok(res) => res.into_response(),
135-
Err(e) =>
136-
(axum::http::StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
140+
handle_directory_request(services, req).await
141+
}
142+
}
143+
144+
async fn handle_directory_request(services: Services, req: axum::extract::Request) -> Response {
145+
let ohttp_server = services.directory.ohttp.clone();
146+
147+
let ohttp_config = OhttpGatewayConfig::new(ohttp_server, services.sentinel_tag);
148+
149+
let (parts, body) = req.into_parts();
150+
151+
use http_body_util::BodyExt as _;
152+
153+
let body_bytes = body
154+
.collect()
155+
.await
156+
.map_err(|_| "Failed to collect body")
157+
.expect("Failed to collect body")
158+
.to_bytes();
159+
160+
let boxed_body = BoxBody::new(http_body_util::Full::new(body_bytes));
161+
162+
let hyper_req = Request::from_parts(parts, boxed_body);
163+
164+
let directory_service = tower::service_fn({
165+
let directory = services.directory.clone();
166+
move |req: Request<BoxBody<Bytes, hyper::Error>>| {
167+
let mut dir = directory.clone();
168+
async move {
169+
dir.call(req).await.map_err(|e| {
170+
Box::new(std::io::Error::other(e.to_string()))
171+
as Box<dyn std::error::Error + Send + Sync>
172+
})
173+
}
137174
}
175+
});
176+
177+
let mut service_with_ohttp = ServiceBuilder::new()
178+
.layer(OhttpGatewayLayer::new(ohttp_config))
179+
.service(directory_service)
180+
.boxed_clone();
181+
182+
match service_with_ohttp.ready().await {
183+
Ok(ready_service) => match ready_service.call(hyper_req).await {
184+
Ok(response) => {
185+
let (parts, body) = response.into_parts();
186+
let axum_body = axum::body::Body::new(body);
187+
Response::from_parts(parts, axum_body).into_response()
188+
}
189+
Err(e) =>
190+
(StatusCode::INTERNAL_SERVER_ERROR, format!("Service error: {}", e)).into_response(),
191+
},
192+
Err(e) =>
193+
(StatusCode::INTERNAL_SERVER_ERROR, format!("Service not ready: {}", e)).into_response(),
138194
}
139195
}
140196

0 commit comments

Comments
 (0)