qiniu_multipart/
mock.rs

1// Copyright 2016 `multipart` Crate Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7//! Mocked types for client-side and server-side APIs.
8use std::io::{self, Read, Write};
9use std::fmt;
10
11use rand::{self, Rng};
12use rand::prelude::ThreadRng;
13
14/// A mock implementation of `client::HttpRequest` which can spawn an `HttpBuffer`.
15///
16/// `client::HttpRequest` impl requires the `client` feature.
17#[derive(Default, Debug)]
18pub struct ClientRequest {
19    boundary: Option<String>,
20    content_len: Option<u64>,
21}
22
23#[cfg(feature = "client")]
24impl ::client::HttpRequest for ClientRequest {
25    type Stream = HttpBuffer;
26    type Error = io::Error;
27
28    fn apply_headers(&mut self, boundary: &str, content_len: Option<u64>) -> bool {
29        self.boundary = Some(boundary.into());
30        self.content_len = content_len;
31        true
32    }
33
34    /// ## Panics
35    /// If `apply_headers()` was not called.
36    fn open_stream(self) -> Result<HttpBuffer, io::Error> {
37        debug!("ClientRequest::open_stream called! {:?}", self);
38        let boundary = self.boundary.expect("ClientRequest::set_headers() was not called!");
39
40        Ok(HttpBuffer::new_empty(boundary, self.content_len))
41    }
42}
43
44
45/// A writable buffer which stores the boundary and content-length, if provided.
46///
47/// Implements `client::HttpStream` if the `client` feature is enabled.
48pub struct HttpBuffer {
49    /// The buffer containing the raw bytes.
50    pub buf: Vec<u8>,
51    /// The multipart boundary.
52    pub boundary: String,
53    /// The value of the content-length header, if set.
54    pub content_len: Option<u64>,
55    rng: ThreadRng,
56}
57
58impl HttpBuffer {
59    /// Create an empty buffer with the given boundary and optional content-length.
60    pub fn new_empty(boundary: String, content_len: Option<u64>) -> HttpBuffer {
61        Self::with_buf(Vec::new(), boundary, content_len)
62    }
63
64    /// Wrap the given buffer with the given boundary and optional content-length.
65    pub fn with_buf(buf: Vec<u8>, boundary: String, content_len: Option<u64>) -> Self {
66        HttpBuffer {
67            buf,
68            boundary,
69            content_len,
70            rng: rand::thread_rng()
71        }
72    }
73
74    /// Get a `ServerRequest` wrapping the data in this buffer.
75    pub fn for_server(&self) -> ServerRequest {
76        ServerRequest {
77            data: &self.buf,
78            boundary: &self.boundary,
79            content_len: self.content_len,
80            rng: rand::thread_rng(),
81        }
82    }
83}
84
85impl Write for HttpBuffer {
86    /// To simulate a network connection, this will copy a random number of bytes
87    /// from `buf` to the buffer.
88    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
89        if buf.is_empty() {
90            debug!("HttpBuffer::write() was passed a zero-sized buffer.");
91            return Ok(0);
92        }
93
94        // Simulate the randomness of a network connection by not always reading everything
95        let len = self.rng.gen_range(1, buf.len() + 1);
96
97        self.buf.write(&buf[..len])
98    }
99
100    fn flush(&mut self) -> io::Result<()> {
101        self.buf.flush()
102    }
103}
104
105#[cfg(feature = "client")]
106impl ::client::HttpStream for HttpBuffer {
107    type Request = ClientRequest;
108    type Response = HttpBuffer;
109    type Error = io::Error;
110
111    /// Returns `Ok(self)`.
112    fn finish(self) -> Result<Self, io::Error> { Ok(self) }
113}
114
115impl fmt::Debug for HttpBuffer {
116    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117        f.debug_struct("multipart::mock::HttpBuffer")
118            .field("buf", &self.buf)
119            .field("boundary", &self.boundary)
120            .field("content_len", &self.content_len)
121            .finish()
122    }
123}
124
125/// A mock implementation of `server::HttpRequest` that can be read.
126///
127/// Implements `server::HttpRequest` if the `server` feature is enabled.
128pub struct ServerRequest<'a> {
129    /// Slice of the source `HttpBuffer::buf`
130    pub data: &'a [u8],
131    /// The multipart boundary.
132    pub boundary: &'a str,
133    /// The value of the content-length header, if set.
134    pub content_len: Option<u64>,
135    rng: ThreadRng,
136}
137
138impl<'a> ServerRequest<'a> {
139    /// Create a new `ServerRequest` with the given data and boundary.
140    ///
141    /// Assumes `content_len: None`
142    pub fn new(data: &'a [u8], boundary: &'a str) -> Self {
143        ServerRequest {
144            data,
145            boundary,
146            content_len: None,
147            rng: rand::thread_rng(),
148        }
149    }
150}
151
152impl<'a> Read for ServerRequest<'a> {
153    /// To simulate a network connection, this will copy a random number of bytes
154    /// from the buffer to `out`.
155    fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
156        if out.is_empty() {
157            debug!("ServerRequest::read() was passed a zero-sized buffer.");
158            return Ok(0);
159        }
160
161        // Simulate the randomness of a network connection by not always reading everything
162        let len = self.rng.gen_range(1, out.len() + 1);
163        self.data.read(&mut out[..len])
164    }
165}
166
167#[cfg(feature = "server")]
168impl<'a> ::server::HttpRequest for ServerRequest<'a> {
169    type Body = Self;
170
171    fn multipart_boundary(&self) -> Option<&str> { Some(self.boundary) }
172
173    fn body(self) -> Self::Body {
174        self
175    }
176}
177
178/// A `Write` adapter that duplicates all data written to the inner writer as well as stdout.
179pub struct StdoutTee<'s, W> {
180    inner: W,
181    stdout: io::StdoutLock<'s>,
182}
183
184impl<'s, W> StdoutTee<'s, W> {
185    /// Constructor
186    pub fn new(inner: W, stdout: &'s io::Stdout) -> Self {
187        Self {
188            inner, stdout: stdout.lock(),
189        }
190    }
191}
192
193impl<'s, W: Write> Write for StdoutTee<'s, W> {
194    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
195        self.inner.write_all(buf)?;
196        self.stdout.write(buf)
197    }
198
199    fn flush(&mut self) -> io::Result<()> {
200        self.inner.flush()?;
201        self.stdout.flush()
202    }
203}