Migrate to actix-web 4 (#171)
* Migrate to actix-web 4 * Change expected swagger test status code * update tokio to 1.0 * fix clippy warnings
This commit is contained in:
parent
90fd6bbcc9
commit
374d0ca56f
11 changed files with 707 additions and 1001 deletions
1550
Cargo.lock
generated
1550
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
32
Cargo.toml
32
Cargo.toml
|
@ -11,39 +11,41 @@ bundle-sqlite = ["libsqlite3-sys"]
|
|||
ui = ["native-windows-gui", "native-windows-derive"]
|
||||
|
||||
[dependencies]
|
||||
actix-files = { version = "0.5" }
|
||||
actix-web = { version = "3" }
|
||||
actix-web-httpauth = { version = "0.5.1" }
|
||||
anyhow = "1.0.52"
|
||||
actix-files = { version = "0.6" }
|
||||
actix-web = { version = "4" }
|
||||
actix-web-httpauth = { version = "0.6" }
|
||||
actix-test = "=0.1.0-beta.13"
|
||||
anyhow = "1.0.56"
|
||||
ape = "0.4.0"
|
||||
base64 = "0.13"
|
||||
branca = "0.10.0"
|
||||
cookie = { version = "0.14", features = ["signed", "key-expansion"] }
|
||||
branca = "0.10.1"
|
||||
cookie = { version = "0.16", features = ["signed", "key-expansion"] }
|
||||
crossbeam-channel = "0.5"
|
||||
diesel_migrations = { version = "1.4", features = ["sqlite"] }
|
||||
futures-util = { version = "0.3" }
|
||||
getopts = "0.2.21"
|
||||
http = "0.2.6"
|
||||
id3 = "1.0.2"
|
||||
libsqlite3-sys = { version = "0.22", features = ["bundled", "bundled-windows"], optional = true }
|
||||
lewton = "0.10.2"
|
||||
libsqlite3-sys = { version = "0.22", features = ["bundled", "bundled-windows"], optional = true }
|
||||
log = "0.4.14"
|
||||
metaflac = "0.2.5"
|
||||
mp3-duration = "0.1.10"
|
||||
mp4ameta = "0.11.0"
|
||||
num_cpus = "1.13.1"
|
||||
opus_headers = "0.1.2"
|
||||
pbkdf2 = "0.11"
|
||||
percent-encoding = "2.1"
|
||||
pbkdf2 = "0.10"
|
||||
rand = "0.8"
|
||||
rayon = "1.5"
|
||||
regex = "1.5.4"
|
||||
regex = "1.5.5"
|
||||
rustfm-scrobble = "1.1.1"
|
||||
serde = { version = "1.0.133", features = ["derive"] }
|
||||
serde_derive = "1.0.133"
|
||||
serde_json = "1.0.74"
|
||||
simplelog = "0.11.1"
|
||||
serde = { version = "1.0.136", features = ["derive"] }
|
||||
serde_derive = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
simplelog = "0.11.2"
|
||||
thiserror = "1.0.30"
|
||||
tokio = "1.0"
|
||||
toml = "0.5"
|
||||
ureq = "1.5.5"
|
||||
url = "2.2"
|
||||
|
@ -54,7 +56,7 @@ default_features = false
|
|||
features = ["libsqlite3-sys", "r2d2", "sqlite", "64-column-tables"]
|
||||
|
||||
[dependencies.image]
|
||||
version = "0.23.14"
|
||||
version = "0.24.1"
|
||||
default_features = false
|
||||
features = ["bmp", "gif", "jpeg", "png"]
|
||||
|
||||
|
@ -64,7 +66,7 @@ native-windows-derive = {version = "1.0.4", optional = true }
|
|||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
daemonize = "0.4.1"
|
||||
sd-notify = "0.3.0"
|
||||
sd-notify = "0.4.0"
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
winres = "0.1"
|
||||
|
|
|
@ -33,13 +33,13 @@ impl Manager {
|
|||
|
||||
if let Some(mount_dirs) = &config.mount_dirs {
|
||||
self.vfs_manager
|
||||
.set_mount_dirs(&mount_dirs)
|
||||
.set_mount_dirs(mount_dirs)
|
||||
.map_err(|_| Error::Unspecified)?;
|
||||
}
|
||||
|
||||
if let Some(ddns_config) = &config.ydns {
|
||||
self.ddns_manager
|
||||
.set_config(&ddns_config)
|
||||
.set_config(ddns_config)
|
||||
.map_err(|_| Error::Unspecified)?;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ impl Index {
|
|||
settings_manager,
|
||||
|
||||
pending_reindex: Arc::new((
|
||||
#[allow(clippy::clippy::mutex_atomic)]
|
||||
#[allow(clippy::mutex_atomic)]
|
||||
Mutex::new(false),
|
||||
Condvar::new(),
|
||||
)),
|
||||
|
|
|
@ -29,7 +29,7 @@ impl Manager {
|
|||
fn get_thumbnail_path(&self, image_path: &Path, thumbnailoptions: &Options) -> PathBuf {
|
||||
let hash = Manager::hash(image_path, thumbnailoptions);
|
||||
let mut thumbnail_path = self.thumbnails_dir_path.clone();
|
||||
thumbnail_path.push(format!("{}.jpg", hash.to_string()));
|
||||
thumbnail_path.push(format!("{}.jpg", hash));
|
||||
thumbnail_path
|
||||
}
|
||||
|
||||
|
|
|
@ -40,19 +40,19 @@ fn read_flac(path: &Path) -> Result<DynamicImage> {
|
|||
fn read_mp3(path: &Path) -> Result<DynamicImage> {
|
||||
let tag = id3::Tag::read_from_path(path)?;
|
||||
|
||||
read_id3(&path, &tag)
|
||||
read_id3(path, &tag)
|
||||
}
|
||||
|
||||
fn read_aiff(path: &Path) -> Result<DynamicImage> {
|
||||
let tag = id3::Tag::read_from_aiff_path(path)?;
|
||||
|
||||
read_id3(&path, &tag)
|
||||
read_id3(path, &tag)
|
||||
}
|
||||
|
||||
fn read_wave(path: &Path) -> Result<DynamicImage> {
|
||||
let tag = id3::Tag::read_from_wav_path(path)?;
|
||||
|
||||
read_id3(&path, &tag)
|
||||
read_id3(path, &tag)
|
||||
}
|
||||
|
||||
fn read_id3(path: &Path, tag: &id3::Tag) -> Result<DynamicImage> {
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
use actix_files::NamedFile;
|
||||
use actix_web::body::{BoxBody, MessageBody};
|
||||
use actix_web::http::header::ContentEncoding;
|
||||
use actix_web::{
|
||||
client::HttpError,
|
||||
delete,
|
||||
dev::{BodyEncoding, MessageBody, Payload, Service, ServiceRequest, ServiceResponse},
|
||||
error::{BlockingError, ErrorForbidden, ErrorInternalServerError, ErrorUnauthorized},
|
||||
dev::{Payload, Service, ServiceRequest, ServiceResponse},
|
||||
error::{ErrorForbidden, ErrorInternalServerError, ErrorUnauthorized},
|
||||
get,
|
||||
http::{ContentEncoding, StatusCode},
|
||||
http::StatusCode,
|
||||
post, put,
|
||||
web::{self, Data, Json, JsonConfig, ServiceConfig},
|
||||
FromRequest, HttpMessage, HttpRequest, HttpResponse, Responder, ResponseError,
|
||||
FromRequest, HttpRequest, HttpResponse, Responder, ResponseError,
|
||||
};
|
||||
use actix_web_httpauth::extractors::{basic::BasicAuth, bearer::BearerAuth};
|
||||
use cookie::{self, *};
|
||||
use futures_util::future::{err, ok, ready, Ready};
|
||||
use futures_util::future::{err, ok};
|
||||
use percent_encoding::percent_decode_str;
|
||||
use std::future::Future;
|
||||
use std::ops::Deref;
|
||||
|
@ -113,7 +114,7 @@ impl Cookies {
|
|||
}
|
||||
|
||||
fn add_signed(&mut self, cookie: Cookie<'static>) {
|
||||
self.jar.signed(&self.key).add(cookie);
|
||||
self.jar.signed_mut(&self.key).add(cookie);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -129,7 +130,6 @@ impl Cookies {
|
|||
impl FromRequest for Cookies {
|
||||
type Error = actix_web::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
|
||||
type Config = ();
|
||||
|
||||
fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future {
|
||||
let request_cookies = match request.cookies() {
|
||||
|
@ -168,7 +168,6 @@ struct Auth {
|
|||
impl FromRequest for Auth {
|
||||
type Error = actix_web::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
|
||||
type Config = ();
|
||||
|
||||
fn from_request(request: &HttpRequest, payload: &mut Payload) -> Self::Future {
|
||||
let user_manager = match request.app_data::<Data<user::Manager>>() {
|
||||
|
@ -256,7 +255,6 @@ struct AdminRights {
|
|||
impl FromRequest for AdminRights {
|
||||
type Error = actix_web::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
|
||||
type Config = ();
|
||||
|
||||
fn from_request(request: &HttpRequest, payload: &mut Payload) -> Self::Future {
|
||||
let user_manager = match request.app_data::<Data<user::Manager>>() {
|
||||
|
@ -289,11 +287,10 @@ impl FromRequest for AdminRights {
|
|||
|
||||
pub fn http_auth_middleware<
|
||||
B: MessageBody + 'static,
|
||||
S: Service<Response = ServiceResponse<B>, Request = ServiceRequest, Error = actix_web::Error>
|
||||
+ 'static,
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error> + 'static,
|
||||
>(
|
||||
request: ServiceRequest,
|
||||
service: &mut S,
|
||||
service: &S,
|
||||
) -> Pin<Box<dyn Future<Output = Result<ServiceResponse<B>, actix_web::Error>>>> {
|
||||
let user_manager = match request.app_data::<Data<user::Manager>>() {
|
||||
Some(m) => m.clone(),
|
||||
|
@ -303,10 +300,7 @@ pub fn http_auth_middleware<
|
|||
let (request, mut payload) = request.into_parts();
|
||||
let auth_future = Auth::from_request(&request, &mut payload);
|
||||
let cookies_future = Cookies::from_request(&request, &mut payload);
|
||||
let request = match ServiceRequest::from_parts(request, payload) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return Box::pin(err(ErrorInternalServerError(APIError::Unspecified))),
|
||||
};
|
||||
let request = ServiceRequest::from_parts(request, payload);
|
||||
|
||||
let response_future = service.call(request);
|
||||
Box::pin(async move {
|
||||
|
@ -339,7 +333,7 @@ fn add_auth_cookies<T>(
|
|||
cookies: &Cookies,
|
||||
username: &str,
|
||||
is_admin: bool,
|
||||
) -> Result<(), HttpError> {
|
||||
) -> Result<(), http::Error> {
|
||||
let mut cookies = cookies.clone();
|
||||
|
||||
cookies.add_signed(
|
||||
|
@ -384,25 +378,20 @@ struct MediaFile {
|
|||
|
||||
impl MediaFile {
|
||||
fn new(named_file: NamedFile) -> Self {
|
||||
Self {
|
||||
named_file: named_file,
|
||||
}
|
||||
Self { named_file }
|
||||
}
|
||||
}
|
||||
|
||||
impl Responder for MediaFile {
|
||||
type Error = actix_web::Error;
|
||||
type Future = Ready<Result<HttpResponse, actix_web::Error>>;
|
||||
type Body = BoxBody;
|
||||
|
||||
fn respond_to(self, req: &HttpRequest) -> Self::Future {
|
||||
let mut response = self.named_file.into_response(req);
|
||||
if let Ok(r) = response.as_mut() {
|
||||
// Intentionally turn off content encoding for media files because:
|
||||
// 1. There is little value in compressing files that are already compressed (mp3, jpg, etc.)
|
||||
// 2. The Content-Length header is incompatible with content encoding (other than identity), and can be valuable for clients
|
||||
r.encoding(ContentEncoding::Identity);
|
||||
}
|
||||
return ready(response);
|
||||
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
|
||||
// Intentionally turn off content encoding for media files because:
|
||||
// 1. There is little value in compressing files that are already compressed (mp3, jpg, etc.)
|
||||
// 2. The Content-Length header is incompatible with content encoding (other than identity), and can be valuable for clients
|
||||
self.named_file
|
||||
.set_content_encoding(ContentEncoding::Identity)
|
||||
.into_response(req)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -412,10 +401,10 @@ where
|
|||
I: Send + 'static,
|
||||
E: Send + std::fmt::Debug + 'static + Into<APIError>,
|
||||
{
|
||||
actix_web::web::block(f).await.map_err(|e| match e {
|
||||
BlockingError::Error(e) => e.into(),
|
||||
BlockingError::Canceled => APIError::Unspecified,
|
||||
})
|
||||
actix_web::web::block(f)
|
||||
.await
|
||||
.map_err(|_| APIError::Unspecified)
|
||||
.and_then(|r| r.map_err(|e| e.into()))
|
||||
}
|
||||
|
||||
#[get("/version")]
|
||||
|
@ -488,8 +477,8 @@ async fn put_mount_dirs(
|
|||
new_mount_dirs: Json<Vec<dto::MountDir>>,
|
||||
) -> Result<HttpResponse, APIError> {
|
||||
let new_mount_dirs: Vec<MountDir> = new_mount_dirs
|
||||
.to_owned()
|
||||
.into_iter()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|m| m.into())
|
||||
.collect();
|
||||
block(move || vfs_manager.set_mount_dirs(&new_mount_dirs)).await?;
|
||||
|
@ -646,7 +635,7 @@ async fn browse(
|
|||
path: web::Path<String>,
|
||||
) -> Result<Json<Vec<index::CollectionFile>>, APIError> {
|
||||
let result = block(move || {
|
||||
let path = percent_decode_str(&(path.0)).decode_utf8_lossy();
|
||||
let path = percent_decode_str(&path).decode_utf8_lossy();
|
||||
index.browse(Path::new(path.as_ref()))
|
||||
})
|
||||
.await?;
|
||||
|
@ -666,7 +655,7 @@ async fn flatten(
|
|||
path: web::Path<String>,
|
||||
) -> Result<Json<Vec<index::Song>>, APIError> {
|
||||
let songs = block(move || {
|
||||
let path = percent_decode_str(&(path.0)).decode_utf8_lossy();
|
||||
let path = percent_decode_str(&path).decode_utf8_lossy();
|
||||
index.flatten(Path::new(path.as_ref()))
|
||||
})
|
||||
.await?;
|
||||
|
@ -712,7 +701,7 @@ async fn get_audio(
|
|||
) -> Result<MediaFile, APIError> {
|
||||
let audio_path = block(move || {
|
||||
let vfs = vfs_manager.get_vfs()?;
|
||||
let path = percent_decode_str(&(path.0)).decode_utf8_lossy();
|
||||
let path = percent_decode_str(&path).decode_utf8_lossy();
|
||||
vfs.virtual_to_real(Path::new(path.as_ref()))
|
||||
.map_err(|_| APIError::VFSPathNotFound)
|
||||
})
|
||||
|
@ -734,7 +723,7 @@ async fn get_thumbnail(
|
|||
|
||||
let thumbnail_path = block(move || {
|
||||
let vfs = vfs_manager.get_vfs()?;
|
||||
let path = percent_decode_str(&(path.0)).decode_utf8_lossy();
|
||||
let path = percent_decode_str(&path).decode_utf8_lossy();
|
||||
let image_path = vfs
|
||||
.virtual_to_real(Path::new(path.as_ref()))
|
||||
.map_err(|_| APIError::VFSPathNotFound)?;
|
||||
|
@ -806,7 +795,7 @@ async fn lastfm_now_playing(
|
|||
if !user_manager.is_lastfm_linked(&auth.username) {
|
||||
return Err(APIError::LastFMAccountNotLinked);
|
||||
}
|
||||
let path = percent_decode_str(&(path.0)).decode_utf8_lossy();
|
||||
let path = percent_decode_str(&path).decode_utf8_lossy();
|
||||
lastfm_manager.now_playing(&auth.username, Path::new(path.as_ref()))?;
|
||||
Ok(())
|
||||
})
|
||||
|
@ -825,7 +814,7 @@ async fn lastfm_scrobble(
|
|||
if !user_manager.is_lastfm_linked(&auth.username) {
|
||||
return Err(APIError::LastFMAccountNotLinked);
|
||||
}
|
||||
let path = percent_decode_str(&(path.0)).decode_utf8_lossy();
|
||||
let path = percent_decode_str(&path).decode_utf8_lossy();
|
||||
lastfm_manager.scrobble(&auth.username, Path::new(path.as_ref()))?;
|
||||
Ok(())
|
||||
})
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use actix_web::{
|
||||
middleware::{normalize::TrailingSlash, Compress, Logger, NormalizePath},
|
||||
middleware::{Compress, Logger, NormalizePath},
|
||||
rt::System,
|
||||
web::{self, ServiceConfig},
|
||||
App as ActixApp, HttpServer,
|
||||
};
|
||||
use anyhow::*;
|
||||
use log::error;
|
||||
|
||||
use crate::app::App;
|
||||
|
@ -31,7 +30,7 @@ pub fn make_config(app: App) -> impl FnOnce(&mut ServiceConfig) + Clone {
|
|||
web::scope("/api")
|
||||
.configure(api::make_config())
|
||||
.wrap_fn(api::http_auth_middleware)
|
||||
.wrap(NormalizePath::new(TrailingSlash::Trim)),
|
||||
.wrap(NormalizePath::trim()),
|
||||
)
|
||||
.service(
|
||||
actix_files::Files::new("/swagger", app.swagger_dir_path)
|
||||
|
@ -46,9 +45,9 @@ pub fn make_config(app: App) -> impl FnOnce(&mut ServiceConfig) + Clone {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn run(app: App) -> Result<()> {
|
||||
System::run(move || {
|
||||
let address = format!("0.0.0.0:{}", app.port);
|
||||
pub fn run(app: App) -> anyhow::Result<()> {
|
||||
let address = ("0.0.0.0", app.port);
|
||||
System::new().block_on(
|
||||
HttpServer::new(move || {
|
||||
ActixApp::new()
|
||||
.wrap(Logger::default())
|
||||
|
@ -57,9 +56,11 @@ pub fn run(app: App) -> Result<()> {
|
|||
})
|
||||
.disable_signals()
|
||||
.bind(address)
|
||||
.map(|server| server.run())
|
||||
.map_err(|e| error!("Error starting HTTP server: {:?}", e))
|
||||
.ok();
|
||||
})?;
|
||||
.map_err(|e| {
|
||||
error!("Error starting HTTP server: {:?}", e);
|
||||
e
|
||||
})?
|
||||
.run()
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use actix_test::TestServer;
|
||||
use actix_web::{
|
||||
middleware::{Compress, Logger},
|
||||
rt::{System, SystemRunner},
|
||||
test,
|
||||
test::*,
|
||||
web::Bytes,
|
||||
App as ActixApp,
|
||||
};
|
||||
|
@ -44,7 +43,7 @@ impl ActixTestService {
|
|||
.timeout(std::time::Duration::from_secs(30));
|
||||
|
||||
for (name, value) in request.headers() {
|
||||
actix_request = actix_request.set_header(name, value.clone());
|
||||
actix_request = actix_request.insert_header((name, value.clone()));
|
||||
}
|
||||
|
||||
if let Some(ref authorization) = self.authorization {
|
||||
|
@ -92,8 +91,8 @@ impl TestService for ActixTestService {
|
|||
|
||||
let app = App::new(5050, paths).unwrap();
|
||||
|
||||
let system_runner = System::new("test");
|
||||
let server = test::start(move || {
|
||||
let system_runner = System::new();
|
||||
let server = actix_test::start(move || {
|
||||
let config = make_config(app.clone());
|
||||
ActixApp::new()
|
||||
.wrap(Logger::default())
|
||||
|
|
|
@ -61,7 +61,7 @@ pub enum ThumbnailSize {
|
|||
Native,
|
||||
}
|
||||
|
||||
#[allow(clippy::clippy::clippy::from_over_into)]
|
||||
#[allow(clippy::from_over_into)]
|
||||
impl Into<Option<u32>> for ThumbnailSize {
|
||||
fn into(self) -> Option<u32> {
|
||||
match self {
|
||||
|
|
|
@ -8,8 +8,7 @@ fn can_get_swagger_index() {
|
|||
let mut service = ServiceType::new(&test_name!());
|
||||
let request = protocol::swagger_index();
|
||||
let response = service.fetch(&request);
|
||||
let status = response.status();
|
||||
assert_eq!(status, StatusCode::FOUND);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Add table
Reference in a new issue