Class: Videojuicer::OAuth::RequestProxy

Inherits:
Object
  • Object
show all
Includes:
Configurable, Exceptions
Defined in:
lib/videojuicer/oauth/request_proxy.rb

Instance Attribute Summary

Attributes included from Configurable

#local_config

Instance Method Summary collapse

Methods included from Configurable

#api_version, #config, #configure!, #consumer_key, #consumer_secret, #host, #port, #protocol, #scope, #seed_name, #token, #token_secret, #user_id

Constructor Details

#initialize(options = {}) ⇒ RequestProxy

Initializes a new RequestProxy object which can be used to make requests. Accepts all the same options as Videojuicer::configure! as well as: token - The OAuth token to use in requests made through this proxy. token_secret - The OAuth token secret to use when encrypting the request signature.



28
29
30
# File 'lib/videojuicer/oauth/request_proxy.rb', line 28

def initialize(options={})
  configure!(options)
end

Instance Method Details

#authified_query_string(method, path, params = {}) ⇒ Object

Authifies the given parameters and converts them into a query string.



182
183
184
# File 'lib/videojuicer/oauth/request_proxy.rb', line 182

def authified_query_string(method, path, params={})
  normalize_params(authify_params(method, path, params))
end

#authify_params(method, path, params) ⇒ Object

Takes a set of business parameters you want sent to the provider, and merges them with the proxy configuration to produce a set of parameters that will be accepted by the OAuth provider.



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/videojuicer/oauth/request_proxy.rb', line 189

def authify_params(method, path, params)
  params = {
    :oauth_consumer_key=>consumer_key,
    :oauth_token=>token,
    :api_version=>api_version,
    :oauth_timestamp=>Time.now.to_i,
    :oauth_nonce=>rand(9999),
    :oauth_signature_method=>"HMAC-SHA1",
    :seed_name=>seed_name,
    :user_id=>user_id
  }.merge(params)
  params.delete_if {|k,v| (!v) or (v.to_s.empty?) }
  params[:oauth_signature] = signature(method, path, params)
  return params
end

#delete(path, params = {}) ⇒ Object

Makes a DELETE request given path and params. The host will be ascertained from the configuration options.



46
# File 'lib/videojuicer/oauth/request_proxy.rb', line 46

def delete(path, params={}); make_request(:delete, host, port, path, params); end

#flatten_params(params, *hash_path) ⇒ Object



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/videojuicer/oauth/request_proxy.rb', line 232

def flatten_params(params, *hash_path)
  op = {}
  params.sort {|a,b| a.to_s<=>b.to_s}.each do |key, value|
    path = hash_path.dup
    path << key.to_s
    
    if value.is_a?(Hash)
      op.merge! flatten_params(value, *path)
    elsif value
      key_path = path.first + path[1..(path.length-1)].collect {|h| "[#{h}]"}.join("")
      op[key_path] = value
    end
  end
  return op
end

#get(path, params = {}) ⇒ Object

Makes a GET request given path and params. The host will be ascertained from the configuration options.



34
# File 'lib/videojuicer/oauth/request_proxy.rb', line 34

def get(path, params={}); make_request(:get, host, port, path, params); end

#handle_response(response, request) ⇒ Object

Handles an HTTPResponse object appropriately. Redirects are followed, error states raise errors and success responses are returned directly.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/videojuicer/oauth/request_proxy.rb', line 103

def handle_response(response, request)
  c = response.code.to_i
  case c
  when 200..399
    # Successful or redirected response
    response
  when 415
    # Validation error
    response
  when 401
    # Authentication problem
    response_error Unauthenticated, request, response
  when 403
    # Attempted to perform a forbidden action
    response_error Forbidden, request, response
  when 404
    # Resource URL not valid
    response_error NoResource, request, response
  when 406
    # Excuse me WTF r u doin
    response_error NotAcceptable, request, response
  when 411
    # App-side server error where request is not properly constructed.
    response_error ContentLengthRequired, request, response
  when 500..600
    # Remote application failure
    response_error RemoteApplicationError, request, response
  else
    response_error UnhandledHTTPStatus, request, response
  end
end

#host_stubObject



97
98
99
# File 'lib/videojuicer/oauth/request_proxy.rb', line 97

def host_stub
  "#{protocol}://#{host}:#{port}"
end

#make_request(method, host, port, path, params = {}) ⇒ Object

Does the actual work of making a request. Returns a Net::HTTPResponse object.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/videojuicer/oauth/request_proxy.rb', line 49

def make_request(method, host, port, path, params={})
  # Strip the files from the parameters to determine what, from the whole bundle, needs signing
  signature_params, multipart_params = split_by_signature_eligibility(params)
  
  if multipart_params.any?
    # Sign the params and include the as multipart
    multipart_params = flatten_params(
      authify_params(method, path, signature_params).deep_merge(multipart_params)
    )
    query_string = ""
  else
    # Use the query string
    query_string = authified_query_string(method, path, signature_params)
  end
  
  # Generate the HTTP Request and handle the response
  url = "#{host_stub}#{path}"
  request = request_class_for_method(method).new("#{path}?#{query_string}")        
  # Generate the multipart body and headers
  if multipart_params.any?          
    post_body, content_type = Multipart::Post.new(multipart_params).to_multipart
    request.content_length = post_body.length
    request.content_type = content_type
    request.body = post_body
  else
    # Send a content-length on POST and PUT to avoid an HTTP 411 response
    case method
    when :post, :put
      request = request_class_for_method(method).new("#{path}")
      request.content_type = "application/x-www-form-urlencoded"
      request.body = query_string
      request.content_length = query_string.length
    end
  end
  
  
  begin
    #response = HTTPClient.send(method, url, multipart_params)
    response = Net::HTTP.start(host, port) {|http| http.request(request) }
  rescue EOFError => e
    raise "EOF error when accessing #{url.inspect}"
  rescue Errno::ECONNREFUSED => e
    raise "Could not connect to #{url.inspect}"
  end
  
  return handle_response(response, request)
