From 9b69d6460e91244bfd77d8ea77c13d21d9050e4c Mon Sep 17 00:00:00 2001 From: Andrew Guschin Date: Thu, 18 Feb 2021 00:25:11 +0400 Subject: Added parsing of query params in GET requests handler --- blog/src/main.rs | 13 ++++++++++ blog/src/querystring.rs | 50 +++++++++++++++++++++++++++++++++++++ spider/Cargo.toml | 2 -- spider/src/http_method.rs | 1 + spider/src/request.rs | 63 +++++++++++++++++++++++++++++++++-------------- 5 files changed, 109 insertions(+), 20 deletions(-) create mode 100644 blog/src/querystring.rs diff --git a/blog/src/main.rs b/blog/src/main.rs index b0b8c71..ebae43a 100644 --- a/blog/src/main.rs +++ b/blog/src/main.rs @@ -2,10 +2,22 @@ use spider::http_server::{HttpHandler, HttpServer}; use spider::request::Request; use spider::response::Response; +mod querystring; +use crate::querystring::parse_qs; +use std::str; + struct MyHandler {} impl HttpHandler for MyHandler { fn do_get(&self, _request: Request) -> Response { + let params = str::from_utf8(&_request.body).unwrap().to_string(); + let params = parse_qs(params); + + println!("{}", _request.path); + for (key, val) in ¶ms { + println!(" {}={}", key, val); + } + return Response::html(String::from("hey"), 200); } @@ -18,6 +30,7 @@ impl MyHandler { pub fn new() -> MyHandler { return MyHandler {}; } + } // TODO(andrew): create logging package. diff --git a/blog/src/querystring.rs b/blog/src/querystring.rs new file mode 100644 index 0000000..8bb174a --- /dev/null +++ b/blog/src/querystring.rs @@ -0,0 +1,50 @@ +use std::str; +use std::collections::HashMap; +use std::num::ParseIntError; + + +pub fn parse_qs(query: String) -> HashMap { + let query = percent_decode(query); + let mut map = HashMap::new(); + for pair in query.split('&') { + let pair = pair.split('=').collect::>(); + map.insert(pair[0].to_string(), pair[1].to_string()); + } + return map; +} + +fn percent_decode(s: String) -> String { + let len = s.len(); + let mut s = s.chars(); + let mut result = String::new(); + let mut decode = String::new(); + + let mut i = 0; + while i < len { + let cur = s.nth(0).unwrap(); + if cur == '%' { + decode.push(s.nth(0).unwrap()); + decode.push(s.nth(0).unwrap()); + i += 3; + continue; + } + if decode.len() != 0 { + match decode_hex(&decode) { + Ok(v) => result.push_str(str::from_utf8(&v).unwrap()), + Err(_) => () + }; + decode = String::new(); + } + + result.push(cur); + i += 1; + } + return result; +} + +fn decode_hex(s: &str) -> Result, ParseIntError> { + (0..s.len()) + .step_by(2) + .map(|i| u8::from_str_radix(&s[i..i + 2], 16)) + .collect() +} diff --git a/spider/Cargo.toml b/spider/Cargo.toml index 84e5702..b35cb43 100644 --- a/spider/Cargo.toml +++ b/spider/Cargo.toml @@ -4,6 +4,4 @@ version = "0.1.0" authors = ["Andrew "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] diff --git a/spider/src/http_method.rs b/spider/src/http_method.rs index a0b2e17..814c93b 100644 --- a/spider/src/http_method.rs +++ b/spider/src/http_method.rs @@ -1,5 +1,6 @@ use std::fmt; +#[derive(PartialEq)] pub enum HttpMethod { GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH } diff --git a/spider/src/request.rs b/spider/src/request.rs index db1f2ad..3bd6a8f 100644 --- a/spider/src/request.rs +++ b/spider/src/request.rs @@ -5,21 +5,21 @@ use crate::http_method::HttpMethod; pub struct Request { pub method: HttpMethod, - resource: String, - http_version: String, - headers: HashMap, - body: Vec, + pub path: String, + pub http_version: String, + pub headers: HashMap, + pub body: Vec, } impl Request { pub fn new( method: HttpMethod - , resource: String + , path: String , http_version: String , headers: HashMap , body: Vec ) -> Request { return Request { method, - resource, + path, http_version, headers, body, @@ -49,15 +49,17 @@ impl Request { } } - let body = data[idx..].to_vec(); + let mut body = data[idx..].to_vec(); let mut headers: HashMap = HashMap::new(); - for line in &lines[1..] { - let line = line - .trim_end_matches("\r\n") - .split(": ") - .collect::>(); - headers.insert(String::from(line[0]), line[1..].join(" ")); + if lines.len() > 0 { + for line in &lines[1..] { + let line = line + .trim_end_matches("\r\n") + .split(": ") + .collect::>(); + headers.insert(String::from(line[0]), line[1..].join(" ")); + } } let request_line = lines[0] @@ -65,23 +67,48 @@ impl Request { .split(" ") .collect::>(); + let (path, params) = split_path(String::from(request_line[1])); + let method = String::from(request_line[0]); match HttpMethod::parse(method) { - Some(method) => + Some(method) => { + if method == HttpMethod::GET { + if let Some(p) = params { + body = p.as_bytes().to_vec(); + } + } return Some(Request::new( method, - // TODO(andrew): add parsing of resource line. - String::from(request_line[1]), + path, String::from(request_line[2]), headers, - body )), + body )); + } None => return None }; } } +fn split_path(resource: String) -> (String, Option) { + let mut idx = None; + for (i, c) in resource.chars().enumerate() { + if c == '?' { + idx = Some(i); + break; + } + } + match idx { + Some(n) => { + let path = &resource[..n]; + let params = &resource[n + 1..]; + return (path.to_string(), Some(params.to_string())); + }, + None => return (resource, None) + }; +} + impl fmt::Display for Request { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - return write!(f, "Request({}, {})", self.method, self.resource); + return write!(f, "Request({}, {})", self.method, self.path); } } -- cgit v1.2.3