feat: #2 impl an HTTPResponse + update tests

This commit is contained in:
landrigun 2022-09-20 09:06:19 +00:00
parent 35cc483774
commit 2c7418f333
6 changed files with 147 additions and 46 deletions

View File

@ -1,3 +1,5 @@
pub mod request; pub mod request;
pub mod response;
pub use request::handle_request; pub use request::handle_request;
pub use response::HTTPResponse;

98
src/handlers/response.rs Normal file
View File

@ -0,0 +1,98 @@
//! response handles the incoming request parsed `HTTPRequest`
//! it will check if the `HTTPRequest` is valid and build an HTTPResponse corresponding to the HTTP
//! message specs. see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages
//! NOTE: only few parts of the specification has been implemented
use json;
use crate::handlers::request::{HTTPRequest, HTTPVersion};
enum HTTPStatusCode {
Http200,
Http400,
Http404,
Http500,
}
impl Into<String> for HTTPStatusCode {
fn into(self) -> String {
match self {
Self::Http200 => "200".to_string(),
Self::Http400 => "400".to_string(),
Self::Http404 => "404".to_string(),
Self::Http500 => "500".to_string(),
}
}
}
pub struct HTTPStatusLine {
version: HTTPVersion,
status_code: HTTPStatusCode,
}
impl Default for HTTPStatusLine {
fn default() -> HTTPStatusLine {
HTTPStatusLine {
version: HTTPVersion::Http1_1,
status_code: HTTPStatusCode::Http400,
}
}
}
impl Into<String> for HTTPStatusLine {
fn into(self) -> String {
let version: String = self.version.into();
let status_code: String = self.status_code.into();
format! {"{} {}", version, status_code}
}
}
pub struct HTTPResponse {
status_line: HTTPStatusLine,
body: json::JsonValue,
}
impl Default for HTTPResponse {
fn default() -> Self {
HTTPResponse {
status_line: HTTPStatusLine::default(),
body: json::parse(r#"{"error": "the incoming request is not valid"}"#).unwrap(),
}
}
}
impl From<HTTPRequest> for HTTPResponse {
fn from(request: HTTPRequest) -> Self {
let mut response = HTTPResponse::default();
if !request.is_valid() {
return response;
}
let body = json::parse(
r#"{"token": "header.payload.signature", "refresh": "header.payload.signature"}"#,
)
.unwrap();
response.status_line.version = request.start_line.version;
response.status_line.status_code = HTTPStatusCode::Http200;
response.body = body;
response
}
}
impl Into<String> for HTTPResponse {
fn into(self) -> String {
// move `self.body` into a new var
let b = self.body;
let body: String = json::stringify(b);
let status_line: String = self.status_line.into();
format!(
"{}\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}",
status_line,
body.len(),
body
)
}
}

View File

@ -1,13 +1,11 @@
mod handlers; mod handlers;
use std::io::prelude::*;
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
net::{TcpListener, TcpStream}, net::{TcpListener, TcpStream},
}; };
use handlers::handle_request; use handlers::{handle_request, HTTPResponse};
const SERVER_URL: &str = "127.0.0.1:9000"; const SERVER_URL: &str = "127.0.0.1:9000";
@ -29,20 +27,9 @@ async fn handle_connection(mut stream: TcpStream) {
let request_string = std::str::from_utf8(&buffer[0..n]).unwrap(); let request_string = std::str::from_utf8(&buffer[0..n]).unwrap();
let request = handle_request(request_string); let request = handle_request(request_string);
if request.is_valid() { let response = HTTPResponse::from(request);
let contents = "{\"status\": \"ok\"}"; let response_str: String = response.into();
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}",
contents.len(),
contents
);
stream.write(response.as_bytes()).await.unwrap(); stream.write(response_str.as_bytes()).await.unwrap();
stream.flush().await.unwrap();
return;
}
let response = "HTTP/1.1 400 OK\r\n\r\n".to_string();
stream.write(response.as_bytes()).await.unwrap();
stream.flush().await.unwrap(); stream.flush().await.unwrap();
} }

View File

@ -16,8 +16,8 @@ do
echo "bad http status code : ${http_response}, expect 200" echo "bad http status code : ${http_response}, expect 200"
exit 1 exit 1
fi fi
if [ $(cat response.txt | jq -r '.[]') != "ok" ] if [ "$(cat response.txt | jq -r '.token')" != "header.payload.signature" ]
then then
echo "bad data returned, expect : ok" echo "bad data returned, expect : ok"
exit 1 exit 1

View File

@ -12,7 +12,6 @@ platformdirs==2.5.2
pluggy==1.0.0 pluggy==1.0.0
py==1.11.0 py==1.11.0
pyparsing==3.0.9 pyparsing==3.0.9
pytest==7.1.3
requests==2.28.1 requests==2.28.1
tomli==2.0.1 tomli==2.0.1
urllib3==1.26.12 urllib3==1.26.12

View File

@ -1,34 +1,49 @@
import requests import requests
from unittest import TestCase
URL = "https://dev.thegux.fr" URL = "https://dev.thegux.fr"
def test_get_target(): class TestResponse(TestCase):
resp = requests.post(URL + "/get/", json={"username": "toto", "password": "tata"}) def test_get_target(self):
assert resp.status_code == 200, "bad status code returned" resp = requests.post(
assert resp.json() is not None, "response data can't be empty" URL + "/get/", json={"username": "toto", "password": "tata"}
assert resp.json()["status"] == "ok", "bad status returned" )
self.assertEqual(resp.status_code, 200, "bad status code returned")
self.assertIsNotNone(resp.json(), "response data can't be empty")
self.assertEqual(
resp.json()["token"], "header.payload.signature", "bad status returned"
)
def test_validate_target(self):
resp = requests.post(
URL + "/validate/", json={"username": "toto", "password": "tata"}
)
self.assertEqual(resp.status_code, 200, "bad status code returned")
self.assertIsNotNone(resp.json(), "response data can't be empty")
self.assertEqual(
resp.json()["token"], "header.payload.signature", "bad status returned"
)
def test_validate_target(): def test_refresh_target(self):
resp = requests.post( resp = requests.post(
URL + "/validate/", json={"username": "toto", "password": "tata"} URL + "/refresh/", json={"username": "toto", "password": "tata"}
) )
assert resp.status_code == 200, "bad status code returned" self.assertEqual(resp.status_code, 200, "bad status code returned")
assert resp.json() is not None, "response data can't be empty" self.assertIsNotNone(resp.json(), "response data can't be empty")
assert resp.json()["status"] == "ok", "bad status returned" self.assertEqual(
resp.json()["token"], "header.payload.signature", "bad status returned"
)
def test_bad_target(self):
def test_refresh_target(): resp = requests.post(
resp = requests.post( URL + "/token/", json={"username": "toto", "password": "tata"}
URL + "/refresh/", json={"username": "toto", "password": "tata"} )
) self.assertEqual(resp.status_code, 400, "bad status code returned")
assert resp.status_code == 200, "bad status code returned" self.assertIsNotNone(resp.json(), "response data must not be empty")
assert resp.json() is not None, "response data can't be empty" self.assertEqual(
assert resp.json()["status"] == "ok", "bad status returned" resp.json()["error"],
"the incoming request is not valid",
"invalid error message returned",
def test_bad_target(): )
resp = requests.post(URL + "/token/", json={"username": "toto", "password": "tata"})
assert resp.status_code == 400, "bad status code returned"
assert resp.text == "", "response data must be empty"