feat(http): basic request parsing done
This commit is contained in:
76
src/http.rs
76
src/http.rs
@@ -3,13 +3,20 @@ use std::fs;
|
|||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::net::TcpStream;
|
use std::net::TcpStream;
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// - we could complement status with a struct that stores the status and the error message.
|
||||||
|
// - an alternative is to have a status-error_mesage mapper in order to send an error explanation to the client
|
||||||
|
type Status = u16;
|
||||||
|
type Body<'a> = Option<&'a str>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ProcessedResponse {
|
pub struct ProcessedResponse {
|
||||||
pub data: String,
|
pub data: String,
|
||||||
status: u16,
|
status: Status,
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryParams<'a> = HashMap<&'a str, &'a str>;
|
type QueryParams<'a> = HashMap<&'a str, &'a str>;
|
||||||
type Headers = HashMap<String, String>;
|
type Headers<'a> = HashMap<&'a str, &'a str>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct HttpRequestQuery<'a> {
|
struct HttpRequestQuery<'a> {
|
||||||
@@ -27,14 +34,15 @@ struct HttpRequestLine<'a> {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct HttpRequest<'a> {
|
struct HttpRequest<'a> {
|
||||||
request: HttpRequestLine<'a>,
|
request: HttpRequestLine<'a>,
|
||||||
headers: Headers,
|
headers: Headers<'a>,
|
||||||
body: Option<String>,
|
body: Body<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_petition(stream: &mut TcpStream) -> std::io::Result<ProcessedResponse> {
|
pub fn process_petition(stream: &mut TcpStream) -> ProcessedResponse {
|
||||||
let mut buffer = [0; 1024]; // TODO: manage this size
|
let mut buffer = [0; 1024]; // TODO: manage this size
|
||||||
let _amount = stream.read(&mut buffer)?;
|
let _amount = stream.read(&mut buffer);
|
||||||
let petition = String::from_utf8_lossy(&buffer[..]);
|
let petition = String::from_utf8_lossy(&buffer[..]);
|
||||||
|
println!("Petition: {:?}", petition);
|
||||||
let petition = parse_request(&petition);
|
let petition = parse_request(&petition);
|
||||||
|
|
||||||
match petition {
|
match petition {
|
||||||
@@ -53,38 +61,46 @@ pub fn process_petition(stream: &mut TcpStream) -> std::io::Result<ProcessedResp
|
|||||||
status: 200,
|
status: 200,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(response)
|
response
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let response: ProcessedResponse = ProcessedResponse {
|
let response: ProcessedResponse = ProcessedResponse {
|
||||||
data: format!("HTTP/1.1 {}\r\nContent-Length: 0\r", error,),
|
data: format!("HTTP/1.1 {}\r\nContent-Length: 0\r\n\r\n", error),
|
||||||
status: error,
|
status: error,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(response)
|
response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_request(request_raw: &str) -> Result<HttpRequest, u16> {
|
fn parse_request(request_raw: &str) -> Result<HttpRequest, Status> {
|
||||||
// TODO: study if better to use match
|
// TODO: study if better to use match
|
||||||
if let Some((heading, rest)) = request_raw.split_once("\n") {
|
if let Some((heading, rest)) = request_raw.split_once("\n") {
|
||||||
// Process heading
|
if let Ok(request) = parse_request_block(heading) {
|
||||||
// split heading with split_whitespace
|
if let Some((headers, body)) = rest.split_once("\n\r\n") {
|
||||||
// for (i, line) in request_raw.enumerate() {
|
if let Ok(headers) = parse_headers(headers) {
|
||||||
// }
|
let body: Body = {
|
||||||
let request = parse_request_block(heading);
|
if body.len() > 0 {
|
||||||
println!("This is a raw request: {:?}", request);
|
Some(body)
|
||||||
if let Some((headers, body)) = rest.split_once("\n\n") {
|
} else {
|
||||||
// Process headers and body
|
None
|
||||||
// split headers over ":"
|
}
|
||||||
|
};
|
||||||
|
return Ok(HttpRequest {
|
||||||
|
request,
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(400)
|
Err(400)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_request_block(request_block: &str) -> Result<HttpRequestLine, u16> {
|
fn parse_request_block(request_block: &str) -> Result<HttpRequestLine, Status> {
|
||||||
let [method, query, version]: [&str; 3] = request_block
|
let [method, query, version]: [&str; 3] = request_block
|
||||||
.split_whitespace()
|
.split_whitespace()
|
||||||
.collect::<Vec<&str>>()
|
.collect::<Vec<&str>>()
|
||||||
@@ -101,7 +117,7 @@ fn parse_request_block(request_block: &str) -> Result<HttpRequestLine, u16> {
|
|||||||
Err(400)
|
Err(400)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_query(query: &str) -> Result<HttpRequestQuery, u16> {
|
fn parse_query(query: &str) -> Result<HttpRequestQuery, Status> {
|
||||||
match query.split_once("?") {
|
match query.split_once("?") {
|
||||||
Some((path, params)) => {
|
Some((path, params)) => {
|
||||||
if let Ok(params) = parse_query_params(params) {
|
if let Ok(params) = parse_query_params(params) {
|
||||||
@@ -118,7 +134,7 @@ fn parse_query(query: &str) -> Result<HttpRequestQuery, u16> {
|
|||||||
Err(400)
|
Err(400)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_query_params(query: &str) -> Result<QueryParams, u16> {
|
fn parse_query_params(query: &str) -> Result<QueryParams, Status> {
|
||||||
let mut param_map: HashMap<&str, &str> = HashMap::new();
|
let mut param_map: HashMap<&str, &str> = HashMap::new();
|
||||||
|
|
||||||
let param_list = query.split("&");
|
let param_list = query.split("&");
|
||||||
@@ -133,3 +149,19 @@ fn parse_query_params(query: &str) -> Result<QueryParams, u16> {
|
|||||||
|
|
||||||
Ok(param_map)
|
Ok(param_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_headers(headers: &str) -> Result<Headers, Status> {
|
||||||
|
let mut header_map: Headers = HashMap::new();
|
||||||
|
|
||||||
|
let header_list = headers.split("\n");
|
||||||
|
|
||||||
|
for header in header_list {
|
||||||
|
if let Some((key, value)) = header.split_once(":") {
|
||||||
|
header_map.insert(key, value);
|
||||||
|
} else {
|
||||||
|
return Err(400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(header_map)
|
||||||
|
}
|
||||||
|
|||||||
16
src/main.rs
16
src/main.rs
@@ -16,20 +16,8 @@ fn main() {
|
|||||||
let response = http::process_petition(&mut _stream);
|
let response = http::process_petition(&mut _stream);
|
||||||
|
|
||||||
// TODO: manage error case
|
// TODO: manage error case
|
||||||
match response {
|
println!("About to responde: {:?}", response);
|
||||||
Ok(data) => {
|
let _amount = _stream.write(response.data.as_bytes()).unwrap();
|
||||||
let _amount = _stream.write(data.data.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("Error: {:?}", e);
|
|
||||||
let _amount = _stream
|
|
||||||
.write(
|
|
||||||
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n"
|
|
||||||
.as_bytes(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_stream.flush().unwrap();
|
_stream.flush().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user