Skip to content

Commit 5dc3e36

Browse files
authored
Merge pull request #39 from UMEssen/configurable-base-path
Configurable base path
2 parents b9357fe + f3ae488 commit 5dc3e36

9 files changed

Lines changed: 82 additions & 18 deletions

File tree

Cargo.lock

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ futures = "0.3.31"
4444
mime = "0.3.17"
4545
tower-http = { version = "0.6.6", features = ["trace", "cors", "timeout"] }
4646
tower = { version = "0.5.2", features = ["limit"] }
47+
url = "2.5.7"
4748
async-trait = "0.1.89"
4849
async-stream = "0.3.6"
4950
uuid = { version = "1.18.1", features = ["v4"] }

docs/topics/configuration.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ server:
124124
max-upload-size: 50000000
125125
request-timeout: 60000
126126
graceful-shutdown: true
127+
base-path: /
127128
```
128129
129130
<deflist>
@@ -145,6 +146,9 @@ server:
145146
Shutdowns will take no longer than the request timeout configured by <code>server.http.request.timeout</code>.
146147
If disabled, the process is stopped immediately, which will potentially lead to incomplete responses for outstanding requests.
147148
</def>
149+
<def title="server.http.base-path" id="server.http.base-path">
150+
Sets the base path for all HTTP endpoints.
151+
</def>
148152
</deflist>
149153
150154
## DIMSE Server Config

src/api/aets.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use axum::response::IntoResponse;
55
use axum::routing::get;
66
use axum::{Json, Router};
77

8-
pub fn api() -> Router<AppState> {
8+
pub fn routes() -> Router<AppState> {
99
Router::new()
1010
.route("/aets", get(all_aets))
1111
.route("/aets/{aet}", get(aet_health))

src/api/home.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use crate::AppState;
2+
use axum::response::IntoResponse;
3+
use axum::routing::get;
4+
use axum::Router;
5+
6+
pub fn routes() -> Router<AppState> {
7+
Router::new().route("/", get(index))
8+
}
9+
10+
// TODO: Return HTML page for a quick user-friendly overview
11+
async fn index() -> impl IntoResponse {
12+
format!(
13+
"This server is running DICOM-RST (v{})",
14+
env!("CARGO_PKG_VERSION")
15+
)
16+
}

src/api/mod.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,26 @@ use crate::AppState;
22
use axum::Router;
33

44
mod aets;
5+
mod home;
56
pub mod qido;
67
pub mod stow;
78
pub mod wado;
89

9-
pub fn routes() -> Router<AppState> {
10-
Router::new().merge(aets::api()).nest(
11-
"/aets/{aet}",
12-
Router::new()
13-
.merge(qido::routes())
14-
.merge(wado::routes())
15-
.merge(stow::routes()),
16-
)
10+
pub fn routes(base_path: &str) -> Router<AppState> {
11+
let router = Router::new()
12+
.merge(home::routes())
13+
.merge(aets::routes())
14+
.nest(
15+
"/aets/{aet}",
16+
Router::new()
17+
.merge(qido::routes())
18+
.merge(wado::routes())
19+
.merge(stow::routes()),
20+
);
21+
22+
// axum no longer supports nesting at the root
23+
match base_path {
24+
"/" | "" => router,
25+
base_path => Router::new().nest(base_path, router),
26+
}
1727
}

src/config/defaults.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ server:
99
max-upload-size: 50000000
1010
request-timeout: 60000
1111
graceful-shutdown: true
12+
base-path: /
1213
dimse:
1314
- aet: DICOM-RST
1415
interface: 0.0.0.0

src/config/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ impl AppConfig {
201201
))
202202
.add_source(File::with_name("config.yaml").required(false))
203203
.add_source(Environment::with_prefix("DICOM_RST").separator("_"))
204+
.set_override_option(
205+
"server.http.base-path",
206+
std::env::var("DICOM_RST_SERVER_HTTP_BASE_PATH").ok(),
207+
)?
204208
.build()?
205209
.try_deserialize()
206210
}
@@ -232,6 +236,27 @@ pub struct HttpServerConfig {
232236
pub max_upload_size: usize,
233237
pub request_timeout: u64,
234238
pub graceful_shutdown: bool,
239+
pub base_path: String,
240+
}
241+
242+
impl HttpServerConfig {
243+
const WILDCARD_ADDRESSES: [&'static str; 3] =
244+
["0.0.0.0", "::", "0000:0000:0000:0000:0000:0000:0000:0000"];
245+
246+
pub fn base_url(&self) -> Result<url::Url, url::ParseError> {
247+
let origin = format!("http://{}:{}", self.interface, self.port);
248+
let mut url = url::Url::parse(&origin)?;
249+
250+
if url
251+
.host()
252+
.is_some_and(|host| Self::WILDCARD_ADDRESSES.contains(&host.to_string().as_str()))
253+
{
254+
url.set_host(Some("127.0.0.1"))?;
255+
}
256+
let url = url.join(&self.base_path)?;
257+
258+
Ok(url)
259+
}
235260
}
236261

237262
impl Default for HttpServerConfig {
@@ -242,6 +267,7 @@ impl Default for HttpServerConfig {
242267
graceful_shutdown: true,
243268
max_upload_size: 50_000_000, // 50 MB
244269
request_timeout: 60_000, // 1 min
270+
base_path: String::from("/"),
245271
}
246272
}
247273
}

src/main.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ async fn run(config: AppConfig) -> anyhow::Result<()> {
129129
});
130130
}
131131

132-
let app = api::routes()
132+
let app = api::routes(&config.server.http.base_path)
133133
.layer(CorsLayer::permissive())
134134
.layer(axum::middleware::from_fn(add_common_headers))
135135
.layer(
@@ -152,7 +152,12 @@ async fn run(config: AppConfig) -> anyhow::Result<()> {
152152
let addr = SocketAddr::from((host, port));
153153
let listener = TcpListener::bind(addr).await?;
154154

155-
info!("Started DICOMweb server on http://{addr}");
155+
info!(
156+
server.address = addr.ip().to_string(),
157+
server.port = addr.port(),
158+
url.full = config.server.http.base_url()?.as_str(),
159+
"Started DICOMweb server"
160+
);
156161
if config.server.http.graceful_shutdown {
157162
axum::serve(listener, app)
158163
.with_graceful_shutdown(shutdown_signal())

0 commit comments

Comments
 (0)