end

#normalize_params(params, *hash_path) ⇒ Object

Returns a string representing a normalised parameter hash. Supports nesting for rails or merb-style object properties supplied as nested hashes. For instance, the key ‘bar inside :foo=>{:bar=>“baz”} will be named foo in the signature and in the eventual request object.



228
229
230
# File 'lib/videojuicer/oauth/request_proxy.rb', line 228

def normalize_params(params, *hash_path)
  flatten_params(params).sort {|a,b| a.to_s <=> b.to_s}.collect {|k, v| "#{CGI.rfc3986_escape(k)}=#{CGI.rfc3986_escape(v.to_s)}" }.join("&")
end

#post(path, params = {}) ⇒ Object

Makes a POST request given path and params. The host will be ascertained from the configuration options.



38
# File 'lib/videojuicer/oauth/request_proxy.rb', line 38

def post(path, params={}); make_request(:post, host, port, path, params); end

#put(path, params = {}) ⇒ Object

Makes a PUT request given path and params. The host will be ascertained from the configuration options.



42
# File 'lib/videojuicer/oauth/request_proxy.rb', line 42

def put(path, params={}); make_request(:put, host, port, path, params); end

#request_class_for_method(m, in_module = Net::HTTP) ⇒ Object

Returns the Net::HTTPRequest subclass needed to make a request for the given method.



249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/videojuicer/oauth/request_proxy.rb', line 249

def request_class_for_method(m, in_module=Net::HTTP)
  case (m.is_a?(Symbol) ? m : m.downcase.to_sym rescue :get)
  when :post
    in_module::Post
  when :put
    in_module::Put
  when :head
    in_module::Head
  when :delete
    in_module::Delete
  else
    in_module::Get
  end
end

#response_error(exception_klass, request, response) ⇒ Object

Handles the response as an error of the desired type.



136
137
138
139
140
141
142
143
144
145
# File 'lib/videojuicer/oauth/request_proxy.rb', line 136

def response_error(exception_klass, request, response)
  begin
    e = JSON.parse(response.body)
    e = e["error"]
    raise exception_klass, "#{e["message"]} \n #{(e["backtrace"] || []).join("\n")}"
  rescue JSON::ParserError
    raise exception_klass, "#{exception_klass.to_s} : Response code was #{response.code} for request: #{request.path}"
  end
  
end

#signature(method, path, params) ⇒ Object

Calculates and returns the encrypted signature for this proxy object and the given request properties.



207
208
209
210
211
# File 'lib/videojuicer/oauth/request_proxy.rb', line 207

def signature(method, path, params)
  base = signature_base_string(method, path, params)
  signature_octet = HMAC::SHA1.digest(signature_secret, base)
  signature_base64 = [signature_octet].pack('m').chomp.gsub(/\n/, '')
end

#signature_base_string(method, path, params) ⇒ Object

Returns the unencrypted signature base string for this proxy object and the given request properties.



220
221
222
# File 'lib/videojuicer/oauth/request_proxy.rb', line 220

def signature_base_string(method, path, params)
  s = [method.to_s.upcase, "#{protocol}://#{host}#{path}", normalize_params(params)].collect {|e| CGI.rfc3986_escape(e)}.join("&")
end

#signature_secretObject

Calculates and returns the signature secret to be used for this proxy object.



214
215
216
# File 'lib/videojuicer/oauth/request_proxy.rb', line 214

def signature_secret
  [consumer_secret, token_secret].collect {|e| CGI.rfc3986_escape(e.to_s)}.join("&")
end

#signed_url(method, path, params = {}) ⇒ Object



177
178
179
# File 'lib/videojuicer/oauth/request_proxy.rb', line 177

def signed_url(method, path, params={})
  "#{protocol}://#{host}:#{port}#{path}?#{authified_query_string(method, path, params)}"
end

#split_by_signature_eligibility(params, *hash_path) ⇒ Object

Splits a given parameter hash into two hashes - one containing all string and non-binary parameters, and one containing all file/binary parameters. This action is performed recursively so that:

params = {:user=>{:attributes=>{:file=>some_file, :name=>"user name"}}, :foo=>"bar"}
normal, multipart = split_multipart_params(params)
normal.inspect # => {:user=>{:attributes=>{:name=>"user name"}}, :foo=>"bar"}
multipart.inspect # => {:user=>{:attributes=>{:file=>some_file}}}


154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/videojuicer/oauth/request_proxy.rb', line 154

def split_by_signature_eligibility(params, *hash_path)
  strings = {}
  files = {}
  params.each do |key, value|
    if value.is_a?(Hash)
      # Call recursively
      s, f = split_by_signature_eligibility(value, *(hash_path+[key]))
      strings = strings.deep_merge(s)
      files = files.deep_merge(f)
    else 
      # Insert it into files at the current key path if it is a binary,
      # and into strings if it is not.
      pwd = (value.respond_to?(:read))? files : strings
      hash_path.each do |component|
        pwd[component] ||= {}
        pwd = pwd[component]
      end
      pwd[key] = value
    end
  end
  return strings, files
end