1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-31 09:24:31 +03:00

Serve static files live from disk in dev mode

This commit is contained in:
Quentin Gliech
2022-04-28 16:08:28 +02:00
parent 8044d0a41b
commit 8a256596d7
17 changed files with 258 additions and 193 deletions

View File

@ -7,7 +7,7 @@ license = "Apache-2.0"
[dependencies]
async-trait = "0.1.53"
axum = { version = "0.5.3", features = ["headers"] }
axum = { version = "0.5.4", features = ["headers"] }
bincode = "1.3.3"
chrono = "0.4.19"
cookie = { version = "0.16.0", features = ["private", "percent-encode"] }
@ -24,7 +24,7 @@ serde_urlencoded = "0.7.1"
serde_json = "1.0.79"
sqlx = "0.5.13"
thiserror = "1.0.30"
tokio = "1.17.0"
tokio = "1.18.0"
tower = { version = "0.4.12", features = ["util"] }
tracing = "0.1.34"
url = "2.2.2"

View File

@ -6,7 +6,7 @@ edition = "2021"
license = "Apache-2.0"
[dependencies]
tokio = { version = "1.17.0", features = ["full"] }
tokio = { version = "1.18.0", features = ["full"] }
futures = "0.3.21"
anyhow = "1.0.57"
clap = { version = "3.1.12", features = ["derive"] }
@ -32,21 +32,22 @@ opentelemetry-jaeger = { version = "0.16.0", features = ["rt-tokio", "reqwest_co
opentelemetry-otlp = { version = "0.10.0", features = ["trace", "metrics"], optional = true }
opentelemetry-zipkin = { version = "0.15.0", features = ["reqwest-client", "reqwest-rustls"], default-features = false, optional = true }
mas-axum-utils = { path = "../axum-utils" }
mas-config = { path = "../config" }
mas-email = { path = "../email" }
mas-handlers = { path = "../handlers" }
mas-http = { path = "../http" }
mas-static-files = { path = "../static-files" }
mas-storage = { path = "../storage" }
mas-tasks = { path = "../tasks" }
mas-templates = { path = "../templates" }
mas-axum-utils = { path = "../axum-utils" }
[dev-dependencies]
indoc = "1.0.4"
[features]
default = ["otlp", "jaeger", "zipkin"]
dev = ["mas-templates/dev", "mas-handlers/dev"]
dev = ["mas-templates/dev", "mas-static-files/dev"]
# Enable OpenTelemetry OTLP exporter. Requires "protoc"
otlp = ["opentelemetry-otlp"]
# Enable OpenTelemetry Jaeger exporter and propagator.

View File

@ -194,6 +194,8 @@ impl Options {
let url_builder = UrlBuilder::new(config.http.public_base.clone());
let static_files = mas_static_files::service(&config.http.web_root);
// Explicitely the config to properly zeroize secret keys
drop(config);
@ -217,6 +219,7 @@ impl Options {
&mailer,
&url_builder,
)
.fallback(static_files)
.layer(ServerLayer::default());
info!("Listening on http://{}", listener.local_addr().unwrap());

View File

@ -6,7 +6,7 @@ edition = "2021"
license = "Apache-2.0"
[dependencies]
tokio = { version = "1.17.0", features = [] }
tokio = { version = "1.18.0", features = [] }
tracing = { version = "0.1.34", features = ["log"] }
async-trait = "0.1.53"

View File

@ -8,7 +8,7 @@ license = "Apache-2.0"
[dependencies]
anyhow = "1.0.57"
async-trait = "0.1.53"
tokio = { version = "1.17.0", features = ["macros"] }
tokio = { version = "1.18.0", features = ["macros"] }
tracing = "0.1.34"
aws-sdk-sesv2 = "0.10.1"
aws-config = "0.10.1"

View File

@ -5,12 +5,9 @@ authors = ["Quentin Gliech <quenting@element.io>"]
edition = "2021"
license = "Apache-2.0"
[features]
dev = ["mas-static-files/dev", "mas-templates/dev"]
[dependencies]
# Async runtime
tokio = { version = "1.17.0", features = ["macros"] }
tokio = { version = "1.18.0", features = ["macros"] }
# Logging and tracing
tracing = "0.1.34"
@ -22,8 +19,8 @@ anyhow = "1.0.57"
# Web server
hyper = { version = "0.14.18", features = ["full"] }
tower = "0.4.12"
tower-http = { version = "0.2.5", features = ["cors"] }
axum = "0.5.3"
tower-http = { version = "0.3.1", features = ["cors"] }
axum = "0.5.4"
axum-macros = "0.2.0"
# Emails
@ -64,7 +61,6 @@ mas-email = { path = "../email" }
mas-http = { path = "../http" }
mas-iana = { path = "../iana" }
mas-jose = { path = "../jose" }
mas-static-files = { path = "../static-files" }
mas-storage = { path = "../storage" }
mas-templates = { path = "../templates" }

View File

@ -125,7 +125,6 @@ where
get(self::oauth2::authorization::step_get),
)
.merge(api_router)
.fallback(mas_static_files::Assets)
.layer(Extension(pool.clone()))
.layer(Extension(templates.clone()))
.layer(Extension(key_store.clone()))

View File

@ -6,7 +6,7 @@ edition = "2021"
license = "Apache-2.0"
[dependencies]
axum = "0.5.3"
axum = "0.5.4"
bytes = "1.1.0"
futures-util = "0.3.21"
headers = "0.3.7"
@ -22,8 +22,8 @@ rustls = "0.20.4"
serde = "1.0.136"
serde_json = "1.0.79"
thiserror = "1.0.30"
tokio = { version = "1.17.0", features = ["sync", "parking_lot"] }
tokio = { version = "1.18.0", features = ["sync", "parking_lot"] }
tower = { version = "0.4.12", features = ["timeout", "limit"] }
tower-http = { version = "0.2.5", features = ["follow-redirect", "decompression-full", "set-header", "compression-full", "cors"] }
tower-http = { version = "0.3.1", features = ["follow-redirect", "decompression-full", "set-header", "compression-full", "cors"] }
tracing = "0.1.34"
tracing-opentelemetry = "0.17.2"

View File

@ -13,6 +13,6 @@ csv = "1.1.6"
futures-util = "0.3.21"
reqwest = { version = "0.11.10", features = ["blocking", "rustls-tls"], default-features = false }
serde = { version = "1.0.136", features = ["derive"] }
tokio = { version = "1.17.0", features = ["full"] }
tokio = { version = "1.18.0", features = ["full"] }
tracing = "0.1.34"
tracing-subscriber = "0.3.11"

View File

@ -30,7 +30,7 @@ serde_with = { version = "1.13.0", features = ["base64"] }
sha2 = "0.10.2"
signature = "1.4.0"
thiserror = "1.0.30"
tokio = { version = "1.17.0", features = ["macros", "rt", "sync"] }
tokio = { version = "1.18.0", features = ["macros", "rt", "sync"] }
tower = { version = "0.4.12", features = ["util"] }
tracing = "0.1.34"
url = { version = "2.2.2", features = ["serde"] }

View File

@ -9,10 +9,12 @@ license = "Apache-2.0"
dev = []
[dependencies]
axum = "0.5.3"
axum = "0.5.4"
headers = "0.3.7"
http = "0.2.6"
http-body = "0.4.4"
mime_guess = "2.0.4"
rust-embed = "6.4.0"
tokio = { version = "1.17.0", features = ["fs"] }
tower = "0.4.12"
tower-http = { version = "0.3.1", features = ["fs"] }
tracing = "0.1.34"

View File

@ -182,9 +182,9 @@
}
},
"node_modules/browserslist": {
"version": "4.20.2",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz",
"integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==",
"version": "4.20.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz",
"integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==",
"dev": true,
"funding": [
{
@ -197,10 +197,10 @@
}
],
"dependencies": {
"caniuse-lite": "^1.0.30001317",
"electron-to-chromium": "^1.4.84",
"caniuse-lite": "^1.0.30001332",
"electron-to-chromium": "^1.4.118",
"escalade": "^3.1.1",
"node-releases": "^2.0.2",
"node-releases": "^2.0.3",
"picocolors": "^1.0.0"
},
"bin": {
@ -496,9 +496,9 @@
"dev": true
},
"node_modules/dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
"dev": true,
"dependencies": {
"domelementtype": "^2.0.1",
@ -510,9 +510,9 @@
}
},
"node_modules/domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"dev": true,
"funding": [
{
@ -551,9 +551,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.86",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.86.tgz",
"integrity": "sha512-EVTZ+igi8x63pK4bPuA95PXIs2b2Cowi3WQwI9f9qManLiZJOD1Lash1J3W4TvvcUCcIR4o/rgi9o8UicXSO+w==",
"version": "1.4.123",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.123.tgz",
"integrity": "sha512-0pHGE53WkYoFbsgwYcVKEpWa6jbzlvkohIEA2CUoZ9b5KC+w/zlMiQHvW/4IBcOh7YoEFqRNavgTk02TBoUTUw==",
"dev": true
},
"node_modules/entities": {
@ -693,9 +693,9 @@
}
},
"node_modules/is-core-module": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
"integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
"integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
@ -771,22 +771,22 @@
}
},
"node_modules/micromatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
"integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dev": true,
"dependencies": {
"braces": "^3.0.1",
"picomatch": "^2.2.3"
"braces": "^3.0.2",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/mini-svg-data-uri": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.3.tgz",
"integrity": "sha512-gSfqpMRC8IxghvMcxzzmMnWpXAChSA+vy4cia33RgerMS8Fex95akUyQZPbxJJmeBGiGmK7n/1OpUX8ksRjIdA==",
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
"integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
"dev": true,
"bin": {
"mini-svg-data-uri": "cli.js"
@ -799,9 +799,9 @@
"dev": true
},
"node_modules/nanoid": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
"integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==",
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
"integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
"dev": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
@ -811,9 +811,9 @@
}
},
"node_modules/node-releases": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz",
"integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz",
"integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==",
"dev": true
},
"node_modules/normalize-path": {
@ -1765,15 +1765,15 @@
}
},
"browserslist": {
"version": "4.20.2",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz",
"integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==",
"version": "4.20.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz",
"integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==",
"dev": true,
"requires": {
"caniuse-lite": "^1.0.30001317",
"electron-to-chromium": "^1.4.84",
"caniuse-lite": "^1.0.30001332",
"electron-to-chromium": "^1.4.118",
"escalade": "^3.1.1",
"node-releases": "^2.0.2",
"node-releases": "^2.0.3",
"picocolors": "^1.0.0"
}
},
@ -1982,9 +1982,9 @@
"dev": true
},
"dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
"dev": true,
"requires": {
"domelementtype": "^2.0.1",
@ -1993,9 +1993,9 @@
}
},
"domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"dev": true
},
"domhandler": {
@ -2019,9 +2019,9 @@
}
},
"electron-to-chromium": {
"version": "1.4.86",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.86.tgz",
"integrity": "sha512-EVTZ+igi8x63pK4bPuA95PXIs2b2Cowi3WQwI9f9qManLiZJOD1Lash1J3W4TvvcUCcIR4o/rgi9o8UicXSO+w==",
"version": "1.4.123",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.123.tgz",
"integrity": "sha512-0pHGE53WkYoFbsgwYcVKEpWa6jbzlvkohIEA2CUoZ9b5KC+w/zlMiQHvW/4IBcOh7YoEFqRNavgTk02TBoUTUw==",
"dev": true
},
"entities": {
@ -2125,9 +2125,9 @@
}
},
"is-core-module": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
"integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
"integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
"dev": true,
"requires": {
"has": "^1.0.3"
@ -2185,19 +2185,19 @@
"dev": true
},
"micromatch": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz",
"integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==",
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"dev": true,
"requires": {
"braces": "^3.0.1",
"picomatch": "^2.2.3"
"braces": "^3.0.2",
"picomatch": "^2.3.1"
}
},
"mini-svg-data-uri": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.3.tgz",
"integrity": "sha512-gSfqpMRC8IxghvMcxzzmMnWpXAChSA+vy4cia33RgerMS8Fex95akUyQZPbxJJmeBGiGmK7n/1OpUX8ksRjIdA==",
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
"integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
"dev": true
},
"minimist": {
@ -2207,15 +2207,15 @@
"dev": true
},
"nanoid": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
"integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==",
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
"integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
"dev": true
},
"node-releases": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz",
"integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz",
"integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==",
"dev": true
},
"normalize-path": {

View File

@ -1,4 +1,4 @@
// Copyright 2021 The Matrix.org Foundation C.I.C.
// Copyright 2021, 2022 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -18,99 +18,157 @@
#![deny(clippy::all, missing_docs, rustdoc::broken_intra_doc_links)]
#![warn(clippy::pedantic)]
use std::{
convert::Infallible,
future::{ready, Ready},
};
#[cfg(not(feature = "dev"))]
mod builtin {
use std::{
fmt::Write,
future::{ready, Ready},
};
use axum::{
response::{IntoResponse, Response},
TypedHeader,
};
use headers::{ContentLength, ContentType, ETag, HeaderMapExt, IfNoneMatch};
use http::{Method, Request, StatusCode};
use rust_embed::RustEmbed;
use tower::Service;
use axum::{
response::{IntoResponse, Response},
TypedHeader,
};
use headers::{ContentLength, ContentType, ETag, HeaderMapExt, IfNoneMatch};
use http::{Method, Request, StatusCode};
use rust_embed::RustEmbed;
use tower::Service;
// TODO: read the assets live from the filesystem
/// Embedded public assets
#[derive(RustEmbed, Clone, Debug)]
#[folder = "public/"]
pub struct Assets;
/// Embedded public assets
#[derive(RustEmbed, Clone)]
#[folder = "public/"]
pub struct Assets;
impl Assets {
fn get_response(
is_head: bool,
path: &str,
if_none_match: Option<IfNoneMatch>,
) -> Option<Response> {
let asset = Self::get(path)?;
impl Assets {
fn get_response(
is_head: bool,
path: &str,
if_none_match: Option<IfNoneMatch>,
) -> Option<Response> {
let asset = Self::get(path)?;
let etag = {
let hash = asset.metadata.sha256_hash();
let mut buf = String::with_capacity(2 + hash.len() * 2);
write!(buf, "\"").unwrap();
for byte in hash {
write!(buf, "{:02x}", byte).unwrap();
}
write!(buf, "\"").unwrap();
buf
};
let etag: ETag = etag.parse().unwrap();
let etag: String = asset
.metadata
.sha256_hash()
.iter()
.map(|x| format!("{:02x}", x))
.collect();
let etag: ETag = format!("\"{}\"", etag).parse().unwrap();
if let Some(if_none_match) = if_none_match {
if if_none_match.precondition_passes(&etag) {
return Some(StatusCode::NOT_MODIFIED.into_response());
if let Some(if_none_match) = if_none_match {
if if_none_match.precondition_passes(&etag) {
return Some(StatusCode::NOT_MODIFIED.into_response());
}
}
let len = asset.data.len().try_into().unwrap();
let mime = mime_guess::from_path(path).first_or_octet_stream();
let res = if is_head {
(
StatusCode::OK,
TypedHeader(ContentType::from(mime)),
TypedHeader(ContentLength(len)),
TypedHeader(etag),
)
.into_response()
} else {
(
StatusCode::OK,
TypedHeader(ContentType::from(mime)),
TypedHeader(ContentLength(len)),
TypedHeader(etag),
asset.data,
)
.into_response()
};
Some(res)
}
}
impl<B> Service<Request<B>> for Assets {
type Response = Response;
type Error = std::io::Error;
type Future = Ready<Result<Self::Response, Self::Error>>;
fn poll_ready(
&mut self,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
std::task::Poll::Ready(Ok(()))
}
let len = asset.data.len().try_into().unwrap();
let mime = mime_guess::from_path(path).first_or_octet_stream();
fn call(&mut self, req: Request<B>) -> Self::Future {
let (parts, _body) = req.into_parts();
let path = parts.uri.path().trim_start_matches('/');
let if_none_match = parts.headers.typed_get();
let is_head = match parts.method {
Method::GET => false,
Method::HEAD => true,
_ => return ready(Ok(StatusCode::METHOD_NOT_ALLOWED.into_response())),
};
let res = if is_head {
(
StatusCode::OK,
TypedHeader(ContentType::from(mime)),
TypedHeader(ContentLength(len)),
TypedHeader(etag),
)
.into_response()
} else {
(
StatusCode::OK,
TypedHeader(ContentType::from(mime)),
TypedHeader(ContentLength(len)),
TypedHeader(etag),
asset.data,
)
.into_response()
};
// TODO: support range requests
let response = Self::get_response(is_head, path, if_none_match)
.unwrap_or_else(|| StatusCode::NOT_FOUND.into_response());
ready(Ok(response))
}
}
Some(res)
/// Serve static files
#[must_use]
pub fn service() -> Assets {
Assets
}
}
impl<B> Service<Request<B>> for Assets {
type Response = Response;
type Error = Infallible;
type Future = Ready<Result<Self::Response, Self::Error>>;
#[cfg(feature = "dev")]
mod builtin {
use std::path::PathBuf;
fn poll_ready(
&mut self,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
std::task::Poll::Ready(Ok(()))
}
use tower_http::services::ServeDir;
fn call(&mut self, req: Request<B>) -> Self::Future {
let path = req.uri().path().trim_start_matches('/');
let if_none_match = req.headers().typed_get();
let is_head = match *req.method() {
Method::GET => false,
Method::HEAD => true,
_ => return ready(Ok(StatusCode::METHOD_NOT_ALLOWED.into_response())),
};
// TODO: support range requests
let response = Self::get_response(is_head, path, if_none_match)
.unwrap_or_else(|| StatusCode::NOT_FOUND.into_response());
ready(Ok(response))
/// Serve static files in dev mode
#[must_use]
pub fn service() -> ServeDir {
let path = PathBuf::from(format!("{}/public", env!("CARGO_MANIFEST_DIR")));
ServeDir::new(path).append_index_html_on_directories(false)
}
}
use std::{convert::Infallible, future::ready, path::PathBuf};
use axum::{
body::HttpBody,
response::Response,
routing::{on_service, MethodFilter},
};
use http::{Request, StatusCode};
use tower::{util::BoxCloneService, ServiceExt};
use tower_http::services::ServeDir;
/// Serve static files
#[must_use]
pub fn service<B: HttpBody + Send + 'static>(
path: &Option<PathBuf>,
) -> BoxCloneService<Request<B>, Response, Infallible> {
let builtin = self::builtin::service();
let svc = if let Some(path) = path {
// TODO: fallback seems to have issues
let handler = ServeDir::new(path)
.append_index_html_on_directories(false)
.fallback(builtin);
on_service(MethodFilter::HEAD | MethodFilter::GET, handler)
} else {
on_service(MethodFilter::HEAD | MethodFilter::GET, builtin)
};
svc.handle_error(|_| ready(StatusCode::INTERNAL_SERVER_ERROR))
.boxed_clone()
}

View File

@ -6,7 +6,7 @@ edition = "2021"
license = "Apache-2.0"
[dependencies]
tokio = "1.17.0"
tokio = "1.18.0"
sqlx = { version = "0.5.13", features = ["runtime-tokio-rustls", "postgres", "migrate", "chrono", "offline", "json"] }
chrono = { version = "0.4.19", features = ["serde"] }
serde = { version = "1.0.136", features = ["derive"] }

View File

@ -6,7 +6,7 @@ edition = "2021"
license = "Apache-2.0"
[dependencies]
tokio = "1.17.0"
tokio = "1.18.0"
async-trait = "0.1.53"
tokio-stream = "0.1.8"
futures-util = "0.3.21"

View File

@ -10,7 +10,7 @@ dev = []
[dependencies]
tracing = "0.1.34"
tokio = { version = "1.17.0", features = ["macros"] }
tokio = { version = "1.18.0", features = ["macros"] }
anyhow = "1.0.57"
thiserror = "1.0.30"