@@ -6,6 +6,7 @@ use crate::{
66 stream:: { Stream , ThreadReceive , ThreadSend } ,
77 uri:: Uri ,
88} ;
9+ use base64:: engine:: { general_purpose:: URL_SAFE , Engine } ;
910use std:: {
1011 convert:: TryFrom ,
1112 fmt,
@@ -15,6 +16,7 @@ use std::{
1516 thread,
1617 time:: { Duration , Instant } ,
1718} ;
19+ use zeroize:: { Zeroize , ZeroizeOnDrop , Zeroizing } ;
1820
1921const CR_LF : & str = "\r \n " ;
2022const DEFAULT_REDIRECT_LIMIT : usize = 5 ;
@@ -85,6 +87,79 @@ impl fmt::Display for HttpVersion {
8587 }
8688}
8789
90+ /// Authentication details:
91+ /// - Basic: username and password
92+ /// - Bearer: token
93+ #[ derive( Debug , PartialEq , Zeroize , ZeroizeOnDrop ) ]
94+ pub struct Authentication ( AuthenticationType ) ;
95+
96+ impl Authentication {
97+ /// Creates a new `Authentication` of type `Basic`.
98+ pub fn basic < T , U > ( username : & T , password : & U ) -> Authentication
99+ where
100+ T : ToString + ?Sized ,
101+ U : ToString + ?Sized ,
102+ {
103+ Authentication ( AuthenticationType :: Basic {
104+ username : username. to_string ( ) ,
105+ password : password. to_string ( ) ,
106+ } )
107+ }
108+
109+ /// Creates a new `Authentication` of type `Bearer`
110+ pub fn bearer < T > ( token : & T ) -> Authentication
111+ where
112+ T : ToString + ?Sized ,
113+ {
114+ Authentication ( AuthenticationType :: Bearer ( token. to_string ( ) ) )
115+ }
116+
117+ /// Generates a HTTP Authorization header. Returns `key` & `value` pair.
118+ /// - Basic: uses base64 encoding on provided credentials
119+ /// - Bearer: uses token as is
120+ pub fn header ( & self ) -> ( String , String ) {
121+ let key = "Authorization" . to_string ( ) ;
122+ let val = String :: with_capacity ( 200 ) + self . 0 . scheme ( ) + " " + & self . 0 . credentials ( ) ;
123+
124+ ( key, val)
125+ }
126+ }
127+
128+ /// Authentication types
129+ #[ derive( Debug , PartialEq , Zeroize , ZeroizeOnDrop ) ]
130+ enum AuthenticationType {
131+ Basic { username : String , password : String } ,
132+ Bearer ( String ) ,
133+ }
134+
135+ impl AuthenticationType {
136+ /// Returns scheme
137+ const fn scheme ( & self ) -> & str {
138+ use AuthenticationType :: * ;
139+
140+ match self {
141+ Basic {
142+ username : _,
143+ password : _,
144+ } => "Basic" ,
145+ Bearer ( _) => "Bearer" ,
146+ }
147+ }
148+
149+ /// Returns encoded credentials
150+ fn credentials ( & self ) -> Zeroizing < String > {
151+ use AuthenticationType :: * ;
152+
153+ match self {
154+ Basic { username, password } => {
155+ let credentials = Zeroizing :: new ( username. to_string ( ) + ":" + password) ;
156+ Zeroizing :: new ( URL_SAFE . encode ( credentials. as_bytes ( ) ) )
157+ }
158+ Bearer ( token) => Zeroizing :: new ( token. to_string ( ) ) ,
159+ }
160+ }
161+ }
162+
88163/// Allows to control redirects
89164#[ derive( Debug , PartialEq , Clone , Copy ) ]
90165pub enum RedirectPolicy < F > {
@@ -258,6 +333,29 @@ impl<'a> RequestMessage<'a> {
258333 self
259334 }
260335
336+ /// Adds an authorization header to existing headers
337+ ///
338+ /// # Examples
339+ /// ```
340+ /// use std::convert::TryFrom;
341+ /// use http_req::{request::{RequestMessage, Authentication}, response::Headers, uri::Uri};
342+ ///
343+ /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
344+ ///
345+ /// let request_msg = RequestMessage::new(&addr)
346+ /// .authentication(Authentication::bearer("secret456token123"));
347+ /// ```
348+ pub fn authentication < T > ( & mut self , auth : T ) -> & mut Self
349+ where
350+ Authentication : From < T > ,
351+ {
352+ let auth = Authentication :: from ( auth) ;
353+ let ( key, val) = auth. header ( ) ;
354+
355+ self . headers . insert_raw ( key, val) ;
356+ self
357+ }
358+
261359 /// Sets the body for request
262360 ///
263361 /// # Examples
@@ -456,6 +554,26 @@ impl<'a> Request<'a> {
456554 self
457555 }
458556
557+ /// Adds an authorization header to existing headers.
558+ ///
559+ /// # Examples
560+ /// ```
561+ /// use std::convert::TryFrom;
562+ /// use http_req::{request::{RequestMessage, Authentication}, response::Headers, uri::Uri};
563+ ///
564+ /// let addr = Uri::try_from("https://www.rust-lang.org/learn").unwrap();
565+ ///
566+ /// let request_msg = RequestMessage::new(&addr)
567+ /// .authentication(Authentication::bearer("secret456token123"));
568+ /// ```
569+ pub fn authentication < T > ( & mut self , auth : T ) -> & mut Self
570+ where
571+ Authentication : From < T > ,
572+ {
573+ self . messsage . authentication ( auth) ;
574+ self
575+ }
576+
459577 /// Sets the body for request.
460578 ///
461579 /// # Examples
@@ -784,6 +902,43 @@ mod tests {
784902 assert_eq ! ( & format!( "{}" , METHOD ) , "HEAD" ) ;
785903 }
786904
905+ #[ test]
906+ fn authentication_basic ( ) {
907+ let auth = Authentication :: basic ( "user" , "password123" ) ;
908+ assert_eq ! (
909+ auth,
910+ Authentication ( AuthenticationType :: Basic {
911+ username: "user" . to_string( ) ,
912+ password: "password123" . to_string( )
913+ } )
914+ ) ;
915+ }
916+
917+ #[ test]
918+ fn authentication_baerer ( ) {
919+ let auth = Authentication :: bearer ( "456secret123token" ) ;
920+ assert_eq ! (
921+ auth,
922+ Authentication ( AuthenticationType :: Bearer ( "456secret123token" . to_string( ) ) )
923+ ) ;
924+ }
925+
926+ #[ test]
927+ fn authentication_header ( ) {
928+ {
929+ let auth = Authentication :: basic ( "user" , "password123" ) ;
930+ let ( key, val) = auth. header ( ) ;
931+ assert_eq ! ( key, "Authorization" . to_string( ) ) ;
932+ assert_eq ! ( val, "Basic dXNlcjpwYXNzd29yZDEyMw==" . to_string( ) ) ;
933+ }
934+ {
935+ let auth = Authentication :: bearer ( "456secret123token" ) ;
936+ let ( key, val) = auth. header ( ) ;
937+ assert_eq ! ( key, "Authorization" . to_string( ) ) ;
938+ assert_eq ! ( val, "Bearer 456secret123token" . to_string( ) ) ;
939+ }
940+ }
941+
787942 #[ test]
788943 fn request_m_new ( ) {
789944 RequestMessage :: new ( & Uri :: try_from ( URI ) . unwrap ( ) ) ;
@@ -831,6 +986,24 @@ mod tests {
831986 assert_eq ! ( req. headers, expect_headers) ;
832987 }
833988
989+ #[ test]
990+ fn request_m_authentication ( ) {
991+ let uri = Uri :: try_from ( URI ) . unwrap ( ) ;
992+ let mut req = RequestMessage :: new ( & uri) ;
993+ let token = "456secret123token" ;
994+ let k = "Authorization" ;
995+ let v = "Bearer " . to_string ( ) + token;
996+
997+ let mut expect_headers = Headers :: new ( ) ;
998+ expect_headers. insert ( "Host" , "doc.rust-lang.org" ) ;
999+ expect_headers. insert ( "User-Agent" , "http_req/0.13.0" ) ;
1000+ expect_headers. insert ( k, & v) ;
1001+
1002+ let req = req. authentication ( Authentication :: bearer ( token) ) ;
1003+
1004+ assert_eq ! ( req. headers, expect_headers) ;
1005+ }
1006+
8341007 #[ test]
8351008 fn request_m_body ( ) {
8361009 let uri = Uri :: try_from ( URI ) . unwrap ( ) ;
0 commit comments