drawbridge_server/
lib.rs

1// SPDX-FileCopyrightText: 2022 Profian Inc. <opensource@profian.com>
2// SPDX-License-Identifier: Apache-2.0
3
4#![forbid(unsafe_code)]
5#![deny(
6    clippy::all,
7    absolute_paths_not_starting_with_crate,
8    deprecated_in_future,
9    missing_copy_implementations,
10    missing_debug_implementations,
11    noop_method_call,
12    rust_2018_compatibility,
13    rust_2018_idioms,
14    rust_2021_compatibility,
15    single_use_lifetimes,
16    trivial_bounds,
17    trivial_casts,
18    trivial_numeric_casts,
19    unreachable_code,
20    unreachable_patterns,
21    unreachable_pub,
22    unstable_features,
23    unused,
24    unused_crate_dependencies,
25    unused_import_braces,
26    unused_lifetimes,
27    unused_results,
28    variant_size_differences
29)]
30
31mod builder;
32mod handle;
33
34pub mod auth;
35pub mod repos;
36pub mod store;
37pub mod tags;
38pub mod trees;
39pub mod users;
40
41pub use auth::{OidcClaims, ScopeContext, ScopeLevel, TlsConfig, TrustedCertificate};
42pub use builder::*;
43pub(crate) use handle::*;
44pub(crate) use store::*;
45
46pub use openidconnect::url;
47
48use anyhow::Context as _;
49use async_std::path::Path;
50use axum::extract::Extension;
51use axum::routing::IntoMakeService;
52use axum::Router;
53use futures::lock::Mutex;
54use futures::{AsyncRead, AsyncWrite};
55use futures_rustls::TlsAcceptor;
56use hyper::server::conn::Http;
57use tokio_util::compat::FuturesAsyncReadCompatExt;
58use tower::MakeService;
59use tracing::trace;
60
61#[allow(missing_debug_implementations)] // TlsAcceptor does not implement Debug
62pub struct App {
63    make_service: Mutex<IntoMakeService<Router>>,
64    tls: TlsAcceptor,
65}
66
67impl App {
68    pub fn builder<S: AsRef<Path>>(store: S, tls: TlsConfig, oidc: OidcConfig) -> Builder<S> {
69        Builder::new(store, tls, oidc)
70    }
71
72    pub async fn new(
73        store: impl AsRef<Path>,
74        tls: TlsConfig,
75        oidc: OidcConfig,
76    ) -> anyhow::Result<Self> {
77        Self::builder(store, tls, oidc).build().await
78    }
79
80    pub async fn handle(
81        &self,
82        stream: impl 'static + Unpin + AsyncRead + AsyncWrite,
83    ) -> anyhow::Result<()> {
84        trace!(target: "app::App::handle", "begin TLS handshake");
85        let stream = self
86            .tls
87            .accept(stream)
88            .await
89            .context("failed to accept TLS connection")?;
90        trace!(target: "app::App::handle", "completed TLS handshake");
91
92        let mut svc = self
93            .make_service
94            .lock()
95            .await
96            .make_service(())
97            .await
98            .context("failed to create app service")?;
99        let (_, conn) = stream.get_ref();
100        if conn.peer_certificates().is_some() {
101            svc = svc.layer(Extension(TrustedCertificate));
102            trace!(target: "app::App::handle", "add TrustedCertificate to extensions");
103        }
104        trace!(target: "app::App::handle", "begin HTTP request serving");
105        Http::new()
106            .serve_connection(stream.compat(), svc)
107            .await
108            .context("failed to handle request")
109    }
110}