Skip to content

Instantly share code, notes, and snippets.

@Inari-Whitebear
Created January 12, 2015 12:46
Show Gist options
  • Save Inari-Whitebear/595ed809f7738bc3012b to your computer and use it in GitHub Desktop.
Save Inari-Whitebear/595ed809f7738bc3012b to your computer and use it in GitHub Desktop.
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