blob: 6d25ccc057ae73e5bec2fbe6b0145096fd400fb0 [file] [log] [blame]
// Copyright 2015 The tiny-http Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use ascii::{AsciiString};
use std::io::Error as IoError;
use std::io::Result as IoResult;
use std::io::{ErrorKind, Read, BufReader, BufWriter};
use std::net::SocketAddr;
use std::str::FromStr;
use common::{HTTPVersion, Method};
use util::{SequentialReader, SequentialReaderBuilder, SequentialWriterBuilder};
use util::RefinedTcpStream;
use Request;
/// A ClientConnection is an object that will store a socket to a client
/// and return Request objects.
pub struct ClientConnection {
// address of the client
remote_addr: IoResult<SocketAddr>,
// sequence of Readers to the stream, so that the data is not read in
// the wrong order
source: SequentialReaderBuilder<BufReader<RefinedTcpStream>>,
// sequence of Writers to the stream, to avoid writing response #2 before
// response #1
sink: SequentialWriterBuilder<BufWriter<RefinedTcpStream>>,
// Reader to read the next header from
next_header_source: SequentialReader<BufReader<RefinedTcpStream>>,
// set to true if we know that the previous request is the last one
no_more_requests: bool,
// true if the connection goes through SSL
secure: bool,
}
/// Error that can happen when reading a request.
enum ReadError {
WrongRequestLine,
WrongHeader(HTTPVersion),
/// the client sent an unrecognized `Expect` header
ExpectationFailed(HTTPVersion),
ReadIoError(IoError),
}
impl ClientConnection {
/// Creates a new ClientConnection that takes ownership of the TcpStream.
pub fn new(write_socket: RefinedTcpStream, mut read_socket: RefinedTcpStream)
-> ClientConnection
{
let remote_addr = read_socket.peer_addr();
let secure = read_socket.secure();
let mut source = SequentialReaderBuilder::new(BufReader::with_capacity(1024, read_socket));
let first_header = source.next().unwrap();
ClientConnection {
source: source,
sink: SequentialWriterBuilder::new(BufWriter::with_capacity(1024, write_socket)),
remote_addr: remote_addr,
next_header_source: first_header,
no_more_requests: false,
secure: secure,
}
}
/// Reads the next line from self.next_header_source.
///
/// Reads until `CRLF` is reached. The next read will start
/// at the first byte of the new line.
fn read_next_line(&mut self) -> IoResult<AsciiString> {
let mut buf = Vec::new();
let mut prev_byte_was_cr = false;
loop {
let byte = self.next_header_source.by_ref().bytes().next();
let byte = match byte {
Some(b) => try!(b),
None => return Err(IoError::new(ErrorKind::ConnectionAborted, "Unexpected EOF"))
};
if byte == b'\n' && prev_byte_was_cr {
buf.pop(); // removing the '\r'
return AsciiString::from_ascii(buf)
.map_err(|_| IoError::new(ErrorKind::InvalidInput, "Header is not in ASCII"))
}
prev_byte_was_cr = byte == b'\r';
buf.push(byte);
}
}
/// Reads a request from the stream.
/// Blocks until the header has been read.
fn read(&mut self) -> Result<Request, ReadError> {
let (method, path, version, headers) = {
// reading the request line
let (method, path, version) = {
let line = try!(self.read_next_line().map_err(|e| ReadError::ReadIoError(e)));
try!(parse_request_line(
line.as_str().trim() // TODO: remove this conversion
))
};
// getting all headers
let headers = {
let mut headers = Vec::new();
loop {
let line = try!(self.read_next_line().map_err(|e| ReadError::ReadIoError(e)));
if line.len() == 0 { break };
headers.push(
match FromStr::from_str(line.as_str().trim()) { // TODO: remove this conversion
Ok(h) => h,
_ => return Err(ReadError::WrongHeader(version))
}
);
}
headers
};
(method, path, version, headers)
};
// building the writer for the request
let writer = self.sink.next().unwrap();
// follow-up for next potential request
let mut data_source = self.source.next().unwrap();
::std::mem::swap(&mut self.next_header_source, &mut data_source);
// building the next reader
let request = try!(::request::new_request(self.secure, method, path, version.clone(),
headers, self.remote_addr.as_ref().unwrap().clone(), data_source, writer)
.map_err(|e| {
use request;
match e {
request::RequestCreationError::CreationIoError(e) => ReadError::ReadIoError(e),
request::RequestCreationError::ExpectationFailed => ReadError::ExpectationFailed(version)
}
}));
// return the request
Ok(request)
}
}
impl Iterator for ClientConnection {
type Item = Request;
/// Blocks until the next Request is available.
/// Returns None when no new Requests will come from the client.
fn next(&mut self) -> Option<Request> {
use {Response, StatusCode};
// the client sent a "connection: close" header in this previous request
// or is using HTTP 1.0, meaning that no new request will come
if self.no_more_requests {
return None
}
loop {
let rq = match self.read() {
Err(ReadError::WrongRequestLine) => {
let writer = self.sink.next().unwrap();
let response = Response::new_empty(StatusCode(400));
response.raw_print(writer, HTTPVersion(1, 1), &[], false, None).ok();
return None; // we don't know where the next request would start,
// se we have to close
},
Err(ReadError::WrongHeader(ver)) => {
let writer = self.sink.next().unwrap();
let response = Response::new_empty(StatusCode(400));
response.raw_print(writer, ver, &[], false, None).ok();
return None; // we don't know where the next request would start,
// se we have to close
},
Err(ReadError::ReadIoError(ref err)) if err.kind() == ErrorKind::TimedOut => {
// request timeout
let writer = self.sink.next().unwrap();
let response = Response::new_empty(StatusCode(408));
response.raw_print(writer, HTTPVersion(1, 1), &[], false, None).ok();
return None; // closing the connection
},
Err(ReadError::ExpectationFailed(ver)) => {
let writer = self.sink.next().unwrap();
let response = Response::new_empty(StatusCode(417));
response.raw_print(writer, ver, &[], true, None).ok();
return None; // TODO: should be recoverable, but needs handling in case of body
},
Err(ReadError::ReadIoError(_)) =>
return None,
Ok(rq) => rq
};
// checking HTTP version
if *rq.http_version() > (1, 1) {
let writer = self.sink.next().unwrap();
let response =
Response::from_string("This server only supports HTTP versions 1.0 and 1.1"
.to_owned()).with_status_code(StatusCode(505));
response.raw_print(writer, HTTPVersion(1, 1), &[], false, None).ok();
continue
}
// updating the status of the connection
{
let connection_header = rq.headers().iter()
.find(|h| h.field.equiv(&"Connection"))
.map(|h| h.value.as_str());
let lowercase = connection_header.map(|h| h.to_ascii_lowercase());
match lowercase {
Some(ref val) if val.contains("close") =>
self.no_more_requests = true,
Some(ref val) if val.contains("upgrade") =>
self.no_more_requests = true,
Some(ref val) if !val.contains("keep-alive") &&
*rq.http_version() == HTTPVersion(1, 0) =>
self.no_more_requests = true,
None if *rq.http_version() == HTTPVersion(1, 0) =>
self.no_more_requests = true,
_ => ()
};
}
// returning the request
return Some(rq);
}
}
}
/// Parses a "HTTP/1.1" string.
fn parse_http_version(version: &str) -> Result<HTTPVersion, ReadError> {
let elems = version.splitn(2, '/').map(|e| e.to_owned()).collect::<Vec<String>>();
if elems.len() != 2 {
return Err(ReadError::WrongRequestLine)
}
let elems = elems[1].splitn(2, '.')
.map(|e| e.to_owned()).collect::<Vec<String>>();
if elems.len() != 2 {
return Err(ReadError::WrongRequestLine)
}
match (FromStr::from_str(&elems[0]), FromStr::from_str(&elems[1])) {
(Ok(major), Ok(minor)) =>
Ok(HTTPVersion(major, minor)),
_ => Err(ReadError::WrongRequestLine)
}
}
/// Parses the request line of the request.
/// eg. GET / HTTP/1.1
fn parse_request_line(line: &str) -> Result<(Method, String, HTTPVersion), ReadError> {
let mut words = line.split(' ');
let method = words.next();
let path = words.next();
let version = words.next();
let (method, path, version) = match (method, path, version) {
(Some(m), Some(p), Some(v)) => (m, p, v),
_ => return Err(ReadError::WrongRequestLine)
};
let method = match FromStr::from_str(method) {
Ok(method) => method,
Err(()) => return Err(ReadError::WrongRequestLine)
};
let version = try!(parse_http_version(version));
Ok((method, path.to_owned(), version))
}
#[cfg(test)]
mod test {
#[test]
fn test_parse_request_line() {
let (method, path, ver) =
match super::parse_request_line("GET /hello HTTP/1.1") {
Err(_) => panic!(),
Ok(v) => v
};
assert!(method == ::Method::Get);
assert!(path == "/hello");
assert!(ver == ::common::HTTPVersion(1, 1));
assert!(super::parse_request_line("GET /hello").is_err());
assert!(super::parse_request_line("qsd qsd qsd").is_err());
}
}