class Bundler::CompactIndexClient::Updater
公共类方法
new(fetcher) 点击切换源码
# File bundler/compact_index_client/updater.rb, line 12 def initialize(fetcher) @fetcher = fetcher end
公共实例方法
update(remote_path, local_path, etag_path) 点击切换源码
# File bundler/compact_index_client/updater.rb, line 16 def update(remote_path, local_path, etag_path) append(remote_path, local_path, etag_path) || replace(remote_path, local_path, etag_path) rescue CacheFile::DigestMismatchError => e raise MismatchedChecksumError.new(remote_path, e.message) rescue Zlib::GzipFile::Error raise Bundler::HTTPError end
私有实例方法
append(remote_path, local_path, etag_path) 点击切换源码
# File bundler/compact_index_client/updater.rb, line 26 def append(remote_path, local_path, etag_path) return false unless local_path.file? && local_path.size.nonzero? CacheFile.copy(local_path) do |file| etag = etag_path.read.tap(&:chomp!) if etag_path.file? # Subtract a byte to ensure the range won't be empty. # Avoids 416 (Range Not Satisfiable) responses. response = @fetcher.call(remote_path, request_headers(etag, file.size - 1)) break true if response.is_a?(Gem::Net::HTTPNotModified) file.digests = parse_digests(response) # server may ignore Range and return the full response if response.is_a?(Gem::Net::HTTPPartialContent) break false unless file.append(response.body.byteslice(1..-1)) else file.write(response.body) end CacheFile.write(etag_path, etag_from_response(response)) true end end
byte_sequence(value) 点击切换源码
解包周围的冒号(字节序列)。包装字符必须匹配,否则返回 nil。 同时处理引号,因为现在 rubygems.org 发送了它们。
# File bundler/compact_index_client/updater.rb, line 97 def byte_sequence(value) return if value.delete_prefix!(":") && !value.delete_suffix!(":") return if value.delete_prefix!('"') && !value.delete_suffix!('"') value end
etag_for_request(etag_path) 点击切换源码
# File bundler/compact_index_client/updater.rb, line 65 def etag_for_request(etag_path) etag_path.read.tap(&:chomp!) if etag_path.file? end
etag_from_response(response) 点击切换源码
# File bundler/compact_index_client/updater.rb, line 69 def etag_from_response(response) return unless response["ETag"] etag = response["ETag"].delete_prefix("W/") return if etag.delete_prefix!('"') && !etag.delete_suffix!('"') etag end
parse_digests(response) 点击切换源码
根据 RFC 8941 HTTP 结构化字段值,解包并返回摘要算法和 base64 值的哈希。 www.rfc-editor.org/rfc/rfc8941#name-parsing-a-byte-sequence 忽略不支持的算法。
# File bundler/compact_index_client/updater.rb, line 80 def parse_digests(response) return unless header = response["Repr-Digest"] || response["Digest"] digests = {} header.split(",") do |param| algorithm, value = param.split("=", 2) algorithm.strip! algorithm.downcase! next unless SUPPORTED_DIGESTS.key?(algorithm) next unless value = byte_sequence(value) digests[algorithm] = value end digests.empty? ? nil : digests end
replace(remote_path, local_path, etag_path) 点击切换源码
请求时不带 Range 标头以获取完整文件或 304 Not Modified
# File bundler/compact_index_client/updater.rb, line 50 def replace(remote_path, local_path, etag_path) etag = etag_path.read.tap(&:chomp!) if etag_path.file? response = @fetcher.call(remote_path, request_headers(etag)) return true if response.is_a?(Gem::Net::HTTPNotModified) CacheFile.write(local_path, response.body, parse_digests(response)) CacheFile.write(etag_path, etag_from_response(response)) end
request_headers(etag, range_start = nil) 点击切换源码
# File bundler/compact_index_client/updater.rb, line 58 def request_headers(etag, range_start = nil) headers = {} headers["Range"] = "bytes=#{range_start}-" if range_start headers["If-None-Match"] = %("#{etag}") if etag headers end