1+ use std:: { collections:: HashMap , fmt:: { write, Display } , hash:: Hash , io, result, str:: FromStr } ;
2+
3+ use super :: response:: HttpResponse ;
4+
5+ #[ derive( Debug ) ]
6+ pub struct HttpRequest {
7+ method : Method ,
8+ pub resource : Resource ,
9+ version : Version ,
10+ headers : HttpHeader ,
11+ pub request_body : String ,
12+ }
13+
14+ impl HttpRequest {
15+ pub fn response ( & self ) -> io:: Result < HttpResponse > {
16+ HttpResponse :: new ( self )
17+ }
18+ pub fn new ( request : & str ) -> io:: Result < HttpRequest > {
19+ let method: Method = Method :: new ( request) ;
20+ let resource: Resource = if let Some ( resource) = Resource :: new ( request) {
21+ resource
22+ } else {
23+ Resource {
24+ path : "" . to_string ( ) ,
25+ }
26+ } ;
27+ let version: Version = Version :: new ( request)
28+ . map_err ( |err| io:: Error :: new ( io:: ErrorKind :: InvalidData , err. msg ) ) ?;
29+
30+ let headers: HttpHeader = if let Some ( headers) = HttpHeader :: new ( request) {
31+ headers
32+ } else {
33+ HttpHeader {
34+ headers : HashMap :: new ( ) ,
35+ }
36+ } ;
37+
38+ let request_body: String = if let Some ( ( _header, body) ) = request. split_once ( "\r \n \r \n " ) {
39+ body. to_string ( )
40+ } else {
41+ String :: new ( )
42+ } ;
43+
44+ Ok ( HttpRequest {
45+ method,
46+ resource,
47+ version,
48+ headers,
49+ request_body,
50+ } )
51+ }
52+ }
53+
54+
55+ #[ derive( Debug ) ]
56+ struct HttpHeader {
57+ headers : HashMap < String , String > ,
58+ }
59+
60+ impl HttpHeader {
61+ pub fn new ( request : & str ) -> Option < HttpHeader > {
62+ let mut httpheader = HttpHeader {
63+ headers : HashMap :: new ( ) ,
64+ } ;
65+ let ( _, header_str) = request. split_once ( "\r \n " ) ?;
66+
67+ for line in header_str. split_terminator ( "\r \n " ) {
68+ if line. is_empty ( ) {
69+ break ;
70+ }
71+ let ( header, value) = line. split_once ( ":" ) ?;
72+ httpheader
73+ . headers
74+ . insert ( header. trim ( ) . to_string ( ) , value. trim ( ) . to_string ( ) ) ;
75+ }
76+ Some ( httpheader)
77+ }
78+ }
79+
80+ #[ derive( Debug ) ]
81+ pub enum Version {
82+ V1_1 ,
83+ V2_0 ,
84+ }
85+
86+ impl Display for Version {
87+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
88+ let msg = match self {
89+ Version :: V1_1 => "HTTP/1.1" ,
90+ Version :: V2_0 => "HTTP/2" ,
91+ } ;
92+ write ! ( f, "{}" , msg)
93+ }
94+ }
95+ pub struct VersionError {
96+ msg : String ,
97+ }
98+
99+ impl Display for VersionError {
100+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
101+ write ! ( f, "{}" , self . msg)
102+ }
103+ }
104+
105+ impl Version {
106+ pub fn new ( request : & str ) -> Result < Self , VersionError > {
107+ Version :: from_str ( request)
108+ }
109+ }
110+ impl FromStr for Version {
111+ type Err = VersionError ;
112+
113+ fn from_str ( request : & str ) -> Result < Self , Self :: Err > {
114+ let request_split = request. split_once ( "\r \n " ) ;
115+ if let Some ( ( metho_line, _rest) ) = request_split {
116+ let splits = metho_line. split_ascii_whitespace ( ) ;
117+ for split in splits {
118+ if split == "HTTP/1.1" {
119+ return Ok ( Version :: V1_1 ) ;
120+ } else if split == "HTTP/2" || split == "HTTP/2.0" {
121+ return Ok ( Version :: V2_0 ) ;
122+ } ;
123+ }
124+ } ;
125+
126+ let invalid = format ! ( "Unknown protocol version in {}" , request) ;
127+ let version_error = VersionError { msg : invalid } ;
128+ Err ( version_error)
129+ }
130+ }
131+
132+ #[ derive( Debug ) ]
133+ enum Method {
134+ Get ,
135+ Post ,
136+ Uninitialised ,
137+ }
138+
139+ impl Method {
140+ pub fn new ( request : & str ) -> Method {
141+ let request_split = request. split_once ( "\r \n " ) ;
142+ if let Some ( ( method_line, _rest) ) = request_split {
143+ let method_line: Option < ( & str , & str ) > = method_line. split_once ( ' ' ) ;
144+ if let Some ( ( method, _rest) ) = method_line {
145+ return match method {
146+ "GET" => Method :: Get ,
147+ "POST" => Method :: Post ,
148+ _ => Method :: Uninitialised ,
149+ } ;
150+ } ;
151+ } ;
152+ Method :: Uninitialised
153+ }
154+ pub fn identify ( s : & str ) -> Method {
155+ match s {
156+ "GET" => Method :: Get ,
157+ "POST" => Method :: Post ,
158+ _ => Method :: Uninitialised ,
159+ }
160+ }
161+ }
162+
163+ #[ derive( Debug ) ]
164+ pub struct Resource {
165+ pub path : String ,
166+ }
167+
168+ impl Resource {
169+ pub fn new ( request : & str ) -> Option < Resource > {
170+ if let Some ( ( request_method, _) ) = request. split_once ( "\r \n " ) {
171+ let ( method, rest) = request_method. split_once ( ' ' ) ?;
172+ return match Method :: identify ( method) {
173+ Method :: Get | Method :: Post => {
174+ let ( resource, _protocol_version) = rest. split_once ( ' ' ) ?;
175+ let resource = resource. trim ( ) ;
176+ let resource = resource. trim_start_matches ( '/' ) ;
177+ return Some ( Resource {
178+ path : resource. to_string ( ) ,
179+ } ) ;
180+ }
181+ Method :: Uninitialised => None ,
182+ } ;
183+ } ;
184+ None
185+ }
186+ }
0 commit comments