Created
January 12, 2015 12:46
-
-
Save Inari-Whitebear/595ed809f7738bc3012b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::io::{TcpListener, TcpStream}; | |
use std::io::{Acceptor, Listener}; | |
use std::collections::HashMap; | |
use std::io::Reader; | |
use std::io::fs::File; | |
use std::io::Read; | |
use std::io::Open; | |
static BASE_PATH: &'static str = "./pub/"; | |
mod web { | |
use std::collections::HashMap; | |
use std::io::TcpStream; | |
use std::str; | |
pub struct HttpData<'a> { | |
pub http_version: Option<&'a str>, | |
pub path: Option<&'a str>, | |
pub headers: HashMap<&'a str, &'a str>, | |
pub body: Option<&'a str>, | |
pub method: Option<&'a str>, | |
pub error: Option<&'a str> //for any errors :3 | |
} | |
//Parse http request stuff and return a struct | |
pub fn parse_http(data: &str) -> HttpData { | |
let mut method = None; | |
let mut path = None; | |
let mut http_version = None; | |
let mut headers: HashMap<&str, &str> = HashMap::new(); | |
let mut error = None; | |
let mut first = true; | |
for line in data.lines_any() { | |
if first { | |
first = false; | |
let words: Vec<&str> = line.words().collect(); | |
if words.len() != 3 { | |
error = Some("400 Bad Request"); | |
break; | |
} | |
method = Some(words[0]); | |
if words[0] != "GET" { | |
error = Some("501 Not Implemented"); | |
break; | |
} | |
path = Some(words[1]); | |
http_version = Some(words[2]); | |
} else { | |
if line.contains(":") { | |
let loc = line.find_str(":").unwrap(); | |
let field = line.slice_to(loc).trim(); | |
let value = line.slice_from(loc + 1us).trim(); | |
headers.insert(field, value); | |
} | |
} | |
} | |
match (&method, &path, &http_version) { | |
(&Some(_), &Some(_), &Some(_)) => { println!("We're all set!"); }, | |
_ => { | |
match error { | |
None => { | |
error = Some("500 Internal Server Error"); | |
}, | |
_ => { } | |
} | |
} | |
}; | |
let body = None; | |
HttpData { | |
http_version: http_version, | |
method: method, | |
headers: headers, | |
path: path, | |
error: error, | |
body: body | |
} | |
} | |
pub struct WebRequest { | |
pub headers: HashMap<String, String>, | |
pub body: Option<String>, | |
pub http_version: Option<String>, | |
pub method: Option<String>, | |
pub path: Option<String>, | |
pub error: Option<String>, | |
pub stream: TcpStream | |
} | |
pub struct WebResponse { | |
pub headers: HashMap<String, String>, | |
pub status: String, | |
pub body: String | |
} | |
impl WebResponse { | |
pub fn send(&self, stream: &mut TcpStream) { | |
let http_version = "HTTP/1.1"; | |
let mut send_str = String::new(); | |
send_str = send_str + http_version + " " + &*self.status + "\r\n"; | |
for (field, value) in self.headers.iter() { | |
send_str = send_str + &**field + ": " + &**value + "\r\n"; | |
} | |
send_str = send_str + "\r\n" + &*self.body; | |
match stream.write_str(&*send_str) { | |
Err(e) => { | |
println!("Error writing to output stream: {0}\r\nDetail: {1}", e.desc, e.detail.unwrap_or("N/A".to_string())); | |
} | |
_ => () | |
}; | |
stream.close_write(); | |
} | |
} | |
impl WebRequest { | |
pub fn from_stream(mut stream: TcpStream) -> WebRequest { | |
let mut method = None; | |
let mut path = None; | |
let mut http_version = None; | |
let mut headers: HashMap<String, String> = HashMap::new(); | |
let mut error = None; | |
let mut buf: [u8; 1024] = [0; 1024]; | |
stream.read(&mut buf); | |
stream.close_read(); | |
let data_opt = str::from_utf8(&buf); | |
match data_opt { | |
Ok(d) => { | |
let http_data = parse_http(d); | |
method = match &http_data.method { | |
&Some(s) => { Some(s.to_string()) } | |
&None => { None } | |
}; | |
path = match &http_data.path { | |
&Some(s) => { Some(s.to_string()) } | |
&None => { None } | |
}; | |
http_version = match &http_data.http_version { | |
&Some(s) => { Some(s.to_string()) } | |
&None => { None } | |
}; | |
error = match &http_data.error { | |
&Some(s) => { Some(s.to_string()) } | |
&None => { None } | |
}; | |
for (field, value) in http_data.headers.iter() { | |
headers.insert(((*field).to_string()), ((*value).to_string())); | |
} | |
}, | |
Err(_) => { error = Some("500 Internal Server Error".to_string()); } | |
} | |
match (&method, &path, &http_version) { | |
(&Some(_), &Some(_), &Some(_)) => { println!("We're all set!"); }, | |
_ => { | |
match error { | |
None => { | |
error = Some("500 Internal Server Error".to_string()); | |
}, | |
_ => { } | |
} | |
} | |
}; | |
WebRequest { | |
headers: headers, | |
body: Some(String::new()), | |
http_version: http_version, | |
method: method, | |
path: path, | |
error: error, | |
stream: stream | |
} | |
} | |
} | |
} | |
fn get_file_contents(path: &str) -> Result<String, &'static str> { | |
let p = Path::new(BASE_PATH.to_string() + path); | |
let res: Result<String, &'static str>; | |
let file_result = File::open_mode(&p, Open, Read); | |
res = match file_result { | |
Ok(mut file) => { | |
let read_result_u8 = file.read_to_end(); | |
match read_result_u8 { | |
Ok(read_data) => { | |
let read_result = std::str::from_utf8(&*read_data); | |
match read_result { | |
Ok(read_str) => { | |
Ok(read_str.to_string()) | |
}, | |
Err(_) => { | |
Err("500 Internal Server Error") | |
} | |
} | |
}, | |
Err(_) => { | |
Err("500 Internal Server Error") | |
} | |
} | |
}, | |
Err(_) => { | |
Err("404 File Not Found") | |
} | |
}; | |
res | |
} | |
fn main() { | |
let listener = TcpListener::bind("127.0.0.1:80").unwrap(); | |
let mut acceptor = listener.listen().unwrap(); | |
fn handle_web_request(mut request: web::WebRequest) { | |
println!("Handling request!"); | |
let data; | |
let status; | |
match request.error { | |
None => { | |
let file_result = get_file_contents(&**request.path.as_ref().unwrap()); | |
match file_result { | |
Ok(dat) => { | |
data = dat; | |
status = "200 OK"; | |
}, | |
Err(err) => { | |
data = String::new(); | |
status = err; | |
} | |
} | |
}, | |
Some(ref e) => { | |
data = String::new(); | |
status = &**e; | |
} | |
} | |
let headers: HashMap<String, String> = HashMap::new(); | |
let web_response = web::WebResponse { | |
status: status.to_string(), | |
body: data, | |
headers: headers | |
}; | |
web_response.send(&mut request.stream); | |
} | |
fn handle_client(stream: TcpStream) { | |
let web_request = web::WebRequest::from_stream(stream); | |
handle_web_request(web_request); | |
} | |
for stream in acceptor.incoming() { | |
match stream { | |
Err(e) => { println!("Connection failed! {}", e); }, | |
Ok(stream) => { | |
handle_client(stream); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment