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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! The module implements the framing layer of HTTP/2 and exposes an API for using it.

use std::u32;
use std::fmt;

use self::frame::settings::HttpSettings;

pub mod frame;
pub mod connection;
pub mod session;
pub mod header;


// 6.5.2 Defined SETTINGS Parameters
pub const DEFAULT_SETTINGS: HttpSettings = HttpSettings {
    header_table_size: 4_096,
    enable_push: true,
    max_concurrent_streams: u32::MAX,
    initial_window_size: 65_535,
    max_frame_size: 16_384,
    max_header_list_size: u32::MAX,
};

/// An alias for the type that represents the ID of an HTTP/2 stream
pub type StreamId = u32;

/// A set of protocol names that the library should use to indicate that HTTP/2
/// is supported during protocol negotiation (NPN or ALPN).
/// We include some of the drafts' protocol names, since there is basically no
/// difference for all intents and purposes (and some servers out there still
/// only officially advertise draft support).
/// TODO: Eventually only use "h2".
pub const ALPN_PROTOCOLS: &'static [&'static [u8]] = &[b"h2", b"h2-16", b"h2-15", b"h2-14"];

/// A sender MUST NOT allow a flow-control window to exceed 231-1 octets. If a sender receives
/// a WINDOW_UPDATE that causes a flow-control window to exceed this maximum,
/// it MUST terminate either the stream or the connection, as appropriate. For streams,
/// the sender sends a RST_STREAM with an error code of FLOW_CONTROL_ERROR; for the connection,
/// a GOAWAY frame with an error code of FLOW_CONTROL_ERROR is sent.
pub const MAX_WINDOW_SIZE: u32 = 0x7fffffff;

// 6.9 WINDOW_UPDATE
/// The payload of a WINDOW_UPDATE frame is one reserved bit plus an unsigned 31-bit integer
/// indicating the number of octets that the sender can transmit in addition to the existing
/// flow-control window. The legal range for the increment to the flow-control window
/// is 1 to 231-1 (2,147,483,647) octets.
pub const MAX_WINDOW_SIZE_INC: u32 = 0x7fffffff;

/// The struct represents the size of a flow control window.
///
/// It exposes methods that allow the manipulation of window sizes, such that they can never
/// overflow the spec-mandated upper bound.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct WindowSize(pub i32);
impl WindowSize {
    /// Tries to increase the window size by the given delta. If the WindowSize would overflow the
    /// maximum allowed value (2^31 - 1), returns an error case. If the increase succeeds, returns
    /// `Ok`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use httpbis::solicit::WindowSize;
    ///
    /// let mut window_size = WindowSize::new(65_535);
    /// assert_eq!(window_size.size(), 65_535);
    /// // An increase within the bounds...
    /// assert!(window_size.try_increase(100).is_ok());
    /// assert_eq!(window_size.size(), 65_635);
    /// // An increase that would overflow
    /// assert!(window_size.try_increase(0x7fffffff).is_err());
    /// assert_eq!(window_size.size(), 65_635);
    /// ```
    pub fn try_increase(&mut self, delta: u32) -> Result<(), ()> {
        // Someone's provided a delta that would definitely overflow the window size.
        if delta > MAX_WINDOW_SIZE_INC || delta == 0 {
            return Err(());
        }
        // Now it is safe to cast the delta to the `i32`.
        match self.0.checked_add(delta as i32) {
            None => {
                // When the add overflows, we will have went over the maximum allowed size of the
                // window size...
                Err(())
            }
            Some(next_val) => {
                // The addition didn't overflow, so the next window size is in the range allowed by
                // the spec.
                self.0 = next_val;
                Ok(())
            }
        }
    }

    /// Tries to decrease the size of the window by the given delta.
    ///
    /// There are situations where the window size should legitimately be allowed to become
    /// negative, so the only situation where the result is an error is if the window size would
    /// underflow, as this would definitely cause the peers to lose sync.
    ///
    /// # Example
    ///
    /// ```rust
    /// use httpbis::solicit::WindowSize;
    ///
    /// let mut window_size = WindowSize::new(65_535);
    /// assert_eq!(window_size.size(), 65_535);
    /// // A decrease...
    /// assert!(window_size.try_decrease(100).is_ok());
    /// assert_eq!(window_size.size(), 65_435);
    /// // A decrease that does not underflow
    /// assert!(window_size.try_decrease(0x7fffffff).is_ok());
    /// assert_eq!(window_size.size(), -2147418212);
    /// // A decrease that *would* underflow
    /// assert!(window_size.try_decrease(0x7fffffff).is_err());
    /// assert_eq!(window_size.size(), -2147418212);
    /// ```
    pub fn try_decrease(&mut self, delta: i32) -> Result<(), ()> {
        match self.0.checked_sub(delta) {
            Some(new) => {
                self.0 = new;
                Ok(())
            }
            None => Err(()),
        }
    }

    pub fn try_decrease_to_positive(&mut self, delta: i32) -> Result<(), ()> {
        match self.0.checked_sub(delta) {
            Some(new) if new >= 0 => {
                self.0 = new;
                Ok(())
            }
            _ => Err(()),
        }
    }

    /// Creates a new `WindowSize` with the given initial size.
    pub fn new(size: i32) -> WindowSize {
        WindowSize(size)
    }
    /// Returns the current size of the window.
    ///
    /// The size is actually allowed to become negative (for instance if the peer changes its
    /// intial window size in the settings); therefore, the return is an `i32`.
    pub fn size(&self) -> i32 {
        self.0
    }
}

impl fmt::Display for WindowSize {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}


/// An enum representing the two possible HTTP schemes.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum HttpScheme {
    /// The variant corresponding to `http://`
    Http,
    /// The variant corresponding to `https://`
    Https,
}

impl HttpScheme {
    /// Returns a byte string representing the scheme.
    #[inline]
    pub fn as_bytes(&self) -> &'static [u8] {
        match *self {
            HttpScheme::Http => b"http",
            HttpScheme::Https => b"https",
        }
    }
}

#[cfg(test)]
pub mod tests;