1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use std::io::Read;
use std::fmt::{self, Debug};

use request::Request;
use response::{Response, Responder, DEFAULT_CHUNK_SIZE};
use http::Status;

/// Streams a response to a client from an arbitrary `Read`er type.
///
/// The client is sent a "chunked" response, where the chunk size is at most
/// 4KiB. This means that at most 4KiB are stored in memory while the response
/// is being sent. This type should be used when sending responses that are
/// arbitrarily large in size, such as when streaming from a local socket.
pub struct Stream<T: Read>(T, u64);

impl<T: Read> Stream<T> {
    /// Create a new stream from the given `reader` and sets the chunk size for
    /// each streamed chunk to `chunk_size` bytes.
    ///
    /// # Example
    ///
    /// Stream a response from whatever is in `stdin` with a chunk size of 10
    /// bytes. Note: you probably shouldn't do this.
    ///
    /// ```rust
    /// use std::io;
    /// use rocket::response::Stream;
    ///
    /// # #[allow(unused_variables)]
    /// let response = Stream::chunked(io::stdin(), 10);
    /// ```
    ///
    /// # Buffering and blocking
    ///
    /// Normally, data will be buffered and sent only in complete `chunk_size`
    /// chunks.
    ///
    /// With the feature `sse` enabled, the `Read`er may signal that data sent
    /// so far should be transmitted in a timely fashion (e.g. it is responding
    /// to a Server-Side Events (JavaScript `EventSource`) request. To do this
    /// it should return an [io::Error](std::io::Error) of kind `WouldBlock`
    /// (which should not normally occur), after returning a collection of data.
    /// This will cause a flush of data seen so far, rather than being treated
    /// as an error.
    ///
    /// Note that long-running responses may easily exhaust Rocket's thread
    /// pool, so consider increasing the number of threads. If doing SSE, also
    /// note the 'maximum open connections' browser limitation which is
    /// described in the [EventSource
    /// documentation](https://developer.mozilla.org/en-US/docs/Web/API/EventSource)
    /// on the Mozilla Developer Network.
    ///
    /// Without the `sse` feature, a `WouldBlock` error is treated as an actual
    /// error.
    pub fn chunked(reader: T, chunk_size: u64) -> Stream<T> {
        Stream(reader, chunk_size)
    }
}

impl<T: Read + Debug> Debug for Stream<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_tuple("Stream").field(&self.0).finish()
    }
}

/// Create a new stream from the given `reader`.
///
/// # Example
///
/// Stream a response from whatever is in `stdin`. Note: you probably
/// shouldn't do this.
///
/// ```rust
/// use std::io;
/// use rocket::response::Stream;
///
/// # #[allow(unused_variables)]
/// let response = Stream::from(io::stdin());
/// ```
impl<T: Read> From<T> for Stream<T> {
    fn from(reader: T) -> Self {
        Stream(reader, DEFAULT_CHUNK_SIZE)
    }
}

/// Sends a response to the client using the "Chunked" transfer encoding. The
/// maximum chunk size is 4KiB.
///
/// # Failure
///
/// If reading from the input stream fails at any point during the response, the
/// response is abandoned, and the response ends abruptly. An error is printed
/// to the console with an indication of what went wrong.
impl<'r, T: Read + 'r> Responder<'r> for Stream<T> {
    fn respond_to(self, _: &Request) -> Result<Response<'r>, Status> {
        Response::build().chunked_body(self.0, self.1).ok()
    }
}