#![crate_name = "tiny_http"]
#![crate_type = "lib"]
#![forbid(unsafe_code)]
#[macro_use]
extern crate log;
extern crate ascii;
extern crate chunked_transfer;
extern crate encoding;
extern crate url;
extern crate chrono;
#[cfg(feature = "ssl")]
extern crate openssl;
use std::error::Error;
use std::io::Error as IoError;
use std::io::Result as IoResult;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::thread;
use std::net;
use std::net::{ToSocketAddrs, TcpStream, Shutdown};
use std::time::Duration;
use std::sync::atomic::Ordering::Relaxed;
use client::ClientConnection;
use util::MessagesQueue;
pub use common::{Header, HeaderField, HTTPVersion, Method, StatusCode};
pub use request::{Request, ReadWrite};
pub use response::{ResponseBox, Response};
mod client;
mod common;
mod request;
mod response;
#[allow(dead_code)] mod util;
pub struct Server {
close: Arc<AtomicBool>,
messages: Arc<MessagesQueue<Message>>,
listening_addr: net::SocketAddr,
}
enum Message {
Error(IoError),
NewRequest(Request),
}
impl From<IoError> for Message {
fn from(e: IoError) -> Message {
Message::Error(e)
}
}
impl From<Request> for Message {
fn from(rq: Request) -> Message {
Message::NewRequest(rq)
}
}
#[doc(hidden)]
trait MustBeShareDummy : Sync + Send {}
#[doc(hidden)]
impl MustBeShareDummy for Server {}
pub struct IncomingRequests<'a> {
server: &'a Server
}
#[derive(Debug, Clone)]
pub struct ServerConfig<A> where A: ToSocketAddrs {
pub addr: A,
pub ssl: Option<SslConfig>,
}
#[derive(Debug, Clone)]
pub struct SslConfig {
pub certificate: Vec<u8>,
pub private_key: Vec<u8>,
}
impl Server {
#[inline]
pub fn http<A>(addr: A) -> Result<Server, Box<Error + Send + Sync + 'static>>
where A: ToSocketAddrs
{
Server::new(ServerConfig {
addr: addr,
ssl: None,
})
}
#[cfg(feature = "ssl")]
#[inline]
pub fn https<A>(addr: A, config: SslConfig)
-> Result<Server, Box<Error + Send + Sync + 'static>>
where A: ToSocketAddrs
{
Server::new(ServerConfig {
addr: addr,
ssl: Some(config),
})
}
pub fn new<A>(config: ServerConfig<A>) -> Result<Server, Box<Error + Send + Sync + 'static>>
where A: ToSocketAddrs
{
let close_trigger = Arc::new(AtomicBool::new(false));
let (server, local_addr) = {
let listener = try!(net::TcpListener::bind(config.addr));
let local_addr = try!(listener.local_addr());
debug!("Server listening on {}", local_addr);
(listener, local_addr)
};
#[cfg(feature = "ssl")]
type SslContext = openssl::ssl::SslContext;
#[cfg(not(feature = "ssl"))]
type SslContext = ();
let ssl: Option<SslContext> = match config.ssl {
#[cfg(feature = "ssl")]
Some(mut config) => {
use openssl::ssl;
use openssl::x509::X509;
use openssl::pkey::PKey;
use openssl::ssl::SslVerifyMode;
let mut ctxt = try!(SslContext::builder(ssl::SslMethod::tls()));
try!(ctxt.set_cipher_list("DEFAULT"));
let certificate = try!(X509::from_pem(&config.certificate[..]));
try!(ctxt.set_certificate(&certificate));
let private_key = try!(PKey::private_key_from_pem(&config.private_key[..]));
try!(ctxt.set_private_key(&private_key));
ctxt.set_verify(SslVerifyMode::NONE);
try!(ctxt.check_private_key());
for b in &mut config.certificate { *b = 0; }
for b in &mut config.private_key { *b = 0; }
Some(ctxt.build())
},
#[cfg(not(feature = "ssl"))]
Some(_) => return Err("Building a server with SSL requires enabling the `ssl` feature \
in tiny-http".to_owned().into()),
None => None,
};
let messages = MessagesQueue::with_capacity(8);
let inside_close_trigger = close_trigger.clone();
let inside_messages = messages.clone();
thread::spawn(move || {
let tasks_pool = util::TaskPool::new();
debug!("Running accept thread");
while !inside_close_trigger.load(Relaxed) {
let new_client = match server.accept() {
Ok((sock, _)) => {
use util::RefinedTcpStream;
let (read_closable, write_closable) = match ssl {
None => {
RefinedTcpStream::new(sock)
},
#[cfg(feature = "ssl")]
Some(ref ssl) => {
let ssl = openssl::ssl::Ssl::new(ssl).expect("Couldn't create ssl");
let sock = match ssl.accept(sock) {
Ok(s) => s,
Err(_) => continue
};
RefinedTcpStream::new(sock)
},
#[cfg(not(feature = "ssl"))]
Some(_) => unreachable!(),
};
Ok(ClientConnection::new(write_closable, read_closable))
},
Err(e) => Err(e),
};
match new_client {
Ok(client) => {
let messages = inside_messages.clone();
let mut client = Some(client);
tasks_pool.spawn(Box::new(move || {
if let Some(client) = client.take() {
for rq in client {
messages.push(rq.into());
}
}
}));
},
Err(e) => {
error!("Error accepting new client: {}", e);
inside_messages.push(e.into());
break;
}
}
}
debug!("Terminating accept thread");
});
Ok(Server {
messages: messages,
close: close_trigger,
listening_addr: local_addr,
})
}
#[inline]
pub fn incoming_requests(&self) -> IncomingRequests {
IncomingRequests { server: self }
}
#[inline]
pub fn server_addr(&self) -> net::SocketAddr {
self.listening_addr.clone()
}
pub fn num_connections(&self) -> usize {
unimplemented!()
}
pub fn recv(&self) -> IoResult<Request> {
match self.messages.pop() {
Message::Error(err) => return Err(err),
Message::NewRequest(rq) => return Ok(rq),
}
}
pub fn recv_timeout(&self, timeout: Duration) -> IoResult<Option<Request>> {
match self.messages.pop_timeout(timeout) {
Some(Message::Error(err)) => return Err(err),
Some(Message::NewRequest(rq)) => return Ok(Some(rq)),
None => return Ok(None)
}
}
pub fn try_recv(&self) -> IoResult<Option<Request>> {
match self.messages.try_pop() {
Some(Message::Error(err)) => return Err(err),
Some(Message::NewRequest(rq)) => return Ok(Some(rq)),
None => return Ok(None)
}
}
}
impl<'a> Iterator for IncomingRequests<'a> {
type Item = Request;
fn next(&mut self) -> Option<Request> {
self.server.recv().ok()
}
}
impl Drop for Server {
fn drop(&mut self) {
self.close.store(true, Relaxed);
let maybe_stream = TcpStream::connect(self.listening_addr);
if let Ok(stream) = maybe_stream {
let _ = stream.shutdown(Shutdown::Both);
}
}
}