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