feat(lib): switch to non-blocking (asynchronous) IO
BREAKING CHANGE: This breaks a lot of the Client and Server APIs. Check the documentation for how Handlers can be used for asynchronous events.
This commit is contained in:
@@ -5,9 +5,61 @@ extern crate env_logger;
|
||||
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
|
||||
use hyper::Client;
|
||||
use hyper::client::{Client, Request, Response, DefaultTransport as HttpStream};
|
||||
use hyper::header::Connection;
|
||||
use hyper::{Decoder, Encoder, Next};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Dump(mpsc::Sender<()>);
|
||||
|
||||
impl Drop for Dump {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.0.send(());
|
||||
}
|
||||
}
|
||||
|
||||
fn read() -> Next {
|
||||
Next::read().timeout(Duration::from_secs(10))
|
||||
}
|
||||
|
||||
impl hyper::client::Handler<HttpStream> for Dump {
|
||||
fn on_request(&mut self, req: &mut Request) -> Next {
|
||||
req.headers_mut().set(Connection::close());
|
||||
read()
|
||||
}
|
||||
|
||||
fn on_request_writable(&mut self, _encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
read()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, res: Response) -> Next {
|
||||
println!("Response: {}", res.status());
|
||||
println!("Headers:\n{}", res.headers());
|
||||
read()
|
||||
}
|
||||
|
||||
fn on_response_readable(&mut self, decoder: &mut Decoder<HttpStream>) -> Next {
|
||||
match io::copy(decoder, &mut io::stdout()) {
|
||||
Ok(0) => Next::end(),
|
||||
Ok(_) => read(),
|
||||
Err(e) => match e.kind() {
|
||||
io::ErrorKind::WouldBlock => Next::read(),
|
||||
_ => {
|
||||
println!("ERROR: {}", e);
|
||||
Next::end()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_error(&mut self, err: hyper::Error) -> Next {
|
||||
println!("ERROR: {}", err);
|
||||
Next::remove()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init().unwrap();
|
||||
@@ -20,26 +72,11 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let client = match env::var("HTTP_PROXY") {
|
||||
Ok(mut proxy) => {
|
||||
// parse the proxy, message if it doesn't make sense
|
||||
let mut port = 80;
|
||||
if let Some(colon) = proxy.rfind(':') {
|
||||
port = proxy[colon + 1..].parse().unwrap_or_else(|e| {
|
||||
panic!("HTTP_PROXY is malformed: {:?}, port parse error: {}", proxy, e);
|
||||
});
|
||||
proxy.truncate(colon);
|
||||
}
|
||||
Client::with_http_proxy(proxy, port)
|
||||
},
|
||||
_ => Client::new()
|
||||
};
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let client = Client::new().expect("Failed to create a Client");
|
||||
client.request(url.parse().unwrap(), Dump(tx)).unwrap();
|
||||
|
||||
let mut res = client.get(&*url)
|
||||
.header(Connection::close())
|
||||
.send().unwrap();
|
||||
|
||||
println!("Response: {}", res.status);
|
||||
println!("Headers:\n{}", res.headers);
|
||||
io::copy(&mut res, &mut io::stdout()).unwrap();
|
||||
// wait till done
|
||||
let _ = rx.recv();
|
||||
client.close();
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#![deny(warnings)]
|
||||
extern crate hyper;
|
||||
|
||||
extern crate env_logger;
|
||||
|
||||
use std::env;
|
||||
use std::io;
|
||||
|
||||
use hyper::Client;
|
||||
use hyper::header::Connection;
|
||||
use hyper::http::h2;
|
||||
|
||||
fn main() {
|
||||
env_logger::init().unwrap();
|
||||
|
||||
let url = match env::args().nth(1) {
|
||||
Some(url) => url,
|
||||
None => {
|
||||
println!("Usage: client <url>");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let client = Client::with_protocol(h2::new_protocol());
|
||||
|
||||
// `Connection: Close` is not a valid header for HTTP/2, but the client handles it gracefully.
|
||||
let mut res = client.get(&*url)
|
||||
.header(Connection::close())
|
||||
.send().unwrap();
|
||||
|
||||
println!("Response: {}", res.status);
|
||||
println!("Headers:\n{}", res.headers);
|
||||
io::copy(&mut res, &mut io::stdout()).unwrap();
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
#[macro_use]
|
||||
// TODO: only import header!, blocked by https://github.com/rust-lang/rust/issues/25003
|
||||
extern crate hyper;
|
||||
|
||||
#[cfg(feature = "serde-serialization")]
|
||||
extern crate serde;
|
||||
|
||||
// A header in the form of `X-Foo: some random string`
|
||||
header! {
|
||||
(Foo, "X-Foo") => [String]
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
@@ -1,18 +1,53 @@
|
||||
#![deny(warnings)]
|
||||
extern crate hyper;
|
||||
extern crate env_logger;
|
||||
extern crate num_cpus;
|
||||
|
||||
use hyper::server::{Request, Response};
|
||||
use std::io::Write;
|
||||
|
||||
use hyper::{Decoder, Encoder, Next};
|
||||
use hyper::net::{HttpStream, HttpListener};
|
||||
use hyper::server::{Server, Handler, Request, Response};
|
||||
|
||||
static PHRASE: &'static [u8] = b"Hello World!";
|
||||
|
||||
fn hello(_: Request, res: Response) {
|
||||
res.send(PHRASE).unwrap();
|
||||
struct Hello;
|
||||
|
||||
impl Handler<HttpStream> for Hello {
|
||||
fn on_request(&mut self, _: Request) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
fn on_request_readable(&mut self, _: &mut Decoder<HttpStream>) -> Next {
|
||||
Next::write()
|
||||
}
|
||||
fn on_response(&mut self, response: &mut Response) -> Next {
|
||||
use hyper::header::ContentLength;
|
||||
response.headers_mut().set(ContentLength(PHRASE.len() as u64));
|
||||
Next::write()
|
||||
}
|
||||
fn on_response_writable(&mut self, encoder: &mut Encoder<HttpStream>) -> Next {
|
||||
let n = encoder.write(PHRASE).unwrap();
|
||||
debug_assert_eq!(n, PHRASE.len());
|
||||
Next::end()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init().unwrap();
|
||||
let _listening = hyper::Server::http("127.0.0.1:3000").unwrap()
|
||||
.handle(hello);
|
||||
|
||||
let listener = HttpListener::bind(&"127.0.0.1:3000".parse().unwrap()).unwrap();
|
||||
let mut handles = Vec::new();
|
||||
|
||||
for _ in 0..num_cpus::get() {
|
||||
let listener = listener.try_clone().unwrap();
|
||||
handles.push(::std::thread::spawn(move || {
|
||||
Server::new(listener)
|
||||
.handle(|_| Hello).unwrap()
|
||||
}));
|
||||
}
|
||||
println!("Listening on http://127.0.0.1:3000");
|
||||
|
||||
for handle in handles {
|
||||
handle.join().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,171 @@
|
||||
#![deny(warnings)]
|
||||
extern crate hyper;
|
||||
extern crate env_logger;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use std::io::copy;
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use hyper::{Get, Post};
|
||||
use hyper::server::{Server, Request, Response};
|
||||
use hyper::uri::RequestUri::AbsolutePath;
|
||||
use hyper::{Get, Post, StatusCode, RequestUri, Decoder, Encoder, Next};
|
||||
use hyper::header::ContentLength;
|
||||
use hyper::net::HttpStream;
|
||||
use hyper::server::{Server, Handler, Request, Response};
|
||||
|
||||
macro_rules! try_return(
|
||||
($e:expr) => {{
|
||||
match $e {
|
||||
Ok(v) => v,
|
||||
Err(e) => { println!("Error: {}", e); return; }
|
||||
struct Echo {
|
||||
buf: Vec<u8>,
|
||||
read_pos: usize,
|
||||
write_pos: usize,
|
||||
eof: bool,
|
||||
route: Route,
|
||||
}
|
||||
|
||||
enum Route {
|
||||
NotFound,
|
||||
Index,
|
||||
Echo(Body),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Body {
|
||||
Len(u64),
|
||||
Chunked
|
||||
}
|
||||
|
||||
static INDEX: &'static [u8] = b"Try POST /echo";
|
||||
|
||||
impl Echo {
|
||||
fn new() -> Echo {
|
||||
Echo {
|
||||
buf: vec![0; 4096],
|
||||
read_pos: 0,
|
||||
write_pos: 0,
|
||||
eof: false,
|
||||
route: Route::NotFound,
|
||||
}
|
||||
}}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn echo(mut req: Request, mut res: Response) {
|
||||
match req.uri {
|
||||
AbsolutePath(ref path) => match (&req.method, &path[..]) {
|
||||
(&Get, "/") | (&Get, "/echo") => {
|
||||
try_return!(res.send(b"Try POST /echo"));
|
||||
return;
|
||||
impl Handler<HttpStream> for Echo {
|
||||
fn on_request(&mut self, req: Request) -> Next {
|
||||
match *req.uri() {
|
||||
RequestUri::AbsolutePath(ref path) => match (req.method(), &path[..]) {
|
||||
(&Get, "/") | (&Get, "/echo") => {
|
||||
info!("GET Index");
|
||||
self.route = Route::Index;
|
||||
Next::write()
|
||||
}
|
||||
(&Post, "/echo") => {
|
||||
info!("POST Echo");
|
||||
let mut is_more = true;
|
||||
self.route = if let Some(len) = req.headers().get::<ContentLength>() {
|
||||
is_more = **len > 0;
|
||||
Route::Echo(Body::Len(**len))
|
||||
} else {
|
||||
Route::Echo(Body::Chunked)
|
||||
};
|
||||
if is_more {
|
||||
Next::read_and_write()
|
||||
} else {
|
||||
Next::write()
|
||||
}
|
||||
}
|
||||
_ => Next::write(),
|
||||
},
|
||||
(&Post, "/echo") => (), // fall through, fighting mutable borrows
|
||||
_ => {
|
||||
*res.status_mut() = hyper::NotFound;
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return;
|
||||
_ => Next::write()
|
||||
}
|
||||
};
|
||||
}
|
||||
fn on_request_readable(&mut self, transport: &mut Decoder<HttpStream>) -> Next {
|
||||
match self.route {
|
||||
Route::Echo(ref body) => {
|
||||
if self.read_pos < self.buf.len() {
|
||||
match transport.read(&mut self.buf[self.read_pos..]) {
|
||||
Ok(0) => {
|
||||
debug!("Read 0, eof");
|
||||
self.eof = true;
|
||||
Next::write()
|
||||
},
|
||||
Ok(n) => {
|
||||
self.read_pos += n;
|
||||
match *body {
|
||||
Body::Len(max) if max <= self.read_pos as u64 => {
|
||||
self.eof = true;
|
||||
Next::write()
|
||||
},
|
||||
_ => Next::read_and_write()
|
||||
}
|
||||
}
|
||||
Err(e) => match e.kind() {
|
||||
io::ErrorKind::WouldBlock => Next::read_and_write(),
|
||||
_ => {
|
||||
println!("read error {:?}", e);
|
||||
Next::end()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Next::write()
|
||||
}
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
let mut res = try_return!(res.start());
|
||||
try_return!(copy(&mut req, &mut res));
|
||||
fn on_response(&mut self, res: &mut Response) -> Next {
|
||||
match self.route {
|
||||
Route::NotFound => {
|
||||
res.set_status(StatusCode::NotFound);
|
||||
Next::end()
|
||||
}
|
||||
Route::Index => {
|
||||
res.headers_mut().set(ContentLength(INDEX.len() as u64));
|
||||
Next::write()
|
||||
}
|
||||
Route::Echo(body) => {
|
||||
if let Body::Len(len) = body {
|
||||
res.headers_mut().set(ContentLength(len));
|
||||
}
|
||||
Next::read_and_write()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, transport: &mut Encoder<HttpStream>) -> Next {
|
||||
match self.route {
|
||||
Route::Index => {
|
||||
transport.write(INDEX).unwrap();
|
||||
Next::end()
|
||||
}
|
||||
Route::Echo(..) => {
|
||||
if self.write_pos < self.read_pos {
|
||||
match transport.write(&self.buf[self.write_pos..self.read_pos]) {
|
||||
Ok(0) => panic!("write ZERO"),
|
||||
Ok(n) => {
|
||||
self.write_pos += n;
|
||||
Next::write()
|
||||
}
|
||||
Err(e) => match e.kind() {
|
||||
io::ErrorKind::WouldBlock => Next::write(),
|
||||
_ => {
|
||||
println!("write error {:?}", e);
|
||||
Next::end()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !self.eof {
|
||||
Next::read()
|
||||
} else {
|
||||
Next::end()
|
||||
}
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init().unwrap();
|
||||
let server = Server::http("127.0.0.1:1337").unwrap();
|
||||
let _guard = server.handle(echo);
|
||||
println!("Listening on http://127.0.0.1:1337");
|
||||
let server = Server::http(&"127.0.0.1:1337".parse().unwrap()).unwrap();
|
||||
let (listening, server) = server.handle(|_| Echo::new()).unwrap();
|
||||
println!("Listening on http://{}", listening);
|
||||
server.run();
|
||||
}
|
||||
|
||||
278
examples/sync.rs
Normal file
278
examples/sync.rs
Normal file
@@ -0,0 +1,278 @@
|
||||
extern crate hyper;
|
||||
extern crate env_logger;
|
||||
extern crate time;
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::marker::PhantomData;
|
||||
use std::thread;
|
||||
use std::sync::{Arc, mpsc};
|
||||
|
||||
pub struct Server {
|
||||
listening: hyper::server::Listening,
|
||||
}
|
||||
|
||||
pub struct Request<'a> {
|
||||
#[allow(dead_code)]
|
||||
inner: hyper::server::Request,
|
||||
tx: &'a mpsc::Sender<Action>,
|
||||
rx: &'a mpsc::Receiver<io::Result<usize>>,
|
||||
ctrl: &'a hyper::Control,
|
||||
}
|
||||
|
||||
impl<'a> Request<'a> {
|
||||
fn new(inner: hyper::server::Request, tx: &'a mpsc::Sender<Action>, rx: &'a mpsc::Receiver<io::Result<usize>>, ctrl: &'a hyper::Control) -> Request<'a> {
|
||||
Request {
|
||||
inner: inner,
|
||||
tx: tx,
|
||||
rx: rx,
|
||||
ctrl: ctrl,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> io::Read for Request<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.tx.send(Action::Read(buf.as_mut_ptr(), buf.len())).unwrap();
|
||||
self.ctrl.ready(hyper::Next::read()).unwrap();
|
||||
self.rx.recv().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Fresh {}
|
||||
pub enum Streaming {}
|
||||
|
||||
pub struct Response<'a, W = Fresh> {
|
||||
status: hyper::StatusCode,
|
||||
headers: hyper::Headers,
|
||||
version: hyper::HttpVersion,
|
||||
tx: &'a mpsc::Sender<Action>,
|
||||
rx: &'a mpsc::Receiver<io::Result<usize>>,
|
||||
ctrl: &'a hyper::Control,
|
||||
_marker: PhantomData<W>,
|
||||
}
|
||||
|
||||
impl<'a> Response<'a, Fresh> {
|
||||
fn new(tx: &'a mpsc::Sender<Action>, rx: &'a mpsc::Receiver<io::Result<usize>>, ctrl: &'a hyper::Control) -> Response<'a, Fresh> {
|
||||
Response {
|
||||
status: hyper::Ok,
|
||||
headers: hyper::Headers::new(),
|
||||
version: hyper::HttpVersion::Http11,
|
||||
tx: tx,
|
||||
rx: rx,
|
||||
ctrl: ctrl,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(self) -> io::Result<Response<'a, Streaming>> {
|
||||
self.tx.send(Action::Respond(self.version.clone(), self.status.clone(), self.headers.clone())).unwrap();
|
||||
self.ctrl.ready(hyper::Next::write()).unwrap();
|
||||
let res = self.rx.recv().unwrap();
|
||||
res.map(move |_| Response {
|
||||
status: self.status,
|
||||
headers: self.headers,
|
||||
version: self.version,
|
||||
tx: self.tx,
|
||||
rx: self.rx,
|
||||
ctrl: self.ctrl,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send(mut self, msg: &[u8]) -> io::Result<()> {
|
||||
self.headers.set(hyper::header::ContentLength(msg.len() as u64));
|
||||
self.start().and_then(|mut res| res.write_all(msg)).map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Write for Response<'a, Streaming> {
|
||||
fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
|
||||
self.tx.send(Action::Write(msg.as_ptr(), msg.len())).unwrap();
|
||||
self.ctrl.ready(hyper::Next::write()).unwrap();
|
||||
let res = self.rx.recv().unwrap();
|
||||
res
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
panic!("Response.flush() not impemented")
|
||||
}
|
||||
}
|
||||
|
||||
struct SynchronousHandler {
|
||||
req_tx: mpsc::Sender<hyper::server::Request>,
|
||||
tx: mpsc::Sender<io::Result<usize>>,
|
||||
rx: mpsc::Receiver<Action>,
|
||||
reading: Option<(*mut u8, usize)>,
|
||||
writing: Option<(*const u8, usize)>,
|
||||
respond: Option<(hyper::HttpVersion, hyper::StatusCode, hyper::Headers)>
|
||||
}
|
||||
|
||||
unsafe impl Send for SynchronousHandler {}
|
||||
|
||||
impl SynchronousHandler {
|
||||
fn next(&mut self) -> hyper::Next {
|
||||
match self.rx.try_recv() {
|
||||
Ok(Action::Read(ptr, len)) => {
|
||||
self.reading = Some((ptr, len));
|
||||
hyper::Next::read()
|
||||
},
|
||||
Ok(Action::Respond(ver, status, headers)) => {
|
||||
self.respond = Some((ver, status, headers));
|
||||
hyper::Next::write()
|
||||
},
|
||||
Ok(Action::Write(ptr, len)) => {
|
||||
self.writing = Some((ptr, len));
|
||||
hyper::Next::write()
|
||||
}
|
||||
Err(mpsc::TryRecvError::Empty) => {
|
||||
// we're too fast, the other thread hasn't had a chance to respond
|
||||
hyper::Next::wait()
|
||||
}
|
||||
Err(mpsc::TryRecvError::Disconnected) => {
|
||||
// they dropped it
|
||||
// TODO: should finish up sending response, whatever it was
|
||||
hyper::Next::end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reading(&mut self) -> Option<(*mut u8, usize)> {
|
||||
self.reading.take().or_else(|| {
|
||||
match self.rx.try_recv() {
|
||||
Ok(Action::Read(ptr, len)) => {
|
||||
Some((ptr, len))
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn writing(&mut self) -> Option<(*const u8, usize)> {
|
||||
self.writing.take().or_else(|| {
|
||||
match self.rx.try_recv() {
|
||||
Ok(Action::Write(ptr, len)) => {
|
||||
Some((ptr, len))
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
})
|
||||
}
|
||||
fn respond(&mut self) -> Option<(hyper::HttpVersion, hyper::StatusCode, hyper::Headers)> {
|
||||
self.respond.take().or_else(|| {
|
||||
match self.rx.try_recv() {
|
||||
Ok(Action::Respond(ver, status, headers)) => {
|
||||
Some((ver, status, headers))
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl hyper::server::Handler<hyper::net::HttpStream> for SynchronousHandler {
|
||||
fn on_request(&mut self, req: hyper::server::Request) -> hyper::Next {
|
||||
if let Err(_) = self.req_tx.send(req) {
|
||||
return hyper::Next::end();
|
||||
}
|
||||
|
||||
self.next()
|
||||
}
|
||||
|
||||
fn on_request_readable(&mut self, decoder: &mut hyper::Decoder<hyper::net::HttpStream>) -> hyper::Next {
|
||||
if let Some(raw) = self.reading() {
|
||||
let slice = unsafe { ::std::slice::from_raw_parts_mut(raw.0, raw.1) };
|
||||
if self.tx.send(decoder.read(slice)).is_err() {
|
||||
return hyper::Next::end();
|
||||
}
|
||||
}
|
||||
self.next()
|
||||
}
|
||||
|
||||
fn on_response(&mut self, req: &mut hyper::server::Response) -> hyper::Next {
|
||||
use std::iter::Extend;
|
||||
if let Some(head) = self.respond() {
|
||||
req.set_status(head.1);
|
||||
req.headers_mut().extend(head.2.iter());
|
||||
if self.tx.send(Ok(0)).is_err() {
|
||||
return hyper::Next::end();
|
||||
}
|
||||
} else {
|
||||
// wtf happened?
|
||||
panic!("no head to respond with");
|
||||
}
|
||||
self.next()
|
||||
}
|
||||
|
||||
fn on_response_writable(&mut self, encoder: &mut hyper::Encoder<hyper::net::HttpStream>) -> hyper::Next {
|
||||
if let Some(raw) = self.writing() {
|
||||
let slice = unsafe { ::std::slice::from_raw_parts(raw.0, raw.1) };
|
||||
if self.tx.send(encoder.write(slice)).is_err() {
|
||||
return hyper::Next::end();
|
||||
}
|
||||
}
|
||||
self.next()
|
||||
}
|
||||
}
|
||||
|
||||
enum Action {
|
||||
Read(*mut u8, usize),
|
||||
Write(*const u8, usize),
|
||||
Respond(hyper::HttpVersion, hyper::StatusCode, hyper::Headers),
|
||||
}
|
||||
|
||||
unsafe impl Send for Action {}
|
||||
|
||||
trait Handler: Send + Sync + 'static {
|
||||
fn handle(&self, req: Request, res: Response);
|
||||
}
|
||||
|
||||
impl<F> Handler for F where F: Fn(Request, Response) + Send + Sync + 'static {
|
||||
fn handle(&self, req: Request, res: Response) {
|
||||
(self)(req, res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Server {
|
||||
fn handle<H: Handler>(addr: &str, handler: H) -> Server {
|
||||
let handler = Arc::new(handler);
|
||||
let (listening, server) = hyper::Server::http(&addr.parse().unwrap()).unwrap()
|
||||
.handle(move |ctrl| {
|
||||
let (req_tx, req_rx) = mpsc::channel();
|
||||
let (blocking_tx, blocking_rx) = mpsc::channel();
|
||||
let (async_tx, async_rx) = mpsc::channel();
|
||||
let handler = handler.clone();
|
||||
thread::Builder::new().name("handler-thread".into()).spawn(move || {
|
||||
let req = Request::new(req_rx.recv().unwrap(), &blocking_tx, &async_rx, &ctrl);
|
||||
let res = Response::new(&blocking_tx, &async_rx, &ctrl);
|
||||
handler.handle(req, res);
|
||||
}).unwrap();
|
||||
|
||||
SynchronousHandler {
|
||||
req_tx: req_tx,
|
||||
tx: async_tx,
|
||||
rx: blocking_rx,
|
||||
reading: None,
|
||||
writing: None,
|
||||
respond: None,
|
||||
}
|
||||
}).unwrap();
|
||||
thread::spawn(move || {
|
||||
server.run();
|
||||
});
|
||||
Server {
|
||||
listening: listening
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init().unwrap();
|
||||
let s = Server::handle("127.0.0.1:0", |mut req: Request, res: Response| {
|
||||
let mut body = [0; 256];
|
||||
let n = req.read(&mut body).unwrap();
|
||||
println!("!!!: received: {:?}", ::std::str::from_utf8(&body[..n]).unwrap());
|
||||
|
||||
res.send(b"Hello World!").unwrap();
|
||||
});
|
||||
println!("listening on {}", s.listening.addr());
|
||||
}
|
||||
Reference in New Issue
Block a user