class S3::Connection
Class responsible for handling connections to amazon hosts
Attributes
Public Class Methods
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
Helper function to change headers from symbols, to in correct form (i.e. with '-' instead of '_')
Parameters¶ ↑
-
headers
- Hash of pairsheadername => 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
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¶m2¶m3=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
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 tos3.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
# 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
# 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
# File lib/s3/connection.rb, line 164 def port use_ssl ? 443 : 80 end
# File lib/s3/connection.rb, line 168 def proxy_settings @proxy.values_at(:host, :port, :user, :password) unless @proxy.nil? || @proxy.empty? end
# 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
# 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