2022-03-12 18:51:46 +00:00
//! 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.
2023-07-17 12:18:03 +01:00
use super ::{ KVHeader , generate_kvheader , Url , wrap_url , unwrap_url } ;
2022-03-12 18:51:46 +00:00
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 ,
2023-07-17 12:18:03 +01:00
pub location : Url ,
2022-03-12 18:51:46 +00:00
pub version : String ,
pub headers : Vec < KVHeader > ,
pub body : String ,
}
/// A structure for HTTP/1.x responses.
/// Generated with packeteer::http1::generate_response.
#[ derive(Debug) ]
pub struct Http1Response {
pub version : String ,
pub status : String ,
pub headers : Vec < KVHeader > ,
pub body : String ,
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 ) ;
2023-07-17 12:18:03 +01:00
let request = Http1Request { method : method . to_string ( ) , location : wrap_url ( & format! ( " https://root/ {} " , location ) ) , version : " 1.1 " . to_string ( ) , headers : vec ! [ hostkv ] , body : body . to_string ( ) } ;
2022-03-12 18:51:46 +00:00
return request
}
/// A function for generating Http1Response structures.
/// By default, packeteer generates HTTP/1.1 responses.
2022-03-13 11:36:10 +00:00
/// Invalid status codes will automatically be turned into code 500 (Internal Server Error)
2022-03-12 18:51:46 +00:00
pub fn generate_response ( code : i32 , body : & str ) -> Http1Response {
let mut xcode = code ;
if code_to_string ( xcode ) = = " 500 Internal Server Error " {
xcode = 500 ;
}
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 " ) ;
2023-07-17 12:18:03 +01:00
let mut request = Http1Request { method : " " . to_string ( ) , location : wrap_url ( " https://example.com " ) , version : " " . to_string ( ) , headers : vec ! [ ] , body : " " . to_string ( ) } ;
2022-03-12 18:51:46 +00:00
let mut reachedbody = false ;
for v in split {
if reachedbody ! = true {
if v . contains ( " HTTP/1.1 " ) {
let split1 : Vec < & str > = v . split ( " " ) . collect ( ) ;
request . method = split1 [ 0 ] . to_string ( ) ;
2023-07-17 12:18:03 +01:00
request . location = wrap_url ( & format! ( " https://root/ {} " , split1 [ 1 ] . to_string ( ) ) ) ;
2022-03-12 18:51:46 +00:00
request . version = " 1.1 " . to_string ( ) ;
} else if v . contains ( " HTTP/1.0 " ) {
let split1 : Vec < & str > = v . split ( " " ) . collect ( ) ;
request . method = split1 [ 0 ] . to_string ( ) ;
2023-07-17 12:18:03 +01:00
request . location = wrap_url ( & format! ( " https://root/ {} " , split1 [ 1 ] . to_string ( ) ) ) ;
2022-03-12 18:51:46 +00:00
request . version = " 1.0 " . to_string ( ) ;
} else if v = = " " {
reachedbody = true ;
} else {
let split1 : Vec < & str > = v . split ( " : " ) . collect ( ) ;
let header = generate_kvheader ( split1 [ 0 ] , split1 [ 1 ] ) ;
request . headers . push ( header )
}
} else {
request . body = v . to_string ( ) ;
}
}
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.
2022-03-13 11:36:10 +00:00
/// Responses passed here with invalid status codes will keep their invalid status code.
2022-03-12 18:51:46 +00:00
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 } ;
let mut reachedbody = false ;
for v in split {
if reachedbody ! = true {
if v . contains ( " HTTP/1.1 " ) {
let split1 : Vec < & str > = v . split ( " " ) . collect ( ) ;
let split2 : Vec < & str > = v . split ( " HTTP/1.1 " ) . collect ( ) ;
response . version = " 1.1 " . to_string ( ) ;
let n : i32 = FromStr ::from_str ( split1 [ 1 ] ) . unwrap ( ) ;
response . status = split2 [ 1 ] . to_string ( ) ;
response . code = n ;
} else if v . contains ( " HTTP/1.0 " ) {
let split1 : Vec < & str > = v . split ( " " ) . collect ( ) ;
let split2 : Vec < & str > = v . split ( " HTTP/1.0 " ) . collect ( ) ;
response . version = " 1.0 " . to_string ( ) ;
let n : i32 = FromStr ::from_str ( split1 [ 1 ] ) . unwrap ( ) ;
response . status = split2 [ 1 ] . to_string ( ) ;
response . code = n ;
} else if v = = " " {
reachedbody = true ;
} else {
let split1 : Vec < & str > = v . split ( " : " ) . collect ( ) ;
let header = generate_kvheader ( split1 [ 0 ] , split1 [ 1 ] ) ;
response . headers . push ( header )
}
} else {
response . body = v . to_string ( ) ;
}
}
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 {
2023-07-17 12:18:03 +01:00
let mut unpacked = format! ( " {} {} HTTP/ {} \r \n " , request . method , unwrap_url ( request . location ) . replace ( " https://root/ " , " / " ) , request . version ) ;
2022-03-12 18:51:46 +00:00
for header in request . headers {
unpacked = format! ( " {} {} : {} \r \n " , unpacked , header . key , header . value ) ;
}
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 {
unpacked = format! ( " {} {} : {} \r \n " , unpacked , header . key , header . value ) ;
}
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 < 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 {
match code {
100 = > " 100 Continue " . to_string ( ) ,
101 = > " 101 Switching Protocols " . to_string ( ) ,
102 = > " 102 Processing " . to_string ( ) ,
103 = > " 103 Early Hints " . to_string ( ) ,
200 = > " 200 OK " . to_string ( ) ,
201 = > " 201 Created " . to_string ( ) ,
202 = > " 202 Accepted " . to_string ( ) ,
203 = > " 203 Non-Authoritative Information " . to_string ( ) ,
204 = > " 204 No Content " . to_string ( ) ,
205 = > " 205 Reset Content " . to_string ( ) ,
206 = > " 206 Partial Content " . to_string ( ) ,
207 = > " 207 Multi-Status " . to_string ( ) ,
208 = > " 208 Already Reported " . to_string ( ) ,
226 = > " 226 IM Used " . to_string ( ) ,
300 = > " 300 Multiple Choices " . to_string ( ) ,
301 = > " 301 Moved Permanently " . to_string ( ) ,
302 = > " 302 Found " . to_string ( ) ,
303 = > " 303 See Other " . to_string ( ) ,
304 = > " 304 Not Modified " . to_string ( ) ,
305 = > " 305 Use Proxy " . to_string ( ) ,
306 = > " 306 Switch Proxy " . to_string ( ) ,
307 = > " 307 Temporary Redirect " . to_string ( ) ,
308 = > " 308 Permanent Redirect " . to_string ( ) ,
400 = > " 400 Bad Request " . to_string ( ) ,
401 = > " 401 Unauthorized " . to_string ( ) ,
402 = > " 402 Payment Required " . to_string ( ) ,
403 = > " 403 Forbidden " . to_string ( ) ,
404 = > " 404 Not Found " . to_string ( ) ,
405 = > " 405 Method Not Allowed " . to_string ( ) ,
406 = > " 406 Not Acceptable " . to_string ( ) ,
407 = > " 407 Proxy Authentication Required " . to_string ( ) ,
408 = > " 408 Request Timeout " . to_string ( ) ,
409 = > " 409 Conflict " . to_string ( ) ,
410 = > " 410 Gone " . to_string ( ) ,
411 = > " 411 Length Required " . to_string ( ) ,
412 = > " 412 Precondition Failed " . to_string ( ) ,
413 = > " 413 Payload Too Large " . to_string ( ) ,
414 = > " 414 URI Too Long " . to_string ( ) ,
415 = > " 415 Unsupported Media Type " . to_string ( ) ,
416 = > " 416 Range Not Satisfiable " . to_string ( ) ,
417 = > " 417 Expectation Failed " . to_string ( ) ,
418 = > " 418 I'm a teapot " . to_string ( ) ,
421 = > " 421 Misdirected Request " . to_string ( ) ,
422 = > " 422 Unprocessable Entity " . to_string ( ) ,
423 = > " 423 Locked " . to_string ( ) ,
424 = > " 424 Failed Dependency " . to_string ( ) ,
425 = > " 425 Too Early " . to_string ( ) ,
426 = > " 426 Upgrade Required " . to_string ( ) ,
428 = > " 428 Precondition Required " . to_string ( ) ,
429 = > " 429 Too Many Requests " . to_string ( ) ,
431 = > " 431 Request Header Fields Too Large " . to_string ( ) ,
451 = > " 451 Unavailable For Legal Reasons " . to_string ( ) ,
500 = > " 500 Internal Server Error " . to_string ( ) ,
501 = > " 501 Not Implemented " . to_string ( ) ,
502 = > " 502 Bad Gateway " . to_string ( ) ,
503 = > " 503 Service Unavailable " . to_string ( ) ,
504 = > " 504 Gateway Timeout " . to_string ( ) ,
505 = > " 505 HTTP Version Not Supported " . to_string ( ) ,
506 = > " 506 Variant Also Negotiates " . to_string ( ) ,
507 = > " 507 Insufficient Storage " . to_string ( ) ,
508 = > " 508 Loop Detected " . to_string ( ) ,
510 = > " 510 Not Extended " . to_string ( ) ,
511 = > " 511 Network Authentication Required " . to_string ( ) ,
_ = > " 500 Internal Server Error " . to_string ( ) ,
}
}