You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby Net::HTTP下载文件异常超时问题的排查与解决

解决Ruby下载文件时的IO::TimeoutError及SSL证书不匹配问题

问题描述

使用Ruby下载https://evs.nci.nih.gov/ftp1/CDISC/ReadMe.txt时,抛出IO::TimeoutError,提示无法建立TCP连接。进一步测试发现:

  • TCPSocket直接连接域名时超时
  • 解析域名得到IPv6和IPv4地址,直接连接IPv4地址会触发SSL证书不匹配错误,但curl可正常下载该文件

根因分析

问题出在Net::HTTP#connect方法中使用的Timeout.timeout存在设计缺陷,导致在同时存在IPv6和IPv4解析结果时,连接逻辑出现超时。而curl会自动尝试合适的地址并正确处理SSL验证,因此可以正常下载。

解决方案

方案1:指定IPv4地址并处理SSL证书验证

先通过域名解析工具(如dig或nslookup)获取目标域名的IPv4地址,然后在代码中指定该地址,并设置hostname用于SSL证书验证,解决证书不匹配问题:

require 'net/http'
require 'openssl'

target_url = URI('https://evs.nci.nih.gov/ftp1/CDISC/ReadMe.txt')
# 替换为实际解析得到的evs.nci.nih.gov的IPv4地址
ipv4_address = '198.245.60.189'

http = Net::HTTP.new(ipv4_address, target_url.port)
http.use_ssl = true
# 启用SSL证书验证,并指定主机名用于证书匹配
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.hostname = target_url.host

# 发起请求并保存文件
response = http.get(target_url.path)
File.write('ReadMe.txt', response.body)

方案2:替换Net::HTTP的连接逻辑,使用Socket.tcp

重写Net::HTTP#connect方法,用Socket.tcp替代默认的Timeout.timeout逻辑,避免超时缺陷:

require 'net/http'
require 'openssl'

target_url = URI('https://evs.nci.nih.gov/ftp1/CDISC/ReadMe.txt')

http = Net::HTTP.new(target_url.host, target_url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.open_timeout = 10

# 重写connect方法,使用Socket.tcp处理连接
def http.connect
  @socket = Net::BufferedIO.new(
    Socket.tcp(@address, @port, nil, nil, connect_timeout: @open_timeout)
  )
  if use_ssl?
    ssl_ctx = @ssl_context ? @ssl_context.dup : OpenSSL::SSL::SSLContext.new
    ssl_ctx.hostname = @address
    @socket = OpenSSL::SSL::SSLSocket.new(@socket, ssl_ctx)
    @socket.sync_close = true
    @socket.connect
  end
end

response = http.get(target_url.path)
File.write('ReadMe.txt', response.body)

内容的提问来源于stack exchange,提问作者Thierry Lambert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 09:46:10