mod sv_web {
use std::io::{Read, Write};
use std::convert::From;
#[derive(PartialEq,Copy,Clone)]
pub enum RequestType
{
GET,
HEAD,
POST,
PUT,
TRACE,
OPTIONS,
DELETE,
NONE
}
impl From<&str> for RequestType
{
fn from(raw_data : &str) -> Self {
match raw_data
{
"GET"=>RequestType::GET ,
"HEAD"=>RequestType::HEAD ,
"POST"=>RequestType::POST ,
"PUT"=>RequestType::PUT ,
"OPTIONS"=>RequestType::OPTIONS ,
"TRACE"=>RequestType::TRACE ,
"DELETE"=>RequestType::DELETE,
_ => RequestType::NONE
}
}
}
impl From<RequestType> for &str
{
fn from(raw_data : RequestType) -> Self {
match raw_data
{
RequestType::GET=>"GET",
RequestType::HEAD =>"HEAD",
RequestType::POST=>"POST",
RequestType::PUT=>"PUT",
RequestType::OPTIONS=>"OPTIONS",
RequestType::TRACE=>"TRACE",
RequestType::DELETE=>"DELETE",
_ =>"NONE"
}
}
}
#[derive(PartialEq,Copy,Clone)]
pub enum ProtocolType
{
NONE,
HTTP10,
HTTP11,
HTTP2
}
impl From<&str> for ProtocolType
{
fn from(raw_data : &str) -> Self {
match raw_data
{
"HTTP/1.1"=>ProtocolType::HTTP11,
"HTTP/1.0"=>ProtocolType::HTTP10,
"HTTP/2.0"|"HTTP/2"=>ProtocolType::HTTP2,
_=>ProtocolType::NONE,
}
}
}
impl From<ProtocolType> for &str
{
fn from(raw_data : ProtocolType) -> Self {
match raw_data
{
ProtocolType::HTTP11=>"HTTP/1.1",
ProtocolType::HTTP10=>"HTTP/1.0",
ProtocolType::HTTP2=>"HTTP/2",
_=>"NONE",
}
}
}
pub struct Header
{
tag :String,
data:String
}
impl Header {
pub fn new(one : String, two : String) -> Header{
Header {tag: one,data:two}
}
}
pub type HeadStructure=std::vec::Vec<Header>;
pub struct Request
{
client: bool ,
protocol : ProtocolType,
service : String,
request_type : RequestType,
header : HeadStructure,
message : String
}
impl Request
{
pub fn new(from_serv : bool ,prot : ProtocolType,serv : String , r_type : RequestType, h : HeadStructure, mess : String) -> Request
{
Request { client : from_serv ,protocol : prot,service: serv, request_type : r_type, header : h , message : mess}
}
}
impl From<&str> for Request {
fn from(raw_data : &str) -> Self {
let end_mark = "\r\n\r\n";
let top_part = match raw_data.find(end_mark){
None => return Request::new(false, ProtocolType::NONE,"/".to_string(),RequestType::NONE, HeadStructure::new(),"".to_string()) ,
Some(top_part)=> top_part
} ;
let temp = &raw_data[0..top_part].chars().filter(|&c| c!='\r').collect::<String>();
let mut lines =temp.lines();
let mut top = lines.nth(0).unwrap().split_whitespace();
let first = top.nth(0).unwrap_or("NONE");
let service = top.nth(0).unwrap_or("NONE");
let second = top.nth(0).unwrap_or("NONE");
let client :bool= !first.starts_with("HTTP");
let rt : RequestType = if client {first.into()} else {second.into()};
let protocol : ProtocolType = if client {second.into()} else {first.into()};
let mut head = HeadStructure::new();
for (_i,j) in lines.enumerate().skip(0) {
let k = j.split(':');
let mut str1 = String::new();
let mut str2 = String::new();
for( n , l) in k.enumerate() {
let kuma = l.chars().filter(|x| !x.is_whitespace()).collect();
match n {
0=> str1=kuma,
1=> str2=kuma,
_=>break
}
}
head.push(Header { tag : str1, data :str2});
}
Request::new(client,protocol, service.to_string(), rt, head, raw_data[top_part+end_mark.len()..].to_string())
}
}
pub fn match_service_to_string(what :i16) -> String
{
match what {
100=>"Continue".to_string(),
101=>"Switching Protocols".to_string(),
102=>"Processing".to_string(),
103=>"Early Hints".to_string(),
200=>"Ok".to_string(),
201=>"Created".to_string(),
202=>"Accepted".to_string(),
203=>"Non-Authoritative Information".to_string(),
204=>"No Content".to_string(),
205=>"Reset Content".to_string(),
206=>"Partial Content".to_string(),
207=>"Multi-Status".to_string(),
208=>"Already Reported".to_string(),
226=>"IM Used".to_string(),
300=>"Multiple Choices".to_string(),
301=>"Moved Permanently".to_string(),
302=>"Found".to_string(),
303=>"See Other".to_string(),
304=>"Not Modified".to_string(),
305=>"Use Proxy".to_string(),
306=>"Switch Proxy".to_string(),
307=>"Temporary Redirect".to_string(),
308=>"Permanent Redirect".to_string(),
400=>"Bad Request".to_string(),
401=>"Unauthorized".to_string(),
402=>"Payment Required".to_string(),
403=>"Forbidden".to_string(),
404=>"Not Found".to_string(),
405=>"Method Not Allowed".to_string(),
406=>"Not Acceptable".to_string(),
407=>"Proxy Authentication Required".to_string(),
408=>"Request Timeout".to_string(),
409=>"Conflict".to_string(),
410=>"Gone".to_string(),
411=>"Length Required".to_string(),
412=>"Precondition Failed".to_string(),
413=>"Payload Too Large".to_string(),
414=>"URI Too Long".to_string(),
415=>"Unsupported Media Type".to_string(),
416=>"Range Not Satisfiable".to_string(),
417=>"Expectation Failed".to_string(),
418=>"I'm a teapot".to_string(),
421=>"Misdirected Request".to_string(),
422=>"Unprocessable Entity".to_string(),
423=>"Locked".to_string(),
424=>"Failed Dependency".to_string(),
425=>"Too Early".to_string(),
426=>"Upgrade Required".to_string(),
428=>"Precondition Required".to_string(),
429=>"Too Many Requests".to_string(),
431=>"Request Header Fields Too Large".to_string(),
451=>"Unavailable For Legal Reasons".to_string(),
_=>"NONE".to_string()
}
}
impl From<Request> for String {
fn from(raw_data : Request) -> Self {
let mut base :String = String::new();
let basea : &str = match raw_data.client
{
true=> raw_data.request_type.into(),
_=>raw_data.protocol.into(),
};
base.push_str(basea);
base.push(' ');
base.push_str(&raw_data.service);
base.push(' ');
let temp1 = if !raw_data.client {
let temp :i16 = raw_data.service.parse::<i16>().unwrap_or(200);
match_service_to_string(temp)
}
else
{
let a :&str= raw_data.protocol.into();
String::from(a)
};
base.push_str(&temp1);
base.push_str("\r\n");
for head in raw_data.header
{
base.push_str(&head.tag);
base.push_str(": ");
base.push_str(&head.data);
base.push_str("\r\n");
}
base.push_str("\r\n\r\n");
base.push_str(&raw_data.message);
base
}
}
pub enum SvError
{
FailToConnect
}
pub struct HtmlClient
{
stream : std::net::TcpStream,
}
impl HtmlClient
{
pub fn new(hostname :&str) -> Result<HtmlClient,SvError>
{
let s = std::net::TcpStream::connect(hostname);
match s
{
Ok(s)=>Ok(HtmlClient {stream: s}),
_=>Err(SvError::FailToConnect)
}
}
}
pub fn get(url : &str,rtype : RequestType) ->Result<Request,SvError> {
let expr = "://";
let pos = match url.find(expr){
None=>0,
Some(n)=>n+expr.len()
};
let no_protocol = &url[pos..];
let protocol_https:bool = match &url[0..pos] {
"https" => true,
_=>false
};
let _max =no_protocol.len();
let fin = '/';
let pos2 :usize = no_protocol.find(fin).unwrap_or(_max);
let done :bool = pos2 ==_max;
let mut hostname : String = String::from(&no_protocol[..pos2]);
let destination = match done {
true=> "/",
_=>&no_protocol[pos2..]
};
let done: &str = match hostname.find(':')
{
None=> match protocol_https {
true=>":443",
_=>":80"
},
Some(_)=> ""
};
hostname.push_str(done);
let mut what = match HtmlClient::new(&hostname.to_string()){
Ok(html_client) => html_client,
Err(sv_error)=> return Err(sv_error)
};
let mut headers = HeadStructure::new( );
headers.push(Header::new("Host".to_string(),hostname.to_string()));
headers.push(Header::new("Accept".to_string(),"*/*".to_string()));
headers.push(Header::new("User-Agent".to_string(),"Rust".to_string()));
let r_to_send : Request = Request::new(true, ProtocolType::HTTP11,destination.to_string(), rtype, headers,"".to_string());
let bu :String = r_to_send.into();
let _stat = match what.stream.write(bu.as_bytes()){
Ok(n)=>n,
_=> return Err(SvError::FailToConnect)
};
let buffer = &mut[0;8000];
let _r = match what.stream.read(buffer)
{
Ok(n)=> n,
_=> return Err(SvError::FailToConnect)
} ;
Ok(Request::from(std::str::from_utf8(buffer).unwrap_or("NONE")))
}
}