Class: Bundler::CompactIndexClient::Updater

Inherits:
Object
  • Object
show all
Defined in:
lib/bundler/vendor/compact_index_client/lib/compact_index_client/updater.rb

Defined Under Namespace

Classes: MisMatchedChecksumError

Instance Method Summary collapse

Constructor Details

#initialize(fetcher) ⇒ Updater

Returns a new instance of Updater.



22
23
24
# File 'lib/bundler/vendor/compact_index_client/lib/compact_index_client/updater.rb', line 22

def initialize(fetcher)
  @fetcher = fetcher
end

Instance Method Details

#checksum_for_file(path) ⇒ Object



72
73
74
75
76
77
78
# File 'lib/bundler/vendor/compact_index_client/lib/compact_index_client/updater.rb', line 72

def checksum_for_file(path)
  return nil unless path.file?
  # This must use IO.read instead of Digest.file().hexdigest
  # because we need to preserve \n line endings on windows when calculating
  # the checksum
  Digest::MD5.hexdigest(IO.read(path))
end

#etag_for(path) ⇒ Object



67
68
69
70
# File 'lib/bundler/vendor/compact_index_client/lib/compact_index_client/updater.rb', line 67

def etag_for(path)
  sum = checksum_for_file(path)
  sum ? %("#{sum}") : nil
end

#update(local_path, remote_path, retrying = nil) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/bundler/vendor/compact_index_client/lib/compact_index_client/updater.rb', line 26

def update(local_path, remote_path, retrying = nil)
  headers = {}

  Dir.mktmpdir(local_path.basename.to_s, local_path.dirname) do |local_temp_dir|
    local_temp_path = Pathname.new(local_temp_dir).join(local_path.basename)

    # download new file if retrying
    if retrying.nil? && local_path.file?
      FileUtils.cp local_path, local_temp_path
      headers["If-None-Match"] = etag_for(local_temp_path)
      headers["Range"] = "bytes=#{local_temp_path.size}-"
    else
      # Fastly ignores Range when Accept-Encoding: gzip is set
      headers["Accept-Encoding"] = "gzip"
    end

    response = @fetcher.call(remote_path, headers)
    return if response.is_a?(Net::HTTPNotModified)

    content = response.body
    if response["Content-Encoding"] == "gzip"
      content = Zlib::GzipReader.new(StringIO.new(content)).read
    end

    mode = response.is_a?(Net::HTTPPartialContent) ? "a" : "w"
    local_temp_path.open(mode) {|f| f << content }

    response_etag = response["ETag"]
    if etag_for(local_temp_path) == response_etag
      FileUtils.mv(local_temp_path, local_path)
      return
    end

    if retrying.nil?
      update(local_path, remote_path, :retrying)
    else
      raise MisMatchedChecksumError.new(remote_path, response_etag, etag_for(local_temp_path))
    end
  end
end