diff --git a/devolutions-gateway/src/http/controllers/http_bridge.rs b/devolutions-gateway/src/http/controllers/http_bridge.rs index a90cf67ea..6aaf8b596 100644 --- a/devolutions-gateway/src/http/controllers/http_bridge.rs +++ b/devolutions-gateway/src/http/controllers/http_bridge.rs @@ -44,7 +44,7 @@ impl HttpBridgeController { let date = jwt::JwtDate::new_with_leeway(i64::try_from(numeric_date).unwrap(), 60); let validator = jwt::JwtValidator::strict(&date); - let jws = jwt::JwtSig::decode(token_str, key, &validator).map_err(|e| (StatusCode::FORBIDDEN, e))?; + let jws = jwt::JwtSig::decode(token_str, key, &validator).map_err(HttpErrorStatus::forbidden)?; Ok(jws.claims) } @@ -59,56 +59,52 @@ impl HttpBridgeController { // FIXME: when updating reqwest 0.10 → 0.11 and hyper 0.13 → 0.14: // Use https://docs.rs/reqwest/0.11.4/reqwest/struct.Body.html#impl-From%3CBody%3E // to get a streaming reqwest Request instead of loading the whole body in memory. - let req: saphir::request::Request = req - .load_body() - .await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e))?; + let req = req.load_body().await.map_err(HttpErrorStatus::internal)?; let req: saphir::request::Request = req.map(reqwest::Body::from); let mut req: http::Request = http::Request::from(req); // === Filter and validate request to forward === // - let headers = req.headers_mut(); - - // Gateway Bridge Claims - let token_hdr = headers - .remove(GATEWAY_BRIDGE_TOKEN_HDR_NAME) - .ok_or((StatusCode::BAD_REQUEST, "Gateway-Bridge-Token header is missing"))?; - let token_str = token_hdr.to_str().map_err(|e| (StatusCode::BAD_REQUEST, e))?; - let claims = self.h_decode_claims(token_str)?; - - // Update request destination - let uri = http::Uri::try_from(claims.target.as_str()).map_err(|e| (StatusCode::BAD_REQUEST, e))?; - *req.uri_mut() = uri; - - // Forward - slog_scope::debug!("Forward HTTP request to {}", req.uri()); - let req = reqwest::Request::try_from(req).map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e))?; - let mut rsp = self - .client - .execute(req) - .await - .map_err(|e| (StatusCode::BAD_GATEWAY, e))?; + let mut rsp = { + // Gateway Bridge Claims + let headers = req.headers_mut(); + let token_hdr = headers + .remove(GATEWAY_BRIDGE_TOKEN_HDR_NAME) + .ok_or((StatusCode::BAD_REQUEST, "Gateway-Bridge-Token header is missing"))?; + let token_str = token_hdr.to_str().map_err(HttpErrorStatus::bad_request)?; + let claims = self.h_decode_claims(token_str)?; + + // Update request destination + let uri = http::Uri::try_from(claims.target.as_str()).map_err(HttpErrorStatus::bad_request)?; + *req.uri_mut() = uri; + + // Forward + slog_scope::debug!("Forward HTTP request to {}", req.uri()); + let req = reqwest::Request::try_from(req).map_err(HttpErrorStatus::internal)?; + self.client.execute(req).await.map_err(HttpErrorStatus::bad_gateway)? + }; // === Create HTTP response using target response === // let mut rsp_builder = Builder::new(); - // Status code - rsp_builder = rsp_builder.status(rsp.status()); - - // Headers - let headers = rsp_builder.headers_mut().unwrap(); - rsp.headers_mut().drain().for_each(|(name, value)| { - if let Some(name) = name { - headers.insert(name, value); + { + // Status code + rsp_builder = rsp_builder.status(rsp.status()); + + // Headers + let headers = rsp_builder.headers_mut().unwrap(); + rsp.headers_mut().drain().for_each(|(name, value)| { + if let Some(name) = name { + headers.insert(name, value); + } + }); + + // Body + match rsp.bytes().await { + Ok(body) => rsp_builder = rsp_builder.body(body), + Err(e) => slog_scope::warn!("Couldn’t get bytes from response body: {}", e), } - }); - - // Body - match rsp.bytes().await { - Ok(body) => rsp_builder = rsp_builder.body(body), - Err(e) => slog_scope::warn!("Couldn’t get bytes from response body: {}", e), } Ok(rsp_builder) diff --git a/devolutions-gateway/src/http/mod.rs b/devolutions-gateway/src/http/mod.rs index d26f5c734..efd12b23a 100644 --- a/devolutions-gateway/src/http/mod.rs +++ b/devolutions-gateway/src/http/mod.rs @@ -31,11 +31,31 @@ impl HttpErrorStatus { source: Box::new(source), } } + + #[track_caller] + fn forbidden(source: T) -> Self { + Self::new(StatusCode::FORBIDDEN, source) + } + + #[track_caller] + fn internal(source: T) -> Self { + Self::new(StatusCode::INTERNAL_SERVER_ERROR, source) + } + + #[track_caller] + fn bad_request(source: T) -> Self { + Self::new(StatusCode::BAD_REQUEST, source) + } + + #[track_caller] + fn bad_gateway(source: T) -> Self { + Self::new(StatusCode::BAD_GATEWAY, source) + } } impl Responder for HttpErrorStatus { fn respond_with_builder(self, builder: Builder, _: &HttpContext) -> Builder { - slog_scope::error!("status {} at {} [source: {}]", self.code, self.loc, self.source); + slog_scope::error!("{} at {} [{}]", self.code, self.loc, self.source); builder.status(self.code) } }