From 98d0931daf6d0b6d2505f632e1eb29b3cb191291 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Wed, 27 Nov 2024 22:32:46 +0530 Subject: [PATCH] Refactor: custom `fastn_core::http::Response` for `fastn_core::serve` `actix_web::HttpResponse` aka `fastn_core::http::Response` is not `Send` which makes it impossible to share it across threads. A direct downside of this is that we can't use a function which uses this type in a `tokio::spawn` because it required types to implement `Send`. A custom `fastn_core::http::Response` is added which is `Send` and used by the `fastn_core::serve` function. A `fastn_core::serve_actix` is also introduced which is a direct replacement for the old `fastn_core::serve`. This will allow us to, for example, use `fastn_core::serve` in a background task to update the cache. --- fastn-core/src/commands/serve.rs | 363 ++++++++++++++----------------- fastn-core/src/commands/test.rs | 12 +- fastn-core/src/error.rs | 13 +- fastn-core/src/http.rs | 72 +++++- 4 files changed, 243 insertions(+), 217 deletions(-) diff --git a/fastn-core/src/commands/serve.rs b/fastn-core/src/commands/serve.rs index bd60eea42f..e0960c7eb7 100644 --- a/fastn-core/src/commands/serve.rs +++ b/fastn-core/src/commands/serve.rs @@ -1,14 +1,15 @@ +use fastn_core::http::Response; + #[tracing::instrument(skip_all)] -fn handle_redirect( - config: &fastn_core::Config, - path: &camino::Utf8Path, -) -> Option { +fn handle_redirect(config: &fastn_core::Config, path: &camino::Utf8Path) -> Option { config .package .redirects .as_ref() .and_then(|v| fastn_core::package::redirects::find_redirect(v, path.as_str())) - .map(|r| fastn_core::http::permanent_redirect(r.to_string())) + .map(|r| Response::PermanentRedirect { + location: r.to_string(), + }) } /// path: /-/// @@ -19,48 +20,25 @@ async fn serve_file( path: &camino::Utf8Path, only_js: bool, preview_session_id: &Option, -) -> fastn_core::http::Response { - if let Err(e) = config +) -> fastn_core::Result { + config .config .package - .auto_import_language(config.request.cookie("fastn-lang"), None) - { - return if config.config.test_command_running { - fastn_core::http::not_found_without_warning(format!( - "fastn-Error: path: {}, {:?}", - path, e - )) - } else { - fastn_core::not_found!("fastn-Error: path: {}, {:?}", path, e) - }; - } + .auto_import_language(config.request.cookie("fastn-lang"), None)?; - let f = match config + let f = config .get_file_and_package_by_id(path.as_str(), preview_session_id) - .await - { - Ok(f) => f, - Err(e) => { - tracing::error!( - msg = "fastn-error path not found", - path = path.as_str(), - error = %e - ); - return if config.config.test_command_running { - fastn_core::http::not_found_without_warning(format!( - "fastn-Error: path: {}, {:?}", - path, e - )) - } else { - fastn_core::not_found!("fastn-Error: path: {}, {:?}", path, e) - }; - } - }; + .await?; if let fastn_core::File::Code(doc) = f { let path = doc.get_full_path().to_string(); - let mime = mime_guess::from_path(path).first_or_text_plain(); - return fastn_core::http::ok_with_content_type(doc.content.into_bytes(), mime); + let mime = Some(mime_guess::from_path(path).first_or_text_plain()); + return Ok(Response::Raw { + content: doc.content.into_bytes(), + mime, + headers: None, + cookies: None, + }); } let main_document = match f { @@ -68,11 +46,11 @@ async fn serve_file( _ => { tracing::error!(msg = "unknown handler", path = path.as_str()); tracing::info!("file: {f:?}"); - return fastn_core::server_error!("unknown handler"); + return Err(fastn_core::Error::UnknownHandler); } }; - match fastn_core::package::package_doc::read_ftd_( + fastn_core::package::package_doc::read_ftd_( config, &main_document, "/", @@ -82,39 +60,14 @@ async fn serve_file( preview_session_id, ) .await - { - Ok(val) => match val { - fastn_core::package::package_doc::FTDResult::Html(body) => { - fastn_core::http::ok_with_content_type(body, mime_guess::mime::TEXT_HTML_UTF_8) - } - fastn_core::package::package_doc::FTDResult::Redirect { url, code } => { - if Some(mime_guess::mime::APPLICATION_JSON) == config.request.content_type() { - fastn_core::http::ok_with_content_type( - // intentionally using `.unwrap()` as this should never fail - serde_json::to_vec(&serde_json::json!({ "redirect": url })).unwrap(), - mime_guess::mime::APPLICATION_JSON, - ) - } else { - fastn_core::http::redirect_with_code(url, code) - } - } - }, - Err(e) => { - tracing::error!( - msg = "fastn-Error", - path = path.as_str(), - error = e.to_string() - ); - fastn_core::server_error!("fastn-Error: path: {}, {:?}", path, e) - } - } + .map(|res| Response::FTDResult { res, cookies: None }) } fn guess_mime_type(path: &str) -> mime_guess::Mime { mime_guess::from_path(path).first_or_octet_stream() } -pub fn clear_sid(req: &fastn_core::http::Request) -> fastn_core::http::Response { +pub fn clear_sid(req: &fastn_core::http::Request) -> actix_web::HttpResponse { let mut cookie = actix_web::cookie::Cookie::build(ft_sys_shared::SESSION_KEY, "") .domain(match req.connection_info.host().split_once(':') { Some((domain, _port)) => domain.to_string(), @@ -135,7 +88,7 @@ pub fn clear_sid(req: &fastn_core::http::Request) -> fastn_core::http::Response ) } -pub fn clear_sid2(req: &fastn_core::http::Request) -> fastn_core::http::Response { +pub fn clear_sid2(req: &fastn_core::http::Request) -> Response { // safari is ignoring cookie if we return a redirect, so we are returning a meta-refresh // further we are not using .secure(true) here because then cookie is not working on // localhost @@ -150,10 +103,91 @@ pub fn clear_sid2(req: &fastn_core::http::Request) -> fastn_core::http::Response .same_site(actix_web::cookie::SameSite::Strict) .finish(); - actix_web::HttpResponse::build(actix_web::http::StatusCode::OK) - .cookie(cookie) - .append_header(("Content-Type", "text/html")) - .body(r#""#) + Response::Raw { + content: r#""#.into(), + headers: Some(vec![("Content-Type".to_string(), "text/html".to_string())]), + cookies: Some(vec![cookie.to_string()]), + mime: None, + } +} + +/// Wraps [serve] to send [actix_web::HttpResponse] +pub async fn actix_serve( + config: &fastn_core::Config, + req: fastn_core::http::Request, + only_js: bool, + preview_session_id: &Option, +) -> fastn_core::Result<(actix_web::HttpResponse, bool)> { + let (res, cacheable) = serve(config, req, only_js, preview_session_id).await?; + + let resp = match res { + Response::FTDResult { res, cookies } => { + match res { + fastn_core::package::package_doc::FTDResult::Html(body) => { + fastn_core::http::ok_with_content_type(body, mime_guess::mime::TEXT_HTML_UTF_8) + } + fastn_core::package::package_doc::FTDResult::Redirect { url, code } => { + if Some(mime_guess::mime::APPLICATION_JSON) == req.content_type() { + fastn_core::http::ok_with_content_type( + // intentionally using `.unwrap()` as this should never fail + serde_json::to_vec(&serde_json::json!({ "redirect": url })).unwrap(), + mime_guess::mime::APPLICATION_JSON, + ) + } else { + fastn_core::http::redirect_with_code(url, code) + } + } + } + } + Response::Raw { + content, + mime, + cookies, + headers, + } => { + let mut resp = &mut actix_web::HttpResponse::Ok(); + + if let Some(mime) = mime { + resp = resp.content_type(mime); + } + + let resp = resp.body(content); + + if let Some(cookies) = cookies { + for cookie in cookies { + resp.headers_mut().append( + actix_web::http::header::SET_COOKIE, + actix_web::http::header::HeaderValue::from_str(cookie.as_str()).unwrap(), + ); + } + } + + if let Some(headers) = headers { + for (k, v) in headers { + resp.headers_mut().insert( + k.parse().unwrap(), + actix_web::http::header::HeaderValue::from_str(v.as_str()).unwrap(), + ); + } + } + + resp + } + Response::PermanentRedirect { location } => { + fastn_core::http::permanent_redirect(location) + } + Response::Wasm { request } => fastn_ds::wasm::to_response(request), + Response::Reqwest { response } => { + fastn_core::http::ResponseBuilder::from_reqwest(response).await + } + Response::DefaultRoute { content, mime } => actix_web::HttpResponse::Ok() + .content_type(mime) + .append_header(("Cache-Control", "public, max-age=31536000")) + .body(content), + Response::NotFound { message } => fastn_core::http::not_found_without_warning(message), + }; + + Ok((resp, cacheable)) } #[tracing::instrument(skip_all)] @@ -162,7 +196,7 @@ pub async fn serve( req: fastn_core::http::Request, only_js: bool, preview_session_id: &Option, -) -> fastn_core::Result<(fastn_core::http::Response, bool)> { +) -> fastn_core::Result<(Response, bool)> { let mut req_config = fastn_core::RequestConfig::new(config, &req, "", "/"); if req.path() == "/-/auth/logout/" { @@ -178,7 +212,7 @@ pub async fn serve( } if let Some(default_response) = handle_default_route(&req, config.package.name.as_str()) { - return default_response.map(|r| (r, true)); + return Ok((default_response, true)); } let path: camino::Utf8PathBuf = req.path().replacen('/', "", 1).parse()?; @@ -209,7 +243,7 @@ pub async fn serve_helper( only_js: bool, path: camino::Utf8PathBuf, preview_session_id: &Option, -) -> fastn_core::Result { +) -> fastn_core::Result { let mut resp = if req_config.request.path() == "/" { serve_file(req_config, &path.join("/"), only_js, preview_session_id).await } else { @@ -250,136 +284,74 @@ pub async fn serve_helper( ) }; - let mut resp = - actix_web::HttpResponse::new(actix_web::http::StatusCode::PERMANENT_REDIRECT); - resp.headers_mut().insert( - actix_web::http::header::LOCATION, - actix_web::http::header::HeaderValue::from_str(path.as_str()).unwrap(), // TODO: - ); - return Ok(resp); + return Ok(Response::PermanentRedirect { location: path }); } } - let file_response = - serve_file(req_config, path.as_path(), only_js, preview_session_id).await; - - tracing::info!( - "before executing proxy: file-status: {}, path: {}", - file_response.status(), - &path - ); - - file_response + serve_file(req_config, path.as_path(), only_js, preview_session_id).await }; - if let Some(r) = req_config.processor_set_response.take() { - return shared_to_http(r); + if let Some(request) = req_config.processor_set_response.take() { + return Ok(Response::Wasm { request }); } - for cookie in &req_config.processor_set_cookies { - resp.headers_mut().append( - actix_web::http::header::SET_COOKIE, - actix_web::http::header::HeaderValue::from_str(cookie.as_str()).unwrap(), - ); - } - - Ok(resp) -} - -fn shared_to_http(r: ft_sys_shared::Request) -> fastn_core::Result { - let status_code = match r.method.parse() { - Ok(v) => v, - Err(e) => { - return Err(fastn_core::Error::GenericError(format!( - "wasm code is not an integer {}: {e:?}", - r.method.as_str() - ))); - } - }; - let mut builder = actix_web::HttpResponse::build(status_code); - let mut resp = builder.status(r.method.parse().unwrap()).body(r.body); - - for (k, v) in r.headers { - resp.headers_mut().insert( - k.parse().unwrap(), - actix_web::http::header::HeaderValue::from_bytes(v.as_slice()).unwrap(), - ); + if !req_config.processor_set_cookies.is_empty() { + resp.as_mut() + .map(|ref mut x| x.attach_cookies(req_config.processor_set_cookies.clone())); } - Ok(resp) + resp } pub fn handle_default_route( req: &fastn_core::http::Request, package_name: &str, -) -> Option> { +) -> Option { if req .path() .ends_with(fastn_core::utils::hashed_default_css_name()) { - return Some(Ok(actix_web::HttpResponse::Ok() - .content_type(mime_guess::mime::TEXT_CSS) - .append_header(("Cache-Control", "public, max-age=31536000")) - .body(ftd::css()))); + Some(Response::DefaultRoute { + content: ftd::css().to_string(), + mime: mime_guess::mime::TEXT_CSS, + }) } else if req .path() .ends_with(fastn_core::utils::hashed_default_js_name()) { - return Some(Ok(actix_web::HttpResponse::Ok() - .content_type(mime_guess::mime::TEXT_JAVASCRIPT) - .append_header(("Cache-Control", "public, max-age=31536000")) - .body(format!( - "{}\n\n{}", - ftd::build_js(), - fastn_core::fastn_2022_js() - )))); + Some(Response::DefaultRoute { + content: format!("{}\n\n{}", ftd::build_js(), fastn_core::fastn_2022_js()), + mime: mime_guess::mime::TEXT_JAVASCRIPT, + }) } else if req .path() .ends_with(fastn_core::utils::hashed_default_ftd_js(package_name)) { - return Some(Ok(actix_web::HttpResponse::Ok() - .content_type(mime_guess::mime::TEXT_JAVASCRIPT) - .append_header(("Cache-Control", "public, max-age=31536000")) - .body(ftd::js::all_js_without_test(package_name)))); + Some(Response::DefaultRoute { + content: ftd::js::all_js_without_test(package_name), + mime: mime_guess::mime::TEXT_JAVASCRIPT, + }) } else if req .path() .ends_with(fastn_core::utils::hashed_markdown_js()) { - return Some(Ok(actix_web::HttpResponse::Ok() - .content_type(mime_guess::mime::TEXT_JAVASCRIPT) - .append_header(("Cache-Control", "public, max-age=31536000")) - .body(ftd::markdown_js()))); - } else if let Some(theme) = - fastn_core::utils::hashed_code_theme_css() - .iter() - .find_map(|(theme, url)| { - if req.path().ends_with(url) { - Some(theme) - } else { - None - } - }) - { - let theme_css = ftd::theme_css(); - return theme_css.get(theme).cloned().map(|theme| { - Ok(actix_web::HttpResponse::Ok() - .content_type(mime_guess::mime::TEXT_CSS) - .append_header(("Cache-Control", "public, max-age=31536000")) - .body(theme)) - }); + Some(Response::DefaultRoute { + content: ftd::markdown_js().to_string(), + mime: mime_guess::mime::TEXT_JAVASCRIPT, + }) } else if req.path().ends_with(fastn_core::utils::hashed_prism_js()) { - return Some(Ok(actix_web::HttpResponse::Ok() - .content_type(mime_guess::mime::TEXT_JAVASCRIPT) - .append_header(("Cache-Control", "public, max-age=31536000")) - .body(ftd::prism_js()))); + Some(Response::DefaultRoute { + content: ftd::prism_js().to_string(), + mime: mime_guess::mime::TEXT_JAVASCRIPT, + }) } else if req.path().ends_with(fastn_core::utils::hashed_prism_css()) { - return Some(Ok(actix_web::HttpResponse::Ok() - .content_type(mime_guess::mime::TEXT_CSS) - .append_header(("Cache-Control", "public, max-age=31536000")) - .body(ftd::prism_css()))); + Some(Response::DefaultRoute { + content: ftd::prism_css().to_string(), + mime: mime_guess::mime::TEXT_CSS, + }) + } else { + None } - - None } async fn handle_static_route( @@ -387,7 +359,7 @@ async fn handle_static_route( package_name: &str, ds: &fastn_ds::DocumentStore, session_id: &Option, -) -> fastn_core::Result { +) -> fastn_core::Result { return match handle_static_route_(path, package_name, ds, session_id).await { Ok(r) => Ok(r), Err(fastn_ds::ReadError::NotFound(_)) => { @@ -401,7 +373,7 @@ async fn handle_static_route( package_name: &str, ds: &fastn_ds::DocumentStore, session_id: &Option, - ) -> Result { + ) -> Result { if path == "/favicon.ico" { return favicon(ds, session_id).await; } @@ -430,21 +402,23 @@ async fn handle_static_route( package_name: &str, ds: &fastn_ds::DocumentStore, session_id: &Option, - ) -> fastn_core::Result { + ) -> fastn_core::Result { // todo: handle dark images using manifest if let Some(new_file_path) = generate_dark_image_path(path) { return handle_static_route_(new_file_path.as_str(), package_name, ds, session_id) .await .or_else(|e| { if let fastn_ds::ReadError::NotFound(e) = e { - Ok(fastn_core::http::not_found_without_warning(e)) + Ok(Response::NotFound { message: e }) } else { Err(e.into()) } }); } - Ok(fastn_core::http::not_found_without_warning("".to_string())) + Ok(Response::NotFound { + message: "".to_string(), + }) } fn generate_dark_image_path(path: &str) -> Option { @@ -468,7 +442,7 @@ async fn handle_static_route( async fn favicon( ds: &fastn_ds::DocumentStore, session_id: &Option, - ) -> Result { + ) -> Result { match static_file(ds, "favicon.ico", session_id).await { Ok(r) => Ok(r), Err(fastn_ds::ReadError::NotFound(_)) => { @@ -483,14 +457,14 @@ async fn handle_static_route( ds: &fastn_ds::DocumentStore, path: &str, session_id: &Option, - ) -> Result { + ) -> Result { ds.read_content(&fastn_ds::Path::new(path), session_id) .await - .map(|r| { - fastn_core::http::ok_with_content_type( - r, - guess_mime_type(path.to_string().as_str()), - ) + .map(|r| Response::Raw { + content: r, + mime: Some(guess_mime_type(path.to_string().as_str())), + cookies: None, + headers: None, }) } } @@ -499,7 +473,7 @@ async fn handle_endpoints( config: &fastn_core::Config, req: &fastn_core::http::Request, session_id: &Option, -) -> Option> { +) -> Option> { let matched_endpoint = config .package .endpoints @@ -526,7 +500,7 @@ async fn handle_endpoints( .handle_wasm(url, req, endpoint.mountpoint.to_string(), session_id) .await { - Ok(r) => Some(Ok(fastn_ds::wasm::to_response(r))), + Ok(r) => Some(Ok(Response::Wasm { request: r })), Err(e) => return Some(Err(e.into())), }; } @@ -541,18 +515,17 @@ async fn handle_endpoints( .await .map_err(fastn_core::Error::DSHttpError) { - Ok(response) => response, + Ok(response) => Response::Reqwest { response }, Err(e) => return Some(Err(e)), }; - let actix_response = fastn_core::http::ResponseBuilder::from_reqwest(response).await; - Some(Ok(actix_response)) + Some(Ok(response)) } async fn handle_apps( config: &fastn_core::Config, req: &fastn_core::http::Request, -) -> Option> { +) -> Option> { let matched_app = config.package.apps.iter().find(|a| { req.path().starts_with( a.end_point @@ -581,11 +554,11 @@ async fn actual_route( req: actix_web::HttpRequest, body: actix_web::web::Bytes, preview_session_id: &Option, -) -> fastn_core::Result { +) -> fastn_core::Result { tracing::info!(method = req.method().as_str(), uri = req.path()); let req = fastn_core::http::Request::from_actix(req, body); - serve(config, req, false, preview_session_id) + actix_serve(config, req, false, preview_session_id) .await .map(|(r, _)| r) } @@ -595,7 +568,7 @@ async fn route( req: actix_web::HttpRequest, body: actix_web::web::Bytes, config: actix_web::web::Data>, -) -> fastn_core::Result { +) -> fastn_core::Result { actual_route(&config, req, body, &None).await } diff --git a/fastn-core/src/commands/test.rs b/fastn-core/src/commands/test.rs index 793cc9dcd9..ea01e873db 100644 --- a/fastn-core/src/commands/test.rs +++ b/fastn-core/src/commands/test.rs @@ -503,7 +503,7 @@ async fn get_post_response_for_id( log_message!(test_parameters.verbose, "Request details"); log_variable!(test_parameters.verbose, &request); - let response = fastn_core::commands::serve::serve(config, request, true, &None) + let response = fastn_core::commands::serve::actix_serve(config, request, true, &None) .await? .0; update_cookies(saved_cookies, &response); @@ -719,7 +719,7 @@ async fn get_js_for_id( log_message!(test_parameters.verbose, "Request details"); log_variable!(test_parameters.verbose, &request); - let response = fastn_core::commands::serve::serve(config, request, true, &None) + let response = fastn_core::commands::serve::actix_serve(config, request, true, &None) .await? .0; update_cookies(saved_cookies, &response); @@ -904,7 +904,7 @@ pub fn assert_optional_headers( } pub fn assert_response( - response: &fastn_core::http::Response, + response: &actix_web::HttpResponse, params: &ftd::Map, ) -> fastn_core::Result<(u16, String)> { if let Some(redirection_url) = params.get(HTTP_REDIRECT_HEADER) { @@ -915,7 +915,7 @@ pub fn assert_response( } pub fn assert_redirect( - response: &fastn_core::http::Response, + response: &actix_web::HttpResponse, redirection_location: &str, ) -> fastn_core::Result<(u16, String)> { let response_status_code = response.status().as_u16(); @@ -938,7 +938,7 @@ pub fn assert_redirect( } pub fn assert_location_and_status( - response: &fastn_core::http::Response, + response: &actix_web::HttpResponse, params: &ftd::Map, ) -> fastn_core::Result<(u16, String)> { // By default, we are expecting status 200 if not http-status is not passed @@ -972,7 +972,7 @@ pub fn assert_location_and_status( } pub fn get_response_location( - response: &fastn_core::http::Response, + response: &actix_web::HttpResponse, ) -> fastn_core::Result> { if let Some(redirect_location) = response.headers().get("Location") { return if let Ok(location) = redirect_location.to_str() { diff --git a/fastn-core/src/error.rs b/fastn-core/src/error.rs index 076f47ed78..964ee32c01 100644 --- a/fastn-core/src/error.rs +++ b/fastn-core/src/error.rs @@ -143,6 +143,9 @@ pub enum Error { #[error("MigrationError: {0}")] MigrationError(#[from] fastn_core::migrations::MigrationError), + + #[error("UnknownHandler")] + UnknownHandler, } impl From for Error { @@ -160,27 +163,27 @@ impl Error { Err(Self::generic(error)) } - pub fn to_html(&self) -> fastn_core::http::Response { + pub fn to_html(&self) -> actix_web::HttpResponse { // TODO: hate this error type, have no idea how to handle things properly at this stage now // we should remove this type and write more precise error types match self { Error::FormError(errors) => { tracing::info!("form error: {:?}", errors); - fastn_core::http::Response::Ok() + actix_web::HttpResponse::Ok() .content_type("application/json") .json(serde_json::json!({"errors": errors})) } Error::NotFound(message) => { tracing::info!("not found: {:?}", message); - fastn_core::http::Response::NotFound().body(message.to_string()) + actix_web::HttpResponse::NotFound().body(message.to_string()) } Error::DSReadError(fastn_ds::ReadError::NotFound(f)) => { tracing::info!("ds read error, not found: {f}"); - fastn_core::http::Response::NotFound().body("page not found: {f}") + actix_web::HttpResponse::NotFound().body("page not found: {f}") } _ => { tracing::error!("error: {:?}", self); - fastn_core::http::Response::InternalServerError() + actix_web::HttpResponse::InternalServerError() .body(format!("internal server error: {self:?}")) } } diff --git a/fastn-core/src/http.rs b/fastn-core/src/http.rs index f1171eaeb2..0d50e5bdb0 100644 --- a/fastn-core/src/http.rs +++ b/fastn-core/src/http.rs @@ -2,6 +2,57 @@ pub const SESSION_COOKIE_NAME: &str = "fastn-sid"; pub const X_FASTN_REQUEST_PATH: &str = "x-fastn-request-path"; pub const X_FASTN_ROOT: &str = "x-fastn-root"; +pub enum Response { + /// FTDResult is the result of processing an FTD file using [fastn_core::serve_file] + FTDResult { + res: fastn_core::package::package_doc::FTDResult, + /// any cookies set via the `http` processor + cookies: Option> + }, + Raw { + content: Vec, + mime: Option, + cookies: Option>, + headers: Option>, + }, + PermanentRedirect { + location: String, + }, + Wasm { + request: ft_sys_shared::Request, + }, + Reqwest { + response: http::Response, + }, + /// DefaultRoute is the result of [fastn_core::handle_default_route] + /// Http Cache-Control header should be applied to this. + DefaultRoute { + content: String, + mime: mime_guess::Mime, + }, + NotFound { + message: String, + } +} + +impl Response { + pub fn attach_cookies(&mut self, cookies: Vec) { + let c = cookies; + match self { + Response::FTDResult { ref mut cookies, .. } => { + *cookies = Some(c); + } + Response::Raw { ref mut cookies, .. } => { + *cookies = Some(c); + } + _ => { + panic!("cookies can only be attached to FTDResult"); + } + } + } +} + + #[macro_export] macro_rules! server_error { ($($t:tt)*) => {{ @@ -23,7 +74,7 @@ macro_rules! unauthorised { }}; } -pub fn api_ok(data: impl serde::Serialize) -> serde_json::Result { +pub fn api_ok(data: impl serde::Serialize) -> serde_json::Result { #[derive(serde::Serialize)] struct SuccessResponse { data: T, @@ -41,39 +92,38 @@ pub fn api_ok(data: impl serde::Serialize) -> serde_json::Result fastn_core::http::Response { +pub fn unauthorised_(msg: String) -> actix_web::HttpResponse { fastn_core::warning!("unauthorised: {}", msg); actix_web::HttpResponse::Unauthorized().body(msg) } -pub fn server_error_(msg: String) -> fastn_core::http::Response { +pub fn server_error_(msg: String) -> actix_web::HttpResponse { fastn_core::warning!("server error: {}", msg); server_error_without_warning(msg) } -pub fn server_error_without_warning(msg: String) -> fastn_core::http::Response { +pub fn server_error_without_warning(msg: String) -> actix_web::HttpResponse { actix_web::HttpResponse::InternalServerError().body(msg) } -pub fn not_found_without_warning(msg: String) -> fastn_core::http::Response { +pub fn not_found_without_warning(msg: String) -> actix_web::HttpResponse { actix_web::HttpResponse::NotFound().body(msg) } -pub fn not_found_(msg: String) -> fastn_core::http::Response { +pub fn not_found_(msg: String) -> actix_web::HttpResponse { fastn_core::warning!("page not found: {}", msg); not_found_without_warning(msg) } impl actix_web::ResponseError for fastn_core::Error {} -pub type Response = actix_web::HttpResponse; pub type StatusCode = actix_web::http::StatusCode; -pub fn permanent_redirect(url: String) -> fastn_core::http::Response { +pub fn permanent_redirect(url: String) -> actix_web::HttpResponse { redirect_with_code(url, 308) } -pub fn redirect_with_code(url: String, code: u16) -> fastn_core::http::Response { +pub fn redirect_with_code(url: String, code: u16) -> actix_web::HttpResponse { match code { 301 => actix_web::HttpResponse::MovedPermanently(), 302 => actix_web::HttpResponse::Found(), @@ -92,7 +142,7 @@ pub fn redirect_with_code(url: String, code: u16) -> fastn_core::http::Response pub fn ok_with_content_type( data: Vec, content_type: mime_guess::Mime, -) -> fastn_core::http::Response { +) -> actix_web::HttpResponse { actix_web::HttpResponse::Ok() .content_type(content_type) .body(data) @@ -550,7 +600,7 @@ pub(crate) fn get_available_port( pub fn user_err( errors: Vec<(String, Vec)>, status_code: fastn_core::http::StatusCode, -) -> fastn_core::Result { +) -> fastn_core::Result { let mut json_error = serde_json::Map::new(); for (k, v) in errors {