class S3::Connection

Class responsible for handling connections to amazon hosts

Attributes

access_key_id[RW]
debug[RW]
host[RW]
proxy[RW]
secret_access_key[RW]
timeout[RW]
use_ssl[RW]
use_ssl?[RW]

Public Class Methods

new(options = {}) click to toggle source

Creates new connection object.

Options

  • :access_key_id - Access key id (REQUIRED)

  • :secret_access_key - Secret access key (REQUIRED)

  • :use_ssl - Use https or http protocol (false by default)

  • :debug - Display debug information on the STDOUT (false by default)

  • :timeout - Timeout to use by the Net::HTTP object (60 by default)

  • :proxy - Hash for Net::HTTP Proxy settings { :host => “proxy.mydomain.com”, :port => “80, :user => ”user_a“, :password => ”secret“ }

  • :chunk_size - Size of a chunk when streaming (1048576 (1 MiB) by default)

# File lib/s3/connection.rb, line 24
def initialize(options = {})
  @access_key_id = options.fetch(:access_key_id)
  @secret_access_key = options.fetch(:secret_access_key)
  @host = options.fetch(:host)
  @use_ssl = options.fetch(:use_ssl, false)
  @debug = options.fetch(:debug, false)
  @timeout = options.fetch(:timeout, 60)
  @proxy = options.fetch(:proxy, nil)
  @chunk_size = options.fetch(:chunk_size, 1_048_576)
end
parse_headers(headers) click to toggle source

Helper function to change headers from symbols, to in correct form (i.e. with '-' instead of '_')

Parameters

  • headers - Hash of pairs headername => value, where value can be Range (for Range header) or any other value which can be translated to string

Returns

Hash of headers translated from symbol to string, containing only interesting headers

# File lib/s3/connection.rb, line 134
def self.parse_headers(headers)
  interesting_keys = [:content_type, :content_length, :cache_control, :x_amz_acl, :x_amz_storage_class, :range,
                      :if_modified_since, :if_unmodified_since,
                      :if_match, :if_none_match,
                      :content_disposition, :content_encoding,
                      :x_amz_copy_source, :x_amz_metadata_directive,
                      :x_amz_copy_source_if_match,
                      :x_amz_copy_source_if_none_match,
                      :x_amz_copy_source_if_unmodified_since,
                      :x_amz_copy_source_if_modified_since]

  parsed_headers = {}
  if headers
    headers.each do |key, value|
      if interesting_keys.include?(key)
        parsed_key = key.to_s.tr('_', '-')
        parsed_value = value
        case value
        when Range
          parsed_value = "bytes=#{value.first}-#{value.last}"
        end
        parsed_headers[parsed_key] = parsed_value
      end
    end
  end
  parsed_headers
end
parse_params(params) click to toggle source

Helper function to parser parameters and create single string of params added to questy string

Parameters

  • params - Hash of parameters

Returns

String – containing all parameters joined in one params string, i.e. param1=val&param2&param3=0

# File lib/s3/connection.rb, line 105
def self.parse_params(params)
  interesting_keys = [:max_keys, :prefix, :marker, :delimiter, :location]

  result = []
  params.each do |key, value|
    if interesting_keys.include?(key)
      parsed_key = key.to_s.tr('_', '-')
      case value
      when nil
        result << parsed_key
      else
        result << "#{parsed_key}=#{value}"
      end
    end
  end
  result.join('&')
end

Public Instance Methods

request(method, options) click to toggle source

Makes request with given HTTP method, sets missing parameters, adds signature to request header and returns response object (Net::HTTPResponse)

Parameters

  • method - HTTP Method symbol, can be :get, :put, :delete

Options:

  • :host - Hostname to connecto to, defaults to s3.amazonaws.com

  • :path - path to send request to (REQUIRED)

  • :body - Request body, only meaningful for :put request

  • :params - Parameters to add to query string for request, can be String or Hash

  • :headers - Hash of headers fields to add to request header

Returns

Net::HTTPResponse object – response from the server

# File lib/s3/connection.rb, line 56
def request(method, options)
  host = @host
  path = options.fetch(:path)
  body = options.fetch(:body, nil)
  params = options.fetch(:params, {})
  headers = options.fetch(:headers, {})
  use_authsign = options.fetch(:use_authsign, false)

  # Must be done before adding params
  # Encodes all characters except forward-slash (/) and explicitly legal URL characters
  path = URI.escape(path, /[^#{URI::REGEXP::PATTERN::UNRESERVED}\/]/)

  if params
    params = params.is_a?(String) ? params : self.class.parse_params(params)
    path << "?#{params}"
  end

  request = Request.new(@chunk_size, method.to_s.upcase, !!body, method.to_s.upcase != 'HEAD', path)

  headers = self.class.parse_headers(headers)
  headers.each do |key, value|
    request[key] = value
  end

  if body
    if body.respond_to?(:read)
      request.body_stream = body
    else
      request.body = body
    end
    request.content_length = body.respond_to?(:lstat) ? body.stat.size : body.size
  end

  if use_authsign
    send_authsign(host, request)
  else
    send_request(host, request)
  end
end

Private Instance Methods

handle_response(response) click to toggle source
# File lib/s3/connection.rb, line 214
def handle_response(response)
  case response.code.to_i
  when 200...300
    response
  when 300...600
    if response.body.nil? || response.body.empty?
      fail Error::ResponseError.new(nil, response)
    else
      code, message = parse_error(response.body)
      fail Error::ResponseError.exception(code).new(message, response)
    end
  else
    fail(ConnectionError.new(response, "Unknown response code: #{response.code}"))
  end
  response
end
http(host) click to toggle source
# File lib/s3/connection.rb, line 172
def http(host)
  http = Net::HTTP.new(host, port, *proxy_settings)
  http.set_debug_output(STDOUT) if @debug
  http.use_ssl = @use_ssl
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @use_ssl
  http.read_timeout = @timeout if @timeout
  http
end
port() click to toggle source
# File lib/s3/connection.rb, line 164
def port
  use_ssl ? 443 : 80
end
proxy_settings() click to toggle source
# File lib/s3/connection.rb, line 168
def proxy_settings
  @proxy.values_at(:host, :port, :user, :password) unless @proxy.nil? || @proxy.empty?
end
send_authsign(host, request, skip_authorization = false) click to toggle source
# File lib/s3/connection.rb, line 200
def send_authsign(host, request, skip_authorization = false)
    request['Date'] ||= Time.now.httpdate
    if request.body
      request['Content-Type'] ||= 'application/octet-stream'
      request['Content-MD5'] = Base64.encode64(Digest::MD5.digest(request.body)).chomp unless request.body.empty?
    end
    unless skip_authorization
    request['Authorization'] = Signature.generate(host: host, request: request,
                       access_key_id: access_key_id,
                       secret_access_key: secret_access_key)
    end
    request
end
send_request(host, request, skip_authorization = false) click to toggle source
# File lib/s3/connection.rb, line 181
def send_request(host, request, skip_authorization = false)
  response = http(host).start do |http|
    host = http.address

    request = send_authsign(host, request)

    http.request(request)
  end

  if response.code.to_i == 307
    if response.body
      doc = Document.new response.body
      send_request(doc.elements['Error'].elements['Endpoint'].text, request, true)
    end
  else
    handle_response(response)
  end
end