aws_smithy_runtime_api/http/
error.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Error types for HTTP requests/responses.
7
8use crate::box_error::BoxError;
9use http_02x::header::{InvalidHeaderName, InvalidHeaderValue};
10use http_02x::uri::InvalidUri;
11use std::error::Error;
12use std::fmt::{Debug, Display, Formatter};
13use std::str::Utf8Error;
14
15#[derive(Debug)]
16/// An error occurred constructing an Http Request.
17///
18/// This is normally due to configuration issues, internal SDK bugs, or other user error.
19pub struct HttpError {
20    kind: Kind,
21    source: Option<BoxError>,
22}
23
24#[derive(Debug)]
25enum Kind {
26    InvalidExtensions,
27    InvalidHeaderName,
28    InvalidHeaderValue,
29    InvalidMethod,
30    InvalidStatusCode,
31    InvalidUri,
32    InvalidUriParts,
33    MissingAuthority,
34    MissingScheme,
35    NonUtf8Header(NonUtf8Header),
36}
37
38#[derive(Debug)]
39pub(super) struct NonUtf8Header {
40    error: Utf8Error,
41    value: Vec<u8>,
42    name: Option<String>,
43}
44
45impl NonUtf8Header {
46    #[cfg(any(feature = "http-1x", feature = "http-02x"))]
47    pub(super) fn new(name: String, value: Vec<u8>, error: Utf8Error) -> Self {
48        Self {
49            error,
50            value,
51            name: Some(name),
52        }
53    }
54
55    pub(super) fn new_missing_name(value: Vec<u8>, error: Utf8Error) -> Self {
56        Self {
57            error,
58            value,
59            name: None,
60        }
61    }
62}
63
64impl HttpError {
65    pub(super) fn invalid_extensions() -> Self {
66        Self {
67            kind: Kind::InvalidExtensions,
68            source: None,
69        }
70    }
71
72    pub(super) fn invalid_header_name(err: InvalidHeaderName) -> Self {
73        Self {
74            kind: Kind::InvalidHeaderName,
75            source: Some(Box::new(err)),
76        }
77    }
78
79    pub(super) fn invalid_method(err: http_1x::method::InvalidMethod) -> Self {
80        Self {
81            kind: Kind::InvalidMethod,
82            source: Some(Box::new(err)),
83        }
84    }
85
86    pub(super) fn invalid_header_value(err: InvalidHeaderValue) -> Self {
87        Self {
88            kind: Kind::InvalidHeaderValue,
89            source: Some(Box::new(err)),
90        }
91    }
92
93    pub(super) fn invalid_status_code() -> Self {
94        Self {
95            kind: Kind::InvalidStatusCode,
96            source: None,
97        }
98    }
99
100    pub(super) fn invalid_uri(err: InvalidUri) -> Self {
101        Self {
102            kind: Kind::InvalidUri,
103            source: Some(Box::new(err)),
104        }
105    }
106
107    pub(super) fn invalid_uri_parts(err: http_02x::Error) -> Self {
108        Self {
109            kind: Kind::InvalidUriParts,
110            source: Some(Box::new(err)),
111        }
112    }
113
114    pub(super) fn missing_authority() -> Self {
115        Self {
116            kind: Kind::MissingAuthority,
117            source: None,
118        }
119    }
120
121    pub(super) fn missing_scheme() -> Self {
122        Self {
123            kind: Kind::MissingScheme,
124            source: None,
125        }
126    }
127
128    pub(super) fn non_utf8_header(non_utf8_header: NonUtf8Header) -> Self {
129        Self {
130            kind: Kind::NonUtf8Header(non_utf8_header),
131            source: None,
132        }
133    }
134}
135
136impl Display for HttpError {
137    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
138        use Kind::*;
139        match &self.kind {
140            InvalidExtensions => write!(f, "Extensions were provided during initialization. This prevents the request format from being converted."),
141            InvalidHeaderName => write!(f, "invalid header name"),
142            InvalidHeaderValue => write!(f, "invalid header value"),
143            InvalidMethod => write!(f, "invalid HTTP method"),
144            InvalidStatusCode => write!(f, "invalid HTTP status code"),
145            InvalidUri => write!(f, "endpoint is not a valid URI"),
146            InvalidUriParts => write!(f, "endpoint parts are not valid"),
147            MissingAuthority => write!(f, "endpoint must contain authority"),
148            MissingScheme => write!(f, "endpoint must contain scheme"),
149            NonUtf8Header(hv) => {
150                // In some cases, we won't know the key so we default to "<unknown>".
151                let key = hv.name.as_deref().unwrap_or("<unknown>");
152                let value = String::from_utf8_lossy(&hv.value);
153                let index = hv.error.valid_up_to();
154                write!(f, "header `{key}={value}` contains non-UTF8 octet at index {index}")
155            },
156        }
157    }
158}
159
160impl Error for HttpError {
161    fn source(&self) -> Option<&(dyn Error + 'static)> {
162        self.source.as_ref().map(|err| err.as_ref() as _)
163    }
164}