Skip to content

Commit 20fac49

Browse files
committed
fix(http): Prevent busy looping
We encountered some issues where the `Conn::ready()` would busy loop on reads. Previously, the `ConnInner::can_read_more()` would not consider whether the previous read got a WouldBlock error, and it didn't consider whether the transport was blocked. Accounting for this additional state fixes the busy loop problem.
1 parent ff55619 commit 20fac49

File tree

1 file changed

+23
-4
lines changed

1 file changed

+23
-4
lines changed

src/http/conn.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ struct ConnInner<K: Key, T: Transport, H: MessageHandler<T>> {
3737
key: K,
3838
state: State<H, T>,
3939
transport: T,
40+
/// Records a WouldBlock error when trying to read
41+
///
42+
/// This flag is used to prevent busy looping
43+
read_would_block: bool,
4044
}
4145

4246
impl<K: Key, T: Transport, H: MessageHandler<T>> fmt::Debug for ConnInner<K, T, H> {
@@ -153,7 +157,10 @@ impl<K: Key, T: Transport, H: MessageHandler<T>> ConnInner<K, T, H> {
153157
Ok(head) => head,
154158
Err(::Error::Io(e)) => match e.kind() {
155159
io::ErrorKind::WouldBlock |
156-
io::ErrorKind::Interrupted => return state,
160+
io::ErrorKind::Interrupted => {
161+
self.read_would_block = true;
162+
return state;
163+
},
157164
_ => {
158165
debug!("io error trying to parse {:?}", e);
159166
return State::Closed;
@@ -250,6 +257,7 @@ impl<K: Key, T: Transport, H: MessageHandler<T>> ConnInner<K, T, H> {
250257
Err(e) => match e.kind() {
251258
io::ErrorKind::WouldBlock => {
252259
// This is the expected case reading in this state
260+
self.read_would_block = true;
253261
state
254262
},
255263
_ => {
@@ -288,7 +296,10 @@ impl<K: Key, T: Transport, H: MessageHandler<T>> ConnInner<K, T, H> {
288296
},
289297
Err(::Error::Io(e)) => match e.kind() {
290298
io::ErrorKind::WouldBlock |
291-
io::ErrorKind::Interrupted => None,
299+
io::ErrorKind::Interrupted => {
300+
self.read_would_block = true;
301+
None
302+
},
292303
_ => {
293304
debug!("io error trying to parse {:?}", e);
294305
return State::Closed;
@@ -482,10 +493,15 @@ impl<K: Key, T: Transport, H: MessageHandler<T>> ConnInner<K, T, H> {
482493
}
483494

484495
fn can_read_more(&self, was_init: bool) -> bool {
485-
match self.state {
496+
let transport_blocked = self.transport.blocked().is_some();
497+
let read_would_block = self.read_would_block;
498+
499+
let state_machine_ok = match self.state {
486500
State::Init { .. } => !was_init && !self.buf.is_empty(),
487501
_ => !self.buf.is_empty()
488-
}
502+
};
503+
504+
!transport_blocked && !read_would_block && state_machine_ok
489505
}
490506

491507
fn on_error<F>(&mut self, err: ::Error, factory: &F) where F: MessageHandlerFactory<K, T> {
@@ -501,6 +517,8 @@ impl<K: Key, T: Transport, H: MessageHandler<T>> ConnInner<K, T, H> {
501517

502518
fn on_readable<F>(&mut self, scope: &mut Scope<F>)
503519
where F: MessageHandlerFactory<K, T, Output=H> {
520+
// Clear would_block flag so state is clear going into read
521+
self.read_would_block = false;
504522
trace!("on_readable -> {:?}", self.state);
505523
let state = mem::replace(&mut self.state, State::Closed);
506524
self.state = self.read(scope, state);
@@ -549,6 +567,7 @@ impl<K: Key, T: Transport, H: MessageHandler<T>> Conn<K, T, H> {
549567
timeout_start: Some(now),
550568
},
551569
transport: transport,
570+
read_would_block: false,
552571
}))
553572
}
554573

0 commit comments

Comments
 (0)