Thumbnail and audio endpoints no longer encode payloads
This commit is contained in:
parent
f27bc4ccfc
commit
39c8cf7595
3 changed files with 70 additions and 9 deletions
|
@ -2,17 +2,17 @@ use actix_files::NamedFile;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
client::HttpError,
|
client::HttpError,
|
||||||
delete,
|
delete,
|
||||||
dev::{MessageBody, Payload, Service, ServiceRequest, ServiceResponse},
|
dev::{BodyEncoding, MessageBody, Payload, Service, ServiceRequest, ServiceResponse},
|
||||||
error::{BlockingError, ErrorForbidden, ErrorInternalServerError, ErrorUnauthorized},
|
error::{BlockingError, ErrorForbidden, ErrorInternalServerError, ErrorUnauthorized},
|
||||||
get,
|
get,
|
||||||
http::StatusCode,
|
http::{ContentEncoding, StatusCode},
|
||||||
post, put,
|
post, put,
|
||||||
web::{self, Data, Json, JsonConfig, ServiceConfig},
|
web::{self, Data, Json, JsonConfig, ServiceConfig},
|
||||||
FromRequest, HttpMessage, HttpRequest, HttpResponse, ResponseError,
|
FromRequest, HttpMessage, HttpRequest, HttpResponse, Responder, ResponseError,
|
||||||
};
|
};
|
||||||
use actix_web_httpauth::extractors::{basic::BasicAuth, bearer::BearerAuth};
|
use actix_web_httpauth::extractors::{basic::BasicAuth, bearer::BearerAuth};
|
||||||
use cookie::{self, *};
|
use cookie::{self, *};
|
||||||
use futures_util::future::{err, ok};
|
use futures_util::future::{err, ok, ready, Ready};
|
||||||
use percent_encoding::percent_decode_str;
|
use percent_encoding::percent_decode_str;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -378,6 +378,34 @@ fn add_auth_cookies<T>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MediaFile {
|
||||||
|
named_file: NamedFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MediaFile {
|
||||||
|
fn new(named_file: NamedFile) -> Self {
|
||||||
|
Self {
|
||||||
|
named_file: named_file,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Responder for MediaFile {
|
||||||
|
type Error = actix_web::Error;
|
||||||
|
type Future = Ready<Result<HttpResponse, actix_web::Error>>;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn block<F, I, E>(f: F) -> Result<I, APIError>
|
async fn block<F, I, E>(f: F) -> Result<I, APIError>
|
||||||
where
|
where
|
||||||
F: FnOnce() -> Result<I, E> + Send + 'static,
|
F: FnOnce() -> Result<I, E> + Send + 'static,
|
||||||
|
@ -681,7 +709,7 @@ async fn get_audio(
|
||||||
vfs_manager: Data<vfs::Manager>,
|
vfs_manager: Data<vfs::Manager>,
|
||||||
_auth: Auth,
|
_auth: Auth,
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
) -> Result<NamedFile, APIError> {
|
) -> Result<MediaFile, APIError> {
|
||||||
let audio_path = block(move || {
|
let audio_path = block(move || {
|
||||||
let vfs = vfs_manager.get_vfs()?;
|
let vfs = vfs_manager.get_vfs()?;
|
||||||
let path = percent_decode_str(&(path.0)).decode_utf8_lossy();
|
let path = percent_decode_str(&(path.0)).decode_utf8_lossy();
|
||||||
|
@ -691,7 +719,7 @@ async fn get_audio(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let named_file = NamedFile::open(&audio_path).map_err(|_| APIError::AudioFileIOError)?;
|
let named_file = NamedFile::open(&audio_path).map_err(|_| APIError::AudioFileIOError)?;
|
||||||
Ok(named_file)
|
Ok(MediaFile::new(named_file))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/thumbnail/{path:.*}")]
|
#[get("/thumbnail/{path:.*}")]
|
||||||
|
@ -701,7 +729,7 @@ async fn get_thumbnail(
|
||||||
_auth: Auth,
|
_auth: Auth,
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
options_input: web::Query<dto::ThumbnailOptions>,
|
options_input: web::Query<dto::ThumbnailOptions>,
|
||||||
) -> Result<NamedFile, APIError> {
|
) -> Result<MediaFile, APIError> {
|
||||||
let options = thumbnail::Options::from(options_input.0);
|
let options = thumbnail::Options::from(options_input.0);
|
||||||
|
|
||||||
let thumbnail_path = block(move || {
|
let thumbnail_path = block(move || {
|
||||||
|
@ -719,7 +747,7 @@ async fn get_thumbnail(
|
||||||
let named_file =
|
let named_file =
|
||||||
NamedFile::open(&thumbnail_path).map_err(|_| APIError::ThumbnailFileIOError)?;
|
NamedFile::open(&thumbnail_path).map_err(|_| APIError::ThumbnailFileIOError)?;
|
||||||
|
|
||||||
Ok(named_file)
|
Ok(MediaFile::new(named_file))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/playlists")]
|
#[get("/playlists")]
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::app::{config, ddns, settings, thumbnail, user, vfs};
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
|
|
||||||
pub const API_MAJOR_VERSION: i32 = 6;
|
pub const API_MAJOR_VERSION: i32 = 6;
|
||||||
pub const API_MINOR_VERSION: i32 = 0;
|
pub const API_MINOR_VERSION: i32 = 1;
|
||||||
pub const COOKIE_SESSION: &str = "session";
|
pub const COOKIE_SESSION: &str = "session";
|
||||||
pub const COOKIE_USERNAME: &str = "username";
|
pub const COOKIE_USERNAME: &str = "username";
|
||||||
pub const COOKIE_ADMIN: &str = "admin";
|
pub const COOKIE_ADMIN: &str = "admin";
|
||||||
|
|
|
@ -34,6 +34,39 @@ fn audio_golden_path() {
|
||||||
let response = service.fetch_bytes(&request);
|
let response = service.fetch_bytes(&request);
|
||||||
assert_eq!(response.status(), StatusCode::OK);
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
assert_eq!(response.body().len(), 24_142);
|
assert_eq!(response.body().len(), 24_142);
|
||||||
|
assert_eq!(
|
||||||
|
response.headers().get(header::CONTENT_LENGTH).unwrap(),
|
||||||
|
"24142"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn audio_does_not_encode_content() {
|
||||||
|
let mut service = ServiceType::new(&test_name!());
|
||||||
|
service.complete_initial_setup();
|
||||||
|
service.login_admin();
|
||||||
|
service.index();
|
||||||
|
service.login();
|
||||||
|
|
||||||
|
let path: PathBuf = [TEST_MOUNT_NAME, "Khemmis", "Hunted", "02 - Candlelight.mp3"]
|
||||||
|
.iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut request = protocol::audio(&path);
|
||||||
|
let headers = request.headers_mut();
|
||||||
|
headers.append(
|
||||||
|
header::ACCEPT_ENCODING,
|
||||||
|
HeaderValue::from_str("gzip, deflate, br").unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let response = service.fetch_bytes(&request);
|
||||||
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
|
assert_eq!(response.body().len(), 24_142);
|
||||||
|
assert_eq!(response.headers().get(header::TRANSFER_ENCODING), None);
|
||||||
|
assert_eq!(
|
||||||
|
response.headers().get(header::CONTENT_LENGTH).unwrap(),
|
||||||
|
"24142"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Add table
Reference in a new issue