@@ -4,6 +4,7 @@ use reqwest;
4
4
use reqwest:: header:: { HeaderMap , HeaderValue , ACCEPT , CONTENT_TYPE } ;
5
5
use serde_json;
6
6
use std:: path:: PathBuf ;
7
+ use std:: str:: FromStr ;
7
8
8
9
#[ derive( GraphQLQuery ) ]
9
10
#[ graphql(
@@ -18,6 +19,7 @@ pub fn introspect_schema(
18
19
location : & str ,
19
20
output : Option < PathBuf > ,
20
21
authorization : Option < String > ,
22
+ headers : Vec < Header > ,
21
23
) -> Result < ( ) , failure:: Error > {
22
24
use std:: io:: Write ;
23
25
@@ -35,6 +37,14 @@ pub fn introspect_schema(
35
37
let client = reqwest:: Client :: new ( ) ;
36
38
37
39
let mut req_builder = client. post ( location) . headers ( construct_headers ( ) ) ;
40
+
41
+ for custom_header in headers {
42
+ req_builder = req_builder. header (
43
+ custom_header. name . as_str ( ) ,
44
+ custom_header. value . as_str ( ) ,
45
+ ) ;
46
+ }
47
+
38
48
if let Some ( token) = authorization {
39
49
req_builder = req_builder. bearer_auth ( token. as_str ( ) ) ;
40
50
} ;
@@ -60,3 +70,82 @@ fn construct_headers() -> HeaderMap {
60
70
headers. insert ( ACCEPT , HeaderValue :: from_static ( "application/json" ) ) ;
61
71
headers
62
72
}
73
+
74
+ #[ derive( Debug , PartialEq ) ]
75
+ pub struct Header {
76
+ name : String ,
77
+ value : String ,
78
+ }
79
+
80
+ impl FromStr for Header {
81
+ type Err = failure:: Error ;
82
+
83
+ fn from_str ( input : & str ) -> Result < Self , Self :: Err > {
84
+ // error: colon required for name/value pair
85
+ if ! input. contains ( ":" ) {
86
+ return Err ( format_err ! ( "Invalid header input. A colon is required to separate the name and value. [{}]" , input) ) ;
87
+ }
88
+
89
+ // split on first colon and trim whitespace from name and value
90
+ let name_value: Vec < & str > = input. splitn ( 2 , ':' ) . collect ( ) ;
91
+ let name = name_value[ 0 ] . trim ( ) ;
92
+ let value = name_value[ 1 ] . trim ( ) ;
93
+
94
+ // error: field name must be
95
+ if name. len ( ) == 0 {
96
+ return Err ( format_err ! ( "Invalid header input. Field name is required before colon. [{}]" , input) ) ;
97
+ }
98
+
99
+ // error: no whitespace in field name
100
+ if name. split_whitespace ( ) . count ( ) > 1 {
101
+ return Err ( format_err ! ( "Invalid header input. Whitespace not allowed in field name. [{}]" , input) ) ;
102
+ }
103
+
104
+ Ok ( Self { name : name. to_string ( ) , value : value. to_string ( ) } )
105
+ }
106
+ }
107
+
108
+ #[ cfg( test) ]
109
+ mod tests {
110
+ use super :: * ;
111
+
112
+ #[ test]
113
+ fn it_errors_invalid_headers ( ) {
114
+ // https://tools.ietf.org/html/rfc7230#section-3.2
115
+
116
+ for input in vec ! [
117
+ "X-Name Value" , // error: colon required for name/value pair
118
+ ": Value" , // error: field name must be
119
+ "X Name: Value" , // error: no whitespace in field name
120
+ "X\t Name: Value" , // error: no whitespace in field name (tab)
121
+ ] {
122
+ let header = Header :: from_str ( input) ;
123
+
124
+ assert ! ( header. is_err( ) , "Expected error: [{}]" , input) ;
125
+ }
126
+ }
127
+
128
+ #[ test]
129
+ fn it_parses_valid_headers ( ) {
130
+ // https://tools.ietf.org/html/rfc7230#section-3.2
131
+
132
+ let expected1 = Header { name : "X-Name" . to_string ( ) , value : "Value" . to_string ( ) } ;
133
+ let expected2 = Header { name : "X-Name" . to_string ( ) , value : "Value:" . to_string ( ) } ;
134
+
135
+ for ( input, expected) in vec ! [
136
+ ( "X-Name: Value" , & expected1) , // ideal
137
+ ( "X-Name:Value" , & expected1) , // no optional whitespace
138
+ ( "X-Name: Value " , & expected1) , // with optional whitespace
139
+ ( "X-Name:\t Value" , & expected1) , // with optional whitespace (tab)
140
+ ( "X-Name: Value:" , & expected2) , // with colon in value
141
+ // not allowed per RFC, but we'll forgive
142
+ ( "X-Name : Value" , & expected1) ,
143
+ ( " X-Name: Value" , & expected1) ,
144
+ ] {
145
+ let header = Header :: from_str ( input) ;
146
+
147
+ assert ! ( header. is_ok( ) , "Expected ok: [{}]" , input) ;
148
+ assert_eq ! ( header. unwrap( ) , * expected, "Expected equality: [{}]" , input) ;
149
+ }
150
+ }
151
+ }
0 commit comments