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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Defines the `FrameBuilder` trait and some default implementations of the trait.

use std::io;
use http::frame::{FrameHeader, pack_header};

/// A trait that provides additional methods for serializing HTTP/2 frames.
///
/// All methods have a default implementation in terms of the `io::Write` API, but types can
/// provide specialized more efficient implementations if possible. This is effectively a
/// workaround for specialization not existing yet in Rust.
pub trait FrameBuilder: io::Write + io::Seek {
    /// Write the given frame header as the next octets (i.e. without moving the cursor to the
    /// beginning of the buffer).
    fn write_header(&mut self, header: FrameHeader) -> io::Result<()> {
        self.write_all(&pack_header(&header))
    }

    /// Overwrite the previously written header, assuming it's the first byte sequence of the
    /// buffer.
    ///
    /// The default implementation seeks to the beginning of the buffer, writes the header, and
    /// then moves the cursor back to its previos position (i.e. offset from the beginning).
    fn overwrite_header(&mut self, header: FrameHeader) -> io::Result<()> {
        let current = try!(self.seek(io::SeekFrom::Current(0)));
        try!(self.seek(io::SeekFrom::Start(0)));
        try!(self.write_header(header));
        try!(self.seek(io::SeekFrom::Start(current)));
        Ok(())
    }

    /// Copy all available bytes from the given `io::Read` instance.
    ///
    /// This method allows poor man's specialization for types that can implement the copy more
    /// efficiently than the `io::copy` function does (i.e. without the intermediate read into a
    /// stack-allocated buffer).
    fn copy_bytes_from<R: io::Read>(&mut self, provider: &mut R) -> io::Result<u64>
        where Self: Sized
    {
        io::copy(provider, self)
    }

    /// Write the given number of padding octets.
    ///
    /// The default implementation invokes the underlying Writer's `write` method `padding_length`
    /// times.
    ///
    /// Other `FrameBuilder` implementations could implement it more efficiently (e.g. if it is
    /// known that the `FrameBuilder` is backed by a zeroed buffer, there's no need to write
    /// anything, only increment a cursor/offset).
    fn write_padding(&mut self, padding_length: u8) -> io::Result<()> {
        for _ in 0..padding_length {
            try!(self.write_all(&[0]));
        }
        Ok(())
    }

    /// Write the given unsigned 32 bit integer to the underlying stream. The integer is written as
    /// four bytes in network endian style.
    fn write_u32(&mut self, num: u32) -> io::Result<()> {
        self.write_all(&[(((num >> 24) & 0x000000FF) as u8),
                         (((num >> 16) & 0x000000FF) as u8),
                         (((num >>  8) & 0x000000FF) as u8),
                         (((num      ) & 0x000000FF) as u8)])
    }
}

impl FrameBuilder for io::Cursor<Vec<u8>> {}

#[cfg(test)]
mod tests {
    use super::FrameBuilder;
    use std::io::{self, Write};
    use http::frame::pack_header;

    #[test]
    fn test_write_header_empty_buffer() {
        let mut buf = io::Cursor::new(Vec::new());
        let header = (10, 0x1, 0x0, 3);
        let expected = pack_header(&header);

        buf.write_header(header).unwrap();

        let frame = buf.into_inner();
        assert_eq!(frame, expected);
    }

    #[test]
    fn test_write_header_and_payload_empty() {
        let mut buf = io::Cursor::new(Vec::new());
        let header = (10, 0x1, 0x0, 3);
        let expected = {
            let mut buf = Vec::new();
            buf.extend(pack_header(&header).to_vec());
            buf.extend(vec![1, 2, 3, 4]);
            buf
        };

        buf.write_header(header).unwrap();
        buf.write_all(&[1, 2, 3, 4]).unwrap();

        let frame = buf.into_inner();
        assert_eq!(frame, expected);
    }

    #[test]
    fn test_rewrite_header_after_payload() {
        let mut buf = io::Cursor::new(Vec::new());
        let original_header = (10, 0x1, 0x0, 3);
        let actual_header = (5, 0x0, 0x0, 5);
        let expected = {
            let mut buf = Vec::new();
            buf.extend(pack_header(&actual_header).to_vec());
            buf.extend(vec![1, 2, 3, 4]);
            buf
        };

        // Sanity check for the test: the two headers must be different!
        assert!(original_header != actual_header);
        // First one header...
        buf.write_header(original_header).unwrap();
        // Then some payload...
        buf.write_all(&[1, 2, 3]).unwrap();
        // Now change the header!
        buf.overwrite_header(actual_header).unwrap();
        // ...and add some more data to the end
        buf.write_all(&[4]).unwrap();

        let frame = buf.into_inner();
        assert_eq!(frame, expected);
    }

    #[test]
    fn test_write_padding() {
        let mut buf = io::Cursor::new(Vec::new());
        buf.write_padding(5).unwrap();

        assert_eq!(buf.into_inner(), vec![0; 5]);
    }

    #[test]
    fn test_write_u32() {
        fn get_written(num: u32) -> Vec<u8> {
            let mut buf = io::Cursor::new(Vec::new());
            buf.write_u32(num).unwrap();
            buf.into_inner()
        }

        assert_eq!(get_written(0x0), vec![0, 0, 0, 0]);
        assert_eq!(get_written(0x1), vec![0, 0, 0, 1]);
        assert_eq!(get_written(0x10), vec![0, 0, 0, 0x10]);
        assert_eq!(get_written(0x10AB00CC), vec![0x10, 0xAB, 0, 0xCC]);
        assert_eq!(get_written(0xFFFFFFFF), vec![0xFF, 0xFF, 0xFF, 0xFF]);
        assert_eq!(get_written(0xEFFFFFFF), vec![0xEF, 0xFF, 0xFF, 0xFF]);
        assert_eq!(get_written(0x7FFFFFFF), vec![0x7F, 0xFF, 0xFF, 0xFF]);
        assert_eq!(get_written(0xFFFFFF7F), vec![0xFF, 0xFF, 0xFF, 0x7F]);
    }
}