//! http utilities use http::StatusCode; use pingora::prelude::*; /// closure that returns the given status with no cause /// /// use with [`Option::ok_or_else`] pub fn status_error( why: &'static str, src: ErrorSource, status: StatusCode, ) -> impl FnOnce() -> Box { move || { Error::create( ErrorType::HTTPStatus(status.into()), src, Some(why.into()), None, ) } } /// closure that returns `500 Internal Server Error`, marked as caused by the error returned to /// the given closure /// /// use with [`Result::map_err`] pub fn internal_error_from(why: &'static str) -> impl FnOnce(E) -> Box where E: Into>, { move |cause| { Error::create( ErrorType::HTTPStatus(StatusCode::INTERNAL_SERVER_ERROR.into()), ErrorSource::Internal, Some(why.into()), Some(cause.into()), ) } } /// closure that returns `500 Internal Server Error` with no cause /// /// use with [`Option::ok_or_else`] pub fn internal_error(why: &'static str) -> impl FnOnce() -> Box { move || { Error::create( ErrorType::HTTPStatus(StatusCode::INTERNAL_SERVER_ERROR.into()), ErrorSource::Internal, Some(why.into()), None, ) } } /// closure that returns the given status, marked as caused by the error returned to the given closure /// /// use with [`Result::map_err`] pub fn status_error_from( why: &'static str, src: ErrorSource, status: http::StatusCode, ) -> impl FnOnce(E) -> Box where E: Into>, { move |cause| { Error::create( ErrorType::HTTPStatus(status.into()), src, Some(why.into()), Some(cause.into()), ) } } /// redirect to the given location /// /// the given callback can be used to inject additional headers, like `set-cookie` pub async fn redirect_response( session: &mut Session, to: &str, bld_resp_header: impl FnOnce(&mut ResponseHeader, &Session) -> Result<()>, ) -> Result<()> { session.set_keepalive(None); session .write_response_header( Box::new({ // per , any redirect is fine save 307, but HTTP 302 seems to // be their example. let mut resp = ResponseHeader::build(StatusCode::FOUND, Some(0))?; resp.insert_header(http::header::LOCATION, to)?; bld_resp_header(&mut resp, session)?; resp }), true, ) .await?; session.finish_body().await?; Ok(()) } /// fetch the cookies for the current request pub fn cookie_jar(req: &'_ RequestHeader) -> Result>> { use cookie_rs::CookieJar; let Some(raw) = req.headers.get(http::header::COOKIE) else { return Ok(None); }; Ok(Some( raw.to_str() .map_err(Box::::from) .and_then(|c| Ok(CookieJar::parse(c)?)) .map_err(status_error_from( "bad cookie header", ErrorSource::Downstream, StatusCode::BAD_REQUEST, ))?, )) }