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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use fastly_shared::FastlyStatus;

use crate::error::HandleError;

use super::super::handle::BodyHandle;
use std::io::Write;
use std::mem::ManuallyDrop;

/// A low-level interface to a streaming HTTP body.
///
/// The interface to this type is very similar to [`BodyHandle`], however it is write-only, and can
/// only be created as a result of calling
/// [`ResponseHandle::send_to_client()`][`crate::handle::ResponseHandle::send_to_client()`] or
/// [`RequestHandle::send_async_streaming()`][`crate::handle::RequestHandle::send_async_streaming()`].
///
/// This type implements [`Write`] to write to the end of a body. Note that these operations are
/// unbuffered, unlike the same operations on the higher-level [`Body`][`crate::Body`] type.
///
/// A streaming body handle will be automatically aborted if it goes out of scope without calling
/// [`finish()`][`Self::finish()`].
#[derive(Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
#[must_use = "streaming body handles must be `.finish()`ed"]
pub struct StreamingBodyHandle {
    // The `BodyHandle` is wrapped in `ManuallyDrop` so that we do not automatically call `close()`
    // when dropping a streaming body. `close()` must only be called when the user affirmatively
    // `finish()`es the streaming body.
    handle: ManuallyDrop<BodyHandle>,
}

impl StreamingBodyHandle {
    /// Finish writing to a streaming body handle.
    pub fn finish(self) -> Result<(), HandleError> {
        match unsafe { fastly_sys::fastly_http_body::close(self.into_u32()) } {
            FastlyStatus::OK => Ok(()),
            FastlyStatus::BADF => Err(HandleError::InvalidHandle),
            other => panic!(
                "unexpected error from `fastly_http_body::close`: {:?}; \
                             please report this as a bug",
                other
            ),
        }
    }

    /// Make a streaming body handle from a non-streaming handle.
    ///
    /// This should only be used when calling the raw ABI directly, and care should be taken not to
    /// reuse or alias handle values.
    pub fn from_body_handle(body_handle: BodyHandle) -> Self {
        Self {
            handle: ManuallyDrop::new(body_handle),
        }
    }

    /// Get the underlying representation of the handle.
    ///
    /// This should only be used when calling the raw ABI directly, and care should be taken not to
    /// reuse or alias handle values.
    pub unsafe fn as_u32(&self) -> u32 {
        self.handle.as_u32()
    }

    /// Turn a handle into its representation without closing the underlying resource.
    ///
    /// This should only be used when calling the raw ABI directly, and care should be taken not to
    /// reuse or alias handle values.
    pub fn into_u32(self) -> u32 {
        unsafe { ManuallyDrop::new(self).as_u32() }
    }

    /// Append another body onto the end of this body.
    ///
    #[doc = include_str!("../../../../docs/snippets/body-append-constant-time.md")]
    ///
    /// The other body will no longer be valid after this call.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use fastly::handle::{BodyHandle, ResponseHandle};
    /// # let response_handle = ResponseHandle::new();
    /// # let other_body = BodyHandle::new();
    /// let mut streaming_body = response_handle.stream_to_client(BodyHandle::new());
    /// streaming_body.append(other_body);
    /// ```
    pub fn append(&mut self, other: BodyHandle) {
        self.handle.append(other)
    }

    /// Write a slice of bytes to the end of this streaming body, and return the number of bytes written.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use fastly::handle::{BodyHandle, ResponseHandle};
    /// # let response_handle = ResponseHandle::new();
    /// let mut streaming_body = response_handle.stream_to_client(BodyHandle::new());
    /// # #[allow(deprecated)]
    /// streaming_body.write_bytes(&[0, 1, 2, 3]);
    /// ```
    #[deprecated(since = "0.9.3", note = "use std::io::Write::write() instead")]
    pub fn write_bytes(&mut self, bytes: &[u8]) -> usize {
        #[allow(deprecated)]
        self.handle.write_bytes(bytes)
    }

    /// Write a string slice to the end of this streaming body, and return the number of bytes written.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use fastly::handle::{BodyHandle, ResponseHandle};
    /// # let response_handle = ResponseHandle::new();
    /// let mut streaming_body = response_handle.stream_to_client(BodyHandle::new());
    /// # #[allow(deprecated)]
    /// streaming_body.write_str("woof woof");
    /// ```
    #[deprecated(since = "0.9.3", note = "use std::io::Write::write() instead")]
    pub fn write_str(&mut self, string: &str) -> usize {
        #[allow(deprecated)]
        self.write_bytes(string.as_bytes())
    }
}

impl Write for StreamingBodyHandle {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.handle.write(buf)
    }

    fn flush(&mut self) -> std::io::Result<()> {
        self.handle.flush()
    }
}