Open radix opened 7 years ago
Well, it's not the problem with tk-http
per se. But with the example code that does accepting connections. buffer_unordered
exists on first error of the future. Which effectively means your main loop exits with error on first error of the accept
system call.
My real-life code for handling accept is along the lines of:
core.run(listener.incoming()
// we need stream that doesn't fail on error
.then(move |item| match item {
Ok(x) => Either::A(ok(Some(x))),
Err(e) => {
warn!("Error accepting: {}", e);
/// pause stream
Either::B(Timeout::new(listen_error_pause, &handle).unwrap()
.and_then(|()| ok(None)))
}
})
.filter_map(|x| x) /// filter out None produced by code dooing pause on error
.map(move |(socket, saddr)| {
Proto::new(socket, &hcfg, Handler::new(saddr, w2.clone()), &h1)
// always succeed
.then(|_| Ok(()))
})
.buffer_unordered(max_connections)
.for_each(move |()| Ok(()))
// `shutter` is a oneshot that we use to force-close the listener
.select(shutter.then(move |_| Ok(())))
// these basically execute only when shutter receives a message
.map(move |(_, _)| info!("Listener {} exited", addr))
.map_err(move |(_, _)| info!("Listener {} exited", addr))
);
Probably you can also do whatever hyper does for listening, i.e. for_each
instead of buffer_unordered
and spawn
for each connection. But the downside of the latter approach is that you have number of connections limited by OS limits rather than by your own limit. I.e. your server can exhaust number of incoming file descriptiors and your app will be unable to establish connections to backend, db, or even open some file.
(I might have to update the example...)
Okay, so this piece of code seems to be quite large and useful not only for HTTP. I'm thinking to either put it into tk-easyloop
or just create another crate like tk-listen
(for client we have tk-pool
likewise we can factor it out for server too). What do you think?
@tailhook shouldn't that kind of logic be in tokio proto?
Well, we don't use tokio-proto for protocol parsing for multiple reasons. So depending on it just for listen
function doesn't look like a good idea. Also, the problem isn't solved in current tokio-proto too. They use for_each
and spawn
(so no limit). So tokio-proto might also benefit from separate library doing listening correctly.
I see, sorry! I'm not yet familiar with the tokio ecosystem :)
Wow! While refactoring this example to a separate library I've found out that this code is a little bit wrong. When listening there are few errors that require sleep and repeat (namely ENFILE and EMFILE), but there are also few errors that should not incur delay (namely ECONNREFUSED), because this means some bad-behaving user might use it to block other connections.
I'll do my best to publish correct code ASAP.
So, I've published tk-listen crate. Probably, I'll update examples here to use the crate.
Good stuff! I do hope that the larger Tokio community picks up on your crate, or maybe even integrates it into some other crate.
I am testing out using tk-http instead of Hyper, and I noticed I can quite easily get my code to crash with some low-level looking network error by holding down the F5 key in Firefox. I haven't been able to trigger an error like this with Hyper.
I'm running this code on Windows 10 with the MSVC toolchain.
To reproduce, checkout this revision of my
pandt
project. Then do the following in the checkout:Then load up http://localhost:1337/ in your browser and spam it until it crashes, which only takes a few seconds for me.
Here's a link to quickly see my mainloop: https://github.com/radix/pandt/commit/254dbed564318f1c83036b14b36239fee834361a#diff-23e1c8400fe6268f1a67705b9c60cde2R84