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