openid_client/types/
http_client.rs

1//! # Http Client Interface for Custom Http Clients
2
3use std::collections::HashMap;
4use std::fmt::Debug;
5use std::future;
6
7use url::Url;
8
9use crate::helpers::string_map_to_form_url_encoded;
10
11/// The Http methods
12#[derive(Debug, Default, Clone)]
13#[cfg_attr(test, derive(PartialEq))]
14pub enum HttpMethod {
15    /// The GET method is used to retrieve data from a server.
16    #[default]
17    GET,
18    /// The POST method is used to submit data to a server.
19    POST,
20    /// The PUT method is used to replace all existing data on a server with the provided data.
21    PUT,
22    /// The PATCH method is used to update a specific part of a resource on a server.
23    PATCH,
24    /// The DELETE method is used to delete a resource from a server.
25    DELETE,
26    /// The HEAD method is used to retrieve only the headers of a resource, without the actual data.
27    HEAD,
28    /// The OPTIONS method is used to retrieve the capabilities of a server.
29    OPTIONS,
30    /// The TRACE method is used to echo the received request back to the client. (Rarely used)
31    TRACE,
32    /// The CONNECT method is used to establish a tunnel through the proxy server. (For use with secure proxies)
33    CONNECT,
34}
35
36/// The expectations set by methods such as discover, token grant, callback etc...
37#[derive(Debug, Clone, Copy)]
38pub struct HttpResponseExpectations {
39    /// Whether or not to expect body with the response
40    pub body: bool,
41    /// Specifies if the request is using bearer auth, and checks for bearer token related errors
42    pub bearer: bool,
43    /// Specifies if the response should be of type json and validates it
44    pub json_body: bool,
45    /// Expected status code from the server
46    pub status_code: u16,
47}
48
49/// The client certificate
50#[derive(Debug)]
51pub struct ClientCertificate {
52    /// Client public certificate in pem format.
53    pub cert: String,
54    /// Client private certificate in pem format.
55    pub key: String,
56}
57
58/// # Request
59/// Request is an internal struct used to create various OIDC requests.
60#[derive(Debug)]
61pub struct HttpRequest {
62    /// Url of the request without query params
63    pub url: Url,
64    /// Http method of the request
65    pub method: HttpMethod,
66    /// Headers that are sent in the request
67    pub headers: HashMap<String, Vec<String>>,
68    /// The request body to be sent
69    pub body: Option<String>,
70    /// Specifies if the request is MTLS and needs client certificate
71    pub mtls: bool,
72    /// Client certificate to be used in the request
73    pub client_certificate: Option<ClientCertificate>,
74    /// Expectations to be fullfilled by the response
75    pub(crate) expectations: HttpResponseExpectations,
76}
77
78impl HttpRequest {
79    pub(crate) fn new() -> Self {
80        Self {
81            url: Url::parse("about:blank").unwrap(),
82
83            headers: HashMap::new(),
84            method: HttpMethod::GET,
85            body: None,
86            client_certificate: None,
87            mtls: false,
88            expectations: HttpResponseExpectations {
89                body: true,
90                bearer: false,
91                status_code: 200,
92                json_body: true,
93            },
94        }
95    }
96
97    pub(crate) fn url(mut self, url: Url) -> Self {
98        self.url = url;
99        self
100    }
101
102    pub(crate) fn method(mut self, method: HttpMethod) -> Self {
103        self.method = method;
104        self
105    }
106
107    pub(crate) fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
108        let name = name.into();
109        let value = value.into();
110
111        if let Some(values) = self.headers.get_mut(&name) {
112            values.push(value);
113        } else {
114            let values = vec![value];
115            self.headers.insert(name, values);
116        }
117        self
118    }
119
120    pub(crate) fn header_replace(mut self, name: impl Into<String>, value: Vec<String>) -> Self {
121        self.headers.insert(name.into(), value);
122        self
123    }
124
125    pub(crate) fn headers(mut self, headers: HashMap<String, Vec<String>>) -> Self {
126        self.headers = headers;
127        self
128    }
129
130    pub(crate) fn json(mut self, json: String) -> Self {
131        self.headers.insert(
132            "content-type".to_string(),
133            vec!["application/json".to_string()],
134        );
135        self.body(json)
136    }
137
138    pub(crate) fn form(mut self, form: HashMap<String, String>) -> Self {
139        let form_body = string_map_to_form_url_encoded(&form).unwrap();
140        self.headers.insert(
141            "content-type".to_string(),
142            vec!["application/x-www-form-urlencoded".to_string()],
143        );
144        self.body(form_body)
145    }
146
147    pub(crate) fn body(mut self, body: String) -> Self {
148        self.headers.insert(
149            "content-length".to_string(),
150            vec![body.as_bytes().len().to_string()],
151        );
152        self.body = Some(body);
153        self
154    }
155
156    pub(crate) fn mtls(mut self, mtls: bool) -> Self {
157        self.mtls = mtls;
158        self
159    }
160
161    pub(crate) fn expect_body(mut self, expect: bool) -> Self {
162        self.expectations.body = expect;
163        self
164    }
165
166    pub(crate) fn expect_status_code(mut self, code: u16) -> Self {
167        self.expectations.status_code = code;
168        self
169    }
170
171    pub(crate) fn expect_json_body(mut self, expect: bool) -> Self {
172        self.expectations.json_body = expect;
173        self
174    }
175
176    pub(crate) fn expect_bearer(mut self, bearer: bool) -> Self {
177        self.expectations.bearer = bearer;
178        self
179    }
180}
181
182/// Represents an HTTP response received from a server.
183#[derive(Debug, Clone)]
184pub struct HttpResponse {
185    /// The HTTP status code of the response (e.g., 200 for success, 404 for Not Found).
186    pub status_code: u16,
187    /// The content type header
188    pub content_type: Option<String>,
189    /// The www authenticate header
190    pub www_authenticate: Option<String>,
191    /// The dpop nonce
192    pub dpop_nonce: Option<String>,
193    /// The optional body content of the response. None if there is no body content (String).
194    pub body: Option<String>,
195}
196
197/// This trait defines the interface for making HTTP requests used by the OpenID library.
198/// Users who need custom HTTP clients need to implement this trait.
199pub trait OidcHttpClient {
200    /// Gets the client certificate for the current request. Return none if the request does not need mtls
201    fn get_client_certificate(
202        &self,
203        _req: &HttpRequest,
204    ) -> impl std::future::Future<Output = Option<ClientCertificate>> + Send {
205        future::ready(None)
206    }
207
208    /// Makes an HTTP request using the provided HttpRequest object.
209    ///
210    /// This function takes an `HttpRequest` object as input and returns a future
211    /// implementing `std::future::Future<Output = Result<HttpResponse, String>>`.
212    /// The future resolves to either a `Result<HttpResponse, String>`.
213    ///  * On success, the result is `Ok(HttpResponse)` containing the HTTP response.
214    ///  * On error, the result is `Err(String)` with an error message describing the failure.
215    ///
216    /// This function allows the library to be agnostic to the specific HTTP client
217    /// implementation used, as long as it implements this trait.
218    fn request(
219        &self,
220        req: HttpRequest,
221    ) -> impl std::future::Future<Output = Result<HttpResponse, String>> + Send;
222}