Skip to content

Commit 74d8dba

Browse files
committed
settings - allow input of initial settings
started out trying to prevent data frames which are too large, ended up with this
1 parent 8b66407 commit 74d8dba

File tree

8 files changed

+194
-41
lines changed

8 files changed

+194
-41
lines changed

examples/http2/file_server/src/main.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ extern crate chrono;
2222

2323
use std::fs::File;
2424
use std::io::prelude::*;
25-
use osmium::http2::{self, net, header, stream as streaming};
25+
use osmium::http2::{self, net, header, stream as streaming, settings};
2626
use osmium::shared::connection_handle::ConnectionHandle;
2727
use osmium::shared;
2828
use chrono::{DateTime, TimeZone, NaiveDateTime, Utc, Local};
@@ -223,5 +223,11 @@ fn main() {
223223
security.set_ssl_cert_path(String::from("../../../tests/cert.pfx"));
224224
settings.set_security(security);
225225

226-
net::Server::new(MyServer {}, settings).start_server();
226+
let mut http2_settings = Vec::new();
227+
http2_settings.push(settings::SettingsParameter::new(settings::SettingName::SettingsHeaderTableSize, 65536));
228+
http2_settings.push(settings::SettingsParameter::new(settings::SettingName::SettingsInitialWindowSize, 131072));
229+
http2_settings.push(settings::SettingsParameter::new(settings::SettingName::SettingsMaxFrameSize, 16384));
230+
settings.set_http2_settings(http2_settings);
231+
232+
net::Server::new(MyServer {}, settings).unwrap().start_server();
227233
}

src/http2/core/connection.rs

+15-7
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ pub struct Connection<'a> {
5959

