Skip to main content

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_1x::header::{InvalidHeaderName, InvalidHeaderValue};
10use http_1x::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    #[cfg(feature = "http-02x")]
27    InvalidExtensions,
28    InvalidHeaderName,
29    InvalidHeaderValue,
30    InvalidMethod,
31    InvalidStatusCode,
32    InvalidUri,
33    InvalidUriParts,
34    MissingAuthority,
35    MissingScheme,
36    NonUtf8Header(NonUtf8Header),
37}
38
39#[derive(Debug)]
40pub(super) struct NonUtf8Header {
41    error: Utf8Error,
42    value: Vec<u8>,
43    name: Option<String>,
44}
45
46impl NonUtf8Header {
47    #[cfg(any(feature = "http-1x", feature = "http-02x"))]
48    pub(super) fn new(name: String, value: Vec<u8>, error: Utf8Error) -> Self {
49        Self {
50            error,
51            value,
52            name: Some(name),
53        }
54    }
55
56    pub(super) fn new_missing_name(value: Vec<u8>, error: Utf8Error) -> Self {
57        Self {
58            error,
59            value,
60            name: None,
61        }
62    }
63}
64
65impl HttpError {
66    #[cfg(feature = "http-02x")]
67    pub(super) fn invalid_extensions() -> Self {
68        Self {
69            kind: Kind::InvalidExtensions,
70            source: None,
71        }
72    }
73
74    pub(super) fn invalid_header_name(err: InvalidHeaderName) -> Self {
75        Self {
76            kind: Kind::InvalidHeaderName,
77            source: Some(Box::new(err)),
78        }
79    }
80
81    pub(super) fn invalid_method(err: http_1x::method::InvalidMethod) -> Self {
82        Self {
83            kind: Kind::InvalidMethod,
84            source: Some(Box::new(err)),
85        }
86    }
87
88    pub(super) fn invalid_header_value(err: InvalidHeaderValue) -> Self {
89        Self {
90            kind: Kind::InvalidHeaderValue,
91            source: Some(Box::new(err)),
92        }
93    }
94
95    pub(super) fn invalid_status_code() -> Self {
96        Self {
97            kind: Kind::InvalidStatusCode,
98            source: None,
99        }
100    }
101
102    pub(super) fn invalid_uri(err: InvalidUri) -> Self {
103        Self {
104            kind: Kind::InvalidUri,
105            source: Some(Box::new(err)),
106        }
107    }
108
109    #[cfg(feature = "http-02x")]
110    pub(super) fn invalid_uri_h0(err: http_02x::uri::InvalidUri) -> Self {
111        Self {
112            kind: Kind::InvalidUri,
113            source: Some(Box::new(err)),
114        }
115    }
116
117    pub(super) fn invalid_uri_parts(err: http_1x::Error) -> Self {
118        Self {
119            kind: Kind::InvalidUriParts,
120            source: Some(Box::new(err)),
121        }
122    }
123
124    pub(super) fn missing_authority() -> Self {
125        Self {
126            kind: Kind::MissingAuthority,
127            source: None,
128        }
129    }
130
131    pub(super) fn missing_scheme() -> Self {
132        Self {
133            kind: Kind::MissingScheme,
134            source: None,
135        }
136    }
137
138    pub(super) fn non_utf8_header(non_utf8_header: NonUtf8Header) -> Self {
139        Self {
140            kind: Kind::NonUtf8Header(non_utf8_header),
141            source: None,
142        }
143    }
144}
145
146impl Display for HttpError {
147    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
148        use Kind::*;
149        match &self.kind {
150            #[cfg(feature = "http-02x")]
151            InvalidExtensions => write!(f, "Extensions were provided during initialization. This prevents the request format from being converted."),
152            InvalidHeaderName => write!(f, "invalid header name"),
153            InvalidHeaderValue => write!(f, "invalid header value"),
154            InvalidMethod => write!(f, "invalid HTTP method"),
155            InvalidStatusCode => write!(f, "invalid HTTP status code"),
156            InvalidUri => write!(f, "endpoint is not a valid URI"),
157            InvalidUriParts => write!(f, "endpoint parts are not valid"),
158            MissingAuthority => write!(f, "endpoint must contain authority"),
159            MissingScheme => write!(f, "endpoint must contain scheme"),
160            NonUtf8Header(hv) => {
161                // In some cases, we won't know the key so we default to "<unknown>".
162                let key = hv.name.as_deref().unwrap_or("<unknown>");
163                let value = String::from_utf8_lossy(&hv.value);
164                let index = hv.error.valid_up_to();
165                write!(f, "header `{key}={value}` contains non-UTF8 octet at index {index}")
166            },
167        }
168    }
169}
170
171impl Error for HttpError {
172    fn source(&self) -> Option<&(dyn Error + 'static)> {
173        self.source.as_ref().map(|err| err.as_ref() as _)
174    }
175}