• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 use std::net::IpAddr;
15 use std::path::Path;
16 
17 use crate::error::HttpClientError;
18 use crate::util::c_openssl::error::ErrorStack;
19 use crate::util::c_openssl::ssl::{
20     Ssl, SslContext, SslContextBuilder, SslFiletype, SslMethod, SslVersion,
21 };
22 use crate::util::c_openssl::x509::{X509Ref, X509Store, X509};
23 use crate::util::AlpnProtocolList;
24 use crate::ErrorKind;
25 
26 /// `TlsContextBuilder` implementation based on `SSL_CTX`.
27 ///
28 /// # Examples
29 ///
30 /// ```
31 /// use ylong_http_client::util::{TlsConfigBuilder, TlsVersion};
32 ///
33 /// let context = TlsConfigBuilder::new()
34 ///     .ca_file("ca.crt")
35 ///     .max_proto_version(TlsVersion::TLS_1_2)
36 ///     .min_proto_version(TlsVersion::TLS_1_2)
37 ///     .cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK")
38 ///     .build();
39 /// ```
40 pub struct TlsConfigBuilder {
41     inner: Result<SslContextBuilder, ErrorStack>,
42     use_sni: bool,
43     verify_hostname: bool,
44     certs_list: Vec<Cert>,
45     #[cfg(feature = "c_openssl_3_0")]
46     paths_list: Vec<String>,
47 }
48 
49 impl TlsConfigBuilder {
50     /// Creates a new, default `SslContextBuilder`.
51     ///
52     /// # Examples
53     ///
54     /// ```
55     /// use ylong_http_client::util::TlsConfigBuilder;
56     ///
57     /// let builder = TlsConfigBuilder::new();
58     /// ```
new() -> Self59     pub fn new() -> Self {
60         Self {
61             inner: SslContext::builder(SslMethod::tls_client()),
62             use_sni: true,
63             verify_hostname: true,
64             certs_list: vec![],
65             #[cfg(feature = "c_openssl_3_0")]
66             paths_list: vec![],
67         }
68     }
69 
70     /// Loads trusted root certificates from a file. The file should contain a
71     /// sequence of PEM-formatted CA certificates.
72     ///
73     /// # Examples
74     ///
75     /// ```
76     /// use ylong_http_client::util::TlsConfigBuilder;
77     ///
78     /// let builder = TlsConfigBuilder::new().ca_file("ca.crt");
79     /// ```
ca_file<T: AsRef<Path>>(mut self, path: T) -> Self80     pub fn ca_file<T: AsRef<Path>>(mut self, path: T) -> Self {
81         self.inner = self
82             .inner
83             .and_then(|mut builder| builder.set_ca_file(path).map(|_| builder));
84         self
85     }
86 
87     /// Sets the maximum supported protocol version. A value of `None` will
88     /// enable protocol versions down the the highest version supported by
89     /// `OpenSSL`.
90     ///
91     /// Requires `OpenSSL 1.1.0` or or `LibreSSL 2.6.1` or newer.
92     ///
93     /// # Examples
94     ///
95     /// ```
96     /// use ylong_http_client::util::{TlsConfigBuilder, TlsVersion};
97     ///
98     /// let builder = TlsConfigBuilder::new().max_proto_version(TlsVersion::TLS_1_2);
99     /// ```
max_proto_version(mut self, version: TlsVersion) -> Self100     pub fn max_proto_version(mut self, version: TlsVersion) -> Self {
101         self.inner = self.inner.and_then(|mut builder| {
102             builder
103                 .set_max_proto_version(version.into_inner())
104                 .map(|_| builder)
105         });
106         self
107     }
108 
109     /// Sets the minimum supported protocol version. A value of `None` will
110     /// enable protocol versions down the the lowest version supported by
111     /// `OpenSSL`.
112     ///
113     /// Requires `OpenSSL 1.1.0` or `LibreSSL 2.6.1` or newer.
114     ///
115     /// # Examples
116     ///
117     /// ```
118     /// use ylong_http_client::util::{TlsConfigBuilder, TlsVersion};
119     ///
120     /// let builder = TlsConfigBuilder::new().min_proto_version(TlsVersion::TLS_1_2);
121     /// ```
min_proto_version(mut self, version: TlsVersion) -> Self122     pub fn min_proto_version(mut self, version: TlsVersion) -> Self {
123         self.inner = self.inner.and_then(|mut builder| {
124             builder
125                 .set_min_proto_version(version.into_inner())
126                 .map(|_| builder)
127         });
128         self
129     }
130 
131     /// Sets the list of supported ciphers for protocols before `TLSv1.3`.
132     ///
133     /// The `set_ciphersuites` method controls the cipher suites for `TLSv1.3`.
134     ///
135     /// See [`ciphers`] for details on the format.
136     ///
137     /// [`ciphers`]: https://www.openssl.org/docs/man1.1.0/apps/ciphers.html
138     ///
139     /// # Examples
140     ///
141     /// ```
142     /// use ylong_http_client::util::TlsConfigBuilder;
143     ///
144     /// let builder = TlsConfigBuilder::new()
145     ///     .cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK");
146     /// ```
cipher_list(mut self, list: &str) -> Self147     pub fn cipher_list(mut self, list: &str) -> Self {
148         self.inner = self
149             .inner
150             .and_then(|mut builder| builder.set_cipher_list(list).map(|_| builder));
151         self
152     }
153 
154     /// Sets the list of supported ciphers for the `TLSv1.3` protocol.
155     ///
156     /// The `set_cipher_list` method controls the cipher suites for protocols
157     /// before `TLSv1.3`.
158     ///
159     /// The format consists of TLSv1.3 cipher suite names separated by `:`
160     /// characters in order of preference.
161     ///
162     /// Requires `OpenSSL 1.1.1` or `LibreSSL 3.4.0` or newer.
163     ///
164     /// # Examples
165     ///
166     /// ```
167     /// use ylong_http_client::util::TlsConfigBuilder;
168     ///
169     /// let builder = TlsConfigBuilder::new()
170     ///     .cipher_suites("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK");
171     /// ```
cipher_suites(mut self, list: &str) -> Self172     pub fn cipher_suites(mut self, list: &str) -> Self {
173         self.inner = self
174             .inner
175             .and_then(|mut builder| builder.set_cipher_suites(list).map(|_| builder));
176         self
177     }
178 
179     /// Loads a leaf certificate from a file.
180     ///
181     /// Only a single certificate will be loaded - use `add_extra_chain_cert` to
182     /// add the remainder of the certificate chain, or
183     /// `set_certificate_chain_file` to load the entire chain from a single
184     /// file.
185     ///
186     /// # Examples
187     ///
188     /// ```
189     /// use ylong_http_client::util::{TlsConfigBuilder, TlsFileType};
190     ///
191     /// let builder = TlsConfigBuilder::new().certificate_file("cert.pem", TlsFileType::PEM);
192     /// ```
certificate_file<T: AsRef<Path>>(mut self, path: T, file_type: TlsFileType) -> Self193     pub fn certificate_file<T: AsRef<Path>>(mut self, path: T, file_type: TlsFileType) -> Self {
194         self.inner = self.inner.and_then(|mut builder| {
195             builder
196                 .set_certificate_file(path, file_type.into_inner())
197                 .map(|_| builder)
198         });
199         self
200     }
201 
202     /// Loads a certificate chain from a file.
203     ///
204     /// The file should contain a sequence of PEM-formatted certificates,
205     /// the first being the leaf certificate, and the remainder forming the
206     /// chain of certificates up to and including the trusted root certificate.
207     ///
208     /// # Examples
209     ///
210     /// ```
211     /// use ylong_http_client::util::TlsConfigBuilder;
212     ///
213     /// let builder = TlsConfigBuilder::new().certificate_chain_file("cert.pem");
214     /// ```
certificate_chain_file<T: AsRef<Path>>(mut self, path: T) -> Self215     pub fn certificate_chain_file<T: AsRef<Path>>(mut self, path: T) -> Self {
216         self.inner = self
217             .inner
218             .and_then(|mut builder| builder.set_certificate_chain_file(path).map(|_| builder));
219         self
220     }
221 
222     /// Adds custom root certificate.
223     ///
224     /// # Examples
225     ///
226     /// ```
227     ///     use ylong_http_client::async_impl::Client;
228     ///     use ylong_http_client::{Certificate, TlsVersion};
229     ///
230     ///     let cert1 = Certificate::from_pem(include_bytes!("../../../tests/file/root-ca.pem")).unwrap();
231     ///     let cert2 = Certificate::from_pem(include_bytes!("../../../tests/file/cert.pem")).unwrap();
232     ///
233     ///     // Creates a `Client`
234     ///     let client = Client::builder()
235     ///         .tls_built_in_root_certs(false)
236     ///         .danger_accept_invalid_certs(false)
237     ///         .max_tls_version(TlsVersion::TLS_1_2)
238     ///         .min_tls_version(TlsVersion::TLS_1_2)
239     ///         .add_root_certificate(cert1)
240     ///         .add_root_certificate(cert2)
241     ///         .build().unwrap();
242     /// ```
add_root_certificates(mut self, mut certs: Vec<Cert>) -> Self243     pub fn add_root_certificates(mut self, mut certs: Vec<Cert>) -> Self {
244         self.certs_list.append(&mut certs);
245         self
246     }
247 
248 
249     /// Adds custom root certificate.
250     ///
251     /// # Examples
252     ///
253     /// ```
254     ///     use ylong_http_client::async_impl::Client;
255     ///     use ylong_http_client::{Certificate, TlsVersion};
256     ///
257     ///     let path_cert = Certificate::from_path("../../../cert/file").unwrap();
258     ///
259     ///     // Creates a `Client`
260     ///     let client = Client::builder()
261     ///         .tls_built_in_root_certs(false)
262     ///         .danger_accept_invalid_certs(false)
263     ///         .max_tls_version(TlsVersion::TLS_1_2)
264     ///         .min_tls_version(TlsVersion::TLS_1_2)
265     ///         .add_root_certificate(path_cert)
266     ///         .build().unwrap();
267     /// ```
268     #[cfg(feature = "c_openssl_3_0")]
add_path_certificates(mut self, path: String) -> Self269     pub fn add_path_certificates(mut self, path: String) -> Self {
270         self.paths_list.push(path);
271         self
272     }
273 
274 
275     /// Sets the protocols to sent to the server for Application Layer Protocol
276     /// Negotiation (ALPN).
277     ///
278     /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
279     ///
280     /// # Examples
281     /// ```
282     /// use ylong_http_client::util::TlsConfigBuilder;
283     ///
284     /// let protocols = b"\x06spdy/1\x08http/1.1";
285     /// let builder = TlsConfigBuilder::new().alpn_protos(protocols);
286     /// ```
alpn_protos(mut self, protocols: &[u8]) -> Self287     pub fn alpn_protos(mut self, protocols: &[u8]) -> Self {
288         self.inner = self
289             .inner
290             .and_then(|mut builder| builder.set_alpn_protos(protocols).map(|_| builder));
291         self
292     }
293 
294     /// Sets the protocols to sent to the server for Application Layer Protocol
295     /// Negotiation (ALPN).
296     ///
297     /// This method is based on `openssl::SslContextBuilder::set_alpn_protos`.
298     ///
299     /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
300     ///
301     /// # Examples
302     /// ```
303     /// use ylong_http_client::util::{AlpnProtocol, AlpnProtocolList, TlsConfigBuilder};
304     ///
305     /// let protocols = AlpnProtocolList::new()
306     ///     .extend(AlpnProtocol::SPDY1)
307     ///     .extend(AlpnProtocol::HTTP11);
308     /// let builder = TlsConfigBuilder::new().alpn_protos(protocols.as_slice());
309     /// ```
alpn_proto_list(mut self, list: AlpnProtocolList) -> Self310     pub fn alpn_proto_list(mut self, list: AlpnProtocolList) -> Self {
311         self.inner = self
312             .inner
313             .and_then(|mut builder| builder.set_alpn_protos(list.as_slice()).map(|_| builder));
314         self
315     }
316 
317     /// Controls the use of built-in system certificates during certificate
318     /// validation. Default to `true` -- uses built-in system certs.
build_in_root_certs(mut self, is_use: bool) -> Self319     pub fn build_in_root_certs(mut self, is_use: bool) -> Self {
320         if !is_use {
321             self.inner = X509Store::new().and_then(|store| {
322                 self.inner.and_then(|mut builder| {
323                     {
324                         builder.set_cert_store(store);
325                         Ok(())
326                     }
327                     .map(|_| builder)
328                 })
329             });
330         }
331         self
332     }
333 
334     /// Controls the use of certificates verification.
335     ///
336     /// Defaults to `false` -- verify certificates.
337     ///
338     /// # Warning
339     ///
340     /// When sets `true`, any certificate for any site will be trusted for use.
341     ///
342     /// # Examples
343     ///
344     /// ```
345     /// use ylong_http_client::util::TlsConfigBuilder;
346     ///
347     /// let builder = TlsConfigBuilder::new().danger_accept_invalid_certs(true);
348     /// ```
danger_accept_invalid_certs(mut self, is_invalid: bool) -> Self349     pub fn danger_accept_invalid_certs(mut self, is_invalid: bool) -> Self {
350         if is_invalid {
351             self.inner = self.inner.and_then(|mut builder| {
352                 {
353                     builder.set_verify(crate::util::c_openssl::ssl::SSL_VERIFY_NONE);
354                     Ok(())
355                 }
356                 .map(|_| builder)
357             });
358         }
359         self
360     }
361 
362     /// Controls the use of hostname verification.
363     ///
364     /// Defaults to `false` -- verify hostname.
365     ///
366     /// # Warning
367     ///
368     /// When sets `true`, any valid certificate for any site will be trusted for
369     /// use from any other.
370     ///
371     /// # Examples
372     ///
373     /// ```
374     /// use ylong_http_client::util::TlsConfigBuilder;
375     ///
376     /// let builder = TlsConfigBuilder::new().danger_accept_invalid_hostnames(true);
377     /// ```
danger_accept_invalid_hostnames(mut self, invalid_hostname: bool) -> Self378     pub fn danger_accept_invalid_hostnames(mut self, invalid_hostname: bool) -> Self {
379         self.verify_hostname = !invalid_hostname;
380         self
381     }
382 
383     /// Controls the use of TLS server name indication.
384     ///
385     /// Defaults to `true` -- sets sni.
386     ///
387     /// # Examples
388     ///
389     /// ```
390     /// use ylong_http_client::util::TlsConfigBuilder;
391     ///
392     /// let builder = TlsConfigBuilder::new().sni(true);
393     /// ```
sni(mut self, use_sni: bool) -> Self394     pub fn sni(mut self, use_sni: bool) -> Self {
395         self.use_sni = use_sni;
396         self
397     }
398 
399     /// Builds a `TlsContext`. Returns `Err` if an error occurred during
400     /// configuration.
401     ///
402     /// # Examples
403     ///
404     /// ```
405     /// use ylong_http_client::util::{TlsConfigBuilder, TlsVersion};
406     ///
407     /// let context = TlsConfigBuilder::new()
408     ///     .ca_file("ca.crt")
409     ///     .max_proto_version(TlsVersion::TLS_1_2)
410     ///     .min_proto_version(TlsVersion::TLS_1_2)
411     ///     .cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK")
412     ///     .build();
413     /// ```
build(mut self) -> Result<TlsConfig, HttpClientError>414     pub fn build(mut self) -> Result<TlsConfig, HttpClientError> {
415         for cert in self.certs_list {
416             self.inner = self.inner.and_then(|mut builder| {
417                 { Ok(builder.cert_store_mut()).map(|store| store.add_cert(cert.0)) }
418                     .map(|_| builder)
419             });
420         }
421 
422         #[cfg(feature = "c_openssl_3_0")]
423         for path in self.paths_list {
424             self.inner = self.inner.and_then(|mut builder| {
425                 { Ok(builder.cert_store_mut()).map(|store| store.add_path(path)) }
426                     .map(|_| builder)
427             });
428         }
429 
430         let ctx = self
431             .inner
432             .map(|builder| builder.build())
433             .map_err(|e| HttpClientError::new_with_cause(ErrorKind::Build, Some(e)))?;
434 
435         Ok(TlsConfig {
436             ctx,
437             use_sni: self.use_sni,
438             verify_hostname: self.verify_hostname,
439         })
440     }
441 }
442 
443 impl Default for TlsConfigBuilder {
default() -> Self444     fn default() -> Self {
445         Self::new()
446     }
447 }
448 
449 /// `TlsContext` is based on `SSL_CTX`, which provides context
450 /// object of `TLS` streams.
451 ///
452 /// # Examples
453 ///
454 /// ```
455 /// use ylong_http_client::util::TlsConfig;
456 ///
457 /// let builder = TlsConfig::builder();
458 /// ```
459 #[derive(Clone)]
460 pub struct TlsConfig {
461     ctx: SslContext,
462     use_sni: bool,
463     verify_hostname: bool,
464 }
465 
466 impl TlsConfig {
467     /// Creates a new, default `TlsContextBuilder`.
468     ///
469     /// # Examples
470     ///
471     /// ```
472     /// use ylong_http_client::util::TlsConfig;
473     ///
474     /// let builder = TlsConfig::builder();
475     /// ```
builder() -> TlsConfigBuilder476     pub fn builder() -> TlsConfigBuilder {
477         TlsConfigBuilder::new()
478     }
479 
480     /// Creates a new, default `TlsSsl`.
ssl_new(&self, domain: &str) -> Result<TlsSsl, ErrorStack>481     pub(crate) fn ssl_new(&self, domain: &str) -> Result<TlsSsl, ErrorStack> {
482         let ctx = &self.ctx;
483         let mut ssl = Ssl::new(ctx)?;
484 
485         // SNI extension in `ClientHello` stage.
486         if self.use_sni && domain.parse::<IpAddr>().is_err() {
487             ssl.set_host_name_in_sni(domain)?;
488         }
489 
490         // Hostname verification in certificate verification.
491         if self.verify_hostname {
492             ssl.set_verify_hostname(domain)?;
493         }
494         Ok(TlsSsl(ssl))
495     }
496 }
497 
498 impl Default for TlsConfig {
default() -> Self499     fn default() -> Self {
500         // It must can be successful.
501         TlsConfig::builder()
502             .build()
503             .expect("TlsConfig build error!")
504     }
505 }
506 
507 /// /// `TlsSsl` is based on `Ssl`
508 pub(crate) struct TlsSsl(Ssl);
509 
510 impl TlsSsl {
into_inner(self) -> Ssl511     pub(crate) fn into_inner(self) -> Ssl {
512         self.0
513     }
514 }
515 
516 /// `TlsVersion` is based on `openssl::SslVersion`, which provides `SSL/TLS`
517 /// protocol version.
518 ///
519 /// # Examples
520 ///
521 /// ```
522 /// use ylong_http_client::util::TlsVersion;
523 ///
524 /// let version = TlsVersion::TLS_1_2;
525 /// ```
526 pub struct TlsVersion(SslVersion);
527 
528 impl TlsVersion {
529     /// Constant for TLS version 1.
530     pub const TLS_1_0: Self = Self(SslVersion::TLS_1_0);
531     /// Constant for TLS version 1.1.
532     pub const TLS_1_1: Self = Self(SslVersion::TLS_1_1);
533     /// Constant for TLS version 1.2.
534     pub const TLS_1_2: Self = Self(SslVersion::TLS_1_2);
535     /// Constant for TLS version 1.3.
536     pub const TLS_1_3: Self = Self(SslVersion::TLS_1_3);
537 
538     /// Consumes `TlsVersion` and then takes `SslVersion`.
into_inner(self) -> SslVersion539     pub(crate) fn into_inner(self) -> SslVersion {
540         self.0
541     }
542 }
543 
544 /// `TlsFileType` is based on `openssl::SslFileType`, which provides an
545 /// identifier of the format of a certificate or key file.
546 ///
547 /// ```
548 /// use ylong_http_client::util::TlsFileType;
549 ///
550 /// let file_type = TlsFileType::PEM;
551 /// ```
552 pub struct TlsFileType(SslFiletype);
553 
554 impl TlsFileType {
555     /// Constant for PEM file type.
556     pub const PEM: Self = Self(SslFiletype::PEM);
557     /// Constant for ASN1 file type.
558     pub const ASN1: Self = Self(SslFiletype::ASN1);
559 
560     /// Consumes `TlsFileType` and then takes `SslFiletype`.
into_inner(self) -> SslFiletype561     pub(crate) fn into_inner(self) -> SslFiletype {
562         self.0
563     }
564 }
565 
566 /// `Cert` is based on `X509`, which indicates `X509` public
567 /// key certificate.
568 ///
569 /// ```
570 /// # use ylong_http_client::Cert;
571 ///
572 /// # fn read_from_pem(pem: &[u8]) {
573 /// let cert = Cert::from_pem(pem);
574 /// # }
575 ///
576 /// # fn read_from_der(der: &[u8]) {
577 /// let cert = Cert::from_der(der);
578 /// # }
579 /// ```
580 pub struct Cert(X509);
581 
582 impl Cert {
583     /// Deserializes a PEM-encoded `Cert` structure.
584     ///
585     /// The input should have a header like below:
586     ///
587     /// ```text
588     /// -----BEGIN CERTIFICATE-----
589     /// ```
590     ///
591     /// # Examples
592     ///
593     /// ```
594     /// # use ylong_http_client::Cert;
595     ///
596     /// # fn read_from_pem(pem: &[u8]) {
597     /// let cert = Cert::from_pem(pem);
598     /// # }
599     /// ```
from_pem(pem: &[u8]) -> Result<Self, HttpClientError>600     pub fn from_pem(pem: &[u8]) -> Result<Self, HttpClientError> {
601         Ok(Self(X509::from_pem(pem).map_err(|e| {
602             HttpClientError::new_with_cause(ErrorKind::Build, Some(e))
603         })?))
604     }
605 
606     /// Deserializes a DER-encoded `Cert` structure.
607     ///
608     /// # Examples
609     ///
610     /// ```
611     /// use ylong_http_client::Cert;
612     ///
613     /// # fn read_from_der(der: &[u8]) {
614     /// let cert = Cert::from_der(der);
615     /// # }
616     /// ```
from_der(der: &[u8]) -> Result<Self, HttpClientError>617     pub fn from_der(der: &[u8]) -> Result<Self, HttpClientError> {
618         Ok(Self(X509::from_der(der).map_err(|e| {
619             HttpClientError::new_with_cause(ErrorKind::Build, Some(e))
620         })?))
621     }
622 
623     /// Deserializes a list of PEM-formatted certificates.
stack_from_pem(pem: &[u8]) -> Result<Vec<Self>, HttpClientError>624     pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<Self>, HttpClientError> {
625         Ok(X509::stack_from_pem(pem)
626             .map_err(|e| HttpClientError::new_with_cause(ErrorKind::Build, Some(e)))?
627             .into_iter()
628             .map(Self)
629             .collect())
630     }
631 
632     /// Gets a reference to `X509Ref`.
as_ref(&self) -> &X509Ref633     pub(crate) fn as_ref(&self) -> &X509Ref {
634         self.0.as_ref()
635     }
636 
637     /// Consumes `X509` and then takes `X509`.
into_inner(self) -> X509638     pub(crate) fn into_inner(self) -> X509 {
639         self.0
640     }
641 }
642 
643 /// Represents a server X509 certificates.
644 ///
645 /// You can use `from_pem` to parse a `&[u8]` into a list of certificates.
646 ///
647 /// # Examples
648 ///
649 /// ```
650 /// use ylong_http_client::Certificate;
651 ///
652 /// fn from_pem(pem: &[u8]) {
653 ///     let certs = Certificate::from_pem(pem);
654 /// }
655 /// ```
656 pub struct Certificate  {
657     inner: CertificateList,
658 }
659 
660 pub(crate) enum CertificateList  {
661     CertList(Vec<Cert>),
662     #[cfg(feature = "c_openssl_3_0")]
663     PathList(String),
664 }
665 
666 impl Certificate {
667     /// Deserializes a list of PEM-formatted certificates.
from_pem(pem: &[u8]) -> Result<Self, HttpClientError>668     pub fn from_pem(pem: &[u8]) -> Result<Self, HttpClientError> {
669         let cert_list = X509::stack_from_pem(pem)
670             .map_err(|e| HttpClientError::new_with_cause(ErrorKind::Build, Some(e)))?
671             .into_iter()
672             .map(Cert)
673             .collect();
674         Ok(Certificate { inner: CertificateList::CertList(cert_list) })
675     }
676 
677     /// Deserializes a list of PEM-formatted certificates.
678     #[cfg(feature = "c_openssl_3_0")]
from_path(path: &str) -> Result<Self, HttpClientError>679     pub fn from_path(path: &str) -> Result<Self, HttpClientError> {
680         Ok(Certificate { inner: CertificateList::PathList(path.to_string()) })
681     }
682 
into_inner(self) -> CertificateList683     pub(crate) fn into_inner(self) -> CertificateList {
684         self.inner
685     }
686 }
687 
688 #[cfg(test)]
689 mod ut_openssl_adapter {
690     use std::io::{Read, Write};
691     use std::net::TcpStream;
692 
693     use crate::util::{Cert, TlsConfigBuilder, TlsFileType, TlsVersion};
694     use crate::{AlpnProtocol, AlpnProtocolList, Certificate};
695     use crate::util::c_openssl::adapter::CertificateList;
696 
697     /// UT test cases for `TlsConfigBuilder::new`.
698     ///
699     /// # Brief
700     /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`
701     /// 2. Checks if the result is as expected.
702     #[test]
ut_tls_config_builder_new()703     fn ut_tls_config_builder_new() {
704         let _ = TlsConfigBuilder::default();
705         let builder = TlsConfigBuilder::new();
706         assert!(builder.ca_file("folder/ca.crt").build().is_err());
707     }
708 
709     /// UT test cases for `TlsConfigBuilder::new`.
710     ///
711     /// # Brief
712     /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`.
713     /// 2. Calls `set_cipher_suites`.
714     /// 3. Provides an invalid path as argument.
715     /// 4. Checks if the result is as expected.
716     #[test]
ut_set_cipher_suites()717     fn ut_set_cipher_suites() {
718         let builder = TlsConfigBuilder::new().cipher_suites("INVALID STRING");
719         assert!(builder.build().is_err());
720     }
721 
722     /// UT test cases for `TlsConfigBuilder::set_max_proto_version`.
723     ///
724     /// # Brief
725     /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`.
726     /// 2. Calls `set_max_proto_version`.
727     /// 3. Checks if the result is as expected.
728     #[test]
ut_set_max_proto_version()729     fn ut_set_max_proto_version() {
730         let builder = TlsConfigBuilder::new()
731             .max_proto_version(TlsVersion::TLS_1_2)
732             .build();
733         assert!(builder.is_ok());
734     }
735 
736     /// UT test cases for `TlsConfigBuilder::set_min_proto_version`.
737     ///
738     /// # Brief
739     /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`.
740     /// 2. Calls `set_min_proto_version`.
741     /// 3. Checks if the result is as expected.
742     #[test]
ut_set_min_proto_version()743     fn ut_set_min_proto_version() {
744         let builder = TlsConfigBuilder::new()
745             .min_proto_version(TlsVersion::TLS_1_2)
746             .build();
747         assert!(builder.is_ok());
748     }
749 
750     /// UT test cases for `TlsConfigBuilder::set_cipher_list`.
751     ///
752     /// # Brief
753     /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`.
754     /// 2. Calls `set_cipher_list`.
755     /// 3. Checks if the result is as expected.
756     #[test]
ut_set_cipher_list()757     fn ut_set_cipher_list() {
758         let builder = TlsConfigBuilder::new()
759             .cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK")
760             .build();
761         assert!(builder.is_ok());
762     }
763 
764     /// UT test cases for `TlsConfigBuilder::set_certificate_file`.
765     ///
766     /// # Brief
767     /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`.
768     /// 2. Calls `set_certificate_file`.
769     /// 3. Provides an invalid path as argument.
770     /// 4. Checks if the result is as expected.
771     #[test]
ut_set_certificate_file()772     fn ut_set_certificate_file() {
773         let builder = TlsConfigBuilder::new()
774             .certificate_file("cert.pem", TlsFileType::PEM)
775             .build();
776         assert!(builder.is_err());
777     }
778 
779     /// UT test cases for `TlsConfigBuilder::set_certificate_chain_file`.
780     ///
781     /// # Brief
782     /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`.
783     /// 2. Calls `set_certificate_chain_file`.
784     /// 3. Provides an invalid path as argument.
785     /// 4. Checks if the result is as expected.
786     #[test]
ut_set_certificate_chain_file()787     fn ut_set_certificate_chain_file() {
788         let builder = TlsConfigBuilder::new()
789             .certificate_chain_file("cert.pem")
790             .build();
791         assert!(builder.is_err());
792     }
793 
794     /// UT test cases for `TlsConfigBuilder::add_root_certificates`.
795     ///
796     /// # Brief
797     /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`.
798     /// 2. Calls `add_root_certificates`.
799     /// 3. Provides PEM-formatted certificates.
800     /// 4. Checks if the result is as expected.
801     #[test]
ut_add_root_certificates()802     fn ut_add_root_certificates() {
803         let certificate = Certificate::from_pem(include_bytes!("../../../tests/file/root-ca.pem"))
804             .expect("Sets certs error.");
805         let certs = match certificate.inner {
806             CertificateList::CertList(c) => c,
807             #[cfg(feature = "c_openssl_3_0")]
808             CertificateList::PathList(_) => vec![],
809         };
810 
811         let builder = TlsConfigBuilder::new()
812             .add_root_certificates(certs)
813             .build();
814         assert!(builder.is_ok());
815     }
816 
817     /// UT test cases for `TlsConfigBuilder::build_in_root_certs`.
818     ///
819     /// # Brief
820     /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`.
821     /// 2. Calls `build_in_root_certs`.
822     /// 3. Checks if the result is as expected.
823     #[test]
ut_build_in_root_certs()824     fn ut_build_in_root_certs() {
825         let builder = TlsConfigBuilder::new().build_in_root_certs(true).build();
826         assert!(builder.is_ok());
827     }
828 
829     /// UT test cases for `TlsConfigBuilder::set_alpn_proto_list`.
830     ///
831     /// # Brief
832     /// 1. Creates a `TlsConfigBuilder` by calling `TlsConfigBuilder::new`.
833     /// 2. Calls `set_alpn_proto_list`.
834     /// 3. Provides `AlpnProtocol`s.
835     /// 4. Checks if the result is as expected.
836     #[test]
ut_set_alpn_proto_list()837     fn ut_set_alpn_proto_list() {
838         let builder = TlsConfigBuilder::new()
839             .alpn_proto_list(
840                 AlpnProtocolList::new()
841                     .extend(AlpnProtocol::HTTP11)
842                     .extend(AlpnProtocol::H2),
843             )
844             .build();
845         assert!(builder.is_ok());
846     }
847 
848     /// UT test cases for `TlsConfig::ssl`.
849     ///
850     /// # Brief
851     /// 1. Creates a `TlsConfig` by calling `TlsConfigBuilder::new` and
852     ///    `TlsConfigBuilder::build`.
853     /// 2. Creates a `TlsSsl` by calling `TlsConfig::ssl_new`.
854     /// 3. Calls `TlsSsl::into_inner`.
855     /// 4. Checks if the result is as expected.
856     #[test]
ut_tls_ssl()857     fn ut_tls_ssl() {
858         let config = TlsConfigBuilder::new()
859             .build()
860             .expect("TlsConfig build error.");
861         let _ssl = config
862             .ssl_new("host name")
863             .expect("Ssl build error.")
864             .into_inner();
865     }
866 
867     /// UT test cases for `TlsConfig::ssl` and `SslRef::set_verify_hostname`.
868     ///
869     /// # Brief
870     /// 1. Creates a `TlsConfig` by calling `TlsConfigBuilder::new` and
871     ///    `TlsConfigBuilder::build`.
872     /// 2. Sets hostname "" and verify_hostname.
873     /// 3. Creates a `Ssl` by calling `TlsConfig::ssl_new` then creates a
874     ///    `SslStream`.
875     /// 4. Calls `write` and `read` by `SslStream`.
876     /// 5. Checks if retures the segmentation fault `invalid memory reference`.
877     #[cfg(feature = "sync")]
878     #[test]
ut_tls_ssl_verify_hostname()879     fn ut_tls_ssl_verify_hostname() {
880         let config = TlsConfigBuilder::new()
881             .sni(false)
882             .danger_accept_invalid_hostnames(false)
883             .build()
884             .expect("TlsConfig build error.");
885 
886         let domain = String::from("");
887         let ssl = config
888             .ssl_new(domain.as_str())
889             .expect("Ssl build error.")
890             .into_inner();
891         let stream = TcpStream::connect("huawei.com:443").expect("Tcp stream error.");
892         let mut tls_stream = ssl.connect(stream).expect("Tls stream error.");
893 
894         tls_stream
895             .write_all(b"GET / HTTP/1.0\r\n\r\n")
896             .expect("Stream write error.");
897         let mut res = vec![];
898         tls_stream
899             .read_to_end(&mut res)
900             .expect("Stream read error.");
901         println!("{}", String::from_utf8_lossy(&res));
902     }
903 
904     /// UT test cases for `Cert::from_pem`.
905     ///
906     /// # Brief
907     /// 1. Creates a `Cert` by calling `Cert::from_pem`.
908     /// 2. Provides an invalid pem as argument.
909     /// 3. Checks if the result is as expected.
910     #[test]
ut_x509_from_pem()911     fn ut_x509_from_pem() {
912         let pem = "(pem-content)";
913         let x509 = Cert::from_pem(pem.as_bytes());
914         assert!(x509.is_err());
915 
916         let cert = include_bytes!("../../../tests/file/root-ca.pem");
917         println!("{:?}", std::str::from_utf8(cert).unwrap());
918         let x509 = Cert::from_pem(cert);
919         assert!(x509.is_ok());
920     }
921 
922     /// UT test cases for `Cert::from_der`.
923     ///
924     /// # Brief
925     /// 1. Creates a `Cert` by calling `Cert::from_der`.
926     /// 2. Provides an invalid der as argument.
927     /// 3. Checks if the result is as expected.
928     #[test]
ut_x509_from_der()929     fn ut_x509_from_der() {
930         let der = "(dar-content)";
931         let x509 = Cert::from_der(der.as_bytes());
932         assert!(x509.is_err());
933     }
934 
935     /// UT test cases for `Cert::stack_from_pem`.
936     ///
937     /// # Brief
938     /// 1. Creates a `Cert` by calling `Cert::stack_from_pem`.
939     /// 2. Provides pem bytes as argument.
940     /// 3. Checks if the result is as expected.
941     #[test]
ut_cert_stack_from_der()942     fn ut_cert_stack_from_der() {
943         let v = include_bytes!("../../../tests/file/root-ca.pem");
944         let x509 = Cert::stack_from_pem(v);
945         assert!(x509.is_ok());
946     }
947 
948     /// UT test cases for `Certificate::from_pem`.
949     ///
950     /// # Brief
951     /// 1. Creates a `Certificate` by calling `Certificate::from_pem`.
952     /// 2. Provides pem bytes as argument.
953     /// 3. Checks if the result is as expected.
954     #[test]
ut_certificate_from_pem()955     fn ut_certificate_from_pem() {
956         let v = include_bytes!("../../../tests/file/root-ca.pem");
957         let certs = Certificate::from_pem(v);
958         assert!(certs.is_ok());
959     }
960 }
961