aws-smithy-http-server 0.40.2

Server runtime for Smithy Rust Server Framework. NOTE: THIS IS HIGHLY EXPERIMENTAL AND SHOULD NOT BE USED YET.
Documentation
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

//! Runtime error type.
//!
//! This module contains [`RuntimeError`] type.
//!
//! As opposed to rejection types (see [`crate::rejection`]), which are an internal detail about
//! the framework, `RuntimeError` is surfaced to clients in HTTP responses: indeed, it implements
//! [`axum_core::response::IntoResponse`]. Rejections can be "grouped" and converted into a
//! specific `RuntimeError` kind: for example, all request rejections due to serialization issues
//! can be conflated under the [`RuntimeErrorKind::Serialization`] enum variant.
//!
//! The HTTP response representation of the specific `RuntimeError` can be protocol-specific: for
//! example, the runtime error in the RestJson1 protocol sets the `X-Amzn-Errortype` header.
//!
//! Generated code works always works with [`crate::rejection`] types when deserializing requests
//! and serializing response. Just before a response needs to be sent, the generated code looks up
//! and converts into the corresponding `RuntimeError`, and then it uses the its
//! [`axum_core::response::IntoResponse`] implementation to render and send a response.

use crate::protocols::Protocol;

#[derive(Debug)]
pub enum RuntimeErrorKind {
    // UnknownOperation,
    /// Request failed to deserialize or response failed to serialize.
    Serialization(crate::Error),
    /// As of writing, this variant can only occur upon failure to extract an
    /// [`crate::extension::Extension`] from the request.
    InternalFailure(crate::Error),
    // UnsupportedMediaType,
    // NotAcceptable,
}

/// String representation of the runtime error type.
/// Used as the value of the `X-Amzn-Errortype` header in RestJson1.
/// Used as the value passed to construct an [`crate::extension::RuntimeErrorExtension`].
impl RuntimeErrorKind {
    pub fn name(&self) -> &'static str {
        match self {
            RuntimeErrorKind::Serialization(_) => "SerializationException",
            RuntimeErrorKind::InternalFailure(_) => "InternalFailureException",
        }
    }
}

#[derive(Debug)]
pub struct RuntimeError {
    pub protocol: Protocol,
    pub kind: RuntimeErrorKind,
}

impl axum_core::response::IntoResponse for RuntimeError {
    fn into_response(self) -> axum_core::response::Response {
        let status_code = match self.kind {
            RuntimeErrorKind::Serialization(_) => http::StatusCode::BAD_REQUEST,
            RuntimeErrorKind::InternalFailure(_) => http::StatusCode::INTERNAL_SERVER_ERROR,
        };

        let body = crate::body::to_boxed(match self.protocol {
            Protocol::RestJson1 => "{}",
            Protocol::RestXml => "",
        });

        let mut builder = http::Response::builder();
        builder = builder.status(status_code);

        match self.protocol {
            Protocol::RestJson1 => {
                builder = builder
                    .header("Content-Type", "application/json")
                    .header("X-Amzn-Errortype", self.kind.name());
            }
            Protocol::RestXml => {
                builder = builder.header("Content-Type", "application/xml");
            }
        }

        builder = builder.extension(crate::extension::RuntimeErrorExtension::new(String::from(
            self.kind.name(),
        )));

        builder.body(body).expect("invalid HTTP response for `RuntimeError`; please file a bug report under https://github.com/awslabs/smithy-rs/issues")
    }
}

impl From<crate::rejection::RequestExtensionNotFoundRejection> for RuntimeErrorKind {
    fn from(err: crate::rejection::RequestExtensionNotFoundRejection) -> Self {
        RuntimeErrorKind::InternalFailure(crate::Error::new(err))
    }
}

impl From<crate::rejection::ResponseRejection> for RuntimeErrorKind {
    fn from(err: crate::rejection::ResponseRejection) -> Self {
        RuntimeErrorKind::Serialization(crate::Error::new(err))
    }
}

impl From<crate::rejection::RequestRejection> for RuntimeErrorKind {
    fn from(err: crate::rejection::RequestRejection) -> Self {
        RuntimeErrorKind::Serialization(crate::Error::new(err))
    }
}