linera_base/
http.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Types used when performing HTTP requests.
5
6use custom_debug_derive::Debug;
7use linera_witty::{WitLoad, WitStore, WitType};
8use serde::{Deserialize, Serialize};
9
10use crate::hex_debug;
11
12/// An HTTP request.
13#[derive(Clone, Debug, Eq, PartialEq, WitLoad, WitStore, WitType)]
14#[witty(name = "http-request")]
15pub struct Request {
16    /// The [`Method`] used for the HTTP request.
17    pub method: Method,
18
19    /// The URL this request is intended to.
20    pub url: String,
21
22    /// The headers that should be included in the request.
23    pub headers: Vec<Header>,
24
25    /// The body of the request.
26    #[debug(with = "hex_debug")]
27    pub body: Vec<u8>,
28}
29
30impl Request {
31    /// Creates an HTTP GET [`Request`] for a `url`.
32    pub fn get(url: impl Into<String>) -> Self {
33        Request {
34            method: Method::Get,
35            url: url.into(),
36            headers: vec![],
37            body: vec![],
38        }
39    }
40
41    /// Creates an HTTP POST [`Request`] for a `url` with a `payload` that's arbitrary bytes.
42    pub fn post(url: impl Into<String>, payload: impl Into<Vec<u8>>) -> Self {
43        Request {
44            method: Method::Post,
45            url: url.into(),
46            headers: vec![],
47            body: payload.into(),
48        }
49    }
50
51    /// Creates an HTTP POST [`Request`] for a `url` with a body that's the `payload` serialized to
52    /// JSON.
53    pub fn post_json(
54        url: impl Into<String>,
55        payload: &impl Serialize,
56    ) -> Result<Self, serde_json::Error> {
57        Ok(Request {
58            method: Method::Post,
59            url: url.into(),
60            headers: vec![Header::new("Content-Type", b"application/json")],
61            body: serde_json::to_vec(payload)?,
62        })
63    }
64
65    /// Adds a header to this [`Request`].
66    pub fn with_header(mut self, name: impl Into<String>, value: impl Into<Vec<u8>>) -> Self {
67        self.headers.push(Header::new(name, value));
68        self
69    }
70}
71
72/// The method used in an HTTP request.
73#[derive(Clone, Copy, Debug, Eq, PartialEq, WitLoad, WitStore, WitType)]
74#[witty(name = "http-method")]
75pub enum Method {
76    /// A GET request.
77    Get,
78
79    /// A POST request.
80    Post,
81
82    /// A PUT request.
83    Put,
84
85    /// A DELETE request.
86    Delete,
87
88    /// A HEAD request.
89    Head,
90
91    /// A OPTIONS request.
92    Options,
93
94    /// A CONNECT request.
95    Connect,
96
97    /// A PATCH request.
98    Patch,
99
100    /// A TRACE request.
101    Trace,
102}
103
104#[cfg(with_reqwest)]
105impl From<Method> for reqwest::Method {
106    fn from(method: Method) -> Self {
107        match method {
108            Method::Get => reqwest::Method::GET,
109            Method::Post => reqwest::Method::POST,
110            Method::Put => reqwest::Method::PUT,
111            Method::Delete => reqwest::Method::DELETE,
112            Method::Head => reqwest::Method::HEAD,
113            Method::Options => reqwest::Method::OPTIONS,
114            Method::Connect => reqwest::Method::CONNECT,
115            Method::Patch => reqwest::Method::PATCH,
116            Method::Trace => reqwest::Method::TRACE,
117        }
118    }
119}
120
121/// A response for an HTTP request.
122#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, WitLoad, WitStore, WitType)]
123#[witty(name = "http-response")]
124pub struct Response {
125    /// The status code of the HTTP response.
126    pub status: u16,
127
128    /// The headers included in the response.
129    pub headers: Vec<Header>,
130
131    /// The body of the response.
132    #[debug(with = "hex_debug")]
133    #[serde(with = "serde_bytes")]
134    pub body: Vec<u8>,
135}
136
137impl Response {
138    /// Creates an HTTP [`Response`] with a user defined `status_code`.
139    pub fn new(status_code: u16) -> Self {
140        Response {
141            status: status_code,
142            headers: vec![],
143            body: vec![],
144        }
145    }
146
147    /// Creates an HTTP [`Response`] with an OK status code and the provided `body`.
148    pub fn ok(body: impl Into<Vec<u8>>) -> Self {
149        Response {
150            status: 200,
151            headers: vec![],
152            body: body.into(),
153        }
154    }
155
156    /// Creates an HTTP [`Response`] with an Unauthorized status code.
157    pub fn unauthorized() -> Self {
158        Response {
159            status: 401,
160            headers: vec![],
161            body: vec![],
162        }
163    }
164
165    /// Adds a header to this [`Response`].
166    pub fn with_header(mut self, name: impl Into<String>, value: impl Into<Vec<u8>>) -> Self {
167        self.headers.push(Header::new(name, value));
168        self
169    }
170}
171
172/// A header for an HTTP request or response.
173#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, WitLoad, WitStore, WitType)]
174#[witty(name = "http-header")]
175pub struct Header {
176    /// The header name.
177    pub name: String,
178
179    /// The value of the header.
180    #[debug(with = "hex_debug")]
181    #[serde(with = "serde_bytes")]
182    pub value: Vec<u8>,
183}
184
185impl Header {
186    /// Creates a new [`Header`] with the provided `name` and `value`.
187    pub fn new(name: impl Into<String>, value: impl Into<Vec<u8>>) -> Self {
188        Header {
189            name: name.into(),
190            value: value.into(),
191        }
192    }
193}