httpx-patched 1.6.2.1
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +191 -0
- data/README.md +162 -0
- data/doc/release_notes/0_0_1.md +7 -0
- data/doc/release_notes/0_0_2.md +9 -0
- data/doc/release_notes/0_0_3.md +9 -0
- data/doc/release_notes/0_0_4.md +7 -0
- data/doc/release_notes/0_0_5.md +5 -0
- data/doc/release_notes/0_10_0.md +66 -0
- data/doc/release_notes/0_10_1.md +37 -0
- data/doc/release_notes/0_10_2.md +5 -0
- data/doc/release_notes/0_11_0.md +74 -0
- data/doc/release_notes/0_11_1.md +5 -0
- data/doc/release_notes/0_11_2.md +5 -0
- data/doc/release_notes/0_11_3.md +5 -0
- data/doc/release_notes/0_12_0.md +55 -0
- data/doc/release_notes/0_13_0.md +58 -0
- data/doc/release_notes/0_13_1.md +5 -0
- data/doc/release_notes/0_13_2.md +9 -0
- data/doc/release_notes/0_14_0.md +79 -0
- data/doc/release_notes/0_14_1.md +7 -0
- data/doc/release_notes/0_14_2.md +6 -0
- data/doc/release_notes/0_14_3.md +5 -0
- data/doc/release_notes/0_14_4.md +5 -0
- data/doc/release_notes/0_14_5.md +11 -0
- data/doc/release_notes/0_15_0.md +53 -0
- data/doc/release_notes/0_15_1.md +8 -0
- data/doc/release_notes/0_15_2.md +9 -0
- data/doc/release_notes/0_15_3.md +5 -0
- data/doc/release_notes/0_15_4.md +5 -0
- data/doc/release_notes/0_16_0.md +93 -0
- data/doc/release_notes/0_16_1.md +5 -0
- data/doc/release_notes/0_17_0.md +49 -0
- data/doc/release_notes/0_18_0.md +69 -0
- data/doc/release_notes/0_18_1.md +12 -0
- data/doc/release_notes/0_18_2.md +10 -0
- data/doc/release_notes/0_18_3.md +7 -0
- data/doc/release_notes/0_18_4.md +14 -0
- data/doc/release_notes/0_18_5.md +10 -0
- data/doc/release_notes/0_18_6.md +5 -0
- data/doc/release_notes/0_18_7.md +5 -0
- data/doc/release_notes/0_19_0.md +39 -0
- data/doc/release_notes/0_19_1.md +5 -0
- data/doc/release_notes/0_19_2.md +7 -0
- data/doc/release_notes/0_19_3.md +6 -0
- data/doc/release_notes/0_19_4.md +14 -0
- data/doc/release_notes/0_19_5.md +13 -0
- data/doc/release_notes/0_19_6.md +5 -0
- data/doc/release_notes/0_19_7.md +5 -0
- data/doc/release_notes/0_19_8.md +5 -0
- data/doc/release_notes/0_1_0.md +9 -0
- data/doc/release_notes/0_20_0.md +36 -0
- data/doc/release_notes/0_20_1.md +5 -0
- data/doc/release_notes/0_20_2.md +7 -0
- data/doc/release_notes/0_20_3.md +6 -0
- data/doc/release_notes/0_20_4.md +17 -0
- data/doc/release_notes/0_20_5.md +3 -0
- data/doc/release_notes/0_21_0.md +96 -0
- data/doc/release_notes/0_21_1.md +12 -0
- data/doc/release_notes/0_22_0.md +13 -0
- data/doc/release_notes/0_22_1.md +11 -0
- data/doc/release_notes/0_22_2.md +5 -0
- data/doc/release_notes/0_22_3.md +55 -0
- data/doc/release_notes/0_22_4.md +6 -0
- data/doc/release_notes/0_22_5.md +6 -0
- data/doc/release_notes/0_23_0.md +42 -0
- data/doc/release_notes/0_23_1.md +5 -0
- data/doc/release_notes/0_23_2.md +5 -0
- data/doc/release_notes/0_23_3.md +6 -0
- data/doc/release_notes/0_23_4.md +5 -0
- data/doc/release_notes/0_24_0.md +48 -0
- data/doc/release_notes/0_24_1.md +12 -0
- data/doc/release_notes/0_24_2.md +12 -0
- data/doc/release_notes/0_24_3.md +12 -0
- data/doc/release_notes/0_24_4.md +18 -0
- data/doc/release_notes/0_24_5.md +6 -0
- data/doc/release_notes/0_24_6.md +5 -0
- data/doc/release_notes/0_24_7.md +10 -0
- data/doc/release_notes/0_2_0.md +5 -0
- data/doc/release_notes/0_2_1.md +16 -0
- data/doc/release_notes/0_3_0.md +12 -0
- data/doc/release_notes/0_3_1.md +6 -0
- data/doc/release_notes/0_4_0.md +51 -0
- data/doc/release_notes/0_4_1.md +3 -0
- data/doc/release_notes/0_5_0.md +15 -0
- data/doc/release_notes/0_5_1.md +14 -0
- data/doc/release_notes/0_6_0.md +5 -0
- data/doc/release_notes/0_6_1.md +6 -0
- data/doc/release_notes/0_6_2.md +6 -0
- data/doc/release_notes/0_6_3.md +13 -0
- data/doc/release_notes/0_6_4.md +21 -0
- data/doc/release_notes/0_6_5.md +22 -0
- data/doc/release_notes/0_6_6.md +19 -0
- data/doc/release_notes/0_6_7.md +5 -0
- data/doc/release_notes/0_7_0.md +46 -0
- data/doc/release_notes/0_8_0.md +27 -0
- data/doc/release_notes/0_8_1.md +8 -0
- data/doc/release_notes/0_8_2.md +7 -0
- data/doc/release_notes/0_9_0.md +38 -0
- data/doc/release_notes/1_0_0.md +60 -0
- data/doc/release_notes/1_0_1.md +5 -0
- data/doc/release_notes/1_0_2.md +7 -0
- data/doc/release_notes/1_1_0.md +32 -0
- data/doc/release_notes/1_1_1.md +17 -0
- data/doc/release_notes/1_1_2.md +12 -0
- data/doc/release_notes/1_1_3.md +18 -0
- data/doc/release_notes/1_1_4.md +6 -0
- data/doc/release_notes/1_1_5.md +12 -0
- data/doc/release_notes/1_2_0.md +49 -0
- data/doc/release_notes/1_2_1.md +6 -0
- data/doc/release_notes/1_2_2.md +10 -0
- data/doc/release_notes/1_2_3.md +16 -0
- data/doc/release_notes/1_2_4.md +8 -0
- data/doc/release_notes/1_2_5.md +7 -0
- data/doc/release_notes/1_2_6.md +13 -0
- data/doc/release_notes/1_3_0.md +18 -0
- data/doc/release_notes/1_3_1.md +17 -0
- data/doc/release_notes/1_3_2.md +6 -0
- data/doc/release_notes/1_3_3.md +5 -0
- data/doc/release_notes/1_3_4.md +6 -0
- data/doc/release_notes/1_4_0.md +43 -0
- data/doc/release_notes/1_4_1.md +19 -0
- data/doc/release_notes/1_4_2.md +20 -0
- data/doc/release_notes/1_4_3.md +11 -0
- data/doc/release_notes/1_4_4.md +14 -0
- data/doc/release_notes/1_5_0.md +126 -0
- data/doc/release_notes/1_5_1.md +6 -0
- data/doc/release_notes/1_6_0.md +50 -0
- data/doc/release_notes/1_6_1.md +17 -0
- data/doc/release_notes/1_6_2.md +11 -0
- data/lib/httpx/adapters/datadog.rb +359 -0
- data/lib/httpx/adapters/faraday.rb +303 -0
- data/lib/httpx/adapters/sentry.rb +121 -0
- data/lib/httpx/adapters/webmock.rb +175 -0
- data/lib/httpx/altsvc.rb +163 -0
- data/lib/httpx/base64.rb +27 -0
- data/lib/httpx/buffer.rb +61 -0
- data/lib/httpx/callbacks.rb +35 -0
- data/lib/httpx/chainable.rb +106 -0
- data/lib/httpx/connection/http1.rb +399 -0
- data/lib/httpx/connection/http2.rb +468 -0
- data/lib/httpx/connection.rb +954 -0
- data/lib/httpx/domain_name.rb +145 -0
- data/lib/httpx/errors.rb +111 -0
- data/lib/httpx/extensions.rb +59 -0
- data/lib/httpx/headers.rb +176 -0
- data/lib/httpx/io/ssl.rb +163 -0
- data/lib/httpx/io/tcp.rb +239 -0
- data/lib/httpx/io/udp.rb +62 -0
- data/lib/httpx/io/unix.rb +71 -0
- data/lib/httpx/io.rb +11 -0
- data/lib/httpx/loggable.rb +56 -0
- data/lib/httpx/options.rb +463 -0
- data/lib/httpx/parser/http1.rb +186 -0
- data/lib/httpx/plugins/auth/basic.rb +20 -0
- data/lib/httpx/plugins/auth/digest.rb +102 -0
- data/lib/httpx/plugins/auth/ntlm.rb +35 -0
- data/lib/httpx/plugins/auth/socks5.rb +22 -0
- data/lib/httpx/plugins/auth.rb +25 -0
- data/lib/httpx/plugins/aws_sdk_authentication.rb +111 -0
- data/lib/httpx/plugins/aws_sigv4.rb +239 -0
- data/lib/httpx/plugins/basic_auth.rb +29 -0
- data/lib/httpx/plugins/brotli.rb +50 -0
- data/lib/httpx/plugins/callbacks.rb +127 -0
- data/lib/httpx/plugins/circuit_breaker/circuit.rb +100 -0
- data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +53 -0
- data/lib/httpx/plugins/circuit_breaker.rb +147 -0
- data/lib/httpx/plugins/content_digest.rb +204 -0
- data/lib/httpx/plugins/cookies/cookie.rb +174 -0
- data/lib/httpx/plugins/cookies/jar.rb +95 -0
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +143 -0
- data/lib/httpx/plugins/cookies.rb +107 -0
- data/lib/httpx/plugins/digest_auth.rb +67 -0
- data/lib/httpx/plugins/expect.rb +120 -0
- data/lib/httpx/plugins/fiber_concurrency.rb +195 -0
- data/lib/httpx/plugins/follow_redirects.rb +233 -0
- data/lib/httpx/plugins/grpc/call.rb +63 -0
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +90 -0
- data/lib/httpx/plugins/grpc/message.rb +55 -0
- data/lib/httpx/plugins/grpc.rb +282 -0
- data/lib/httpx/plugins/h2c.rb +127 -0
- data/lib/httpx/plugins/internal_telemetry.rb +107 -0
- data/lib/httpx/plugins/ntlm_auth.rb +62 -0
- data/lib/httpx/plugins/oauth.rb +183 -0
- data/lib/httpx/plugins/persistent.rb +82 -0
- data/lib/httpx/plugins/proxy/http.rb +184 -0
- data/lib/httpx/plugins/proxy/socks4.rb +135 -0
- data/lib/httpx/plugins/proxy/socks5.rb +194 -0
- data/lib/httpx/plugins/proxy/ssh.rb +94 -0
- data/lib/httpx/plugins/proxy.rb +349 -0
- data/lib/httpx/plugins/push_promise.rb +81 -0
- data/lib/httpx/plugins/query.rb +35 -0
- data/lib/httpx/plugins/rate_limiter.rb +55 -0
- data/lib/httpx/plugins/response_cache/file_store.rb +140 -0
- data/lib/httpx/plugins/response_cache/store.rb +33 -0
- data/lib/httpx/plugins/response_cache.rb +333 -0
- data/lib/httpx/plugins/retries.rb +230 -0
- data/lib/httpx/plugins/ssrf_filter.rb +145 -0
- data/lib/httpx/plugins/stream.rb +183 -0
- data/lib/httpx/plugins/stream_bidi.rb +315 -0
- data/lib/httpx/plugins/upgrade/h2.rb +64 -0
- data/lib/httpx/plugins/upgrade.rb +86 -0
- data/lib/httpx/plugins/webdav.rb +86 -0
- data/lib/httpx/plugins/xml.rb +76 -0
- data/lib/httpx/pmatch_extensions.rb +33 -0
- data/lib/httpx/pool.rb +190 -0
- data/lib/httpx/punycode.rb +22 -0
- data/lib/httpx/request/body.rb +158 -0
- data/lib/httpx/request.rb +328 -0
- data/lib/httpx/resolver/entry.rb +30 -0
- data/lib/httpx/resolver/https.rb +256 -0
- data/lib/httpx/resolver/multi.rb +102 -0
- data/lib/httpx/resolver/native.rb +547 -0
- data/lib/httpx/resolver/resolver.rb +173 -0
- data/lib/httpx/resolver/system.rb +255 -0
- data/lib/httpx/resolver.rb +189 -0
- data/lib/httpx/response/body.rb +242 -0
- data/lib/httpx/response/buffer.rb +115 -0
- data/lib/httpx/response.rb +304 -0
- data/lib/httpx/selector.rb +282 -0
- data/lib/httpx/session.rb +612 -0
- data/lib/httpx/session_extensions.rb +30 -0
- data/lib/httpx/timers.rb +133 -0
- data/lib/httpx/transcoder/body.rb +43 -0
- data/lib/httpx/transcoder/chunker.rb +115 -0
- data/lib/httpx/transcoder/deflate.rb +37 -0
- data/lib/httpx/transcoder/form.rb +68 -0
- data/lib/httpx/transcoder/gzip.rb +71 -0
- data/lib/httpx/transcoder/json.rb +71 -0
- data/lib/httpx/transcoder/multipart/decoder.rb +141 -0
- data/lib/httpx/transcoder/multipart/encoder.rb +120 -0
- data/lib/httpx/transcoder/multipart/mime_type_detector.rb +78 -0
- data/lib/httpx/transcoder/multipart/part.rb +35 -0
- data/lib/httpx/transcoder/multipart.rb +31 -0
- data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
- data/lib/httpx/transcoder/utils/deflater.rb +75 -0
- data/lib/httpx/transcoder.rb +91 -0
- data/lib/httpx/utils.rb +75 -0
- data/lib/httpx/version.rb +5 -0
- data/lib/httpx.rb +66 -0
- data/sig/altsvc.rbs +33 -0
- data/sig/buffer.rbs +27 -0
- data/sig/callbacks.rbs +15 -0
- data/sig/chainable.rbs +55 -0
- data/sig/connection/http1.rbs +85 -0
- data/sig/connection/http2.rbs +116 -0
- data/sig/connection.rbs +169 -0
- data/sig/domain_name.rbs +17 -0
- data/sig/errors.rbs +69 -0
- data/sig/headers.rbs +49 -0
- data/sig/httpx.rbs +27 -0
- data/sig/io/ssl.rbs +27 -0
- data/sig/io/tcp.rbs +72 -0
- data/sig/io/udp.rbs +25 -0
- data/sig/io/unix.rbs +26 -0
- data/sig/io.rbs +3 -0
- data/sig/loggable.rbs +17 -0
- data/sig/options.rbs +202 -0
- data/sig/parser/http1.rbs +59 -0
- data/sig/plugins/auth/basic.rbs +17 -0
- data/sig/plugins/auth/digest.rbs +25 -0
- data/sig/plugins/auth/ntlm.rbs +20 -0
- data/sig/plugins/auth/socks5.rbs +18 -0
- data/sig/plugins/auth.rbs +13 -0
- data/sig/plugins/aws_sdk_authentication.rbs +43 -0
- data/sig/plugins/aws_sigv4.rbs +78 -0
- data/sig/plugins/basic_auth.rbs +15 -0
- data/sig/plugins/brotli.rbs +22 -0
- data/sig/plugins/callbacks.rbs +38 -0
- data/sig/plugins/circuit_breaker.rbs +71 -0
- data/sig/plugins/compression.rbs +57 -0
- data/sig/plugins/content_digest.rbs +51 -0
- data/sig/plugins/cookies/cookie.rbs +55 -0
- data/sig/plugins/cookies/jar.rbs +26 -0
- data/sig/plugins/cookies/set_cookie_parser.rbs +22 -0
- data/sig/plugins/cookies.rbs +28 -0
- data/sig/plugins/digest_auth.rbs +21 -0
- data/sig/plugins/expect.rbs +15 -0
- data/sig/plugins/fiber_concurrency.rbs +51 -0
- data/sig/plugins/follow_redirects.rbs +47 -0
- data/sig/plugins/grpc/call.rbs +23 -0
- data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
- data/sig/plugins/grpc/message.rbs +17 -0
- data/sig/plugins/grpc.rbs +65 -0
- data/sig/plugins/h2c.rbs +27 -0
- data/sig/plugins/ntlm_auth.rbs +21 -0
- data/sig/plugins/oauth.rbs +68 -0
- data/sig/plugins/persistent.rbs +14 -0
- data/sig/plugins/proxy/http.rbs +30 -0
- data/sig/plugins/proxy/socks4.rbs +37 -0
- data/sig/plugins/proxy/socks5.rbs +49 -0
- data/sig/plugins/proxy/ssh.rbs +18 -0
- data/sig/plugins/proxy.rbs +70 -0
- data/sig/plugins/push_promise.rbs +23 -0
- data/sig/plugins/query.rbs +18 -0
- data/sig/plugins/rate_limiter.rbs +13 -0
- data/sig/plugins/response_cache/file_store.rbs +19 -0
- data/sig/plugins/response_cache/store.rbs +13 -0
- data/sig/plugins/response_cache.rbs +86 -0
- data/sig/plugins/retries.rbs +66 -0
- data/sig/plugins/ssrf_filter.rbs +26 -0
- data/sig/plugins/stream.rbs +54 -0
- data/sig/plugins/stream_bidi.rbs +68 -0
- data/sig/plugins/upgrade/h2.rbs +9 -0
- data/sig/plugins/upgrade.rbs +29 -0
- data/sig/plugins/webdav.rbs +23 -0
- data/sig/plugins/xml.rbs +37 -0
- data/sig/pool.rbs +51 -0
- data/sig/punycode.rbs +5 -0
- data/sig/request/body.rbs +34 -0
- data/sig/request.rbs +88 -0
- data/sig/resolver/entry.rbs +13 -0
- data/sig/resolver/https.rbs +45 -0
- data/sig/resolver/multi.rbs +32 -0
- data/sig/resolver/native.rbs +74 -0
- data/sig/resolver/resolver.rbs +64 -0
- data/sig/resolver/system.rbs +34 -0
- data/sig/resolver.rbs +48 -0
- data/sig/response/body.rbs +52 -0
- data/sig/response/buffer.rbs +23 -0
- data/sig/response.rbs +103 -0
- data/sig/selector.rbs +68 -0
- data/sig/session.rbs +104 -0
- data/sig/timers.rbs +54 -0
- data/sig/transcoder/body.rbs +24 -0
- data/sig/transcoder/chunker.rbs +49 -0
- data/sig/transcoder/deflate.rbs +12 -0
- data/sig/transcoder/form.rbs +34 -0
- data/sig/transcoder/gzip.rbs +27 -0
- data/sig/transcoder/json.rbs +28 -0
- data/sig/transcoder/multipart.rbs +103 -0
- data/sig/transcoder/utils/body_reader.rbs +15 -0
- data/sig/transcoder/utils/deflater.rbs +28 -0
- data/sig/transcoder.rbs +43 -0
- data/sig/utils.rbs +19 -0
- metadata +518 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# domain_name.rb - Domain Name manipulation library for Ruby
|
|
5
|
+
#
|
|
6
|
+
# Copyright (C) 2011-2017 Akinori MUSHA, All rights reserved.
|
|
7
|
+
#
|
|
8
|
+
# Redistribution and use in source and binary forms, with or without
|
|
9
|
+
# modification, are permitted provided that the following conditions
|
|
10
|
+
# are met:
|
|
11
|
+
# 1. Redistributions of source code must retain the above copyright
|
|
12
|
+
# notice, this list of conditions and the following disclaimer.
|
|
13
|
+
# 2. Redistributions in binary form must reproduce the above copyright
|
|
14
|
+
# notice, this list of conditions and the following disclaimer in the
|
|
15
|
+
# documentation and/or other materials provided with the distribution.
|
|
16
|
+
#
|
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
18
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
19
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
20
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
21
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
23
|
+
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
24
|
+
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
25
|
+
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
26
|
+
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
27
|
+
# SUCH DAMAGE.
|
|
28
|
+
|
|
29
|
+
require "ipaddr"
|
|
30
|
+
|
|
31
|
+
module HTTPX
|
|
32
|
+
# Represents a domain name ready for extracting its registered domain
|
|
33
|
+
# and TLD.
|
|
34
|
+
class DomainName
|
|
35
|
+
include Comparable
|
|
36
|
+
|
|
37
|
+
# The full host name normalized, ASCII-ized and downcased using the
|
|
38
|
+
# Unicode NFC rules and the Punycode algorithm. If initialized with
|
|
39
|
+
# an IP address, the string representation of the IP address
|
|
40
|
+
# suitable for opening a connection to.
|
|
41
|
+
attr_reader :hostname
|
|
42
|
+
|
|
43
|
+
# The Unicode representation of the #hostname property.
|
|
44
|
+
#
|
|
45
|
+
# :attr_reader: hostname_idn
|
|
46
|
+
|
|
47
|
+
# The least "universally original" domain part of this domain name.
|
|
48
|
+
# For example, "example.co.uk" for "www.sub.example.co.uk". This
|
|
49
|
+
# may be nil if the hostname does not have one, like when it is an
|
|
50
|
+
# IP address, an effective TLD or higher itself, or of a
|
|
51
|
+
# non-canonical domain.
|
|
52
|
+
attr_reader :domain
|
|
53
|
+
|
|
54
|
+
class << self
|
|
55
|
+
def new(domain)
|
|
56
|
+
return domain if domain.is_a?(self)
|
|
57
|
+
|
|
58
|
+
super(domain)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Normalizes a _domain_ using the Punycode algorithm as necessary.
|
|
62
|
+
# The result will be a downcased, ASCII-only string.
|
|
63
|
+
def normalize(domain)
|
|
64
|
+
unless domain.ascii_only?
|
|
65
|
+
domain = domain.chomp(".").unicode_normalize(:nfc)
|
|
66
|
+
domain = Punycode.encode_hostname(domain)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
domain.downcase
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Parses _hostname_ into a DomainName object. An IP address is also
|
|
74
|
+
# accepted. An IPv6 address may be enclosed in square brackets.
|
|
75
|
+
def initialize(hostname)
|
|
76
|
+
hostname = String(hostname)
|
|
77
|
+
|
|
78
|
+
raise ArgumentError, "domain name must not start with a dot: #{hostname}" if hostname.start_with?(".")
|
|
79
|
+
|
|
80
|
+
begin
|
|
81
|
+
@ipaddr = IPAddr.new(hostname)
|
|
82
|
+
@hostname = @ipaddr.to_s
|
|
83
|
+
return
|
|
84
|
+
rescue IPAddr::Error
|
|
85
|
+
nil
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
@hostname = DomainName.normalize(hostname)
|
|
89
|
+
tld = if (last_dot = @hostname.rindex("."))
|
|
90
|
+
@hostname[(last_dot + 1)..-1]
|
|
91
|
+
else
|
|
92
|
+
@hostname
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# unknown/local TLD
|
|
96
|
+
@domain = if last_dot
|
|
97
|
+
# fallback - accept cookies down to second level
|
|
98
|
+
# cf. http://www.dkim-reputation.org/regdom-libs/
|
|
99
|
+
if (penultimate_dot = @hostname.rindex(".", last_dot - 1))
|
|
100
|
+
@hostname[(penultimate_dot + 1)..-1]
|
|
101
|
+
else
|
|
102
|
+
@hostname
|
|
103
|
+
end
|
|
104
|
+
else
|
|
105
|
+
# no domain part - must be a local hostname
|
|
106
|
+
tld
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Checks if the server represented by this domain is qualified to
|
|
111
|
+
# send and receive cookies with a domain attribute value of
|
|
112
|
+
# _domain_. A true value given as the second argument represents
|
|
113
|
+
# cookies without a domain attribute value, in which case only
|
|
114
|
+
# hostname equality is checked.
|
|
115
|
+
def cookie_domain?(domain, host_only = false)
|
|
116
|
+
# RFC 6265 #5.3
|
|
117
|
+
# When the user agent "receives a cookie":
|
|
118
|
+
return self == @domain if host_only
|
|
119
|
+
|
|
120
|
+
domain = DomainName.new(domain)
|
|
121
|
+
|
|
122
|
+
# RFC 6265 #5.1.3
|
|
123
|
+
# Do not perform subdomain matching against IP addresses.
|
|
124
|
+
@hostname == domain.hostname if @ipaddr
|
|
125
|
+
|
|
126
|
+
# RFC 6265 #4.1.1
|
|
127
|
+
# Domain-value must be a subdomain.
|
|
128
|
+
@domain && self <= domain && domain <= @domain
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def <=>(other)
|
|
132
|
+
other = DomainName.new(other)
|
|
133
|
+
othername = other.hostname
|
|
134
|
+
if othername == @hostname
|
|
135
|
+
0
|
|
136
|
+
elsif @hostname.end_with?(othername) && @hostname[-othername.size - 1, 1] == "."
|
|
137
|
+
# The other is higher
|
|
138
|
+
-1
|
|
139
|
+
else
|
|
140
|
+
# The other is lower
|
|
141
|
+
1
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
data/lib/httpx/errors.rb
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
# the default exception class for exceptions raised by HTTPX.
|
|
5
|
+
class Error < StandardError; end
|
|
6
|
+
|
|
7
|
+
class UnsupportedSchemeError < Error; end
|
|
8
|
+
|
|
9
|
+
class ConnectionError < Error; end
|
|
10
|
+
|
|
11
|
+
# Error raised when there was a timeout. Its subclasses allow for finer-grained
|
|
12
|
+
# control of which timeout happened.
|
|
13
|
+
class TimeoutError < Error
|
|
14
|
+
# The timeout value which caused this error to be raised.
|
|
15
|
+
attr_reader :timeout
|
|
16
|
+
|
|
17
|
+
# initializes the timeout exception with the +timeout+ causing the error, and the
|
|
18
|
+
# error +message+ for it.
|
|
19
|
+
def initialize(timeout, message)
|
|
20
|
+
@timeout = timeout
|
|
21
|
+
super(message)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# clones this error into a HTTPX::ConnectionTimeoutError.
|
|
25
|
+
def to_connection_error
|
|
26
|
+
ex = ConnectTimeoutError.new(@timeout, message)
|
|
27
|
+
ex.set_backtrace(backtrace)
|
|
28
|
+
ex
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Raise when it can't acquire a connection from the pool.
|
|
33
|
+
class PoolTimeoutError < TimeoutError; end
|
|
34
|
+
|
|
35
|
+
# Error raised when there was a timeout establishing the connection to a server.
|
|
36
|
+
# This may be raised due to timeouts during TCP and TLS (when applicable) connection
|
|
37
|
+
# establishment.
|
|
38
|
+
class ConnectTimeoutError < TimeoutError; end
|
|
39
|
+
|
|
40
|
+
# Error raised when there was a timeout while sending a request, or receiving a response
|
|
41
|
+
# from the server.
|
|
42
|
+
class RequestTimeoutError < TimeoutError
|
|
43
|
+
# The HTTPX::Request request object this exception refers to.
|
|
44
|
+
attr_reader :request
|
|
45
|
+
|
|
46
|
+
# initializes the exception with the +request+ and +response+ it refers to, and the
|
|
47
|
+
# +timeout+ causing the error, and the
|
|
48
|
+
def initialize(request, response, timeout)
|
|
49
|
+
@request = request
|
|
50
|
+
@response = response
|
|
51
|
+
super(timeout, "Timed out after #{timeout} seconds")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def marshal_dump
|
|
55
|
+
[message]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Error raised when there was a timeout while receiving a response from the server.
|
|
60
|
+
class ReadTimeoutError < RequestTimeoutError; end
|
|
61
|
+
|
|
62
|
+
# Error raised when there was a timeout while sending a request from the server.
|
|
63
|
+
class WriteTimeoutError < RequestTimeoutError; end
|
|
64
|
+
|
|
65
|
+
# Error raised when there was a timeout while waiting for the HTTP/2 settings frame from the server.
|
|
66
|
+
class SettingsTimeoutError < TimeoutError; end
|
|
67
|
+
|
|
68
|
+
# Error raised when there was a timeout while resolving a domain to an IP.
|
|
69
|
+
class ResolveTimeoutError < TimeoutError; end
|
|
70
|
+
|
|
71
|
+
# Error raise when there was a timeout waiting for readiness of the socket the request is related to.
|
|
72
|
+
class OperationTimeoutError < TimeoutError; end
|
|
73
|
+
|
|
74
|
+
# Error raised when there was an error while resolving a domain to an IP.
|
|
75
|
+
class ResolveError < Error; end
|
|
76
|
+
|
|
77
|
+
# Error raised when there was an error while resolving a domain to an IP
|
|
78
|
+
# using a HTTPX::Resolver::Native resolver.
|
|
79
|
+
class NativeResolveError < ResolveError
|
|
80
|
+
attr_reader :host
|
|
81
|
+
|
|
82
|
+
attr_accessor :connection
|
|
83
|
+
|
|
84
|
+
# initializes the exception with the +connection+ it refers to, the +host+ domain
|
|
85
|
+
# which failed to resolve, and the error +message+.
|
|
86
|
+
def initialize(connection, host, message = "Can't resolve #{host}")
|
|
87
|
+
@connection = connection
|
|
88
|
+
@host = host
|
|
89
|
+
super(message)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# The exception class for HTTP responses with 4xx or 5xx status.
|
|
94
|
+
class HTTPError < Error
|
|
95
|
+
# The HTTPX::Response response object this exception refers to.
|
|
96
|
+
attr_reader :response
|
|
97
|
+
|
|
98
|
+
# Creates the instance and assigns the HTTPX::Response +response+.
|
|
99
|
+
def initialize(response)
|
|
100
|
+
@response = response
|
|
101
|
+
super("HTTP Error: #{@response.status} #{@response.headers}\n#{@response.body}")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# The HTTP response status.
|
|
105
|
+
#
|
|
106
|
+
# error.status #=> 404
|
|
107
|
+
def status
|
|
108
|
+
@response.status
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module HTTPX
|
|
6
|
+
module ArrayExtensions
|
|
7
|
+
module FilterMap
|
|
8
|
+
refine Array do
|
|
9
|
+
# Ruby 2.7 backport
|
|
10
|
+
def filter_map
|
|
11
|
+
return to_enum(:filter_map) unless block_given?
|
|
12
|
+
|
|
13
|
+
each_with_object([]) do |item, res|
|
|
14
|
+
processed = yield(item)
|
|
15
|
+
res << processed if processed
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end unless Array.method_defined?(:filter_map)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module Intersect
|
|
22
|
+
refine Array do
|
|
23
|
+
# Ruby 3.1 backport
|
|
24
|
+
def intersect?(arr)
|
|
25
|
+
if size < arr.size
|
|
26
|
+
smaller = self
|
|
27
|
+
else
|
|
28
|
+
smaller, arr = arr, self
|
|
29
|
+
end
|
|
30
|
+
(arr & smaller).size > 0
|
|
31
|
+
end
|
|
32
|
+
end unless Array.method_defined?(:intersect?)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
module URIExtensions
|
|
37
|
+
# uri 0.11 backport, ships with ruby 3.1
|
|
38
|
+
refine URI::Generic do
|
|
39
|
+
|
|
40
|
+
def non_ascii_hostname
|
|
41
|
+
@non_ascii_hostname
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def non_ascii_hostname=(hostname)
|
|
45
|
+
@non_ascii_hostname = hostname
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def authority
|
|
49
|
+
return host if port == default_port
|
|
50
|
+
|
|
51
|
+
"#{host}:#{port}"
|
|
52
|
+
end unless URI::HTTP.method_defined?(:authority)
|
|
53
|
+
|
|
54
|
+
def origin
|
|
55
|
+
"#{scheme}://#{authority}"
|
|
56
|
+
end unless URI::HTTP.method_defined?(:origin)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
class Headers
|
|
5
|
+
class << self
|
|
6
|
+
def new(headers = nil)
|
|
7
|
+
return headers if headers.is_a?(self)
|
|
8
|
+
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(headers = nil)
|
|
14
|
+
if headers.nil? || headers.empty?
|
|
15
|
+
@headers = headers.to_h
|
|
16
|
+
return
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
@headers = {}
|
|
20
|
+
|
|
21
|
+
headers.each do |field, value|
|
|
22
|
+
field = downcased(field)
|
|
23
|
+
|
|
24
|
+
value = array_value(value)
|
|
25
|
+
|
|
26
|
+
current = @headers[field]
|
|
27
|
+
|
|
28
|
+
if current.nil?
|
|
29
|
+
@headers[field] = value
|
|
30
|
+
else
|
|
31
|
+
current.concat(value)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# cloned initialization
|
|
37
|
+
def initialize_clone(orig, **kwargs)
|
|
38
|
+
super
|
|
39
|
+
@headers = orig.instance_variable_get(:@headers).clone(**kwargs)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# dupped initialization
|
|
43
|
+
def initialize_dup(orig)
|
|
44
|
+
super
|
|
45
|
+
@headers = orig.instance_variable_get(:@headers).dup
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# freezes the headers hash
|
|
49
|
+
def freeze
|
|
50
|
+
@headers.freeze
|
|
51
|
+
super
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# merges headers with another header-quack.
|
|
55
|
+
# the merge rule is, if the header already exists,
|
|
56
|
+
# ignore what the +other+ headers has. Otherwise, set
|
|
57
|
+
#
|
|
58
|
+
def merge(other)
|
|
59
|
+
headers = dup
|
|
60
|
+
other.each do |field, value|
|
|
61
|
+
headers[downcased(field)] = value
|
|
62
|
+
end
|
|
63
|
+
headers
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# returns the comma-separated values of the header field
|
|
67
|
+
# identified by +field+, or nil otherwise.
|
|
68
|
+
#
|
|
69
|
+
def [](field)
|
|
70
|
+
a = @headers[downcased(field)] || return
|
|
71
|
+
a.join(", ")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# sets +value+ (if not nil) as single value for the +field+ header.
|
|
75
|
+
#
|
|
76
|
+
def []=(field, value)
|
|
77
|
+
return unless value
|
|
78
|
+
|
|
79
|
+
@headers[downcased(field)] = array_value(value)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# deletes all values associated with +field+ header.
|
|
83
|
+
#
|
|
84
|
+
def delete(field)
|
|
85
|
+
canonical = downcased(field)
|
|
86
|
+
@headers.delete(canonical) if @headers.key?(canonical)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# adds additional +value+ to the existing, for header +field+.
|
|
90
|
+
#
|
|
91
|
+
def add(field, value)
|
|
92
|
+
(@headers[downcased(field)] ||= []) << String(value)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# helper to be used when adding an header field as a value to another field
|
|
96
|
+
#
|
|
97
|
+
# h2_headers.add_header("vary", "accept-encoding")
|
|
98
|
+
# h2_headers["vary"] #=> "accept-encoding"
|
|
99
|
+
# h1_headers.add_header("vary", "accept-encoding")
|
|
100
|
+
# h1_headers["vary"] #=> "Accept-Encoding"
|
|
101
|
+
#
|
|
102
|
+
alias_method :add_header, :add
|
|
103
|
+
|
|
104
|
+
# returns the enumerable headers store in pairs of header field + the values in
|
|
105
|
+
# the comma-separated string format
|
|
106
|
+
#
|
|
107
|
+
def each(extra_headers = nil)
|
|
108
|
+
return enum_for(__method__, extra_headers) { @headers.size } unless block_given?
|
|
109
|
+
|
|
110
|
+
@headers.each do |field, value|
|
|
111
|
+
yield(field, value.join(", ")) unless value.empty?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
extra_headers.each do |field, value|
|
|
115
|
+
yield(field, value) unless value.empty?
|
|
116
|
+
end if extra_headers
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def ==(other)
|
|
120
|
+
other == to_hash
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def empty?
|
|
124
|
+
@headers.empty?
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# the headers store in Hash format
|
|
128
|
+
def to_hash
|
|
129
|
+
Hash[to_a]
|
|
130
|
+
end
|
|
131
|
+
alias_method :to_h, :to_hash
|
|
132
|
+
|
|
133
|
+
# the headers store in array of pairs format
|
|
134
|
+
def to_a
|
|
135
|
+
Array(each)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# headers as string
|
|
139
|
+
def to_s
|
|
140
|
+
@headers.to_s
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# :nocov:
|
|
144
|
+
def inspect
|
|
145
|
+
"#<#{self.class}:#{object_id} " \
|
|
146
|
+
"#{to_hash.inspect}>"
|
|
147
|
+
end
|
|
148
|
+
# :nocov:
|
|
149
|
+
|
|
150
|
+
# this is internal API and doesn't abide to other public API
|
|
151
|
+
# guarantees, like downcasing strings.
|
|
152
|
+
# Please do not use this outside of core!
|
|
153
|
+
#
|
|
154
|
+
def key?(downcased_key)
|
|
155
|
+
@headers.key?(downcased_key)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# returns the values for the +field+ header in array format.
|
|
159
|
+
# This method is more internal, and for this reason doesn't try
|
|
160
|
+
# to "correct" the user input, i.e. it doesn't downcase the key.
|
|
161
|
+
#
|
|
162
|
+
def get(field)
|
|
163
|
+
@headers[field] || EMPTY
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
private
|
|
167
|
+
|
|
168
|
+
def array_value(value)
|
|
169
|
+
Array(value)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def downcased(field)
|
|
173
|
+
String(field).downcase
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
data/lib/httpx/io/ssl.rb
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
|
|
5
|
+
module HTTPX
|
|
6
|
+
TLSError = OpenSSL::SSL::SSLError
|
|
7
|
+
|
|
8
|
+
class SSL < TCP
|
|
9
|
+
# rubocop:disable Style/MutableConstant
|
|
10
|
+
TLS_OPTIONS = { alpn_protocols: %w[h2 http/1.1].freeze }
|
|
11
|
+
# https://github.com/jruby/jruby-openssl/issues/284
|
|
12
|
+
# TODO: remove when dropping support for jruby-openssl < 0.15.4
|
|
13
|
+
TLS_OPTIONS[:verify_hostname] = true if RUBY_ENGINE == "jruby" && JOpenSSL::VERSION < "0.15.4"
|
|
14
|
+
# rubocop:enable Style/MutableConstant
|
|
15
|
+
TLS_OPTIONS.freeze
|
|
16
|
+
|
|
17
|
+
attr_writer :ssl_session
|
|
18
|
+
|
|
19
|
+
def initialize(_, _, options)
|
|
20
|
+
super
|
|
21
|
+
|
|
22
|
+
@ssl_session = nil
|
|
23
|
+
ctx_options = TLS_OPTIONS.merge(options.ssl)
|
|
24
|
+
@sni_hostname = ctx_options.delete(:hostname) || @hostname
|
|
25
|
+
|
|
26
|
+
if @keep_open && @io.is_a?(OpenSSL::SSL::SSLSocket)
|
|
27
|
+
# externally initiated ssl socket
|
|
28
|
+
@ctx = @io.context
|
|
29
|
+
@state = :negotiated
|
|
30
|
+
else
|
|
31
|
+
@ctx = OpenSSL::SSL::SSLContext.new
|
|
32
|
+
@ctx.set_params(ctx_options) unless ctx_options.empty?
|
|
33
|
+
unless @ctx.session_cache_mode.nil? # a dummy method on JRuby
|
|
34
|
+
@ctx.session_cache_mode =
|
|
35
|
+
OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT | OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
yield(self) if block_given?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@verify_hostname = @ctx.verify_hostname
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if OpenSSL::SSL::SSLContext.method_defined?(:session_new_cb=)
|
|
45
|
+
def session_new_cb(&pr)
|
|
46
|
+
@ctx.session_new_cb = proc { |_, sess| pr.call(sess) }
|
|
47
|
+
end
|
|
48
|
+
else
|
|
49
|
+
# session_new_cb not implemented under JRuby
|
|
50
|
+
def session_new_cb; end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def protocol
|
|
54
|
+
@io.alpn_protocol || super
|
|
55
|
+
rescue StandardError
|
|
56
|
+
super
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if RUBY_ENGINE == "jruby"
|
|
60
|
+
# in jruby, alpn_protocol may return ""
|
|
61
|
+
# https://github.com/jruby/jruby-openssl/issues/287
|
|
62
|
+
def protocol
|
|
63
|
+
proto = @io.alpn_protocol
|
|
64
|
+
|
|
65
|
+
return super if proto.nil? || proto.empty?
|
|
66
|
+
|
|
67
|
+
proto
|
|
68
|
+
rescue StandardError
|
|
69
|
+
super
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def can_verify_peer?
|
|
74
|
+
@ctx.verify_mode == OpenSSL::SSL::VERIFY_PEER
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def verify_hostname(host)
|
|
78
|
+
return false if @ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE
|
|
79
|
+
return false if !@io.respond_to?(:peer_cert) || @io.peer_cert.nil?
|
|
80
|
+
|
|
81
|
+
OpenSSL::SSL.verify_certificate_identity(@io.peer_cert, host)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def connected?
|
|
85
|
+
@state == :negotiated
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def ssl_session_expired?
|
|
89
|
+
@ssl_session.nil? || Process.clock_gettime(Process::CLOCK_REALTIME) >= (@ssl_session.time.to_f + @ssl_session.timeout)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def connect
|
|
93
|
+
return if @state == :negotiated
|
|
94
|
+
|
|
95
|
+
unless @state == :connected
|
|
96
|
+
super
|
|
97
|
+
return unless @state == :connected
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
unless @io.is_a?(OpenSSL::SSL::SSLSocket)
|
|
101
|
+
if (hostname_is_ip = (@ip == @sni_hostname)) && @ctx.verify_hostname
|
|
102
|
+
# IPv6 address would be "[::1]", must turn to "0000:0000:0000:0000:0000:0000:0000:0001" for cert SAN check
|
|
103
|
+
@sni_hostname = @ip.to_string
|
|
104
|
+
# IP addresses in SNI is not valid per RFC 6066, section 3.
|
|
105
|
+
@ctx.verify_hostname = false
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
@io = OpenSSL::SSL::SSLSocket.new(@io, @ctx)
|
|
109
|
+
|
|
110
|
+
@io.hostname = @sni_hostname unless hostname_is_ip
|
|
111
|
+
@io.session = @ssl_session unless ssl_session_expired?
|
|
112
|
+
@io.sync_close = true
|
|
113
|
+
end
|
|
114
|
+
try_ssl_connect
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def try_ssl_connect
|
|
118
|
+
ret = @io.connect_nonblock(exception: false)
|
|
119
|
+
log(level: 3, color: :cyan) { "TLS CONNECT: #{ret}..." }
|
|
120
|
+
case ret
|
|
121
|
+
when :wait_readable
|
|
122
|
+
@interests = :r
|
|
123
|
+
return
|
|
124
|
+
when :wait_writable
|
|
125
|
+
@interests = :w
|
|
126
|
+
return
|
|
127
|
+
end
|
|
128
|
+
@io.post_connection_check(@sni_hostname) if @ctx.verify_mode != OpenSSL::SSL::VERIFY_NONE && @verify_hostname
|
|
129
|
+
transition(:negotiated)
|
|
130
|
+
@interests = :w
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def transition(nextstate)
|
|
136
|
+
case nextstate
|
|
137
|
+
when :negotiated
|
|
138
|
+
return unless @state == :connected
|
|
139
|
+
|
|
140
|
+
when :closed
|
|
141
|
+
return unless @state == :negotiated ||
|
|
142
|
+
@state == :connected
|
|
143
|
+
end
|
|
144
|
+
do_transition(nextstate)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def log_transition_state(nextstate)
|
|
148
|
+
return super unless nextstate == :negotiated
|
|
149
|
+
|
|
150
|
+
server_cert = @io.peer_cert
|
|
151
|
+
|
|
152
|
+
"#{super}\n\n" \
|
|
153
|
+
"SSL connection using #{@io.ssl_version} / #{Array(@io.cipher).first}\n" \
|
|
154
|
+
"ALPN, server accepted to use #{protocol}\n" \
|
|
155
|
+
"Server certificate:\n " \
|
|
156
|
+
"subject: #{server_cert.subject}\n " \
|
|
157
|
+
"start date: #{server_cert.not_before}\n " \
|
|
158
|
+
"expire date: #{server_cert.not_after}\n " \
|
|
159
|
+
"issuer: #{server_cert.issuer}\n " \
|
|
160
|
+
"SSL certificate verify ok."
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|