From 854b52db9c6c652e38cd086f6b00ab54597f5793 Mon Sep 17 00:00:00 2001 From: Celeste Date: Sat, 12 Mar 2022 18:01:21 +0000 Subject: [PATCH] Introduce http1 (up/down)grading and add human-readable documentation. --- Cargo.toml | 2 +- src/lib.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c35f811..c98c6de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "packeteer" description = "An attempt at a Rust library that can be used to assist in programmatically analysing, serving and handling received protocol packets." -version = "0.1.1" +version = "0.1.2" edition = "2021" authors = ["Celeste "] license = "LGPL-3.0-or-later" diff --git a/src/lib.rs b/src/lib.rs index 7afbd86..5c07eca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,19 +1,33 @@ +//! A packet manipulation, generation, unpackination, and constructionation station. +//! +//! In all seriousness, packeteer is a crate made to assist with programmatically analysing, serving and handling received protocol packets. +//! This is great for servers, clients, proxies as well as packet sniffers, capturers and analysers. +//! Packeteer doesn't implement any ability to send or receive requests/responses nor does it handle streams or threads for you and is instead supposed to be used in applications to structure packets and potentially do automated operations on them as defined by packeteer modules. + +/// A global structure for key value headers. +/// Can be automatically generated with the function packeteer::generate_kvheader. #[derive(Debug)] pub struct KVHeader { pub key: String, pub value: String, } +/// A public function for generating a KVHeader structure from two string references representing the key and value. pub fn generate_kvheader(key: &str, value: &str) -> KVHeader { let result = KVHeader { key: key.to_string(), value: value.to_string() }; return result } +/// The module handling HTTP/1.0 and HTTP/1.1 operations. +/// It contains structures for HTTP/1.x requests and responses as well as functions for generating, unpacking and constructing them. +/// It can also upgrade and downgrade HTTP/1.x requests. #[cfg(feature = "http1")] pub mod http1 { use super::{KVHeader, generate_kvheader}; use std::str::FromStr; + /// A structure for HTTP/1.x requests. + /// Generated with packeteer::http1::generate_request. #[derive(Debug)] pub struct Http1Request { pub method: String, @@ -22,6 +36,8 @@ pub mod http1 { pub headers: Vec, pub body: String, } + /// A structure for HTTP/1.x responses. + /// Generated with packeteer::http1::generate_response. #[derive(Debug)] pub struct Http1Response { pub version: String, @@ -31,11 +47,15 @@ pub mod http1 { pub code: i32, } + /// A function for generating Http1Request structures. + /// By default, packeteer generates HTTP/1.1 requests. pub fn generate_request(method: &str, host: &str, location: &str, body: &str) -> Http1Request { let hostkv = generate_kvheader("Host", host); let request = Http1Request { method: method.to_string(), location: location.to_string(), version: "1.1".to_string(), headers: vec![hostkv], body: body.to_string() }; return request } + /// A function for generating Http1Response structures. + /// By default, packeteer generates HTTP/1.1 responses. pub fn generate_response(code: i32, body: &str) -> Http1Response { let mut xcode = code; if code_to_string(xcode) == "500 Internal Server Error" { @@ -44,6 +64,8 @@ pub mod http1 { let response = Http1Response { version: "1.1".to_string(), status: code_to_string(xcode), headers: vec![], body: body.to_string(), code: xcode }; return response } + /// A function for converting HTTP/1.x request strings into Http1Request. + /// Construction functions aim to be as transparent as possible so apart from turning it into a structure, the request stays relatively unchanged. pub fn construct_request(request: &str) -> Http1Request { let split = request.split("\r\n"); let mut request = Http1Request { method: "".to_string(), location: "".to_string(), version: "".to_string(), headers: vec![], body: "".to_string() }; @@ -73,6 +95,8 @@ pub mod http1 { } return request } + /// A function for converting HTTP/1.x response strings into Http1Response. + /// Construction functions aim to be as transparent as possible so apart from turning it into a structure, the response stays relatively unchanged. pub fn construct_response(response: &str) -> Http1Response { let split = response.split("\r\n"); let mut response = Http1Response { version: "".to_string(), status: "".to_string(), headers: vec![], body: "".to_string(), code: 0 }; @@ -106,6 +130,8 @@ pub mod http1 { } return response } + /// A function for converting Http1Request structures into HTTP/1.x request strings. + /// Unpacking functions are simply taking the data stored in the structure and concatenating it into a string so nothing changes apart from the conversion. pub fn unpack_request(request: Http1Request) -> String { let mut unpacked = format!("{} {} HTTP/{}\r\n", request.method, request.location, request.version); for header in request.headers { @@ -114,6 +140,8 @@ pub mod http1 { unpacked = format!("{}\r\n{}",unpacked,request.body); return unpacked; } + /// A function for converting Http1Response structures into HTTP/1.x response strings. + /// Unpacking functions are simply taking the data stored in the structure and concatenating it into a string so nothing changes apart from the conversion. pub fn unpack_response(response: Http1Response) -> String { let mut unpacked = format!("HTTP/{} {}\r\n", response.version, response.status); for header in response.headers { @@ -122,7 +150,40 @@ pub mod http1 { unpacked = format!("{}\r\n{}",unpacked,response.body); return unpacked; } + /// A function for downgrading request structures to HTTP/1.0, this is destructive as it removes the keep-alive and Host headers for compatibility. + /// I don't recommend using this unless you have a specific purpose for downgrading (like accommodating strict HTTP/1.0 clients). + pub fn downgrade_request(mut request: Http1Request) -> Http1Request { + let mut i: usize = 0; + let mut earmark: Vec = vec![]; + for header in &request.headers { + if header.key == "Connection" && header.value == "keep-alive" { + earmark.push(i); + } + if header.key == "Host" { + earmark.push(i); + } + i = i + 1 + } + for i in earmark { + request.headers.remove(i); + } + request.version = "1.0".to_string(); + return request + } + /// A function for upgrading request structures to HTTP/1.1, this function doesn't remove any headers. + /// It adds on Host and keep-alive headers at user request. + pub fn upgrade_request(mut request: Http1Request, host: &str, keepalive: bool) -> Http1Request { + if keepalive { + request.headers.push(generate_kvheader("Connection", "keep-alive")); + } + if host != "" { + request.headers.push(generate_kvheader("Host", host)); + } + request.version = "1.1".to_string(); + return request + } + /// A lookup table-like function for switching i32 codes for their human-readable string equivalent. fn code_to_string(code: i32) -> String { match code { 100 => "100 Continue".to_string(),