feat: #2 impl an HTTPResponse + update tests
This commit is contained in:
		
							parent
							
								
									35cc483774
								
							
						
					
					
						commit
						2c7418f333
					
				| @ -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
									
								
							
							
						
						
									
										98
									
								
								src/handlers/response.rs
									
									
									
									
									
										Normal 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 | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -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(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -17,7 +17,7 @@ do | |||||||
| 		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 | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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): | ||||||
| def test_validate_target(): |  | ||||||
|         resp = requests.post( |         resp = requests.post( | ||||||
|             URL + "/validate/", json={"username": "toto", "password": "tata"} |             URL + "/validate/", 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_refresh_target(self): | ||||||
| def test_refresh_target(): |  | ||||||
|         resp = requests.post( |         resp = requests.post( | ||||||
|             URL + "/refresh/", 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_bad_target(): |         resp = requests.post( | ||||||
|     resp = requests.post(URL + "/token/", json={"username": "toto", "password": "tata"}) |             URL + "/token/", json={"username": "toto", "password": "tata"} | ||||||
|     assert resp.status_code == 400, "bad status code returned" |         ) | ||||||
|     assert resp.text == "", "response data must be empty" |         self.assertEqual(resp.status_code, 400, "bad status code returned") | ||||||
|  |         self.assertIsNotNone(resp.json(), "response data must not be empty") | ||||||
|  |         self.assertEqual( | ||||||
|  |             resp.json()["error"], | ||||||
|  |             "the incoming request is not valid", | ||||||
|  |             "invalid error message returned", | ||||||
|  |         ) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user