wreq-rb 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +2688 -0
  3. data/Cargo.toml +6 -0
  4. data/README.md +179 -0
  5. data/ext/wreq_rb/Cargo.toml +39 -0
  6. data/ext/wreq_rb/extconf.rb +22 -0
  7. data/ext/wreq_rb/src/client.rs +565 -0
  8. data/ext/wreq_rb/src/error.rs +25 -0
  9. data/ext/wreq_rb/src/lib.rs +20 -0
  10. data/ext/wreq_rb/src/response.rs +132 -0
  11. data/lib/wreq-rb/version.rb +5 -0
  12. data/lib/wreq-rb.rb +17 -0
  13. data/patches/0001-add-transfer-size-tracking.patch +292 -0
  14. data/vendor/wreq/Cargo.toml +306 -0
  15. data/vendor/wreq/LICENSE +202 -0
  16. data/vendor/wreq/README.md +122 -0
  17. data/vendor/wreq/examples/cert_store.rs +77 -0
  18. data/vendor/wreq/examples/connect_via_lower_priority_tokio_runtime.rs +258 -0
  19. data/vendor/wreq/examples/emulation.rs +118 -0
  20. data/vendor/wreq/examples/form.rs +14 -0
  21. data/vendor/wreq/examples/http1_websocket.rs +37 -0
  22. data/vendor/wreq/examples/http2_websocket.rs +45 -0
  23. data/vendor/wreq/examples/json_dynamic.rs +41 -0
  24. data/vendor/wreq/examples/json_typed.rs +47 -0
  25. data/vendor/wreq/examples/keylog.rs +16 -0
  26. data/vendor/wreq/examples/request_with_emulation.rs +115 -0
  27. data/vendor/wreq/examples/request_with_interface.rs +37 -0
  28. data/vendor/wreq/examples/request_with_local_address.rs +16 -0
  29. data/vendor/wreq/examples/request_with_proxy.rs +13 -0
  30. data/vendor/wreq/examples/request_with_redirect.rs +22 -0
  31. data/vendor/wreq/examples/request_with_version.rs +15 -0
  32. data/vendor/wreq/examples/tor_socks.rs +24 -0
  33. data/vendor/wreq/examples/unix_socket.rs +33 -0
  34. data/vendor/wreq/src/client/body.rs +304 -0
  35. data/vendor/wreq/src/client/conn/conn.rs +231 -0
  36. data/vendor/wreq/src/client/conn/connector.rs +549 -0
  37. data/vendor/wreq/src/client/conn/http.rs +1023 -0
  38. data/vendor/wreq/src/client/conn/proxy/socks.rs +233 -0
  39. data/vendor/wreq/src/client/conn/proxy/tunnel.rs +260 -0
  40. data/vendor/wreq/src/client/conn/proxy.rs +39 -0
  41. data/vendor/wreq/src/client/conn/tls_info.rs +98 -0
  42. data/vendor/wreq/src/client/conn/uds.rs +44 -0
  43. data/vendor/wreq/src/client/conn/verbose.rs +149 -0
  44. data/vendor/wreq/src/client/conn.rs +323 -0
  45. data/vendor/wreq/src/client/core/body/incoming.rs +485 -0
  46. data/vendor/wreq/src/client/core/body/length.rs +118 -0
  47. data/vendor/wreq/src/client/core/body.rs +34 -0
  48. data/vendor/wreq/src/client/core/common/buf.rs +149 -0
  49. data/vendor/wreq/src/client/core/common/rewind.rs +141 -0
  50. data/vendor/wreq/src/client/core/common/watch.rs +76 -0
  51. data/vendor/wreq/src/client/core/common.rs +3 -0
  52. data/vendor/wreq/src/client/core/conn/http1.rs +342 -0
  53. data/vendor/wreq/src/client/core/conn/http2.rs +307 -0
  54. data/vendor/wreq/src/client/core/conn.rs +11 -0
  55. data/vendor/wreq/src/client/core/dispatch.rs +299 -0
  56. data/vendor/wreq/src/client/core/error.rs +435 -0
  57. data/vendor/wreq/src/client/core/ext.rs +201 -0
  58. data/vendor/wreq/src/client/core/http1.rs +178 -0
  59. data/vendor/wreq/src/client/core/http2.rs +483 -0
  60. data/vendor/wreq/src/client/core/proto/h1/conn.rs +988 -0
  61. data/vendor/wreq/src/client/core/proto/h1/decode.rs +1170 -0
  62. data/vendor/wreq/src/client/core/proto/h1/dispatch.rs +684 -0
  63. data/vendor/wreq/src/client/core/proto/h1/encode.rs +580 -0
  64. data/vendor/wreq/src/client/core/proto/h1/io.rs +879 -0
  65. data/vendor/wreq/src/client/core/proto/h1/role.rs +694 -0
  66. data/vendor/wreq/src/client/core/proto/h1.rs +104 -0
  67. data/vendor/wreq/src/client/core/proto/h2/client.rs +650 -0
  68. data/vendor/wreq/src/client/core/proto/h2/ping.rs +539 -0
  69. data/vendor/wreq/src/client/core/proto/h2.rs +379 -0
  70. data/vendor/wreq/src/client/core/proto/headers.rs +138 -0
  71. data/vendor/wreq/src/client/core/proto.rs +58 -0
  72. data/vendor/wreq/src/client/core/rt/bounds.rs +57 -0
  73. data/vendor/wreq/src/client/core/rt/timer.rs +150 -0
  74. data/vendor/wreq/src/client/core/rt/tokio.rs +99 -0
  75. data/vendor/wreq/src/client/core/rt.rs +25 -0
  76. data/vendor/wreq/src/client/core/upgrade.rs +267 -0
  77. data/vendor/wreq/src/client/core.rs +16 -0
  78. data/vendor/wreq/src/client/emulation.rs +161 -0
  79. data/vendor/wreq/src/client/http/client/error.rs +142 -0
  80. data/vendor/wreq/src/client/http/client/exec.rs +29 -0
  81. data/vendor/wreq/src/client/http/client/extra.rs +77 -0
  82. data/vendor/wreq/src/client/http/client/lazy.rs +79 -0
  83. data/vendor/wreq/src/client/http/client/pool.rs +1105 -0
  84. data/vendor/wreq/src/client/http/client/util.rs +104 -0
  85. data/vendor/wreq/src/client/http/client.rs +1003 -0
  86. data/vendor/wreq/src/client/http/future.rs +99 -0
  87. data/vendor/wreq/src/client/http.rs +1629 -0
  88. data/vendor/wreq/src/client/layer/config/options.rs +156 -0
  89. data/vendor/wreq/src/client/layer/config.rs +116 -0
  90. data/vendor/wreq/src/client/layer/cookie.rs +161 -0
  91. data/vendor/wreq/src/client/layer/decoder.rs +139 -0
  92. data/vendor/wreq/src/client/layer/redirect/future.rs +270 -0
  93. data/vendor/wreq/src/client/layer/redirect/policy.rs +63 -0
  94. data/vendor/wreq/src/client/layer/redirect.rs +145 -0
  95. data/vendor/wreq/src/client/layer/retry/classify.rs +105 -0
  96. data/vendor/wreq/src/client/layer/retry/scope.rs +51 -0
  97. data/vendor/wreq/src/client/layer/retry.rs +151 -0
  98. data/vendor/wreq/src/client/layer/timeout/body.rs +233 -0
  99. data/vendor/wreq/src/client/layer/timeout/future.rs +90 -0
  100. data/vendor/wreq/src/client/layer/timeout.rs +177 -0
  101. data/vendor/wreq/src/client/layer.rs +15 -0
  102. data/vendor/wreq/src/client/multipart.rs +717 -0
  103. data/vendor/wreq/src/client/request.rs +818 -0
  104. data/vendor/wreq/src/client/response.rs +534 -0
  105. data/vendor/wreq/src/client/ws/json.rs +99 -0
  106. data/vendor/wreq/src/client/ws/message.rs +453 -0
  107. data/vendor/wreq/src/client/ws.rs +714 -0
  108. data/vendor/wreq/src/client.rs +27 -0
  109. data/vendor/wreq/src/config.rs +140 -0
  110. data/vendor/wreq/src/cookie.rs +579 -0
  111. data/vendor/wreq/src/dns/gai.rs +249 -0
  112. data/vendor/wreq/src/dns/hickory.rs +78 -0
  113. data/vendor/wreq/src/dns/resolve.rs +180 -0
  114. data/vendor/wreq/src/dns.rs +69 -0
  115. data/vendor/wreq/src/error.rs +502 -0
  116. data/vendor/wreq/src/ext.rs +398 -0
  117. data/vendor/wreq/src/hash.rs +143 -0
  118. data/vendor/wreq/src/header.rs +506 -0
  119. data/vendor/wreq/src/into_uri.rs +187 -0
  120. data/vendor/wreq/src/lib.rs +586 -0
  121. data/vendor/wreq/src/proxy/mac.rs +82 -0
  122. data/vendor/wreq/src/proxy/matcher.rs +806 -0
  123. data/vendor/wreq/src/proxy/uds.rs +66 -0
  124. data/vendor/wreq/src/proxy/win.rs +31 -0
  125. data/vendor/wreq/src/proxy.rs +569 -0
  126. data/vendor/wreq/src/redirect.rs +575 -0
  127. data/vendor/wreq/src/retry.rs +198 -0
  128. data/vendor/wreq/src/sync.rs +129 -0
  129. data/vendor/wreq/src/tls/conn/cache.rs +123 -0
  130. data/vendor/wreq/src/tls/conn/cert_compression.rs +125 -0
  131. data/vendor/wreq/src/tls/conn/ext.rs +82 -0
  132. data/vendor/wreq/src/tls/conn/macros.rs +34 -0
  133. data/vendor/wreq/src/tls/conn/service.rs +138 -0
  134. data/vendor/wreq/src/tls/conn.rs +681 -0
  135. data/vendor/wreq/src/tls/keylog/handle.rs +64 -0
  136. data/vendor/wreq/src/tls/keylog.rs +99 -0
  137. data/vendor/wreq/src/tls/options.rs +464 -0
  138. data/vendor/wreq/src/tls/x509/identity.rs +122 -0
  139. data/vendor/wreq/src/tls/x509/parser.rs +71 -0
  140. data/vendor/wreq/src/tls/x509/store.rs +228 -0
  141. data/vendor/wreq/src/tls/x509.rs +68 -0
  142. data/vendor/wreq/src/tls.rs +154 -0
  143. data/vendor/wreq/src/trace.rs +55 -0
  144. data/vendor/wreq/src/util.rs +122 -0
  145. data/vendor/wreq/tests/badssl.rs +228 -0
  146. data/vendor/wreq/tests/brotli.rs +350 -0
  147. data/vendor/wreq/tests/client.rs +1098 -0
  148. data/vendor/wreq/tests/connector_layers.rs +227 -0
  149. data/vendor/wreq/tests/cookie.rs +306 -0
  150. data/vendor/wreq/tests/deflate.rs +347 -0
  151. data/vendor/wreq/tests/emulation.rs +260 -0
  152. data/vendor/wreq/tests/gzip.rs +347 -0
  153. data/vendor/wreq/tests/layers.rs +261 -0
  154. data/vendor/wreq/tests/multipart.rs +165 -0
  155. data/vendor/wreq/tests/proxy.rs +438 -0
  156. data/vendor/wreq/tests/redirect.rs +629 -0
  157. data/vendor/wreq/tests/retry.rs +135 -0
  158. data/vendor/wreq/tests/support/delay_server.rs +117 -0
  159. data/vendor/wreq/tests/support/error.rs +16 -0
  160. data/vendor/wreq/tests/support/layer.rs +183 -0
  161. data/vendor/wreq/tests/support/mod.rs +9 -0
  162. data/vendor/wreq/tests/support/server.rs +232 -0
  163. data/vendor/wreq/tests/timeouts.rs +281 -0
  164. data/vendor/wreq/tests/unix_socket.rs +135 -0
  165. data/vendor/wreq/tests/upgrade.rs +98 -0
  166. data/vendor/wreq/tests/zstd.rs +559 -0
  167. metadata +225 -0
