1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 #![cfg(all(
15 feature = "async",
16 feature = "http1_1",
17 feature = "__c_openssl",
18 feature = "tokio_base"
19 ))]
20
21 #[macro_use]
22 mod common;
23
24 use std::path::PathBuf;
25
26 use ylong_http_client::PubKeyPins;
27
28 use crate::common::init_test_work_runtime;
29
30 /// SDV test cases for `async::Client`.
31 ///
32 /// # Brief
33 /// 1. Starts a hyper https server with the tokio coroutine.
34 /// 2. Creates an async::Client that with public key pinning.
35 /// 3. The client sends a request message.
36 /// 4. Verifies the received request on the server.
37 /// 5. The server sends a response message.
38 /// 6. Verifies the received response on the client.
39 /// 7. Shuts down the server.
40 #[test]
sdv_client_public_key_pinning()41 fn sdv_client_public_key_pinning() {
42 define_service_handle!(HTTPS;);
43 set_server_fn!(
44 ASYNC;
45 ylong_server_fn,
46 Request: {
47 Method: "GET",
48 Header: "Content-Length", "5",
49 Body: "hello",
50 },
51 Response: {
52 Status: 200,
53 Version: "HTTP/1.1",
54 Header: "Content-Length", "3",
55 Body: "hi!",
56 },
57 );
58 let runtime = init_test_work_runtime(1);
59 let mut handles_vec = vec![];
60 let dir = env!("CARGO_MANIFEST_DIR");
61 let mut path = PathBuf::from(dir);
62 path.push("tests/file/root-ca.pem");
63
64 {
65 start_server!(
66 HTTPS;
67 ServerNum: 1,
68 Runtime: runtime,
69 Handles: handles_vec,
70 ServeFnName: ylong_server_fn,
71 );
72 let handle = handles_vec.pop().expect("No more handles !");
73
74 let pins = PubKeyPins::builder()
75 .add(
76 format!("https://127.0.0.1:{}", handle.port).as_str(),
77 "sha256//VHQAbNl67nmkZJNESeTKvTxb5bQmd1maWnMKG/tjcAY=",
78 )
79 .build()
80 .unwrap();
81
82 let client = ylong_http_client::async_impl::Client::builder()
83 .tls_ca_file(path.to_str().unwrap())
84 .add_public_key_pins(pins)
85 .danger_accept_invalid_hostnames(true)
86 .build()
87 .unwrap();
88
89 let shutdown_handle = runtime.spawn(async move {
90 async_client_assertions!(
91 ServerHandle: handle,
92 ClientRef: client,
93 Request: {
94 Method: "GET",
95 Host: "127.0.0.1",
96 Header: "Content-Length", "5",
97 Body: "hello",
98 },
99 Response: {
100 Status: 200,
101 Version: "HTTP/1.1",
102 Header: "Content-Length", "3",
103 Body: "hi!",
104 },
105 );
106 });
107 runtime
108 .block_on(shutdown_handle)
109 .expect("Runtime block on server shutdown failed");
110 }
111
112 {
113 start_server!(
114 HTTPS;
115 ServerNum: 1,
116 Runtime: runtime,
117 Handles: handles_vec,
118 ServeFnName: ylong_server_fn,
119 );
120 let handle = handles_vec.pop().expect("No more handles !");
121
122 // Two wrong public keys and a correct public key in the middle.
123 let pins = PubKeyPins::builder()
124 .add(
125 format!("https://127.0.0.1:{}", handle.port).as_str(),
126 "sha256//YhKJKSzoTt2b5FP18fvpHo7fJYqQCjAa3HWY3tvRMwE=;sha256//VHQAbNl67nmkZJNESeTKvTxb5bQmd1maWnMKG/tjcAY=;sha256//t62CeU2tQiqkexU74Gxa2eg7fRbEgoChTociMee9wno=",
127 )
128 .build()
129 .unwrap();
130
131 let client = ylong_http_client::async_impl::Client::builder()
132 .tls_ca_file(path.to_str().unwrap())
133 .add_public_key_pins(pins)
134 .danger_accept_invalid_hostnames(true)
135 .build()
136 .unwrap();
137
138 let shutdown_handle = runtime.spawn(async move {
139 async_client_assertions!(
140 ServerHandle: handle,
141 ClientRef: client,
142 Request: {
143 Method: "GET",
144 Host: "127.0.0.1",
145 Header: "Content-Length", "5",
146 Body: "hello",
147 },
148 Response: {
149 Status: 200,
150 Version: "HTTP/1.1",
151 Header: "Content-Length", "3",
152 Body: "hi!",
153 },
154 );
155 });
156 runtime
157 .block_on(shutdown_handle)
158 .expect("Runtime block on server shutdown failed");
159 }
160
161 {
162 start_server!(
163 HTTPS;
164 ServerNum: 1,
165 Runtime: runtime,
166 Handles: handles_vec,
167 ServeFnName: ylong_server_fn,
168 );
169 let handle = handles_vec.pop().expect("No more handles !");
170
171 // The public key of an irrelevant domain.
172 let pins = PubKeyPins::builder()
173 .add(
174 "https://ylong_http.test:6789",
175 "sha256//t62CeU2tQiqkexU74Gxa2eg7fRbEgoChTociMee9wno=",
176 )
177 .build()
178 .unwrap();
179
180 let client = ylong_http_client::async_impl::Client::builder()
181 .tls_ca_file(path.to_str().unwrap())
182 .add_public_key_pins(pins)
183 .danger_accept_invalid_hostnames(true)
184 .build()
185 .unwrap();
186
187 let shutdown_handle = runtime.spawn(async move {
188 async_client_assertions!(
189 ServerHandle: handle,
190 ClientRef: client,
191 Request: {
192 Method: "GET",
193 Host: "127.0.0.1",
194 Header: "Content-Length", "5",
195 Body: "hello",
196 },
197 Response: {
198 Status: 200,
199 Version: "HTTP/1.1",
200 Header: "Content-Length", "3",
201 Body: "hi!",
202 },
203 );
204 });
205 runtime
206 .block_on(shutdown_handle)
207 .expect("Runtime block on server shutdown failed");
208 }
209 }
210
211 /// SDV test cases for `async::Client`.
212 ///
213 /// # Brief
214 /// 1. Starts a hyper https server with the tokio coroutine.
215 /// 2. Creates an async::Client with an error public key pinning.
216 /// 3. The client sends a request message.
217 /// 4. Verifies the received request on the server.
218 /// 5. The server sends a response message.
219 /// 6. Verifies the received response on the client.
220 /// 7. Shuts down the server.
221 #[test]
sdv_client_public_key_pinning_error()222 fn sdv_client_public_key_pinning_error() {
223 define_service_handle!(HTTPS;);
224 set_server_fn!(
225 ASYNC;
226 ylong_server_fn,
227 Request: {
228 Method: "GET",
229 Header: "Content-Length", "5",
230 Body: "hello",
231 },
232 Response: {
233 Status: 200,
234 Version: "HTTP/1.1",
235 Header: "Content-Length", "3",
236 Body: "hi!",
237 },
238 );
239
240 let runtime = init_test_work_runtime(1);
241
242 let mut handles_vec = vec![];
243 start_server!(
244 HTTPS;
245 ServerNum: 1,
246 Runtime: runtime,
247 Handles: handles_vec,
248 ServeFnName: ylong_server_fn,
249 );
250 let handle = handles_vec.pop().expect("No more handles !");
251
252 let pins = PubKeyPins::builder()
253 .add(
254 format!("https://127.0.0.1:{}", handle.port).as_str(),
255 "sha256//YhKJKSzoTt2b5FP18fvpHo7fJYqQCjAa3HWY3tvRMwE=",
256 )
257 .build()
258 .unwrap();
259
260 let dir = env!("CARGO_MANIFEST_DIR");
261 let mut path = PathBuf::from(dir);
262 path.push("tests/file/root-ca.pem");
263
264 let client = ylong_http_client::async_impl::Client::builder()
265 .tls_ca_file(path.to_str().unwrap())
266 .add_public_key_pins(pins)
267 .danger_accept_invalid_hostnames(true)
268 .build()
269 .unwrap();
270
271 let shutdown_handle = runtime.spawn(async move {
272 let request = ylong_http_client::async_impl::Request::builder()
273 .method("GET")
274 .url(format!("{}:{}", "127.0.0.1", handle.port).as_str())
275 .header("Content-Length", "5")
276 .body(ylong_http_client::async_impl::Body::slice("hello"))
277 .expect("Request build failed");
278
279 let response = client.request(request).await.err();
280
281 assert_eq!(
282 format!("{:?}", response.expect("response is not an error")),
283 "HttpClientError { ErrorKind: Connect, Cause: Custom { kind: Other, error: SslError { \
284 code: SslErrorCode(1), internal: Some(User(VerifyError { ErrorKind: PubKeyPinning, \
285 Cause: Pinned public key verification failed. })) } } }"
286 );
287 });
288 runtime
289 .block_on(shutdown_handle)
290 .expect("Runtime block on server shutdown failed");
291 }
292