mirror of
https://github.com/localsend/localsend.git
synced 2026-06-22 20:00:07 +00:00
feat: prepare v2 http server
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
pub(crate) mod v2;
|
||||
pub(crate) mod v3;
|
||||
pub(crate) mod web;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
use hyper::body::Incoming;
|
||||
use hyper::StatusCode;
|
||||
use crate::http::dto::{RegisterDto, RegisterResponseDto};
|
||||
use crate::http::server::{AppState, JsonResponse, RequestClientInfo};
|
||||
use crate::http::server::collect_to_json::CollectToJson;
|
||||
use crate::http::server::error::AppError;
|
||||
|
||||
pub(crate) async fn register(
|
||||
body: Incoming,
|
||||
state: AppState,
|
||||
client_info: RequestClientInfo,
|
||||
) -> Result<JsonResponse<RegisterResponseDto>, AppError> {
|
||||
let payload = body.collect_to_json::<RegisterDto>().await?;
|
||||
|
||||
let info = state.info.lock().await.clone();
|
||||
let has_web_interface = state.web.lock().await.is_some();
|
||||
|
||||
Ok(JsonResponse {
|
||||
status: StatusCode::OK,
|
||||
body: RegisterResponseDto {
|
||||
alias: info.alias,
|
||||
version: info.version,
|
||||
device_model: info.device_model,
|
||||
device_type: info.device_type,
|
||||
token: info.token,
|
||||
has_web_interface,
|
||||
},
|
||||
})
|
||||
}
|
||||
+70
-18
@@ -6,6 +6,7 @@ mod error;
|
||||
use crate::crypto::cert::public_key_from_cert_der;
|
||||
use crate::http::dto::ErrorResponse;
|
||||
use crate::http::server::client_cert_verifier::CustomClientCertVerifier;
|
||||
use crate::http::server::controller::web::WebPageState;
|
||||
use crate::http::server::error::AppError;
|
||||
use crate::http::state::ClientInfo;
|
||||
use bytes::Bytes;
|
||||
@@ -24,7 +25,6 @@ use std::num::NonZeroUsize;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{oneshot, Mutex};
|
||||
use crate::http::server::controller::web::WebPageState;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AppState {
|
||||
@@ -67,6 +67,7 @@ impl LsHttpServer {
|
||||
port: u16,
|
||||
tls_config: Option<TlsConfig>,
|
||||
info: ClientInfo,
|
||||
legacy_enabled: bool,
|
||||
) -> anyhow::Result<LsHttpServer> {
|
||||
let ipv4_socket_addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), port);
|
||||
let ipv6_socket_addr = SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), port);
|
||||
@@ -79,11 +80,11 @@ impl LsHttpServer {
|
||||
let state = state.clone();
|
||||
async move {
|
||||
tokio::select! {
|
||||
_ = start_server_with_addr(ipv4_socket_addr, tls_config.clone(), state.clone()) => {
|
||||
_ = start_server_with_addr(ipv4_socket_addr, tls_config.clone(), state.clone(), legacy_enabled) => {
|
||||
tracing::info!("Server stopped on: {}", ipv4_socket_addr);
|
||||
}
|
||||
_ = async {
|
||||
if start_server_with_addr(ipv6_socket_addr, tls_config, state).await.is_err() {
|
||||
if start_server_with_addr(ipv6_socket_addr, tls_config, state, legacy_enabled).await.is_err() {
|
||||
tracing::warn!("Failed to start server on: {}", ipv6_socket_addr);
|
||||
|
||||
// Keep the future running forever, so we continue using "ipv4 only" even if ipv6 fails.
|
||||
@@ -124,6 +125,7 @@ async fn start_server_with_addr(
|
||||
socket_addr: SocketAddr,
|
||||
tls_config: Option<TlsConfig>,
|
||||
app_state: AppState,
|
||||
legacy_enabled: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let _ = rustls::crypto::ring::default_provider().install_default();
|
||||
|
||||
@@ -177,7 +179,7 @@ async fn start_server_with_addr(
|
||||
req.extensions_mut()
|
||||
.insert::<RequestClientInfo>(client_info.clone());
|
||||
req.extensions_mut().insert::<AppState>(app_state.clone());
|
||||
handle_request(req)
|
||||
handle_request(req, legacy_enabled)
|
||||
}),
|
||||
)
|
||||
.await
|
||||
@@ -194,7 +196,7 @@ async fn start_server_with_addr(
|
||||
},
|
||||
);
|
||||
req.extensions_mut().insert::<AppState>(app_state.clone());
|
||||
handle_request(req)
|
||||
handle_request(req, legacy_enabled)
|
||||
}),
|
||||
)
|
||||
.await
|
||||
@@ -251,19 +253,24 @@ impl RequestClientInfo {
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_request(req: Request<Incoming>) -> Result<Response<Full<Bytes>>, hyper::Error> {
|
||||
Ok(handle_request_inner(req).await.unwrap_or_else(|err| {
|
||||
tracing::error!("Error handling request: {err:?}");
|
||||
JsonResponse {
|
||||
status: err.status,
|
||||
body: ErrorResponse {
|
||||
message: err
|
||||
.message
|
||||
.unwrap_or_else(|| "Internal Server Error".to_string()),
|
||||
},
|
||||
}
|
||||
.into_response()
|
||||
}))
|
||||
async fn handle_request(
|
||||
req: Request<Incoming>,
|
||||
legacy_enabled: bool,
|
||||
) -> Result<Response<Full<Bytes>>, hyper::Error> {
|
||||
Ok(handle_request_inner(req, legacy_enabled)
|
||||
.await
|
||||
.unwrap_or_else(|err| {
|
||||
tracing::error!("Error handling request: {err:?}");
|
||||
JsonResponse {
|
||||
status: err.status,
|
||||
body: ErrorResponse {
|
||||
message: err
|
||||
.message
|
||||
.unwrap_or_else(|| "Internal Server Error".to_string()),
|
||||
},
|
||||
}
|
||||
.into_response()
|
||||
}))
|
||||
}
|
||||
|
||||
struct JsonResponse<T: Serialize> {
|
||||
@@ -291,6 +298,7 @@ impl<T: Serialize> JsonResponse<T> {
|
||||
|
||||
async fn handle_request_inner(
|
||||
mut req: Request<Incoming>,
|
||||
legacy_enabled: bool,
|
||||
) -> Result<Response<Full<Bytes>>, AppError> {
|
||||
let Some(state) = req.extensions_mut().remove::<AppState>() else {
|
||||
return Err(AppError::status(StatusCode::INTERNAL_SERVER_ERROR, None));
|
||||
@@ -301,6 +309,50 @@ async fn handle_request_inner(
|
||||
};
|
||||
|
||||
match (req.method(), req.uri().path()) {
|
||||
(&Method::POST, "/api/localsend/v2/register") => {
|
||||
if !legacy_enabled {
|
||||
return Err(AppError::status(StatusCode::NOT_FOUND, None));
|
||||
}
|
||||
|
||||
Ok(
|
||||
controller::v2::register(req.into_body(), state, client_info)
|
||||
.await?
|
||||
.into_response(),
|
||||
)
|
||||
}
|
||||
(&Method::POST, "/api/localsend/v2/prepare-upload") => {
|
||||
if !legacy_enabled {
|
||||
return Err(AppError::status(StatusCode::NOT_FOUND, None));
|
||||
}
|
||||
|
||||
Ok(
|
||||
controller::v2::register(req.into_body(), state, client_info)
|
||||
.await?
|
||||
.into_response(),
|
||||
)
|
||||
}
|
||||
(&Method::POST, "/api/localsend/v2/upload") => {
|
||||
if !legacy_enabled {
|
||||
return Err(AppError::status(StatusCode::NOT_FOUND, None));
|
||||
}
|
||||
|
||||
Ok(
|
||||
controller::v2::register(req.into_body(), state, client_info)
|
||||
.await?
|
||||
.into_response(),
|
||||
)
|
||||
}
|
||||
(&Method::POST, "/api/localsend/v2/cancel") => {
|
||||
if !legacy_enabled {
|
||||
return Err(AppError::status(StatusCode::NOT_FOUND, None));
|
||||
}
|
||||
|
||||
Ok(
|
||||
controller::v2::register(req.into_body(), state, client_info)
|
||||
.await?
|
||||
.into_response(),
|
||||
)
|
||||
}
|
||||
(&Method::POST, "/api/localsend/v3/nonce") => {
|
||||
Ok(
|
||||
controller::v3::nonce_exchange(req.into_body(), state, client_info)
|
||||
|
||||
+5
-6
@@ -6,6 +6,7 @@ mod webrtc;
|
||||
|
||||
use crate::crypto::token;
|
||||
use crate::http::client::LsHttpClient;
|
||||
use crate::http::dto::{PrepareUploadRequestDto, ProtocolType, RegisterDto};
|
||||
use crate::http::server::TlsConfig;
|
||||
use crate::model::discovery::DeviceType;
|
||||
use crate::webrtc::signaling::{ClientInfo, WsServerMessage};
|
||||
@@ -19,7 +20,6 @@ use tokio::io;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tracing::Level;
|
||||
use crate::http::dto::{PrepareUploadRequestDto, ProtocolType, RegisterDto};
|
||||
|
||||
#[tokio::main]
|
||||
#[cfg(feature = "full")]
|
||||
@@ -141,6 +141,7 @@ async fn server_test() -> Result<()> {
|
||||
private_key: PRIVATE_KEY.to_string(),
|
||||
}),
|
||||
client_info,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(u64::MAX)).await;
|
||||
@@ -151,11 +152,9 @@ async fn server_test() -> Result<()> {
|
||||
async fn client_test() -> Result<()> {
|
||||
let client = LsHttpClient::try_new(PRIVATE_KEY, CERT)?;
|
||||
|
||||
let nonce = client.nonce(
|
||||
&ProtocolType::Https,
|
||||
"localhost",
|
||||
53317,
|
||||
).await?;
|
||||
let nonce = client
|
||||
.nonce(&ProtocolType::Https, "localhost", 53317)
|
||||
.await?;
|
||||
|
||||
println!("Received Nonce: {}", nonce);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user