diff options
Diffstat (limited to 'lib/bundler/mirror.rb')
-rw-r--r-- | lib/bundler/mirror.rb | 223 |
1 files changed, 0 insertions, 223 deletions
diff --git a/lib/bundler/mirror.rb b/lib/bundler/mirror.rb deleted file mode 100644 index a6fa070eb8..0000000000 --- a/lib/bundler/mirror.rb +++ /dev/null @@ -1,223 +0,0 @@ -# frozen_string_literal: true - -require "socket" - -module Bundler - class Settings - # Class used to build the mirror set and then find a mirror for a given URI - # - # @param prober [Prober object, nil] by default a TCPSocketProbe, this object - # will be used to probe the mirror address to validate that the mirror replies. - class Mirrors - def initialize(prober = nil) - @all = Mirror.new - @prober = prober || TCPSocketProbe.new - @mirrors = {} - end - - # Returns a mirror for the given uri. - # - # Depending on the uri having a valid mirror or not, it may be a - # mirror that points to the provided uri - def for(uri) - if @all.validate!(@prober).valid? - @all - else - fetch_valid_mirror_for(Settings.normalize_uri(uri)) - end - end - - def each - @mirrors.each do |k, v| - yield k, v.uri.to_s - end - end - - def parse(key, value) - config = MirrorConfig.new(key, value) - mirror = if config.all? - @all - else - @mirrors[config.uri] ||= Mirror.new - end - config.update_mirror(mirror) - end - - private - - def fetch_valid_mirror_for(uri) - downcased = uri.to_s.downcase - mirror = @mirrors[downcased] || @mirrors[URI(downcased).host] || Mirror.new(uri) - mirror.validate!(@prober) - mirror = Mirror.new(uri) unless mirror.valid? - mirror - end - end - - # A mirror - # - # Contains both the uri that should be used as a mirror and the - # fallback timeout which will be used for probing if the mirror - # replies on time or not. - class Mirror - DEFAULT_FALLBACK_TIMEOUT = 0.1 - - attr_reader :uri, :fallback_timeout - - def initialize(uri = nil, fallback_timeout = 0) - self.uri = uri - self.fallback_timeout = fallback_timeout - @valid = nil - end - - def uri=(uri) - @uri = if uri.nil? - nil - else - URI(uri.to_s) - end - @valid = nil - end - - def fallback_timeout=(timeout) - case timeout - when true, "true" - @fallback_timeout = DEFAULT_FALLBACK_TIMEOUT - when false, "false" - @fallback_timeout = 0 - else - @fallback_timeout = timeout.to_i - end - @valid = nil - end - - def ==(other) - !other.nil? && uri == other.uri && fallback_timeout == other.fallback_timeout - end - - def valid? - return false if @uri.nil? - return @valid unless @valid.nil? - false - end - - def validate!(probe = nil) - @valid = false if uri.nil? - if @valid.nil? - @valid = fallback_timeout == 0 || (probe || TCPSocketProbe.new).replies?(self) - end - self - end - end - - # Class used to parse one configuration line - # - # Gets the configuration line and the value. - # This object provides a `update_mirror` method - # used to setup the given mirror value. - class MirrorConfig - attr_accessor :uri, :value - - def initialize(config_line, value) - uri, fallback = - config_line.match(%r{\Amirror\.(all|.+?)(\.fallback_timeout)?\/?\z}).captures - @fallback = !fallback.nil? - @all = false - if uri == "all" - @all = true - else - @uri = URI(uri).absolute? ? Settings.normalize_uri(uri) : uri - end - @value = value - end - - def all? - @all - end - - def update_mirror(mirror) - if @fallback - mirror.fallback_timeout = @value - else - mirror.uri = Settings.normalize_uri(@value) - end - end - end - - # Class used for probing TCP availability for a given mirror. - class TCPSocketProbe - def replies?(mirror) - MirrorSockets.new(mirror).any? do |socket, address, timeout| - begin - socket.connect_nonblock(address) - rescue Errno::EINPROGRESS - wait_for_writtable_socket(socket, address, timeout) - rescue # Connection failed somehow, again - false - end - end - end - - private - - def wait_for_writtable_socket(socket, address, timeout) - if IO.select(nil, [socket], nil, timeout) - probe_writtable_socket(socket, address) - else # TCP Handshake timed out, or there is something dropping packets - false - end - end - - def probe_writtable_socket(socket, address) - socket.connect_nonblock(address) - rescue Errno::EISCONN - true - rescue # Connection failed - false - end - end - end - - # Class used to build the list of sockets that correspond to - # a given mirror. - # - # One mirror may correspond to many different addresses, both - # because of it having many dns entries or because - # the network interface is both ipv4 and ipv5 - class MirrorSockets - def initialize(mirror) - @timeout = mirror.fallback_timeout - @addresses = Socket.getaddrinfo(mirror.uri.host, mirror.uri.port).map do |address| - SocketAddress.new(address[0], address[3], address[1]) - end - end - - def any? - @addresses.any? do |address| - socket = Socket.new(Socket.const_get(address.type), Socket::SOCK_STREAM, 0) - socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) - value = yield socket, address.to_socket_address, @timeout - socket.close unless socket.closed? - value - end - end - end - - # Socket address builder. - # - # Given a socket type, a host and a port, - # provides a method to build sockaddr string - class SocketAddress - attr_reader :type, :host, :port - - def initialize(type, host, port) - @type = type - @host = host - @port = port - end - - def to_socket_address - Socket.pack_sockaddr_in(@port, @host) - end - end -end |