implementation
This commit is contained in:
83
src/server/mod.rs
Normal file
83
src/server/mod.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
//! # Server
|
||||
use std::io::net::tcp::TcpListener;
|
||||
use std::io::{Acceptor, Listener, IoResult};
|
||||
use std::io::net::ip::{IpAddr, Port};
|
||||
|
||||
pub use self::request::Request;
|
||||
pub use self::response::Response;
|
||||
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
|
||||
/// A server can listen on a TCP socket.
|
||||
///
|
||||
/// Once listening, it will create a `Request`/`Response` pair for each
|
||||
/// incoming connection, and hand them to the provided handler.
|
||||
pub struct Server {
|
||||
ip: IpAddr,
|
||||
port: Port
|
||||
}
|
||||
|
||||
|
||||
impl Server {
|
||||
|
||||
/// Creates a server to be used for `http` conenctions.
|
||||
pub fn http(ip: IpAddr, port: Port) -> Server {
|
||||
Server {
|
||||
ip: ip,
|
||||
port: port
|
||||
}
|
||||
}
|
||||
|
||||
/// Binds to a socket, and starts handling connections.
|
||||
pub fn listen<H: Handler>(&self, mut handler: H) {
|
||||
let listener = match TcpListener::bind(self.ip.to_string().as_slice(), self.port) {
|
||||
Ok(listener) => listener,
|
||||
Err(err) => fail!("Listen failed: {}", err)
|
||||
};
|
||||
let mut acceptor = listener.listen();
|
||||
|
||||
for conn in acceptor.incoming() {
|
||||
match conn {
|
||||
Ok(stream) => {
|
||||
debug!("Incoming stream");
|
||||
let clone = stream.clone();
|
||||
let req = match Request::new(stream) {
|
||||
Ok(r) => r,
|
||||
Err(err) => {
|
||||
error!("creating Request: {}", err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let mut res = Response::new(clone);
|
||||
res.version = req.version;
|
||||
match handler.handle(req, res) {
|
||||
Ok(..) => debug!("Stream handled"),
|
||||
Err(e) => {
|
||||
error!("Error from handler: {}", e)
|
||||
//TODO try to send a status code
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Connection failed: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// A handler that can handle incoming requests for a server.
|
||||
pub trait Handler {
|
||||
/// Receives a `Request`/`Response` pair, and should perform some action on them.
|
||||
///
|
||||
/// This could reading from the request, and writing to the response.
|
||||
fn handle(&mut self, req: Request, res: Response) -> IoResult<()>;
|
||||
}
|
||||
|
||||
impl<'a> Handler for |Request, Response|: 'a -> IoResult<()> {
|
||||
fn handle(&mut self, req: Request, res: Response) -> IoResult<()> {
|
||||
(*self)(req, res)
|
||||
}
|
||||
}
|
||||
72
src/server/request.rs
Normal file
72
src/server/request.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
//! # Server Requests
|
||||
//!
|
||||
//! These are requests that a `hyper::Server` receives, and include its method,
|
||||
//! target URI, headers, and message body.
|
||||
use std::io::{Reader, IoResult};
|
||||
use std::io::net::ip::SocketAddr;
|
||||
use std::io::net::tcp::TcpStream;
|
||||
|
||||
use {HttpResult};
|
||||
use version::{HttpVersion};
|
||||
use method;
|
||||
use header::{Headers, ContentLength};
|
||||
use rfc7230::{read_request_line};
|
||||
use rfc7230::{HttpReader, SizedReader, ChunkedReader};
|
||||
use uri::RequestUri;
|
||||
|
||||
/// A request bundles several parts of an incoming TCP stream, given to a `Handler`.
|
||||
pub struct Request {
|
||||
/// The IP address of the remote connection.
|
||||
pub remote_addr: SocketAddr,
|
||||
/// The `Method`, such as `Get`, `Post`, etc.
|
||||
pub method: method::Method,
|
||||
/// The headers of the incoming request.
|
||||
pub headers: Headers,
|
||||
/// The target request-uri for this request.
|
||||
pub uri: RequestUri,
|
||||
/// The version of HTTP for this request.
|
||||
pub version: HttpVersion,
|
||||
body: HttpReader<TcpStream>
|
||||
}
|
||||
|
||||
|
||||
impl Request {
|
||||
|
||||
/// Create a new Request, reading the StartLine and Headers so they are
|
||||
/// immediately useful.
|
||||
pub fn new(mut tcp: TcpStream) -> HttpResult<Request> {
|
||||
let (method, uri, version) = try!(read_request_line(&mut tcp));
|
||||
let mut headers = try!(Headers::from_raw(&mut tcp));
|
||||
|
||||
debug!("{} {} {}", method, uri, version);
|
||||
debug!("{}", headers);
|
||||
|
||||
let remote_addr = try_io!(tcp.peer_name());
|
||||
|
||||
// TODO: handle Transfer-Encoding
|
||||
let body = if headers.has::<ContentLength>() {
|
||||
match headers.get_ref::<ContentLength>() {
|
||||
Some(&ContentLength(len)) => SizedReader(tcp, len),
|
||||
None => unreachable!()
|
||||
}
|
||||
} else {
|
||||
ChunkedReader(tcp, None)
|
||||
};
|
||||
|
||||
Ok(Request {
|
||||
remote_addr: remote_addr,
|
||||
method: method,
|
||||
uri: uri,
|
||||
headers: headers,
|
||||
version: version,
|
||||
body: body,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Reader for Request {
|
||||
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
|
||||
self.body.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
81
src/server/response.rs
Normal file
81
src/server/response.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
//! # Server Responses
|
||||
//!
|
||||
//! These are responses sent by a `hyper::Server` to clients, after
|
||||
//! receiving a request.
|
||||
use std::io::IoResult;
|
||||
use std::io::net::tcp::TcpStream;
|
||||
|
||||
use header;
|
||||
use status;
|
||||
use version;
|
||||
use rfc7230::{CR, LF, LINE_ENDING};
|
||||
|
||||
|
||||
/// The outgoing half for a Tcp connection, created by a `Server` and given to a `Handler`.
|
||||
pub struct Response {
|
||||
/// The status code for the request.
|
||||
pub status: status::StatusCode,
|
||||
/// The outgoing headers on this response.
|
||||
pub headers: header::Headers,
|
||||
/// The HTTP version of this response.
|
||||
pub version: version::HttpVersion,
|
||||
|
||||
headers_written: bool, // TODO: can this check be moved to compile time?
|
||||
body: TcpStream
|
||||
}
|
||||
|
||||
impl Response {
|
||||
|
||||
/// Creates a new Response that can be used to write to a network stream.
|
||||
pub fn new(tcp: TcpStream) -> Response {
|
||||
Response {
|
||||
status: status::Ok,
|
||||
version: version::Http11,
|
||||
headers: header::Headers::new(),
|
||||
headers_written: false,
|
||||
body: tcp
|
||||
}
|
||||
}
|
||||
|
||||
fn write_head(&mut self) -> IoResult<()> {
|
||||
if self.headers_written {
|
||||
debug!("headers previsouly written, nooping");
|
||||
return Ok(());
|
||||
}
|
||||
self.headers_written = true;
|
||||
debug!("writing head: {} {}", self.version, self.status);
|
||||
try!(write!(self.body, "{} {}{}{}", self.version, self.status, CR as char, LF as char));
|
||||
|
||||
for (name, header) in self.headers.iter() {
|
||||
debug!("headers {}: {}", name, header);
|
||||
try!(write!(self.body, "{}: {}", name, header));
|
||||
try!(self.body.write(LINE_ENDING));
|
||||
}
|
||||
|
||||
self.body.write(LINE_ENDING)
|
||||
}
|
||||
|
||||
/// Flushes all writing of a response to the client.
|
||||
pub fn end(&mut self) -> IoResult<()> {
|
||||
try!(self.flush());
|
||||
self.body.close_write()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Writer for Response {
|
||||
fn write(&mut self, msg: &[u8]) -> IoResult<()> {
|
||||
if !self.headers_written {
|
||||
try!(self.write_head());
|
||||
}
|
||||
debug!("write {:u} bytes", msg.len());
|
||||
self.body.write(msg)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> IoResult<()> {
|
||||
if !self.headers_written {
|
||||
try!(self.write_head());
|
||||
}
|
||||
self.body.flush()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user