6060
impl<'a> Connection<'a> {
6161
pub fn new(
62-
hpack_send_context: hpack_context::SendContext<'a>,
63-
hpack_recv_context: hpack_context::RecvContext<'a>,
64-
initial_settings: framing::settings::SettingsFrame,
62+
hpack_send_context: hpack_context::SendContext<'a>,
63+
hpack_recv_context: hpack_context::RecvContext<'a>,
64+
initial_local_settings: settings::Settings,
65+
initial_remote_settings_frame: framing::settings::SettingsFrame,
6566
shutdown_signaller: shutdown_signal::ShutdownSignaller
66-
) -> Connection<'a>
67+
) -> Connection<'a>
6768
{
6869
let mut new_con = Connection {
6970
send_frames: VecDeque::new(),
@@ -73,7 +74,7 @@ impl<'a> Connection<'a> {
7374
streams: HashMap::new(),
7475
stream_blocker: stream_blocker::StreamBlocker::new(),
7576
promised_streams_queue: VecDeque::new(),
76-
connection_shared_state: Rc::new(RefCell::new(connection_shared_state::ConnectionSharedState::new())),
77+
connection_shared_state: Rc::new(RefCell::new(connection_shared_state::ConnectionSharedState::new(initial_local_settings))),
7778
highest_remote_initiated_stream_identifier: 0,
7879
shutdown_initiated: false,
7980
shutdown_signaller: shutdown_signaller,
@@ -83,7 +84,7 @@ impl<'a> Connection<'a> {
8384

8485
// TODO The ONLY time when ack is not required is when a 101 switching protocols is sent.
8586
// Switching to true for now, and need to tidy up later.
86-
new_con.apply_settings(initial_settings, true);
87+
new_con.apply_settings(initial_remote_settings_frame, true);
8788

8889
new_con
8990
}
@@ -102,7 +103,6 @@ impl<'a> Connection<'a> {
102103
return;
103104
}
104105

105-
// TODO handle frame type not recognised.
106106
let frame_type = match frame.header.frame_type {
107107
Some(ref frame_type) => frame_type.clone(),
108108
None => {
@@ -198,6 +198,14 @@ impl<'a> Connection<'a> {
198198
return;
199199
}
200200

201+
if frame.header.length > self.connection_shared_state.borrow().local_settings.max_frame_size {
202+
self.shutdown_connection(error::HttpError::ConnectionError(
203+
error::ErrorCode::ProtocolError,
204+
error::ErrorName::DataFramePayloadLargerThanSettingsValue
205+
));
206+
return;
207+
}
208+
201209
self.handle_flow_control_for_recv(frame.header.length);
202210
self.move_to_stream(frame_type, frame, app);
203211
},

src/http2/core/connection_shared_state.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use http2::stream::StreamId;
2121

2222
pub struct ConnectionSharedState {
2323
pub remote_settings: settings::Settings,
24+
pub local_settings: settings::Settings,
2425
next_server_created_stream_id: StreamId,
2526
// If streams were ever made concurrent it would be VITAL that this is locked. It is used to communicate to
2627
// the client which streams have started processing, or at least the highest numbered one. That means no more
@@ -29,9 +30,10 @@ pub struct ConnectionSharedState {
2930
}
3031

3132
impl ConnectionSharedState {
32-
pub fn new() -> Self {
33+
pub fn new(local_settings: settings::Settings) -> Self {
3334
ConnectionSharedState {
3435
remote_settings: settings::Settings::spec_default(),
36+
local_settings: local_settings,
3537
next_server_created_stream_id: 2,
3638
highest_started_processing_stream_id: 0
3739
}

src/http2/error.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ pub enum ErrorName {
114114
InvalidInitialWindowSize,
115115
ConnectionFlowControlWindowNotRespected,
116116
SettingsAcknowledgementWithNonZeroPayloadLength,
117-
SettingsFramePayloadSizeNotAMultipleOfSix
117+
SettingsFramePayloadSizeNotAMultipleOfSix,
118+
DataFramePayloadLargerThanSettingsValue
118119
}
119120

120121
impl From<ErrorName> for Vec<u8> {
@@ -170,6 +171,9 @@ impl From<ErrorName> for Vec<u8> {
170171
},
171172
ErrorName::SettingsFramePayloadSizeNotAMultipleOfSix => {
172173
"settings frame payload size is not a multiple of 6"
174+
},
175+
ErrorName::DataFramePayloadLargerThanSettingsValue => {
176+
"data frame payload larger than settings value"
173177
}
174178
}.to_owned().as_bytes().to_vec()
175179
}

src/http2/frame/settings.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use http2::error;
2727
const FLAG_ACK: u8 = 0x1;
2828

2929
// TODO this has been copied out to the http2::settings module.
30-
#[derive(Debug)]
30+
#[derive(Debug, Clone)]
3131
pub struct SettingsParameter {
3232
name: settings::SettingName,
3333
value: u32
@@ -43,7 +43,7 @@ impl SettingsParameter {
4343
}
4444
}
4545

46-
#[derive(Debug)]
46+
#[derive(Debug, Clone)]
4747
pub struct SettingsFrameCompressModel {
4848
flags: u8,
4949
parameters: Vec<SettingsParameter>

src/http2/net/mod.rs

+41-16
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use std::sync::Arc;
2626
use std::rc::Rc;
2727
use std::marker;
2828
use std::mem;
29+
use std::net;
2930

3031
// tokio
3132
use futures::{Stream, Sink, Future, stream};
@@ -49,6 +50,11 @@ use http2::stream as streaming;
4950
use http2::settings;
5051
use shared::server_settings;
5152

53+
#[derive(Debug)]
54+
pub enum ServerError {
55+
InvalidSettingsConfiguration
56+
}
57+
5258
// TODO this doesn't really belong in the net package.
5359
pub struct Server<T, R, S>
5460
where T: server_trait::OsmiumServer<Request=R, Response=S>,
@@ -57,38 +63,61 @@ pub struct Server<T, R, S>
5763
{
5864
hpack: hpack::HPack,
5965
app: T,
60-
server_settings: server_settings::ServerSettings
66+
security_settings: Option<server_settings::SecuritySettings>,
67+
bind_address: net::SocketAddr,
68+
local_settings: settings::Settings,
69+
local_settings_frame: framing::settings::SettingsFrameCompressModel
6170
}
6271

6372
impl<T, R, S> Server<T, R, S>
6473
where T: 'static + server_trait::OsmiumServer<Request=R, Response=S> + marker::Sync + marker::Send,
6574
R: 'static + convert::From<streaming::StreamRequest>,
6675
S: 'static + convert::Into<streaming::StreamResponse>
6776
{
68-
pub fn new(app: T, server_settings: server_settings::ServerSettings) -> Self {
69-
Server {
77+
pub fn new(app: T, server_settings: server_settings::ServerSettings) -> Result<Self, ServerError> {
78+
// Read the settings configuration.
79+
let mut local_settings = settings::Settings::spec_default();
80+
if local_settings.apply_changes(server_settings.get_http2_settings()).is_err() {
81+
return Err(ServerError::InvalidSettingsConfiguration);
82+
};
83+
84+
let mut local_settings_frame = framing::settings::SettingsFrameCompressModel::new();
85+
for setting in server_settings.get_http2_settings() {
86+
local_settings_frame.add_parameter(setting.get_name(), setting.get_value());
87+
}
88+
89+
// Build the server bind address.
90+
let addr = format!("{}:{}", server_settings.get_host(), server_settings.get_port()).parse().unwrap();
91+
92+
Ok(Server {
7093
hpack: hpack::HPack::new(),
7194
app: app,
72-
server_settings: server_settings
73-
}
95+
security_settings: server_settings.get_security(),
96+
bind_address: addr,
97+
local_settings: local_settings,
98+
local_settings_frame: local_settings_frame
99+
})
74100
}
75101

76102
// The start method consumes self so that it can ensure it can be used on the connection threads.
77103
// The connection threads take a closure which must have static lifetime. If server startup
78104
// succeeds, then the a shared pointer to self is returned.
79-
pub fn start_server(self) -> Arc<Box<Self>>
105+
pub fn start_server(mut self) -> Arc<Box<Self>>
80106
{
81107
// tokio event loop
82108
let mut event_loop = tokio_core::reactor::Core::new().unwrap();
83109
let handle = event_loop.handle();
84110

85111
// create a listener for incoming tcp connections
86-
let addr = format!("{}:{}", self.server_settings.get_host(), self.server_settings.get_port()).parse().unwrap();
87-
let listener = tokio_core::net::TcpListener::bind(&addr, &handle).unwrap();
112+
let listener = tokio_core::net::TcpListener::bind(&self.bind_address, &handle).unwrap();
88113

89114
let thread_pool = Rc::new(ThreadPool::new(10));
90115

91-
let acceptor_factory = acceptor_factory::AcceptorFactory::new(&self.server_settings.get_security());
116+
// Create the acceptor factory.
117+
let mut security = None;
118+
mem::swap(&mut security, &mut self.security_settings);
119+
// TODO might not exist.
120+
let acceptor_factory = acceptor_factory::AcceptorFactory::new(&security.unwrap());
92121

93122
// TODO this should vary depending on the startup type chosen (http/https)
94123
let handshake: Box<self::h2handshake::H2Handshake> = Box::new(https::HttpsH2Handshake::new(acceptor_factory));
@@ -100,13 +129,8 @@ impl<T, R, S> Server<T, R, S>
100129
debug!("Starting connection on {}", _remote_addr);
101130

102131
let inner_handle = handle.clone();
103-
104-
let mut settings_response = framing::settings::SettingsFrameCompressModel::new();
105-
settings_response.add_parameter(settings::SettingName::SettingsHeaderTableSize, 65536);
106-
settings_response.add_parameter(settings::SettingName::SettingsInitialWindowSize, 131072);
107-
settings_response.add_parameter(settings::SettingName::SettingsMaxFrameSize, 16384);
108-
109-
let handshake_future = handshake.attempt_handshake(socket, Box::new(settings_response))
132+
133+
let handshake_future = handshake.attempt_handshake(socket, Box::new(server_instance.local_settings_frame.clone()))
110134
.map_err(|e| {
111135
error!("I/O error while attempting connection handshake {}", e);
112136
})
@@ -137,6 +161,7 @@ impl<T, R, S> Server<T, R, S>
137161
let mut connection = connection::Connection::new(
138162
server_instance.hpack.new_send_context(),
139163
server_instance.hpack.new_recv_context(),
164+
server_instance.local_settings.clone(),
140165
temp_frame,
141166
shutdown_signal::ShutdownSignaller::new(shutdown_read_tx)
142167
);

src/http2/settings.rs

+106-2
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515
// You should have received a copy of the GNU General Public License
1616
// along with Osmium. If not, see <http://www.gnu.org/licenses/>.
1717

18+
// osmium
19+
use http2::error;
20+
1821
pub const INITIAL_MAX_FRAME_SIZE: u32 = 0x4000;
1922
pub const MAXIMUM_MAX_FRAME_SIZE: u32 = 0xFFFFFF;
2023

2124
pub const INITIAL_FLOW_CONTROL_WINDOW_SIZE: u32 = 0xFFFF;
2225
pub const MAXIMUM_FLOW_CONTROL_WINDOW_SIZE: u32 = 0x7FFFFFFF;
2326

24-
#[derive(Debug)]
27+
#[derive(Debug, Clone)]
2528
pub enum SettingName {
2629
SettingsHeaderTableSize,
2730
SettingsEnablePush,
@@ -37,7 +40,7 @@ pub struct SettingsParameter {
3740
value: u32
3841
}
3942

40-
#[derive(Debug)]
43+
#[derive(Debug, Clone)]
4144
pub struct Settings {
4245
pub header_table_size: u32,
4346
pub enable_push: bool,
@@ -47,6 +50,20 @@ pub struct Settings {
4750
pub max_header_list_size: Option<u32>
4851
}
4952

53+
impl SettingsParameter {
54+
pub fn new(name: SettingName, value: u32) -> Self {
55+
SettingsParameter { name, value }
56+
}
57+
58+
pub fn get_name(&self) -> SettingName {
59+
self.name.clone()
60+
}
61+
62+
pub fn get_value(&self) -> u32 {
63+
self.value
64+
}
65+
}
66+
5067
impl Settings {
5168
pub fn spec_default() -> Self {
5269
Settings {
@@ -58,6 +75,93 @@ impl Settings {
5875
max_header_list_size: None
5976
}
6077
}
78+
79+
pub fn apply_changes(&mut self, changes: &[SettingsParameter]) -> Result<Vec<SettingName>, error::HttpError> {
80+
let mut changes_applied = Vec::with_capacity(changes.len());
81+
82+
for setting in changes {
83+
match setting.get_name() {
84+
SettingName::SettingsHeaderTableSize => {
85+
self.header_table_size = setting.get_value();
86+
87+
changes_applied.push(setting.get_name());
88+
},
89+
SettingName::SettingsEnablePush => {
90+
match setting.get_value() {
91+
0 => {
92+
self.enable_push = false;
93+
},
94+
1 => {
95+
self.enable_push = true;
96+
},
97+
_ => {
98+
// (6.5.2) Any value other than 0 or 1 MUST be treated as a connection
99+
// error (Section 5.4.1) of type PROTOCOL_ERROR.
100+
return Err(
101+
error::HttpError::ConnectionError(
102+
error::ErrorCode::ProtocolError,
103+
error::ErrorName::EnablePushSettingInvalidValue
104+
)
105+
);
106+
}
107+
}
108+
109+
changes_applied.push(setting.get_name())
110+
},
111+
SettingName::SettingsMaxConcurrentStreams => {
112+
self.max_concurrent_streams = Some(setting.get_value());
113+
114+
changes_applied.push(setting.get_name());
115+
},
116+
SettingName::SettingsInitialWindowSize => {
117+
let val = setting.get_value();
118+
119+
if val <= MAXIMUM_FLOW_CONTROL_WINDOW_SIZE {
120+
self.initial_window_size = val;
121+
}
122+
else {
123+
// (6.5.2) Values above the maximum flow-control window size of 231-1 MUST be treated as a
124+
// connection error (Section 5.4.1) of type FLOW_CONTROL_ERROR.
125+
return Err(
126+
error::HttpError::ConnectionError(
127+
error::ErrorCode::ProtocolError,
128+
error::ErrorName::InvalidInitialWindowSize
129+
)
130+
)
131+
}
132+
133+
changes_applied.push(setting.get_name());
134+
},
135+
SettingName::SettingsMaxFrameSize => {
136+
let val = setting.get_value();
137+
138+
if INITIAL_MAX_FRAME_SIZE <= val && val <= MAXIMUM_MAX_FRAME_SIZE {
139+
self.max_frame_size = val;
140+
}
141+
else {
142+
// (6.5.2) The initial value is 214 (16,384) octets. The value advertised by an endpoint MUST be between this initial
143+
// value and the maximum allowed frame size (224-1 or 16,777,215 octets), inclusive. Values outside this range MUST
144+
// be treated as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
145+
return Err(
146+
error::HttpError::ConnectionError(
147+
error::ErrorCode::ProtocolError,
148+
error::ErrorName::InvalidMaxFrameSize
149+
)
150+
);
151+
}
152+
153+
changes_applied.push(setting.get_name());
154+
},
155+
SettingName::SettingsMaxHeaderListSize => {
156+
self.max_header_list_size = Some(setting.get_value());
157+
158+
changes_applied.push(setting.get_name());
159+
}
160+
}
161+
}
162+
163+
Ok(changes_applied)
164+
}
61165
}
62166

63167
impl From<SettingName> for u16 {

0 commit comments

Comments
 (0)