aditjind_crate/http/
request.rs

1/*
2 * Licensed to Elasticsearch B.V. under one or more contributor
3 * license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright
5 * ownership. Elasticsearch B.V. licenses this file to you under
6 * the Apache License, Version 2.0 (the "License"); you may
7 * not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *	http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20/*
21 * SPDX-License-Identifier: Apache-2.0
22 *
23 * The OpenSearch Contributors require contributions made to
24 * this file be licensed under the Apache-2.0 license or a
25 * compatible open source license.
26 *
27 * Modifications Copyright OpenSearch Contributors. See
28 * GitHub history for details.
29 */
30
31//! HTTP request components
32
33use crate::error::Error;
34use bytes::{BufMut, Bytes, BytesMut};
35use percent_encoding::AsciiSet;
36use serde::Serialize;
37
38// similar to percent-encoding's NON_ALPHANUMERIC AsciiSet, but with some characters removed
39pub(crate) const PARTS_ENCODED: &AsciiSet = &percent_encoding::NON_ALPHANUMERIC
40    .remove(b'_')
41    .remove(b'-')
42    .remove(b'.')
43    .remove(b',')
44    .remove(b'*');
45
46/// Body of an API call.
47///
48/// Some Elasticsearch APIs accept a body as part of the API call. Most APIs
49/// expect JSON, however, there are some APIs that expect newline-delimited JSON (NDJSON).
50/// The [Body] trait allows modelling different API body implementations.
51pub trait Body {
52    /// An existing immutable buffer that can be used to avoid
53    /// having to write to another buffer that will then be written to the request stream.
54    ///
55    /// If this method returns `Some`, the bytes must be the same as
56    /// those that would be written by [Body::write].
57    fn bytes(&self) -> Option<Bytes> {
58        None
59    }
60
61    /// Write to a buffer that will be written to the request stream
62    fn write(&self, bytes: &mut BytesMut) -> Result<(), Error>;
63}
64
65impl<'a, B: ?Sized> Body for &'a B
66where
67    B: Body,
68{
69    fn bytes(&self) -> Option<Bytes> {
70        (**self).bytes()
71    }
72
73    fn write(&self, bytes: &mut BytesMut) -> Result<(), Error> {
74        (**self).write(bytes)
75    }
76}
77
78/// A JSON body of an API call.
79pub struct JsonBody<T>(pub(crate) T);
80
81impl<T> JsonBody<T>
82where
83    T: Serialize,
84{
85    /// Creates a new instance of [JsonBody] for a type `T` that implements [serde::Serialize]
86    pub fn new(t: T) -> Self {
87        Self(t)
88    }
89}
90
91impl<T> From<T> for JsonBody<T>
92where
93    T: Serialize,
94{
95    /// Creates a new instance of [JsonBody] from a type `T` that implements [serde::Serialize]
96    fn from(t: T) -> Self {
97        JsonBody(t)
98    }
99}
100
101impl<T> Body for JsonBody<T>
102where
103    T: Serialize,
104{
105    fn write(&self, bytes: &mut BytesMut) -> Result<(), Error> {
106        let writer = bytes.writer();
107        serde_json::to_writer(writer, &self.0)?;
108
109        Ok(())
110    }
111}
112
113/// A Newline-delimited body of an API call
114pub struct NdBody<T>(pub(crate) Vec<T>);
115
116impl<T> NdBody<T>
117where
118    T: Body,
119{
120    /// Creates a new instance of [NdBody], for a collection of `T` that implement [Body].
121    ///
122    /// Accepts `T` that implement [Body] as opposed to [serde::Serialize], because each `T`
123    /// itself may need to serialize to newline delimited.
124    pub fn new(b: Vec<T>) -> Self {
125        Self(b)
126    }
127}
128
129impl<T> Body for NdBody<T>
130where
131    T: Body,
132{
133    fn write(&self, bytes: &mut BytesMut) -> Result<(), Error> {
134        for line in &self.0 {
135            line.write(bytes)?;
136            // only write a newline if the T impl does not
137            if let Some(b) = bytes.last() {
138                if b != &(b'\n') {
139                    bytes.put_u8(b'\n');
140                }
141            }
142        }
143        Ok(())
144    }
145}
146
147impl Body for Bytes {
148    fn bytes(&self) -> Option<Bytes> {
149        Some(self.clone())
150    }
151
152    fn write(&self, bytes: &mut BytesMut) -> Result<(), Error> {
153        self.as_ref().write(bytes)
154    }
155}
156
157impl Body for BytesMut {
158    fn bytes(&self) -> Option<Bytes> {
159        Some(self.clone().freeze())
160    }
161
162    fn write(&self, bytes: &mut BytesMut) -> Result<(), Error> {
163        self.as_ref().write(bytes)
164    }
165}
166
167impl Body for Vec<u8> {
168    fn write(&self, bytes: &mut BytesMut) -> Result<(), Error> {
169        self.as_slice().write(bytes)
170    }
171}
172
173impl<'a> Body for &'a [u8] {
174    fn write(&self, bytes: &mut BytesMut) -> Result<(), Error> {
175        bytes.reserve(self.len());
176        bytes.put_slice(*self);
177        Ok(())
178    }
179}
180
181impl Body for String {
182    fn write(&self, bytes: &mut BytesMut) -> Result<(), Error> {
183        self.as_bytes().write(bytes)
184    }
185}
186
187impl<'a> Body for &'a str {
188    fn write(&self, bytes: &mut BytesMut) -> Result<(), Error> {
189        self.as_bytes().write(bytes)
190    }
191}
192
193impl Body for () {
194    fn write(&self, _bytes: &mut BytesMut) -> Result<(), Error> {
195        Ok(())
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use crate::http::request::{Body, JsonBody, NdBody};
202    use bytes::BytesMut;
203    use serde_json::json;
204
205    #[test]
206    fn serialize_into_jsonbody_writes_to_bytes() -> Result<(), failure::Error> {
207        let mut bytes = BytesMut::new();
208        let body: JsonBody<_> = json!({"foo":"bar","baz":1}).into();
209        let _ = body.write(&mut bytes)?;
210        // NOTE: serde_json writes properties lexicographically
211        assert_eq!(b"{\"baz\":1,\"foo\":\"bar\"}", &bytes[..]);
212
213        Ok(())
214    }
215
216    #[test]
217    fn bodies_into_ndbody_writes_to_bytes() -> Result<(), failure::Error> {
218        let mut bytes = BytesMut::new();
219        let mut bodies: Vec<JsonBody<_>> = Vec::with_capacity(2);
220        bodies.push(json!({"item":1}).into());
221        bodies.push(json!({"item":2}).into());
222
223        let body = NdBody(bodies);
224        let _ = body.write(&mut bytes)?;
225        assert_eq!(b"{\"item\":1}\n{\"item\":2}\n", &bytes[..]);
226
227        Ok(())
228    }
229
230    #[test]
231    fn bytes_body_writes_to_bytes_mut() -> Result<(), failure::Error> {
232        let mut bytes_mut = BytesMut::with_capacity(21);
233        let bytes = bytes::Bytes::from(&b"{\"foo\":\"bar\",\"baz\":1}"[..]);
234        let _ = bytes.write(&mut bytes_mut)?;
235        assert_eq!(&bytes[..], &bytes_mut[..]);
236
237        Ok(())
238    }
239
240    #[test]
241    fn bytes_body_returns_usable_buf() -> Result<(), failure::Error> {
242        let mut bytes_mut = BytesMut::with_capacity(21);
243        let buf = bytes::Bytes::from(&b"{\"foo\":\"bar\",\"baz\":1}"[..]);
244
245        let bytes = buf.bytes().expect("bytes always returns Some");
246        let _ = buf.write(&mut bytes_mut)?;
247        assert_eq!(&buf[..], &bytes_mut[..]);
248        assert_eq!(&bytes[..], &bytes_mut[..]);
249
250        Ok(())
251    }
252
253    #[test]
254    fn vec_body_writes_to_bytes_mut() -> Result<(), failure::Error> {
255        let mut bytes_mut = BytesMut::with_capacity(21);
256        let bytes = b"{\"foo\":\"bar\",\"baz\":1}".to_vec();
257        let _ = bytes.write(&mut bytes_mut)?;
258        assert_eq!(&bytes[..], &bytes_mut[..]);
259
260        Ok(())
261    }
262
263    #[test]
264    fn bytes_slice_body_writes_to_bytes_mut() -> Result<(), failure::Error> {
265        let mut bytes_mut = BytesMut::with_capacity(21);
266        let bytes: &'static [u8] = b"{\"foo\":\"bar\",\"baz\":1}";
267        let _ = bytes.write(&mut bytes_mut)?;
268        assert_eq!(&bytes[..], &bytes_mut[..]);
269
270        Ok(())
271    }
272
273    #[test]
274    fn string_body_writes_to_bytes_mut() -> Result<(), failure::Error> {
275        let mut bytes_mut = BytesMut::with_capacity(21);
276        let s = String::from("{\"foo\":\"bar\",\"baz\":1}");
277        let _ = s.write(&mut bytes_mut)?;
278        assert_eq!(s.as_bytes(), &bytes_mut[..]);
279
280        Ok(())
281    }
282
283    #[test]
284    fn string_slice_body_writes_to_bytes_mut() -> Result<(), failure::Error> {
285        let mut bytes_mut = BytesMut::with_capacity(21);
286        let s: &'static str = "{\"foo\":\"bar\",\"baz\":1}";
287        let _ = s.write(&mut bytes_mut)?;
288        assert_eq!(s.as_bytes(), &bytes_mut[..]);
289
290        Ok(())
291    }
292}