@@ -0,0 +1,122 @@
1
+ use boring2::{
2
+ pkcs12::Pkcs12,
3
+ pkey::{PKey, Private},
4
+ x509::X509,
5
+ };
6
+
7
+ use crate::Error;
8
+
9
+ /// Represents a private key and X509 cert as a client certificate.
10
+ #[derive(Debug, Clone)]
11
+ pub struct Identity {
12
+ pkey: PKey<Private>,
13
+ cert: X509,
14
+ chain: Vec<X509>,
15
+ }
16
+
17
+ impl Identity {
18
+ /// Parses a DER-formatted PKCS #12 archive, using the specified password to decrypt the key.
19
+ ///
20
+ /// The archive should contain a leaf certificate and its private key, as well any intermediate
21
+ /// certificates that allow clients to build a chain to a trusted root.
22
+ /// The chain certificates should be in order from the leaf certificate towards the root.
23
+ ///
24
+ /// PKCS #12 archives typically have the file extension `.p12` or `.pfx`, and can be created
25
+ /// with the OpenSSL `pkcs12` tool:
26
+ ///
27
+ /// ```bash
28
+ /// openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem -certfile chain_certs.pem
29
+ /// ```
30
+ ///
31
+ /// # Examples
32
+ ///
33
+ /// ```
34
+ /// # use std::fs::File;
35
+ /// # use std::io::Read;
36
+ /// # fn pkcs12() -> Result<(), Box<dyn std::error::Error>> {
37
+ /// let mut buf = Vec::new();
38
+ /// File::open("my-ident.pfx")?.read_to_end(&mut buf)?;
39
+ /// let pkcs12 = wreq::Identity::from_pkcs12_der(&buf, "my-privkey-password")?;
40
+ /// # drop(pkcs12);
41
+ /// # Ok(())
42
+ /// # }
43
+ /// ```
44
+ pub fn from_pkcs12_der(buf: &[u8], pass: &str) -> crate::Result<Identity> {
45
+ let pkcs12 = Pkcs12::from_der(buf).map_err(Error::tls)?;
46
+ let parsed = pkcs12.parse(pass).map_err(Error::tls)?;
47
+ Ok(Identity {
48
+ pkey: parsed.pkey,
49
+ cert: parsed.cert,
50
+ // > The stack is the reverse of what you might expect due to the way
51
+ // > PKCS12_parse is implemented, so we need to load it backwards.
52
+ // > https://github.com/sfackler/rust-native-tls/commit/05fb5e583be589ab63d9f83d986d095639f8ec44
53
+ chain: parsed.chain.into_iter().flatten().rev().collect(),
54
+ })
55
+ }
56
+
57
+ /// Parses a chain of PEM encoded X509 certificates, with the leaf certificate first.
58
+ /// `key` is a PEM encoded PKCS #8 formatted private key for the leaf certificate.
59
+ ///
60
+ /// The certificate chain should contain any intermediate certificates that should be sent to
61
+ /// clients to allow them to build a chain to a trusted root.
62
+ ///
63
+ /// A certificate chain here means a series of PEM encoded certificates concatenated together.
64
+ ///
65
+ /// # Examples
66
+ ///
67
+ /// ```
68
+ /// # use std::fs;
69
+ /// # fn pkcs8() -> Result<(), Box<dyn std::error::Error>> {
70
+ /// let cert = fs::read("client.pem")?;
71
+ /// let key = fs::read("key.pem")?;
72
+ /// let pkcs8 = wreq::Identity::from_pkcs8_pem(&cert, &key)?;
73
+ /// # drop(pkcs8);
74
+ /// # Ok(())
75
+ /// # }
76
+ /// ```
77
+ pub fn from_pkcs8_pem(buf: &[u8], key: &[u8]) -> crate::Result<Identity> {
78
+ if !key.starts_with(b"-----BEGIN PRIVATE KEY-----") {
79
+ return Err(Error::builder("expected PKCS#8 PEM"));
80
+ }
81
+
82
+ let pkey = PKey::private_key_from_pem(key).map_err(Error::tls)?;
83
+ let mut cert_chain = X509::stack_from_pem(buf).map_err(Error::tls)?.into_iter();
84
+ let cert = cert_chain.next().ok_or_else(|| {
85
+ Error::builder("at least one certificate must be provided to create an identity")
86
+ })?;
87
+ let chain = cert_chain.collect();
88
+ Ok(Identity { pkey, cert, chain })
89
+ }
90
+
91
+ pub(crate) fn add_to_tls(
92
+ &self,
93
+ connector: &mut boring2::ssl::SslConnectorBuilder,
94
+ ) -> crate::Result<()> {
95
+ connector.set_certificate(&self.cert).map_err(Error::tls)?;
96
+ connector.set_private_key(&self.pkey).map_err(Error::tls)?;
97
+ for cert in self.chain.iter() {
98
+ // https://www.openssl.org/docs/manmaster/man3/SSL_CTX_add_extra_chain_cert.html
99
+ // specifies that "When sending a certificate chain, extra chain certificates are
100
+ // sent in order following the end entity certificate."
101
+ connector
102
+ .add_extra_chain_cert(cert.clone())
103
+ .map_err(Error::tls)?;
104
+ }
105
+ Ok(())
106
+ }
107
+ }
108
+
109
+ #[cfg(test)]
110
+ mod test {
111
+ use super::Identity;
112
+
113
+ #[test]
114
+ fn identity_from_pkcs12_der_invalid() {
115
+ Identity::from_pkcs12_der(b"not der", "nope").unwrap_err();
116
+ }
117
+
118
+ #[test]
119
+ fn identity_from_pkcs8_pem_invalid() {
120
+ Identity::from_pkcs8_pem(b"not pem", b"not key").unwrap_err();
121
+ }
122
+ }
@@ -0,0 +1,71 @@
1
+ use boring2::x509::store::{X509Store, X509StoreBuilder};
2
+
3
+ use super::{Certificate, CertificateInput};
4
+ use crate::{Error, Result};
5
+
6
+ pub fn parse_certs<'c, I>(
7
+ certs: I,
8
+ parser: fn(&'c [u8]) -> crate::Result<Certificate>,
9
+ ) -> Result<X509Store>
10
+ where
11
+ I: IntoIterator,
12
+ I::Item: Into<CertificateInput<'c>>,
13
+ {
14
+ let mut store = X509StoreBuilder::new().map_err(Error::tls)?;
15
+ let certs = filter_map_certs(certs, parser);
16
+ process_certs(certs.into_iter(), &mut store)?;
17
+ Ok(store.build())
18
+ }
19
+
20
+ pub fn parse_certs_with_stack<C, F>(certs: C, parse: F) -> Result<X509Store>
21
+ where
22
+ C: AsRef<[u8]>,
23
+ F: Fn(C) -> Result<Vec<Certificate>>,
24
+ {
25
+ let mut store = X509StoreBuilder::new().map_err(Error::tls)?;
26
+ let certs = parse(certs)?;
27
+ process_certs(certs.into_iter(), &mut store)?;
28
+ Ok(store.build())
29
+ }
30
+
31
+ pub fn process_certs<I>(iter: I, store: &mut X509StoreBuilder) -> Result<()>
32
+ where
33
+ I: Iterator<Item = Certificate>,
34
+ {
35
+ let mut valid_count = 0;
36
+ let mut invalid_count = 0;
37
+ for cert in iter {
38
+ if let Err(_err) = store.add_cert(cert.0) {
39
+ invalid_count += 1;
40
+ warn!("tls failed to parse certificate: {:?}", _err);
41
+ } else {
42
+ valid_count += 1;
43
+ }
44
+ }
45
+
46
+ if valid_count == 0 && invalid_count > 0 {
47
+ return Err(Error::builder("invalid certificate"));
48
+ }
49
+
50
+ Ok(())
51
+ }
52
+
53
+ pub fn filter_map_certs<'c, I>(
54
+ certs: I,
55
+ parser: fn(&'c [u8]) -> Result<Certificate>,
56
+ ) -> impl Iterator<Item = Certificate>
57
+ where
58
+ I: IntoIterator,
59
+ I::Item: Into<CertificateInput<'c>>,
60
+ {
61
+ certs
62
+ .into_iter()
63
+ .map(Into::into)
64
+ .filter_map(move |data| match data.with_parser(parser) {
65
+ Ok(cert) => Some(cert),
66
+ Err(_err) => {
67
+ warn!("tls failed to parse certificate: {:?}", _err);
68
+ None
69
+ }
70
+ })
71
+ }
@@ -0,0 +1,228 @@
1
+ use std::sync::Arc;
2
+
3
+ use boring2::{
4
+ ssl::SslConnectorBuilder,
5
+ x509::store::{X509Store, X509StoreBuilder},
6
+ };
7
+
8
+ use super::{
9
+ Certificate, CertificateInput,
10
+ parser::{filter_map_certs, parse_certs, parse_certs_with_stack, process_certs},
11
+ };
12
+ use crate::{Error, Result};
13
+
14
+ /// A builder for constructing a `CertStore`.
15
+ pub struct CertStoreBuilder {
16
+ builder: Result<X509StoreBuilder>,
17
+ }
18
+
19
+ // ====== impl CertStoreBuilder ======
20
+
21
+ impl CertStoreBuilder {
22
+ /// Adds a DER-encoded certificate to the certificate store.
23
+ #[inline]
24
+ pub fn add_der_cert<'c, C>(self, cert: C) -> Self
25
+ where
26
+ C: Into<CertificateInput<'c>>,
27
+ {
28
+ self.parse_cert(cert, Certificate::from_der)
29
+ }
30
+
31
+ /// Adds a PEM-encoded certificate to the certificate store.
32
+ #[inline]
33
+ pub fn add_pem_cert<'c, C>(self, cert: C) -> Self
34
+ where
35
+ C: Into<CertificateInput<'c>>,
36
+ {
37
+ self.parse_cert(cert, Certificate::from_pem)
38
+ }
39
+
40
+ /// Adds multiple DER-encoded certificates to the certificate store.
41
+ #[inline]
42
+ pub fn add_der_certs<'c, I>(self, certs: I) -> Self
43
+ where
44
+ I: IntoIterator,
45
+ I::Item: Into<CertificateInput<'c>>,
46
+ {
47
+ self.parse_certs(certs, Certificate::from_der)
48
+ }
49
+
50
+ /// Adds multiple PEM-encoded certificates to the certificate store.
51
+ #[inline]
52
+ pub fn add_pem_certs<'c, I>(self, certs: I) -> Self
53
+ where
54
+ I: IntoIterator,
55
+ I::Item: Into<CertificateInput<'c>>,
56
+ {
57
+ self.parse_certs(certs, Certificate::from_pem)
58
+ }
59
+
60
+ /// Adds a PEM-encoded certificate stack to the certificate store.
61
+ pub fn add_stack_pem_certs<C>(mut self, certs: C) -> Self
62
+ where
63
+ C: AsRef<[u8]>,
64
+ {
65
+ if let Ok(ref mut builder) = self.builder {
66
+ let result = Certificate::stack_from_pem(certs.as_ref())
67
+ .and_then(|certs| process_certs(certs.into_iter(), builder));
68
+
69
+ if let Err(err) = result {
70
+ self.builder = Err(err);
71
+ }
72
+ }
73
+ self
74
+ }
75
+
76
+ /// Load certificates from their default locations.
77
+ ///
78
+ /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR`
79
+ /// environment variables if present, or defaults specified at OpenSSL
80
+ /// build time otherwise.
81
+ pub fn set_default_paths(mut self) -> Self {
82
+ if let Ok(ref mut builder) = self.builder {
83
+ if let Err(err) = builder.set_default_paths() {
84
+ self.builder = Err(Error::tls(err));
85
+ }
86
+ }
87
+ self
88
+ }
89
+
90
+ /// Constructs the `CertStore`.
91
+ ///
92
+ /// This method finalizes the builder and constructs the `CertStore`
93
+ /// containing all the added certificates.
94
+ #[inline]
95
+ pub fn build(self) -> Result<CertStore> {
96
+ self.builder
97
+ .map(X509StoreBuilder::build)
98
+ .map(Arc::new)
99
+ .map(CertStore)
100
+ }
101
+ }
102
+
103
+ impl CertStoreBuilder {
104
+ fn parse_cert<'c, C, P>(mut self, cert: C, parser: P) -> Self
105
+ where
106
+ C: Into<CertificateInput<'c>>,
107
+ P: Fn(&'c [u8]) -> Result<Certificate>,
108
+ {
109
+ if let Ok(ref mut builder) = self.builder {
110
+ let input = cert.into();
111
+ let result = input
112
+ .with_parser(parser)
113
+ .and_then(|cert| builder.add_cert(cert.0).map_err(Error::tls));
114
+
115
+ if let Err(err) = result {
116
+ self.builder = Err(err);
117
+ }
118
+ }
119
+ self
120
+ }
121
+
122
+ fn parse_certs<'c, I>(mut self, certs: I, parser: fn(&'c [u8]) -> Result<Certificate>) -> Self
123
+ where
124
+ I: IntoIterator,
125
+ I::Item: Into<CertificateInput<'c>>,
126
+ {
127
+ if let Ok(ref mut builder) = self.builder {
128
+ let certs = filter_map_certs(certs, parser);
129
+ if let Err(err) = process_certs(certs, builder) {
130
+ self.builder = Err(err);
131
+ }
132
+ }
133
+ self
134
+ }
135
+ }
136
+
137
+ /// A thread-safe certificate store for TLS connections.
138
+ ///
139
+ /// [`CertStore`] manages a collection of trusted certificates used for verifying peer identities.
140
+ /// It is designed to be shared and reused across requests and connections, similar to `Client`.
141
+ ///
142
+ /// Internally, [`CertStore`] uses an [`Arc`] for reference counting, so you do **not** need to wrap
143
+ /// it in an additional [`Rc`] or [`Arc`] for sharing between threads or tasks.
144
+ ///
145
+ /// To configure a [`CertStore`], use [`CertStore::builder()`]. You can also construct it from DER
146
+ /// or PEM certificates, or load system defaults.
147
+ ///
148
+ /// [`Rc`]: std::rc::Rc
149
+ /// [`Arc`]: std::sync::Arc
150
+ #[derive(Clone)]
151
+ pub struct CertStore(Arc<X509Store>);
152
+
153
+ // ====== impl CertStore ======
154
+
155
+ impl CertStore {
156
+ /// Creates a new `CertStoreBuilder`.
157
+ #[inline]
158
+ pub fn builder() -> CertStoreBuilder {
159
+ CertStoreBuilder {
160
+ builder: X509StoreBuilder::new().map_err(Error::builder),
161
+ }
162
+ }
163
+
164
+ /// Creates a new `CertStore` from a collection of DER-encoded certificates.
165
+ #[inline]
166
+ pub fn from_der_certs<'c, C>(certs: C) -> Result<CertStore>
167
+ where
168
+ C: IntoIterator,
169
+ C::Item: Into<CertificateInput<'c>>,
170
+ {
171
+ parse_certs(certs, Certificate::from_der)
172
+ .map(Arc::new)
173
+ .map(CertStore)
174
+ }
175
+
176
+ /// Creates a new `CertStore` from a collection of PEM-encoded certificates.
177
+ #[inline]
178
+ pub fn from_pem_certs<'c, C>(certs: C) -> Result<CertStore>
179
+ where
180
+ C: IntoIterator,
181
+ C::Item: Into<CertificateInput<'c>>,
182
+ {
183
+ parse_certs(certs, Certificate::from_pem)
184
+ .map(Arc::new)
185
+ .map(CertStore)
186
+ }
187
+
188
+ /// Creates a new `CertStore` from a PEM-encoded certificate stack.
189
+ #[inline]
190
+ pub fn from_pem_stack<C>(certs: C) -> Result<CertStore>
191
+ where
192
+ C: AsRef<[u8]>,
193
+ {
194
+ parse_certs_with_stack(certs, Certificate::stack_from_pem)
195
+ .map(Arc::new)
196
+ .map(CertStore)
197
+ }
198
+ }
199
+
200
+ impl CertStore {
201
+ #[inline]
202
+ pub(crate) fn add_to_tls(&self, tls: &mut SslConnectorBuilder) {
203
+ tls.set_cert_store_ref(&self.0);
204
+ }
205
+ }
206
+
207
+ impl Default for CertStore {
208
+ fn default() -> Self {
209
+ #[cfg(feature = "webpki-roots")]
210
+ static LOAD_CERTS: std::sync::LazyLock<CertStore> = std::sync::LazyLock::new(|| {
211
+ CertStore::builder()
212
+ .add_der_certs(webpki_root_certs::TLS_SERVER_ROOT_CERTS)
213
+ .build()
214
+ .expect("failed to load default cert store")
215
+ });
216
+
217
+ #[cfg(not(feature = "webpki-roots"))]
218
+ {
219
+ CertStore::builder()
220
+ .set_default_paths()
221
+ .build()
222
+ .expect("failed to load default cert store")
223
+ }
224
+
225
+ #[cfg(feature = "webpki-roots")]
226
+ LOAD_CERTS.clone()
227
+ }
228
+ }
@@ -0,0 +1,68 @@
1
+ mod identity;
2
+ mod parser;
3
+ mod store;
4
+
5
+ use boring2::x509::X509;
6
+
7
+ pub use self::{
8
+ identity::Identity,
9
+ store::{CertStore, CertStoreBuilder},
10
+ };
11
+ use crate::Error;
12
+
13
+ /// A certificate input.
14
+ pub enum CertificateInput<'c> {
15
+ /// Raw DER or PEM data.
16
+ Raw(&'c [u8]),
17
+ /// An already parsed certificate.
18
+ Parsed(Certificate),
19
+ }
20
+
21
+ impl<'a> CertificateInput<'a> {
22
+ pub(crate) fn with_parser<F>(self, parser: F) -> crate::Result<Certificate>
23
+ where
24
+ F: Fn(&'a [u8]) -> crate::Result<Certificate>,
25
+ {
26
+ match self {
27
+ CertificateInput::Raw(data) => parser(data),
28
+ CertificateInput::Parsed(cert) => Ok(cert),
29
+ }
30
+ }
31
+ }
32
+
33
+ impl From<Certificate> for CertificateInput<'_> {
34
+ fn from(cert: Certificate) -> Self {
35
+ CertificateInput::Parsed(cert)
36
+ }
37
+ }
38
+
39
+ impl<'c, T: AsRef<[u8]> + ?Sized + 'c> From<&'c T> for CertificateInput<'c> {
40
+ fn from(value: &'c T) -> CertificateInput<'c> {
41
+ CertificateInput::Raw(value.as_ref())
42
+ }
43
+ }
44
+
45
+ /// A certificate.
46
+ #[derive(Clone)]
47
+ pub struct Certificate(X509);
48
+
49
+ impl Certificate {
50
+ /// Parse a certificate from DER data.
51
+ #[inline]
52
+ pub fn from_der<C: AsRef<[u8]>>(cert: C) -> crate::Result<Self> {
53
+ X509::from_der(cert.as_ref()).map(Self).map_err(Error::tls)
54
+ }
55
+
56
+ /// Parse a certificate from PEM data.
57
+ #[inline]
58
+ pub fn from_pem<C: AsRef<[u8]>>(cert: C) -> crate::Result<Self> {
59
+ X509::from_pem(cert.as_ref()).map(Self).map_err(Error::tls)
60
+ }
61
+
62
+ /// Parse a stack of certificates from DER data.
63
+ #[inline]
64
+ pub fn stack_from_pem<C: AsRef<[u8]>>(cert: C) -> crate::Result<Vec<Self>> {
65
+ let certs = X509::stack_from_pem(cert.as_ref()).map_err(Error::tls)?;
66
+ Ok(certs.into_iter().map(Self).collect())
67
+ }
68
+ }
@@ -0,0 +1,154 @@
1
+ //! TLS options configuration
2
+ //!
3
+ //! By default, a `Client` will make use of BoringSSL for TLS.
4
+ //!
5
+ //! - Various parts of TLS can also be configured or even disabled on the `ClientBuilder`.
6
+
7
+ pub(crate) mod conn;
8
+ mod keylog;
9
+ mod options;
10
+ mod x509;
11
+
12
+ use boring2::ssl;
13
+ pub use boring2::ssl::{CertificateCompressionAlgorithm, ExtensionType};
14
+ use bytes::{BufMut, Bytes, BytesMut};
15
+
16
+ pub use self::{
17
+ keylog::KeyLog,
18
+ options::{TlsOptions, TlsOptionsBuilder},
19
+ x509::{CertStore, CertStoreBuilder, Certificate, Identity},
20
+ };
21
+
22
+ /// Http extension carrying extra TLS layer information.
23
+ /// Made available to clients on responses when `tls_info` is set.
24
+ #[derive(Debug, Clone)]
25
+ pub struct TlsInfo {
26
+ pub(crate) peer_certificate: Option<Bytes>,
27
+ pub(crate) peer_certificate_chain: Option<Vec<Bytes>>,
28
+ }
29
+
30
+ impl TlsInfo {
31
+ /// Get the DER encoded leaf certificate of the peer.
32
+ pub fn peer_certificate(&self) -> Option<&[u8]> {
33
+ self.peer_certificate.as_deref()
34
+ }
35
+
36
+ /// Get the DER encoded certificate chain of the peer.
37
+ ///
38
+ /// This includes the leaf certificate on the client side.
39
+ pub fn peer_certificate_chain(&self) -> Option<impl Iterator<Item = &[u8]>> {
40
+ self.peer_certificate_chain
41
+ .as_ref()
42
+ .map(|v| v.iter().map(|b| b.as_ref()))
43
+ }
44
+ }
45
+
46
+ /// A TLS protocol version.
47
+ #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
48
+ pub struct TlsVersion(ssl::SslVersion);
49
+
50
+ impl TlsVersion {
51
+ /// Version 1.0 of the TLS protocol.
52
+ pub const TLS_1_0: TlsVersion = TlsVersion(ssl::SslVersion::TLS1);
53
+
54
+ /// Version 1.1 of the TLS protocol.
55
+ pub const TLS_1_1: TlsVersion = TlsVersion(ssl::SslVersion::TLS1_1);
56
+
57
+ /// Version 1.2 of the TLS protocol.
58
+ pub const TLS_1_2: TlsVersion = TlsVersion(ssl::SslVersion::TLS1_2);
59
+
60
+ /// Version 1.3 of the TLS protocol.
61
+ pub const TLS_1_3: TlsVersion = TlsVersion(ssl::SslVersion::TLS1_3);
62
+ }
63
+
64
+ /// A TLS ALPN protocol.
65
+ #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
66
+ pub struct AlpnProtocol(&'static [u8]);
67
+
68
+ impl AlpnProtocol {
69
+ /// Prefer HTTP/1.1
70
+ pub const HTTP1: AlpnProtocol = AlpnProtocol(b"http/1.1");
71
+
72
+ /// Prefer HTTP/2
73
+ pub const HTTP2: AlpnProtocol = AlpnProtocol(b"h2");
74
+
75
+ /// Prefer HTTP/3
76
+ pub const HTTP3: AlpnProtocol = AlpnProtocol(b"h3");
77
+
78
+ /// Create a new [`AlpnProtocol`] from a static byte slice.
79
+ #[inline]
80
+ pub const fn new(value: &'static [u8]) -> Self {
81
+ AlpnProtocol(value)
82
+ }
83
+
84
+ #[inline]
85
+ fn encode(self) -> Bytes {
86
+ Self::encode_sequence(std::iter::once(&self))
87
+ }
88
+
89
+ fn encode_sequence<'a, I>(items: I) -> Bytes
90
+ where
91
+ I: IntoIterator<Item = &'a AlpnProtocol>,
92
+ {
93
+ let mut buf = BytesMut::new();
94
+ for item in items {
95
+ buf.put_u8(item.0.len() as u8);
96
+ buf.extend_from_slice(item.0);
97
+ }
98
+ buf.freeze()
99
+ }
100
+ }
101
+
102
+ /// A TLS ALPS protocol.
103
+ #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
104
+ pub struct AlpsProtocol(&'static [u8]);
105
+
106
+ impl AlpsProtocol {
107
+ /// Prefer HTTP/1.1
108
+ pub const HTTP1: AlpsProtocol = AlpsProtocol(b"http/1.1");
109
+
110
+ /// Prefer HTTP/2
111
+ pub const HTTP2: AlpsProtocol = AlpsProtocol(b"h2");
112
+
113
+ /// Prefer HTTP/3
114
+ pub const HTTP3: AlpsProtocol = AlpsProtocol(b"h3");
115
+ }
116
+
117
+ #[cfg(test)]
118
+ mod tests {
119
+ use super::*;
120
+
121
+ #[test]
122
+ fn alpn_protocol_encode() {
123
+ let alpn = AlpnProtocol::encode_sequence(&[AlpnProtocol::HTTP1, AlpnProtocol::HTTP2]);
124
+ assert_eq!(alpn, Bytes::from_static(b"\x08http/1.1\x02h2"));
125
+
126
+ let alpn = AlpnProtocol::encode_sequence(&[AlpnProtocol::HTTP3]);
127
+ assert_eq!(alpn, Bytes::from_static(b"\x02h3"));
128
+
129
+ let alpn = AlpnProtocol::encode_sequence(&[AlpnProtocol::HTTP1, AlpnProtocol::HTTP3]);
130
+ assert_eq!(alpn, Bytes::from_static(b"\x08http/1.1\x02h3"));
131
+
132
+ let alpn = AlpnProtocol::encode_sequence(&[AlpnProtocol::HTTP2, AlpnProtocol::HTTP3]);
133
+ assert_eq!(alpn, Bytes::from_static(b"\x02h2\x02h3"));
134
+
135
+ let alpn = AlpnProtocol::encode_sequence(&[
136
+ AlpnProtocol::HTTP1,
137
+ AlpnProtocol::HTTP2,
138
+ AlpnProtocol::HTTP3,
139
+ ]);
140
+ assert_eq!(alpn, Bytes::from_static(b"\x08http/1.1\x02h2\x02h3"));
141
+ }
142
+
143
+ #[test]
144
+ fn alpn_protocol_encode_single() {
145
+ let alpn = AlpnProtocol::HTTP1.encode();
146
+ assert_eq!(alpn, b"\x08http/1.1".as_ref());
147
+
148
+ let alpn = AlpnProtocol::HTTP2.encode();
149
+ assert_eq!(alpn, b"\x02h2".as_ref());
150
+
151
+ let alpn = AlpnProtocol::HTTP3.encode();
152
+ assert_eq!(alpn, b"\x02h3".as_ref());
153
+ }
154
+ }
@@ -0,0 +1,55 @@
1
+ macro_rules! debug {
2
+ ($($arg:tt)+) => {
3
+ {
4
+ #[cfg(feature = "tracing")]
5
+ {
6
+ ::tracing::debug!($($arg)+);
7
+ }
8
+ }
9
+ }
10
+ }
11
+
12
+ macro_rules! trace {
13
+ ($($arg:tt)*) => {
14
+ {
15
+ #[cfg(feature = "tracing")]
16
+ {
17
+ ::tracing::trace!($($arg)+);
18
+ }
19
+ }
20
+ }
21
+ }
22
+
23
+ macro_rules! trace_span {
24
+ ($($arg:tt)*) => {
25
+ {
26
+ #[cfg(feature = "tracing")]
27
+ {
28
+ let _span = ::tracing::trace_span!($($arg)+);
29
+ let _ = _span.entered();
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ macro_rules! warn {
36
+ ($($arg:tt)*) => {
37
+ {
38
+ #[cfg(feature = "tracing")]
39
+ {
40
+ ::tracing::warn!($($arg)+);
41
+ }
42
+ }
43
+ }
44
+ }
45
+
46
+ macro_rules! error {
47
+ ($($arg:tt)*) => {
48
+ {
49
+ #[cfg(feature = "tracing")]
50
+ {
51
+ ::tracing::error!($($arg)+);
52
+ }
53
+ }
54
+ }
55
+ }