Introduce http1 (up/down)grading and add human-readable documentation.

This commit is contained in:
abbie 2022-03-12 18:01:21 +00:00
parent 47084d8abe
commit 854b52db9c
No known key found for this signature in database
GPG key ID: 04DDE463F9200F87
2 changed files with 62 additions and 1 deletions

View file

@ -1,7 +1,7 @@
[package] [package]
name = "packeteer" name = "packeteer"
description = "An attempt at a Rust library that can be used to assist in programmatically analysing, serving and handling received protocol packets." 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" edition = "2021"
authors = ["Celeste <colean@colean.cc>"] authors = ["Celeste <colean@colean.cc>"]
license = "LGPL-3.0-or-later" license = "LGPL-3.0-or-later"

View file

@ -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)] #[derive(Debug)]
pub struct KVHeader { pub struct KVHeader {
pub key: String, pub key: String,
pub value: 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 { pub fn generate_kvheader(key: &str, value: &str) -> KVHeader {
let result = KVHeader { key: key.to_string(), value: value.to_string() }; let result = KVHeader { key: key.to_string(), value: value.to_string() };
return result 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")] #[cfg(feature = "http1")]
pub mod http1 { pub mod http1 {
use super::{KVHeader, generate_kvheader}; use super::{KVHeader, generate_kvheader};
use std::str::FromStr; use std::str::FromStr;
/// A structure for HTTP/1.x requests.
/// Generated with packeteer::http1::generate_request.
#[derive(Debug)] #[derive(Debug)]
pub struct Http1Request { pub struct Http1Request {
pub method: String, pub method: String,
@ -22,6 +36,8 @@ pub mod http1 {
pub headers: Vec<KVHeader>, pub headers: Vec<KVHeader>,
pub body: String, pub body: String,
} }
/// A structure for HTTP/1.x responses.
/// Generated with packeteer::http1::generate_response.
#[derive(Debug)] #[derive(Debug)]
pub struct Http1Response { pub struct Http1Response {
pub version: String, pub version: String,
@ -31,11 +47,15 @@ pub mod http1 {
pub code: i32, 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 { pub fn generate_request(method: &str, host: &str, location: &str, body: &str) -> Http1Request {
let hostkv = generate_kvheader("Host", host); 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() }; 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 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 { pub fn generate_response(code: i32, body: &str) -> Http1Response {
let mut xcode = code; let mut xcode = code;
if code_to_string(xcode) == "500 Internal Server Error" { 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 }; let response = Http1Response { version: "1.1".to_string(), status: code_to_string(xcode), headers: vec![], body: body.to_string(), code: xcode };
return response 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 { pub fn construct_request(request: &str) -> Http1Request {
let split = request.split("\r\n"); let split = request.split("\r\n");
let mut request = Http1Request { method: "".to_string(), location: "".to_string(), version: "".to_string(), headers: vec![], body: "".to_string() }; 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 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 { pub fn construct_response(response: &str) -> Http1Response {
let split = response.split("\r\n"); let split = response.split("\r\n");
let mut response = Http1Response { version: "".to_string(), status: "".to_string(), headers: vec![], body: "".to_string(), code: 0 }; 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 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 { pub fn unpack_request(request: Http1Request) -> String {
let mut unpacked = format!("{} {} HTTP/{}\r\n", request.method, request.location, request.version); let mut unpacked = format!("{} {} HTTP/{}\r\n", request.method, request.location, request.version);
for header in request.headers { for header in request.headers {
@ -114,6 +140,8 @@ pub mod http1 {
unpacked = format!("{}\r\n{}",unpacked,request.body); unpacked = format!("{}\r\n{}",unpacked,request.body);
return unpacked; 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 { pub fn unpack_response(response: Http1Response) -> String {
let mut unpacked = format!("HTTP/{} {}\r\n", response.version, response.status); let mut unpacked = format!("HTTP/{} {}\r\n", response.version, response.status);
for header in response.headers { for header in response.headers {
@ -122,7 +150,40 @@ pub mod http1 {
unpacked = format!("{}\r\n{}",unpacked,response.body); unpacked = format!("{}\r\n{}",unpacked,response.body);
return unpacked; 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<usize> = 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 { fn code_to_string(code: i32) -> String {
match code { match code {
100 => "100 Continue".to_string(), 100 => "100 Continue".to_string(),