challenge: 2
This commit is contained in:
parent
480fb7a744
commit
2eb4135c71
@ -4,7 +4,8 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = "0.7.4"
|
axum = { version = "0.7.4", features = ["query"] }
|
||||||
|
serde = { version = "1.0.215", features = ["derive"] }
|
||||||
shuttle-axum = "0.49.0"
|
shuttle-axum = "0.49.0"
|
||||||
shuttle-runtime = "0.49.0"
|
shuttle-runtime = "0.49.0"
|
||||||
tokio = "1.28.2"
|
tokio = "1.28.2"
|
||||||
|
84
src/lib.rs
84
src/lib.rs
@ -1,7 +1,7 @@
|
|||||||
mod routes;
|
mod routes;
|
||||||
|
|
||||||
pub use routes::hello_world;
|
pub use routes::hello_world;
|
||||||
use routes::{hello_bird, minus_one};
|
use routes::{hello_bird, ipv4_dest, ipv4_key, ipv6_dest, ipv6_key, minus_one};
|
||||||
|
|
||||||
pub fn router() -> axum::Router {
|
pub fn router() -> axum::Router {
|
||||||
use axum::routing::get;
|
use axum::routing::get;
|
||||||
@ -10,6 +10,10 @@ pub fn router() -> axum::Router {
|
|||||||
Router::new()
|
Router::new()
|
||||||
.route("/hello_world", get(hello_world))
|
.route("/hello_world", get(hello_world))
|
||||||
.route("/-1/seek", get(minus_one))
|
.route("/-1/seek", get(minus_one))
|
||||||
|
.route("/2/dest", get(ipv4_dest))
|
||||||
|
.route("/2/key", get(ipv4_key))
|
||||||
|
.route("/2/v6/dest", get(ipv6_dest))
|
||||||
|
.route("/2/v6/key", get(ipv6_key))
|
||||||
.route("/", get(hello_bird))
|
.route("/", get(hello_bird))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,4 +53,82 @@ mod test {
|
|||||||
response.assert_header("location", "https://www.youtube.com/watch?v=9Gc4QTqslN4");
|
response.assert_header("location", "https://www.youtube.com/watch?v=9Gc4QTqslN4");
|
||||||
response.assert_status(StatusCode::FOUND);
|
response.assert_status(StatusCode::FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ipv4_dest() {
|
||||||
|
let server = test_server();
|
||||||
|
|
||||||
|
let response = server.get("/2/dest?from=10.0.0.0&key=1.2.3.255").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text("11.2.3.255");
|
||||||
|
|
||||||
|
let response = server.get("/2/dest?from=invalid-from&key=1.2.3.255").await;
|
||||||
|
response.assert_status_bad_request();
|
||||||
|
|
||||||
|
let response = server.get("/2/dest?from=10.0.0.0&key=invalid-key").await;
|
||||||
|
response.assert_status_bad_request();
|
||||||
|
|
||||||
|
let response = server
|
||||||
|
.get("/2/dest?from=128.128.33.0&key=255.0.255.33")
|
||||||
|
.await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text("127.128.32.33");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ipv4_key() {
|
||||||
|
let server = test_server();
|
||||||
|
|
||||||
|
let response = server.get("/2/key?from=10.0.0.0&to=11.2.3.255").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text("1.2.3.255");
|
||||||
|
|
||||||
|
let response = server.get("/2/key?from=invalid-from&to=1.2.3.255").await;
|
||||||
|
response.assert_status_bad_request();
|
||||||
|
|
||||||
|
let response = server.get("/2/key?from=10.0.0.0&to=invalid-to").await;
|
||||||
|
response.assert_status_bad_request();
|
||||||
|
|
||||||
|
let response = server
|
||||||
|
.get("/2/key?from=128.128.33.0&to=127.128.32.33")
|
||||||
|
.await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text("255.0.255.33");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ipv6_dest() {
|
||||||
|
let server = test_server();
|
||||||
|
|
||||||
|
let response = server.get("/2/v6/dest?from=fe80::1&key=5:6:7::3333").await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text("fe85:6:7::3332");
|
||||||
|
|
||||||
|
let response = server
|
||||||
|
.get("/2/v6/dest?from=invalid-from&key=5:6:7::3333")
|
||||||
|
.await;
|
||||||
|
response.assert_status_bad_request();
|
||||||
|
|
||||||
|
let response = server.get("/2/v6/dest?from=fe80::1&key=invalid-key").await;
|
||||||
|
response.assert_status_bad_request();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ipv6_key() {
|
||||||
|
let server = test_server();
|
||||||
|
|
||||||
|
let response = server
|
||||||
|
.get("/2/v6/key?from=aaaa::aaaa&to=5555:ffff:c:0:0:c:1234:5555")
|
||||||
|
.await;
|
||||||
|
response.assert_status_ok();
|
||||||
|
response.assert_text("ffff:ffff:c::c:1234:ffff");
|
||||||
|
|
||||||
|
let response = server
|
||||||
|
.get("/2/v6/dest?from=invalid-from&to=5:6:7::3333")
|
||||||
|
.await;
|
||||||
|
response.assert_status_bad_request();
|
||||||
|
|
||||||
|
let response = server.get("/2/v6/dest?from=fe80::1&to=invalid-to").await;
|
||||||
|
response.assert_status_bad_request();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
mod hello_bird;
|
mod hello_bird;
|
||||||
mod hello_world;
|
mod hello_world;
|
||||||
mod minus_one;
|
mod minus_one;
|
||||||
|
mod task_two;
|
||||||
|
|
||||||
pub use hello_bird::hello_bird;
|
pub use hello_bird::hello_bird;
|
||||||
pub use hello_world::hello_world;
|
pub use hello_world::hello_world;
|
||||||
pub use minus_one::minus_one;
|
pub use minus_one::minus_one;
|
||||||
|
pub use task_two::ipv4_dest;
|
||||||
|
pub use task_two::ipv4_key;
|
||||||
|
pub use task_two::ipv6_dest;
|
||||||
|
pub use task_two::ipv6_key;
|
||||||
|
37
src/routes/task_two/ipv4_dest.rs
Normal file
37
src/routes/task_two/ipv4_dest.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#![allow(dead_code, clippy::unused_async)]
|
||||||
|
|
||||||
|
use std::{net::Ipv4Addr, str::FromStr};
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
extract::Query,
|
||||||
|
http::StatusCode,
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::DestParams;
|
||||||
|
|
||||||
|
pub async fn ipv4_dest(params: Query<DestParams>) -> Result<String, Response> {
|
||||||
|
let params: DestParams = params.0;
|
||||||
|
let Ok(from) = Ipv4Addr::from_str(¶ms.from) else {
|
||||||
|
return Err((StatusCode::BAD_REQUEST, "Invalid from IP address").into_response());
|
||||||
|
};
|
||||||
|
let Ok(key) = Ipv4Addr::from_str(¶ms.key) else {
|
||||||
|
return Err((StatusCode::BAD_REQUEST, "Invalid key IP address").into_response());
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(calculate_ipv4_dest(from, key))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_ipv4_dest(from: Ipv4Addr, key: Ipv4Addr) -> String {
|
||||||
|
let result: Vec<u8> = from
|
||||||
|
.octets()
|
||||||
|
.iter()
|
||||||
|
.zip(key.octets().iter())
|
||||||
|
.map(|(a, b)| a.overflowing_add(*b).0)
|
||||||
|
.collect();
|
||||||
|
result
|
||||||
|
.iter()
|
||||||
|
.map(std::string::ToString::to_string)
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(".")
|
||||||
|
}
|
36
src/routes/task_two/ipv4_key.rs
Normal file
36
src/routes/task_two/ipv4_key.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#![allow(dead_code, clippy::unused_async)]
|
||||||
|
|
||||||
|
use std::{net::Ipv4Addr, str::FromStr};
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
extract::Query,
|
||||||
|
http::StatusCode,
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::KeyParams;
|
||||||
|
|
||||||
|
pub async fn ipv4_key(params: Query<KeyParams>) -> Result<String, Response> {
|
||||||
|
let params: KeyParams = params.0;
|
||||||
|
let Ok(from) = Ipv4Addr::from_str(¶ms.from) else {
|
||||||
|
return Err((StatusCode::BAD_REQUEST, "Invalid from IP address").into_response());
|
||||||
|
};
|
||||||
|
let Ok(to) = Ipv4Addr::from_str(¶ms.to) else {
|
||||||
|
return Err((StatusCode::BAD_REQUEST, "Invalid key IP address").into_response());
|
||||||
|
};
|
||||||
|
Ok(calculate_ipv4_key(from, to))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_ipv4_key(from: Ipv4Addr, to: Ipv4Addr) -> String {
|
||||||
|
let result: Vec<u8> = to
|
||||||
|
.octets()
|
||||||
|
.iter()
|
||||||
|
.zip(from.octets().iter())
|
||||||
|
.map(|(a, b)| a.overflowing_sub(*b).0)
|
||||||
|
.collect();
|
||||||
|
result
|
||||||
|
.iter()
|
||||||
|
.map(std::string::ToString::to_string)
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(".")
|
||||||
|
}
|
37
src/routes/task_two/ipv6_dest.rs
Normal file
37
src/routes/task_two/ipv6_dest.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#![allow(dead_code, clippy::unused_async)]
|
||||||
|
|
||||||
|
use std::{net::Ipv6Addr, str::FromStr};
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
extract::Query,
|
||||||
|
http::StatusCode,
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::DestParams;
|
||||||
|
|
||||||
|
pub async fn ipv6_dest(params: Query<DestParams>) -> Result<String, Response> {
|
||||||
|
let params: DestParams = params.0;
|
||||||
|
let Ok(from) = Ipv6Addr::from_str(¶ms.from) else {
|
||||||
|
return Err((StatusCode::BAD_REQUEST, "Invalid from IP address").into_response());
|
||||||
|
};
|
||||||
|
let Ok(key) = Ipv6Addr::from_str(¶ms.key) else {
|
||||||
|
return Err((StatusCode::BAD_REQUEST, "Invalid key IP address").into_response());
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(calculate_ipv6_dest(from, key))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_ipv6_dest(from: Ipv6Addr, key: Ipv6Addr) -> String {
|
||||||
|
let result: Vec<String> = from
|
||||||
|
.segments()
|
||||||
|
.iter()
|
||||||
|
.zip(key.segments().iter())
|
||||||
|
.map(|(a, b)| a ^ b)
|
||||||
|
.map(|s| format!("{s:x}"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let ip: Ipv6Addr = result.join(":").parse().unwrap();
|
||||||
|
|
||||||
|
ip.to_string()
|
||||||
|
}
|
37
src/routes/task_two/ipv6_key.rs
Normal file
37
src/routes/task_two/ipv6_key.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#![allow(dead_code, clippy::unused_async)]
|
||||||
|
|
||||||
|
use std::{net::Ipv6Addr, str::FromStr};
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
extract::Query,
|
||||||
|
http::StatusCode,
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::KeyParams;
|
||||||
|
|
||||||
|
pub async fn ipv6_key(params: Query<KeyParams>) -> Result<String, Response> {
|
||||||
|
let params: KeyParams = params.0;
|
||||||
|
let Ok(from) = Ipv6Addr::from_str(¶ms.from) else {
|
||||||
|
return Err((StatusCode::BAD_REQUEST, "Invalid from IP address").into_response());
|
||||||
|
};
|
||||||
|
let Ok(to) = Ipv6Addr::from_str(¶ms.to) else {
|
||||||
|
return Err((StatusCode::BAD_REQUEST, "Invalid key IP address").into_response());
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(calculate_ipv6_key(from, to))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_ipv6_key(from: Ipv6Addr, to: Ipv6Addr) -> String {
|
||||||
|
let result: Vec<String> = to
|
||||||
|
.segments()
|
||||||
|
.iter()
|
||||||
|
.zip(from.segments().iter())
|
||||||
|
.map(|(a, b)| a ^ b)
|
||||||
|
.map(|s| format!("{s:x}"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let ip: Ipv6Addr = result.join(":").parse().unwrap();
|
||||||
|
|
||||||
|
ip.to_string()
|
||||||
|
}
|
72
src/routes/task_two/mod.rs
Normal file
72
src/routes/task_two/mod.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#![allow(dead_code, clippy::unused_async)]
|
||||||
|
|
||||||
|
mod ipv4_dest;
|
||||||
|
mod ipv4_key;
|
||||||
|
mod ipv6_dest;
|
||||||
|
mod ipv6_key;
|
||||||
|
|
||||||
|
pub use ipv4_dest::ipv4_dest;
|
||||||
|
pub use ipv4_key::ipv4_key;
|
||||||
|
pub use ipv6_dest::ipv6_dest;
|
||||||
|
pub use ipv6_key::ipv6_key;
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct DestParams {
|
||||||
|
from: String,
|
||||||
|
key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct KeyParams {
|
||||||
|
from: String,
|
||||||
|
to: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use ipv4_dest::calculate_ipv4_dest;
|
||||||
|
use ipv4_key::calculate_ipv4_key;
|
||||||
|
use ipv6_dest::calculate_ipv6_dest;
|
||||||
|
use ipv6_key::calculate_ipv6_key;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calculate_ipv4_dest() {
|
||||||
|
let from = Ipv4Addr::from_str("10.0.0.0").unwrap();
|
||||||
|
let key = Ipv4Addr::from_str("1.2.3.255").unwrap();
|
||||||
|
assert_eq!(calculate_ipv4_dest(from, key), "11.2.3.255");
|
||||||
|
|
||||||
|
let from = Ipv4Addr::from_str("128.128.33.0").unwrap();
|
||||||
|
let key = Ipv4Addr::from_str("255.0.255.33").unwrap();
|
||||||
|
assert_eq!(calculate_ipv4_dest(from, key), "127.128.32.33");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calculate_ipv4_key() {
|
||||||
|
let from = Ipv4Addr::from_str("10.0.0.0").unwrap();
|
||||||
|
let to = Ipv4Addr::from_str("11.2.3.255").unwrap();
|
||||||
|
assert_eq!(calculate_ipv4_key(from, to), "1.2.3.255");
|
||||||
|
|
||||||
|
let from = Ipv4Addr::from_str("128.128.33.0").unwrap();
|
||||||
|
let to = Ipv4Addr::from_str("127.128.32.33").unwrap();
|
||||||
|
assert_eq!(calculate_ipv4_key(from, to), "255.0.255.33");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calculate_ipv6_dest() {
|
||||||
|
let from = Ipv6Addr::from_str("fe80::1").unwrap();
|
||||||
|
let key = Ipv6Addr::from_str("5:6:7::3333").unwrap();
|
||||||
|
assert_eq!(calculate_ipv6_dest(from, key), "fe85:6:7::3332");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calculate_ipv6_key() {
|
||||||
|
let from = Ipv6Addr::from_str("aaaa::aaaa").unwrap();
|
||||||
|
let to = Ipv6Addr::from_str("5555:ffff:c:0:0:c:1234:5555").unwrap();
|
||||||
|
assert_eq!(calculate_ipv6_key(from, to), "ffff:ffff:c::c:1234:ffff");
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user