mod writer;
mod sender;
mod file_response;
pub use file_response::*;
pub use writer::*;
pub use sender::*;
#[doc(hidden)]
pub struct HeaderResponseBuilder {
first_line: FirstLine,
data:Vec<u8>,
}
impl HeaderResponseBuilder {
pub fn custom(
first_line: FirstLine)->Self
{
let data = Vec::with_capacity(1024);
Self {
first_line,
data
}
}
pub fn to_bytes(&self)->Vec<u8>{
let mut f = self.first_line.to_bytes();
f.extend_from_slice(&self.data);
f.extend_from_slice(b"\r\n");
f
}
pub fn set_header_key_value(&mut self,key:impl std::fmt::Display,value:impl std::fmt::Display){
self.data
.extend_from_slice(
format!("{key}: {value}\r\n").as_bytes()
);
}
pub fn switching_protocols_headers()->Self{
Self::custom(
FirstLine {
http_version: HttpVersion::Http1,
status: HttpStatus {
code: 101,
value: "Switching Protocols".to_string()
}
}
)
}
pub fn required_h2_protocol_headers()->Self{
Self::custom(
FirstLine {
http_version: HttpVersion::Http1,
status: HttpStatus {
code: 426,
value: "Upgrade Required".to_string()
}
}
)
}
pub fn temporary_redirect_header(url:&str)->Self{
let mut headers = Self::custom(
FirstLine {
http_version: HttpVersion::Http1,
status: HttpStatus {
code: 307,
value: "Internal Redirect".to_string()
}
}
);
headers.set_header_key_value("Location",url);
headers
}
pub fn permanent_redirect_header(url:&str)->Self{
let mut headers = Self::custom(
FirstLine {
http_version: HttpVersion::Http1,
status: HttpStatus {
code: 301,
value: "Permanently Redirect".to_string()
}
}
);
headers.set_header_key_value("Location",url);
headers
}
pub fn found_redirect_header(url:&str)->Self{
let mut headers = Self::custom(
FirstLine {
http_version: HttpVersion::Http1,
status: HttpStatus {
code: 302,
value: "Found Redirect".to_string()
}
}
);
headers.set_header_key_value("Location",url);
headers
}
pub fn required_h2()->Self {
let mut headers = Self::required_h2_protocol_headers();
headers.set_header_key_value("connection","Upgrade");
headers.set_header_key_value("Upgrade","h2c");
headers
}
pub fn switch_to_h2c_headers()->Self {
let mut headers = Self::switching_protocols_headers();
headers.set_header_key_value("connection","Upgrade");
headers.set_header_key_value("Upgrade","h2c");
headers.set_header_key_value("Content-Length","0");
headers
}
pub fn bad_request_headers()->Self{
Self::custom(
FirstLine{
http_version:HttpVersion::Http1_1,
status:HttpStatus { code: 400 , value: "Bad Request".to_owned() },
}
)
}
pub fn not_found_headers()->Self{
Self::custom(
FirstLine{
http_version:HttpVersion::Http1_1,
status:HttpStatus { code: 404 , value: "Not Found".to_owned() },
}
)
}
pub fn change_first_line(&mut self,first_line: FirstLine){
self.first_line = first_line;
}
pub fn change_first_line_to_partial_content(&mut self){
self.change_first_line(FirstLine{
http_version:HttpVersion::Http1_1,
status:HttpStatus { code: 206 , value: "Partial".to_owned() },
});
}
pub fn success_partial_content()->Self{
Self::custom(
FirstLine{
http_version:HttpVersion::Http1_1,
status:HttpStatus { code: 206 , value: "Partial".to_owned() },
}
)
}
pub fn success()->Self{
Self::custom(
FirstLine{
http_version:HttpVersion::Http1_1,
status:HttpStatus { code: 200 , value: "OK".to_owned() },
}
)
}
}
pub enum HttpVersion {
Http1,
Http1_1,
Http2,
Http3
}
impl HttpVersion {
pub const fn to_bytes(&self)->&[u8]{
self.to_str().as_bytes()
}
pub const fn to_str(&self)->&str{
match self {
HttpVersion::Http1 => {"HTTP/1.0"}
HttpVersion::Http1_1 => {"HTTP/1.1"}
HttpVersion::Http2 => {"HTTP/2"}
HttpVersion::Http3 => {"HTTP/3"}
}
}
}
pub struct HttpStatus {
pub code:u16,
pub value:String
}
pub struct FirstLine {
pub http_version:HttpVersion,
pub status:HttpStatus
}
impl FirstLine {
pub fn to_bytes(&self)->Vec<u8>{
let mut bytes = Vec::with_capacity(1024);
let version =
match self.http_version {
HttpVersion::Http1 => { "HTTP/1.0" }
HttpVersion::Http1_1 => { "HTTP/1.1" }
HttpVersion::Http2 => { "HTTP/2" }
HttpVersion::Http3 => { "HTTP/3" }
}.as_bytes();
bytes.extend_from_slice(version);
bytes.extend_from_slice(format!(" {} {}\r\n",self.status.code,self.status.value).as_bytes());
bytes
}
}
#[doc(hidden)]
pub struct BodyResponseBuilder<'a> {
mechanism:HandlingResponseMechanism<'a>,
}
impl <'a> BodyResponseBuilder<'a> {
pub fn new(mechanism:HandlingResponseMechanism<'a>)->Self{
Self{
mechanism
}
}
}
#[doc(hidden)]
pub enum HandlingResponseMechanism<'a> {
File(&'a[&'a str]),
NormalResponse(&'a[u8]),
None
}
#[doc(hidden)]
pub struct ResponseBuilder<'a>{
pub headers:HeaderResponseBuilder,
body:BodyResponseBuilder<'a>
}
impl <'a> ResponseBuilder<'a> {
pub fn empty()->ResponseBuilder<'a>{
Self{
headers:HeaderResponseBuilder::success(),
body:BodyResponseBuilder::new(HandlingResponseMechanism::NormalResponse(
b""
))
}
}
pub fn from_bytes_response(bytes:&'a [u8])->ResponseBuilder<'a>{
let mut headers = HeaderResponseBuilder::success();
headers.set_header_key_value("Content-Length",bytes.len());
let body =
BodyResponseBuilder::new(
HandlingResponseMechanism::NormalResponse(
bytes
)
);
Self {
headers,
body
}
}
pub fn from_str(str:&'a str)->ResponseBuilder<'a>{
let res = Self::from_bytes_response(str.as_bytes());
res
}
pub fn from_string_ref(data:&'a String)->ResponseBuilder<'a>{ Self::from_bytes_response(data.as_bytes())}
pub fn to_bytes(&self)->Option<Vec<u8>>{
let this = self;
let mut res = this.headers.first_line.to_bytes();
res.extend_from_slice(this.headers.data.as_slice());
match &self.body.mechanism {
HandlingResponseMechanism::NormalResponse(bytes)=>{
res.extend_from_slice(b"\r\n");
res.extend_from_slice(*bytes);
Some(res)
}
HandlingResponseMechanism::File(_files)=>{
None
}
_ => {
None
}
}
}
}