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