11use std:: {
22 env,
33 fs:: File ,
4+ io,
45 io:: { Write , stdout} ,
56 net:: { TcpStream , ToSocketAddrs } ,
67 process,
78} ;
89
9- use http:: { HttpMethod , HttpRequest } ;
10+ use http:: { HttpMethod , HttpRequest , HttpResponse } ;
1011mod config;
1112use config:: ClientConfig ;
1213
14+ use crate :: config:: HttpType ;
15+
1316fn open_file ( fname : & str ) -> Box < dyn Write > {
1417 Box :: new ( File :: create ( fname) . unwrap_or_else ( |_| {
1518 eprintln ! ( "Couldn't create file: {fname}" ) ;
1619 process:: exit ( 1 ) ;
1720 } ) )
1821}
1922
23+ #[ cfg( not( feature = "tls" ) ) ]
24+ #[ inline]
25+ fn send_request (
26+ tcp : TcpStream ,
27+ _http_type : HttpType ,
28+ _host : String ,
29+ req : HttpRequest ,
30+ ) -> http:: Result < HttpResponse > {
31+ req. send_to ( tcp)
32+ }
33+
34+ #[ cfg( feature = "tls" ) ]
35+ fn send_request (
36+ tcp : TcpStream ,
37+ http_type : HttpType ,
38+ host : String ,
39+ req : HttpRequest ,
40+ ) -> http:: Result < HttpResponse > {
41+ use std:: sync:: Arc ;
42+
43+ if matches ! ( http_type, HttpType :: Https ) {
44+ let root_store = rustls:: RootCertStore {
45+ roots : webpki_roots:: TLS_SERVER_ROOTS . into ( ) ,
46+ } ;
47+ let mut config = rustls:: ClientConfig :: builder ( )
48+ . with_root_certificates ( root_store)
49+ . with_no_client_auth ( ) ;
50+
51+ config. key_log = Arc :: new ( rustls:: KeyLogFile :: new ( ) ) ;
52+
53+ let conn =
54+ rustls:: ClientConnection :: new ( Arc :: new ( config) , host. try_into ( ) . unwrap ( ) ) . unwrap ( ) ;
55+ let tls = rustls:: StreamOwned :: new ( conn, tcp) ;
56+ req. send_to ( tls)
57+ } else {
58+ req. send_to ( tcp)
59+ }
60+ }
61+
2062pub fn main ( ) -> http:: Result < ( ) > {
2163 let conf = ClientConfig :: parse ( env:: args ( ) . skip ( 1 ) ) . unwrap_or_else ( |err| {
2264 eprintln ! ( "ERROR: {err}" ) ;
@@ -34,19 +76,21 @@ pub fn main() -> http::Result<()> {
3476 . url
3577 . split ( '/' )
3678 . filter ( |s| !s. is_empty ( ) )
37- . last ( )
79+ . next_back ( )
3880 . unwrap_or ( & conf. host ) ;
3981 open_file ( fname)
4082 }
4183 } ;
4284
4385 let req = HttpRequest :: builder ( )
44- . method ( HttpMethod :: GET )
45- . url ( conf. url )
86+ . method ( conf . method )
87+ . url ( conf. url . clone ( ) . into_boxed_str ( ) )
4688 . version ( 1.1 )
47- . header ( "Host" , conf. host )
89+ . header ( "Host" , conf. host . clone ( ) . into_boxed_str ( ) )
4890 . header ( "Accept" , "*/*" )
4991 . header ( "User-Agent" , "http-client" )
92+ . header ( "Connection" , "close" )
93+ . header ( "Accept-Encoding" , "identity" )
5094 . build ( )
5195 . unwrap ( ) ;
5296
@@ -57,14 +101,32 @@ pub fn main() -> http::Result<()> {
57101 process:: exit ( 1 ) ;
58102 }
59103 } ;
60- let mut result = req. send_to ( tcp) . unwrap_or_else ( |err| {
61- eprint ! ( "ERROR: {err}" ) ;
62- process:: exit ( 1 ) ;
63- } ) ;
104+
105+ let mut result =
106+ send_request ( tcp, conf. http_type , conf. host . clone ( ) , req) . unwrap_or_else ( |err| {
107+ eprint ! ( "ERROR: {err}" ) ;
108+ process:: exit ( 1 ) ;
109+ } ) ;
110+
111+ if matches ! ( conf. method, HttpMethod :: HEAD ) {
112+ println ! ( "Headers" ) ;
113+ for ( k, v) in result. headers ( ) {
114+ println ! ( "{k}: {v}" ) ;
115+ }
116+ return Ok ( ( ) ) ;
117+ }
64118
65119 match result. write_to ( & mut out) {
66120 Ok ( _) => { /* eprintln!("\n\n{n} bytes transfered") */ }
67- Err ( err) => eprintln ! ( "\n \n ERROR: {err}" ) ,
121+ Err ( err) => {
122+ match err. kind ( ) {
123+ /* Ignore this error kind
124+ * https://docs.rs/rustls/latest/rustls/manual/_03_howto/index.html#unexpected-eof
125+ * */
126+ io:: ErrorKind :: UnexpectedEof => { }
127+ _ => eprintln ! ( "\n \n ERROR: {err}" ) ,
128+ }
129+ }
68130 }
69131
70132 Ok ( ( ) )
0 commit comments