azure_core_mirror/headers/
mod.rs

1//! Azure HTTP headers.
2mod utilities;
3
4use crate::error::{Error, ErrorKind, ResultExt};
5use std::{fmt::Debug, str::FromStr};
6pub use utilities::*;
7
8/// A trait for converting a type into request headers
9pub trait AsHeaders {
10    type Iter: Iterator<Item = (HeaderName, HeaderValue)>;
11    fn as_headers(&self) -> Self::Iter;
12}
13
14impl<T> AsHeaders for T
15where
16    T: Header,
17{
18    type Iter = std::option::IntoIter<(HeaderName, HeaderValue)>;
19
20    fn as_headers(&self) -> Self::Iter {
21        Some((self.name(), self.value())).into_iter()
22    }
23}
24
25impl<T> AsHeaders for Option<T>
26where
27    T: Header,
28{
29    type Iter = std::option::IntoIter<(HeaderName, HeaderValue)>;
30
31    fn as_headers(&self) -> Self::Iter {
32        match self {
33            Some(h) => h.as_headers(),
34            None => None.into_iter(),
35        }
36    }
37}
38
39/// View a type as an HTTP header.
40///
41/// Ad interim there are two default functions: `add_to_builder` and `add_to_request`.
42///
43/// While not restricted by the type system, please add HTTP headers only. In particular, do not
44/// interact with the body of the request.
45///
46/// As soon as the migration to the pipeline architecture will be complete we will phase out
47/// `add_to_builder`.
48pub trait Header {
49    fn name(&self) -> HeaderName;
50    fn value(&self) -> HeaderValue;
51}
52
53/// A collection of headers
54#[derive(Clone, Debug, PartialEq, Eq, Default)]
55pub struct Headers(std::collections::HashMap<HeaderName, HeaderValue>);
56
57impl Headers {
58    pub fn new() -> Self {
59        Self::default()
60    }
61
62    /// Optionally get a header value as a String
63    pub fn get_optional_string(&self, key: &HeaderName) -> Option<String> {
64        self.get_as(key).ok()
65    }
66
67    /// Get a header value as a str or error if it is not found
68    pub fn get_str(&self, key: &HeaderName) -> crate::Result<&str> {
69        self.get_with(key, |s| crate::Result::Ok(s.as_str()))
70    }
71
72    /// Optionally get a header value as a str
73    pub fn get_optional_str(&self, key: &HeaderName) -> Option<&str> {
74        self.get_str(key).ok()
75    }
76
77    /// Get a header value parsing it as the type or error if it's not found or it fails to parse
78    pub fn get_as<V, E>(&self, key: &HeaderName) -> crate::Result<V>
79    where
80        V: FromStr<Err = E>,
81        E: std::error::Error + Send + Sync + 'static,
82    {
83        self.get_with(key, |s| s.as_str().parse())
84    }
85
86    /// Optionally get a header value parsing it as the type or error if it fails to parse
87    pub fn get_optional_as<V, E>(&self, key: &HeaderName) -> crate::Result<Option<V>>
88    where
89        V: FromStr<Err = E>,
90        E: std::error::Error + Send + Sync + 'static,
91    {
92        self.get_optional_with(key, |s| s.as_str().parse())
93    }
94
95    /// Get a header value using the parser or error if it is not found or fails to parse
96    pub fn get_with<'a, V, F, E>(&'a self, key: &HeaderName, parser: F) -> crate::Result<V>
97    where
98        F: FnOnce(&'a HeaderValue) -> Result<V, E>,
99        E: std::error::Error + Send + Sync + 'static,
100    {
101        self.get_optional_with(key, parser)?.ok_or_else(|| {
102            Error::with_message(ErrorKind::DataConversion, || {
103                format!("header not found {}", key.as_str())
104            })
105        })
106    }
107
108    /// Optionally get a header value using the parser or error if it fails to parse
109    pub fn get_optional_with<'a, V, F, E>(
110        &'a self,
111        key: &HeaderName,
112        parser: F,
113    ) -> crate::Result<Option<V>>
114    where
115        F: FnOnce(&'a HeaderValue) -> Result<V, E>,
116        E: std::error::Error + Send + Sync + 'static,
117    {
118        self.0
119            .get(key)
120            .map(|v: &HeaderValue| {
121                parser(v).with_context(ErrorKind::DataConversion, || {
122                    let ty = std::any::type_name::<V>();
123                    format!("unable to parse header '{key:?}: {v:?}' into {ty}",)
124                })
125            })
126            .transpose()
127    }
128
129    /// Insert a header name/value pair
130    pub fn insert<K, V>(&mut self, key: K, value: V)
131    where
132        K: Into<HeaderName>,
133        V: Into<HeaderValue>,
134    {
135        self.0.insert(key.into(), value.into());
136    }
137
138    /// Add headers to the headers collection
139    pub fn add<H>(&mut self, header: H)
140    where
141        H: AsHeaders,
142    {
143        for (key, value) in header.as_headers() {
144            self.insert(key, value);
145        }
146    }
147
148    /// Iterate over all the header name/value pairs
149    pub fn iter(&self) -> impl Iterator<Item = (&HeaderName, &HeaderValue)> {
150        self.0.iter()
151    }
152}
153
154impl IntoIterator for Headers {
155    type Item = (HeaderName, HeaderValue);
156
157    type IntoIter = std::collections::hash_map::IntoIter<HeaderName, HeaderValue>;
158
159    fn into_iter(self) -> Self::IntoIter {
160        self.0.into_iter()
161    }
162}
163
164impl From<std::collections::HashMap<HeaderName, HeaderValue>> for Headers {
165    fn from(c: std::collections::HashMap<HeaderName, HeaderValue>) -> Self {
166        Self(c)
167    }
168}
169
170/// A header name
171#[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
172pub struct HeaderName(std::borrow::Cow<'static, str>);
173
174impl HeaderName {
175    pub const fn from_static(s: &'static str) -> Self {
176        Self(std::borrow::Cow::Borrowed(s))
177    }
178
179    fn from_cow<C>(c: C) -> Self
180    where
181        C: Into<std::borrow::Cow<'static, str>>,
182    {
183        let c = c.into();
184        assert!(
185            c.chars().all(|c| c.is_lowercase() || !c.is_alphabetic()),
186            "header names must be lowercase: {c}"
187        );
188        Self(c)
189    }
190
191    pub fn as_str(&self) -> &str {
192        self.0.as_ref()
193    }
194}
195
196impl From<&'static str> for HeaderName {
197    fn from(s: &'static str) -> Self {
198        Self::from_cow(s)
199    }
200}
201
202impl From<String> for HeaderName {
203    fn from(s: String) -> Self {
204        Self::from_cow(s.to_lowercase())
205    }
206}
207
208/// A header value
209#[derive(Clone, Debug, PartialEq, Eq)]
210pub struct HeaderValue(std::borrow::Cow<'static, str>);
211
212impl HeaderValue {
213    pub const fn from_static(s: &'static str) -> Self {
214        Self(std::borrow::Cow::Borrowed(s))
215    }
216
217    pub fn from_cow<C>(c: C) -> Self
218    where
219        C: Into<std::borrow::Cow<'static, str>>,
220    {
221        Self(c.into())
222    }
223
224    pub fn as_str(&self) -> &str {
225        self.0.as_ref()
226    }
227}
228
229impl From<&'static str> for HeaderValue {
230    fn from(s: &'static str) -> Self {
231        Self::from_cow(s)
232    }
233}
234
235impl From<String> for HeaderValue {
236    fn from(s: String) -> Self {
237        Self::from_cow(s)
238    }
239}
240
241impl From<&String> for HeaderValue {
242    fn from(s: &String) -> Self {
243        s.clone().into()
244    }
245}
246
247// headers are case insensitive
248// we are using all lowercase values
249// same as https://github.com/hyperium/http/blob/master/util/src/main.rs
250
251pub const ACCEPT: HeaderName = HeaderName::from_static("accept");
252pub const ACCEPT_ENCODING: HeaderName = HeaderName::from_static("accept-encoding");
253pub const ACL: HeaderName = HeaderName::from_static("x-ms-acl");
254pub const ACCOUNT_KIND: HeaderName = HeaderName::from_static("x-ms-account-kind");
255pub const ACTIVITY_ID: HeaderName = HeaderName::from_static("x-ms-activity-id");
256pub const APP: HeaderName = HeaderName::from_static("x-ms-app");
257pub const AUTHORIZATION: HeaderName = HeaderName::from_static("authorization");
258pub const APPEND_POSITION: HeaderName = HeaderName::from_static("x-ms-blob-condition-appendpos");
259pub const BLOB_ACCESS_TIER: HeaderName = HeaderName::from_static("x-ms-access-tier");
260pub const BLOB_CONTENT_LENGTH: HeaderName = HeaderName::from_static("x-ms-blob-content-length");
261pub const BLOB_PUBLIC_ACCESS: HeaderName = HeaderName::from_static("x-ms-blob-public-access");
262pub const BLOB_SEQUENCE_NUMBER: HeaderName = HeaderName::from_static("x-ms-blob-sequence-number");
263pub const BLOB_TYPE: HeaderName = HeaderName::from_static("x-ms-blob-type");
264pub const BLOB_CACHE_CONTROL: HeaderName = HeaderName::from_static("x-ms-blob-cache-control");
265pub const CACHE_CONTROL: HeaderName = HeaderName::from_static("cache-control");
266pub const CLIENT_REQUEST_ID: HeaderName = HeaderName::from_static("x-ms-client-request-id");
267pub const CLIENT_VERSION: HeaderName = HeaderName::from_static("x-ms-client-version");
268pub const CONTENT_DISPOSITION: HeaderName =
269    HeaderName::from_static("x-ms-blob-content-disposition");
270pub const CONTENT_ENCODING: HeaderName = HeaderName::from_static("content-encoding");
271pub const CONTENT_LANGUAGE: HeaderName = HeaderName::from_static("content-language");
272pub const CONTENT_LENGTH: HeaderName = HeaderName::from_static("content-length");
273pub const CONTENT_LOCATION: HeaderName = HeaderName::from_static("content-location");
274pub const CONTENT_MD5: HeaderName = HeaderName::from_static("content-md5");
275pub const CONTENT_RANGE: HeaderName = HeaderName::from_static("content-range");
276pub const CONTENT_SECURITY_POLICY: HeaderName = HeaderName::from_static("content-security-policy");
277pub const CONTENT_TYPE: HeaderName = HeaderName::from_static("content-type");
278pub const CONTINUATION: HeaderName = HeaderName::from_static("x-ms-continuation");
279pub const COPY_COMPLETION_TIME: HeaderName = HeaderName::from_static("x-ms-copy-completion-time");
280pub const COPY_PROGRESS: HeaderName = HeaderName::from_static("x-ms-copy-progress");
281pub const COPY_SOURCE: HeaderName = HeaderName::from_static("x-ms-copy-source");
282pub const COPY_STATUS: HeaderName = HeaderName::from_static("x-ms-copy-status");
283pub const COPY_STATUS_DESCRIPTION: HeaderName =
284    HeaderName::from_static("x-ms-copy-status-description");
285pub const CREATION_TIME: HeaderName = HeaderName::from_static("x-ms-creation-time");
286pub const DATE: HeaderName = HeaderName::from_static("date");
287pub const DELETE_SNAPSHOTS: HeaderName = HeaderName::from_static("x-ms-delete-snapshots");
288pub const DELETE_TYPE_PERMANENT: HeaderName = HeaderName::from_static("x-ms-delete-type-permanent");
289pub const ETAG: HeaderName = HeaderName::from_static("etag");
290pub const ERROR_CODE: HeaderName = HeaderName::from_static("x-ms-error-code");
291pub const HAS_IMMUTABILITY_POLICY: HeaderName =
292    HeaderName::from_static("x-ms-has-immutability-policy");
293pub const HAS_LEGAL_HOLD: HeaderName = HeaderName::from_static("x-ms-has-legal-hold");
294pub const IF_MATCH: HeaderName = HeaderName::from_static("if-match");
295pub const IF_MODIFIED_SINCE: HeaderName = HeaderName::from_static("if-modified-since");
296pub const IF_NONE_MATCH: HeaderName = HeaderName::from_static("if-none-match");
297pub const IF_RANGE: HeaderName = HeaderName::from_static("if-range");
298pub const IF_UNMODIFIED_SINCE: HeaderName = HeaderName::from_static("if-unmodified-since");
299pub const IF_SEQUENCE_NUMBER_EQ: HeaderName = HeaderName::from_static("x-ms-if-sequence-number-eq");
300pub const IF_SEQUENCE_NUMBER_LE: HeaderName = HeaderName::from_static("x-ms-if-sequence-number-le");
301pub const IF_SEQUENCE_NUMBER_LT: HeaderName = HeaderName::from_static("x-ms-if-sequence-number-lt");
302pub const IF_TAGS: HeaderName = HeaderName::from_static("x-ms-if-tags");
303pub const ITEM_COUNT: HeaderName = HeaderName::from_static("x-ms-item-count");
304pub const ITEM_TYPE: HeaderName = HeaderName::from_static("x-ms-item-type");
305pub const KEEP_ALIVE: HeaderName = HeaderName::from_static("keep-alive");
306pub const LAST_MODIFIED: HeaderName = HeaderName::from_static("last-modified");
307pub const LEASE_ACTION: HeaderName = HeaderName::from_static("x-ms-lease-action");
308pub const LEASE_BREAK_PERIOD: HeaderName = HeaderName::from_static("x-ms-lease-break-period");
309pub const LEASE_DURATION: HeaderName = HeaderName::from_static("x-ms-lease-duration");
310pub const LEASE_ID: HeaderName = HeaderName::from_static("x-ms-lease-id");
311pub const LEASE_STATE: HeaderName = HeaderName::from_static("x-ms-lease-state");
312pub const LEASE_STATUS: HeaderName = HeaderName::from_static("x-ms-lease-status");
313pub const LEASE_TIME: HeaderName = HeaderName::from_static("x-ms-lease-time");
314pub const LINK: HeaderName = HeaderName::from_static("link");
315pub const LOCATION: HeaderName = HeaderName::from_static("location");
316pub const MAX_ITEM_COUNT: HeaderName = HeaderName::from_static("x-ms-max-item-count");
317pub const META_PREFIX: HeaderName = HeaderName::from_static("x-ms-meta-");
318pub const MS_DATE: HeaderName = HeaderName::from_static("x-ms-date");
319pub const MS_RANGE: HeaderName = HeaderName::from_static("x-ms-range");
320pub const NAMESPACE_ENABLED: HeaderName = HeaderName::from_static("x-ms-namespace-enabled");
321pub const PAGE_WRITE: HeaderName = HeaderName::from_static("x-ms-page-write");
322pub const PROPERTIES: HeaderName = HeaderName::from_static("x-ms-properties");
323pub const PREFER: HeaderName = HeaderName::from_static("prefer");
324pub const PROPOSED_LEASE_ID: HeaderName = HeaderName::from_static("x-ms-proposed-lease-id");
325pub const RANGE: HeaderName = HeaderName::from_static("range");
326pub const RANGE_GET_CONTENT_CRC64: HeaderName =
327    HeaderName::from_static("x-ms-range-get-content-crc64");
328pub const RANGE_GET_CONTENT_MD5: HeaderName = HeaderName::from_static("x-ms-range-get-content-md5");
329pub const REQUEST_ID: HeaderName = HeaderName::from_static("x-ms-request-id");
330pub const REQUEST_SERVER_ENCRYPTED: HeaderName =
331    HeaderName::from_static("x-ms-request-server-encrypted");
332pub const REQUIRES_SYNC: HeaderName = HeaderName::from_static("x-ms-requires-sync");
333pub const RETRY_AFTER: HeaderName = HeaderName::from_static("retry-after");
334pub const SERVER: HeaderName = HeaderName::from_static("server");
335pub const SERVER_ENCRYPTED: HeaderName = HeaderName::from_static("x-ms-server-encrypted");
336pub const SESSION_TOKEN: HeaderName = HeaderName::from_static("x-ms-session-token");
337pub const SKU_NAME: HeaderName = HeaderName::from_static("x-ms-sku-name");
338pub const SOURCE_IF_MATCH: HeaderName = HeaderName::from_static("x-ms-source-if-match");
339pub const SOURCE_IF_MODIFIED_SINCE: HeaderName =
340    HeaderName::from_static("x-ms-source-if-modified-since");
341pub const SOURCE_IF_NONE_MATCH: HeaderName = HeaderName::from_static("x-ms-source-if-none-match");
342pub const SOURCE_IF_UNMODIFIED_SINCE: HeaderName =
343    HeaderName::from_static("x-ms-source-if-unmodified-since");
344pub const SOURCE_LEASE_ID: HeaderName = HeaderName::from_static("x-ms-source-lease-id");
345pub const TAGS: HeaderName = HeaderName::from_static("x-ms-tags");
346pub const USER: HeaderName = HeaderName::from_static("x-ms-user");
347pub const USER_AGENT: HeaderName = HeaderName::from_static("user-agent");
348pub const VERSION: HeaderName = HeaderName::from_static("x-ms-version");
349pub const WWW_AUTHENTICATE: HeaderName = HeaderName::from_static("www-authenticate");