Module: TempmailSdk::Http
- Defined in:
- lib/tempmail_sdk/http.rb
Overview
共享 HTTP 客户端 所有 provider 通过此模块发起 HTTP 请求,自动应用全局配置(代理、超时、SSL 等) 基于标准库 Net::HTTP 实现,无第三方依赖
Defined Under Namespace
Classes: Response
Constant Summary collapse
- CONN_RESET_ERRORS =
复用持久连接时,陈旧连接被对端关闭会抛出的错误类型。 原实现每次新建连接,故对这些错误重建连接重试一次,语义等价且幂等。
[EOFError, Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, IOError].freeze
Class Method Summary collapse
-
.acquire_entry(uri, config) ⇒ Object
获取(或初始化)目标主机的池条目;配置变更时整体重建连接池.
-
.build_http(uri, config, timeout) ⇒ Object
构造 Net::HTTP 实例(应用代理、TLS、超时).
-
.build_request(method, uri, config, headers, json, body) ⇒ Object
构造请求对象并写入请求头/请求体.
-
.get(url, headers: nil, timeout: nil) ⇒ Response
带全局配置的 GET 请求(自动注入伪造来源 IP 请求头).
-
.merge_spoof_headers(headers) ⇒ Object
将伪造来源 IP 请求头合并到用户指定的 headers 中(每次请求生成新的随机 IP).
-
.perform_with_reuse(entry, uri, config, timeout, req) ⇒ Object
使用/建立持久连接执行请求;陈旧连接被对端关闭时重建一次.
-
.pool_key(uri) ⇒ Object
池键:区分协议/主机/端口即可(配置变更整体清池).
-
.post(url, headers: nil, timeout: nil, json: nil, body: nil) ⇒ Object
带全局配置的 POST 请求.
-
.random_ipv4 ⇒ Object
生成随机 IPv4 地址(每段 1-254),用于伪造来源 IP 请求头.
-
.request(method, url, headers: nil, timeout: nil, json: nil, body: nil) ⇒ Object
通用请求执行(复用持久连接池).
-
.safe_finish(http) ⇒ Object
安全关闭连接,忽略关闭期异常.
-
.spoof_ip_headers ⇒ Object
生成一组伪造来源 IP 的请求头,所有头使用同一个随机 IP.
-
.start_http(uri, config, timeout) ⇒ Object
建立并启动持久连接(启用 keep-alive).
Class Method Details
.acquire_entry(uri, config) ⇒ Object
获取(或初始化)目标主机的池条目;配置变更时整体重建连接池
138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/tempmail_sdk/http.rb', line 138 def acquire_entry(uri, config) key = pool_key(uri) @pool_mutex.synchronize do version = Config.config_version if version != @pool_config_version @pool.each_value { |e| safe_finish(e[:http]) } @pool.clear @pool_config_version = version end @pool[key] ||= { http: nil, mutex: Mutex.new } end end |
.build_http(uri, config, timeout) ⇒ Object
构造 Net::HTTP 实例(应用代理、TLS、超时)
193 194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/tempmail_sdk/http.rb', line 193 def build_http(uri, config, timeout) http = if config.proxy && !config.proxy.empty? p = URI.parse(config.proxy) Net::HTTP.new(uri.host, uri.port, p.host, p.port, p.user, p.password) else Net::HTTP.new(uri.host, uri.port) end http.use_ssl = (uri.scheme == "https") http.verify_mode = OpenSSL::SSL::VERIFY_NONE if config.insecure && http.use_ssl? http.open_timeout = timeout http.read_timeout = timeout http end |
.build_request(method, uri, config, headers, json, body) ⇒ Object
构造请求对象并写入请求头/请求体
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/tempmail_sdk/http.rb', line 208 def build_request(method, uri, config, headers, json, body) klass = method == :post ? Net::HTTP::Post : Net::HTTP::Get req = klass.new(uri.request_uri) merged = merge_spoof_headers(headers) config.headers&.each { |k, v| req[k] = v } merged.each { |k, v| req[k] = v } if json req.body = JSON.generate(json) req["Content-Type"] ||= "application/json" elsif body req.body = body end req end |
.get(url, headers: nil, timeout: nil) ⇒ Response
带全局配置的 GET 请求(自动注入伪造来源 IP 请求头)
112 113 114 |
# File 'lib/tempmail_sdk/http.rb', line 112 def get(url, headers: nil, timeout: nil) request(:get, url, headers: headers, timeout: timeout) end |
.merge_spoof_headers(headers) ⇒ Object
将伪造来源 IP 请求头合并到用户指定的 headers 中(每次请求生成新的随机 IP)
101 102 103 104 105 |
# File 'lib/tempmail_sdk/http.rb', line 101 def merge_spoof_headers(headers) merged = spoof_ip_headers merged.merge!(headers) if headers merged end |
.perform_with_reuse(entry, uri, config, timeout, req) ⇒ Object
使用/建立持久连接执行请求;陈旧连接被对端关闭时重建一次
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/tempmail_sdk/http.rb', line 152 def perform_with_reuse(entry, uri, config, timeout, req) http = entry[:http] if http.nil? || !http.started? http = start_http(uri, config, timeout) entry[:http] = http else http.open_timeout = timeout http.read_timeout = timeout end begin Response.new(http.request(req)) rescue *CONN_RESET_ERRORS safe_finish(http) http = start_http(uri, config, timeout) entry[:http] = http Response.new(http.request(req)) end end |
.pool_key(uri) ⇒ Object
池键:区分协议/主机/端口即可(配置变更整体清池)
173 174 175 |
# File 'lib/tempmail_sdk/http.rb', line 173 def pool_key(uri) "#{uri.scheme}://#{uri.host}:#{uri.port}" end |
.post(url, headers: nil, timeout: nil, json: nil, body: nil) ⇒ Object
带全局配置的 POST 请求
119 120 121 |
# File 'lib/tempmail_sdk/http.rb', line 119 def post(url, headers: nil, timeout: nil, json: nil, body: nil) request(:post, url, headers: headers, timeout: timeout, json: json, body: body) end |
.random_ipv4 ⇒ Object
生成随机 IPv4 地址(每段 1-254),用于伪造来源 IP 请求头
86 87 88 |
# File 'lib/tempmail_sdk/http.rb', line 86 def random_ipv4 Array.new(4) { rand(1..254) }.join(".") end |
.request(method, url, headers: nil, timeout: nil, json: nil, body: nil) ⇒ Object
通用请求执行(复用持久连接池)
124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/tempmail_sdk/http.rb', line 124 def request(method, url, headers: nil, timeout: nil, json: nil, body: nil) config = Config.get_config uri = URI.parse(url) effective_timeout = timeout || config.timeout || 15 req = build_request(method, uri, config, headers, json, body) entry = acquire_entry(uri, config) # 同一连接同一时刻仅允许一个请求,保证 socket 读写不交错 entry[:mutex].synchronize do perform_with_reuse(entry, uri, config, effective_timeout, req) end end |
.safe_finish(http) ⇒ Object
安全关闭连接,忽略关闭期异常
186 187 188 189 190 |
# File 'lib/tempmail_sdk/http.rb', line 186 def safe_finish(http) http&.finish if http&.started? rescue StandardError nil end |
.spoof_ip_headers ⇒ Object
生成一组伪造来源 IP 的请求头,所有头使用同一个随机 IP
91 92 93 94 95 96 97 98 |
# File 'lib/tempmail_sdk/http.rb', line 91 def spoof_ip_headers ip = random_ipv4 { "X-Forwarded-For" => ip, "X-Real-IP" => ip, "X-Originating-IP" => ip } end |
.start_http(uri, config, timeout) ⇒ Object
建立并启动持久连接(启用 keep-alive)
178 179 180 181 182 183 |
# File 'lib/tempmail_sdk/http.rb', line 178 def start_http(uri, config, timeout) http = build_http(uri, config, timeout) http.keep_alive_timeout = 30 http.start http end |