@@ -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,11 @@ 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 ( custom_header. name . as_str ( ) , custom_header. value . as_str ( ) ) ;
43
+ }
44
+
38
45
if let Some ( token) = authorization {
39
46
req_builder = req_builder. bearer_auth ( token. as_str ( ) ) ;
40
47
} ;
@@ -60,3 +67,100 @@ fn construct_headers() -> HeaderMap {
60
67
headers. insert ( ACCEPT , HeaderValue :: from_static ( "application/json" ) ) ;
61
68
headers
62
69
}
70
+
71
+ #[ derive( Debug , PartialEq ) ]
72
+ pub struct Header {
73
+ name : String ,
74
+ value : String ,
75
+ }
76
+
77
+ impl FromStr for Header {
78
+ type Err = failure:: Error ;
79
+
80
+ fn from_str ( input : & str ) -> Result < Self , Self :: Err > {
81
+ // error: colon required for name/value pair
82
+ if !input. contains ( ":" ) {
83
+ return Err ( format_err ! (
84
+ "Invalid header input. A colon is required to separate the name and value. [{}]" ,
85
+ input
86
+ ) ) ;
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 ! (
97
+ "Invalid header input. Field name is required before colon. [{}]" ,
98
+ input
99
+ ) ) ;
100
+ }
101
+
102
+ // error: no whitespace in field name
103
+ if name. split_whitespace ( ) . count ( ) > 1 {
104
+ return Err ( format_err ! (
105
+ "Invalid header input. Whitespace not allowed in field name. [{}]" ,
106
+ input
107
+ ) ) ;
108
+ }
109
+
110
+ Ok ( Self {
111
+ name : name. to_string ( ) ,
112
+ value : value. to_string ( ) ,
113
+ } )
114
+ }
115
+ }
116
+
117
+ #[ cfg( test) ]
118
+ mod tests {
119
+ use super :: * ;
120
+
121
+ #[ test]
122
+ fn it_errors_invalid_headers ( ) {
123
+ // https://tools.ietf.org/html/rfc7230#section-3.2
124
+
125
+ for input in vec ! [
126
+ "X-Name Value" , // error: colon required for name/value pair
127
+ ": Value" , // error: field name must be
128
+ "X Name: Value" , // error: no whitespace in field name
129
+ "X\t Name: Value" , // error: no whitespace in field name (tab)
130
+ ] {
131
+ let header = Header :: from_str ( input) ;
132
+
133
+ assert ! ( header. is_err( ) , "Expected error: [{}]" , input) ;
134
+ }
135
+ }
136
+
137
+ #[ test]
138
+ fn it_parses_valid_headers ( ) {
139
+ // https://tools.ietf.org/html/rfc7230#section-3.2
140
+
141
+ let expected1 = Header {
142
+ name : "X-Name" . to_string ( ) ,
143
+ value : "Value" . to_string ( ) ,
144
+ } ;
145
+ let expected2 = Header {
146
+ name : "X-Name" . to_string ( ) ,
147
+ value : "Value:" . to_string ( ) ,
148
+ } ;
149
+
150
+ for ( input, expected) in vec ! [
151
+ ( "X-Name: Value" , & expected1) , // ideal
152
+ ( "X-Name:Value" , & expected1) , // no optional whitespace
153
+ ( "X-Name: Value " , & expected1) , // with optional whitespace
154
+ ( "X-Name:\t Value" , & expected1) , // with optional whitespace (tab)
155
+ ( "X-Name: Value:" , & expected2) , // with colon in value
156
+ // not allowed per RFC, but we'll forgive
157
+ ( "X-Name : Value" , & expected1) ,
158
+ ( " X-Name: Value" , & expected1) ,
159
+ ] {
160
+ let header = Header :: from_str ( input) ;
161
+
162
+ assert ! ( header. is_ok( ) , "Expected ok: [{}]" , input) ;
163
+ assert_eq ! ( header. unwrap( ) , * expected, "Expected equality: [{}]" , input) ;
164
+ }
165
+ }
166
+ }
0 commit comments