curb-fu 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/curb-fu.rb +64 -0
- data/lib/curb-fu/authentication.rb +5 -0
- data/lib/curb-fu/core_ext.rb +59 -0
- data/lib/curb-fu/entity.rb +54 -0
- data/lib/curb-fu/request.rb +12 -0
- data/lib/curb-fu/request/base.rb +118 -0
- data/lib/curb-fu/request/common.rb +34 -0
- data/lib/curb-fu/request/parameter.rb +33 -0
- data/lib/curb-fu/request/test.rb +153 -0
- data/lib/curb-fu/response.rb +179 -0
- data/lib/curb-fu/test.rb +6 -0
- data/lib/curb-fu/test/request_logger.rb +31 -0
- data/lib/curb-fu/test/server.rb +21 -0
- data/spec/fixtures/foo.txt +1 -0
- data/spec/lib/curb-fu/core_ext_spec.rb +100 -0
- data/spec/lib/curb-fu/entity_spec.rb +37 -0
- data/spec/lib/curb-fu/request/base_spec.rb +240 -0
- data/spec/lib/curb-fu/request/parameter_spec.rb +77 -0
- data/spec/lib/curb-fu/request/test_spec.rb +223 -0
- data/spec/lib/curb-fu/response_spec.rb +99 -0
- data/spec/lib/curb_fu_spec.rb +49 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +11 -0
- metadata +95 -0
data/lib/curb-fu.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
$:.unshift(dir) unless $:.include?(dir)
|
3
|
+
require 'curb-fu/response'
|
4
|
+
require 'curb-fu/request'
|
5
|
+
require 'curb-fu/entity'
|
6
|
+
require 'curb-fu/authentication'
|
7
|
+
require 'curb-fu/core_ext'
|
8
|
+
require 'curb-fu/test'
|
9
|
+
|
10
|
+
module CurbFu
|
11
|
+
class << self
|
12
|
+
def get(*args)
|
13
|
+
CurbFu::Request.get(*args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def post(*args)
|
17
|
+
CurbFu::Request.post(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def put(*args)
|
21
|
+
CurbFu::Request.put(*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(*args)
|
25
|
+
CurbFu::Request.delete(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_accessor :stubs
|
29
|
+
|
30
|
+
def stubs=(val)
|
31
|
+
if val
|
32
|
+
@stubs = {}
|
33
|
+
val.each do |hostname, rack_app|
|
34
|
+
stub(hostname, rack_app)
|
35
|
+
end
|
36
|
+
|
37
|
+
unless CurbFu::Request.include?(CurbFu::Request::Test)
|
38
|
+
CurbFu::Request.send(:include, CurbFu::Request::Test)
|
39
|
+
end
|
40
|
+
else
|
41
|
+
@stubs = nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def stub(hostname, rack_app)
|
46
|
+
raise "You must use CurbFu.stubs= to define initial stubs before using stub()" if @stubs.nil?
|
47
|
+
@stubs[hostname] = CurbFu::Request::Test::Interface.new(rack_app, hostname)
|
48
|
+
end
|
49
|
+
|
50
|
+
def stubs
|
51
|
+
@stubs
|
52
|
+
end
|
53
|
+
|
54
|
+
def debug=(val)
|
55
|
+
@debug = val ? true : false
|
56
|
+
end
|
57
|
+
|
58
|
+
def debug?
|
59
|
+
@debug
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
##
|
4
|
+
# ActiveSupport look alike for to_param. Very useful.
|
5
|
+
module CurbFu
|
6
|
+
module HashExtensions
|
7
|
+
def self.included(base)
|
8
|
+
base.send(:include, InstanceMethods) unless base.methods.include?(:to_param)
|
9
|
+
#base.extend(ClassMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
def to_param(prefix)
|
14
|
+
collect do |k, v|
|
15
|
+
key_prefix = prefix ? "#{prefix}[#{k}]" : k
|
16
|
+
v.to_param(key_prefix)
|
17
|
+
end.join("&")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module ObjectExtensions
|
23
|
+
def self.included(base)
|
24
|
+
base.send(:include, InstanceMethods) unless base.methods.include?(:to_param)
|
25
|
+
#base.extend(ClassMethods)
|
26
|
+
end
|
27
|
+
|
28
|
+
module InstanceMethods
|
29
|
+
def to_param(prefix)
|
30
|
+
value = CGI::escape(to_s)
|
31
|
+
"#{prefix}=#{value}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module ArrayExtensions
|
37
|
+
def self.included(base)
|
38
|
+
base.send(:include, InstanceMethods) unless base.methods.include?(:to_param)
|
39
|
+
#base.extend(ClassMethods)
|
40
|
+
end
|
41
|
+
|
42
|
+
module InstanceMethods
|
43
|
+
def to_param(prefix)
|
44
|
+
prefix = "#{prefix}[]"
|
45
|
+
collect { |item| "#{item.to_param(prefix)}" }.join('&')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Hash
|
52
|
+
include CurbFu::HashExtensions
|
53
|
+
end
|
54
|
+
class Array
|
55
|
+
include CurbFu::ArrayExtensions
|
56
|
+
end
|
57
|
+
class Object
|
58
|
+
include CurbFu::ObjectExtensions
|
59
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module CurbFu
|
5
|
+
class Entity
|
6
|
+
attr_accessor :content_type, :data
|
7
|
+
|
8
|
+
def initialze(data)
|
9
|
+
self.data = data
|
10
|
+
end
|
11
|
+
|
12
|
+
def data
|
13
|
+
@data ||= ''
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_request_data
|
17
|
+
return data if data.is_a?(String)
|
18
|
+
|
19
|
+
case content_type
|
20
|
+
when :uri then
|
21
|
+
to_uri
|
22
|
+
when 'application/json',:json then
|
23
|
+
to_json
|
24
|
+
# when 'application/xml',:xml then to_xml
|
25
|
+
when 'text/www-form-urlencoded',:form,:url then
|
26
|
+
to_www_form_url_encoded
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# def to_xml
|
31
|
+
# "XML"
|
32
|
+
# end
|
33
|
+
|
34
|
+
def to_uri
|
35
|
+
strings = data.inject([]) do |acc, (name, value)|
|
36
|
+
acc << "#{name.to_s}=#{value}"
|
37
|
+
acc
|
38
|
+
end
|
39
|
+
URI.escape(strings.join('&'))
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_json
|
43
|
+
data.to_json
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_www_form_url_encoded
|
47
|
+
strings = data.inject([]) do |acc, (name, value)|
|
48
|
+
acc << "#{CGI.escape(name.to_s)}=#{CGI.escape(value)}"
|
49
|
+
acc
|
50
|
+
end
|
51
|
+
strings.join('&')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module CurbFu
|
2
|
+
class Request
|
3
|
+
module Base
|
4
|
+
include Common
|
5
|
+
|
6
|
+
def build(url_params, query_params = {})
|
7
|
+
curb = Curl::Easy.new(build_url(url_params, query_params))
|
8
|
+
|
9
|
+
headers = global_headers
|
10
|
+
|
11
|
+
unless url_params.is_a?(String)
|
12
|
+
curb.userpwd = "#{url_params[:username]}:#{url_params[:password]}" if url_params[:username]
|
13
|
+
if url_params[:authtype]
|
14
|
+
curb.http_auth_types = url_params[:authtype]
|
15
|
+
elsif url_params[:username]
|
16
|
+
curb.http_auth_types = CurbFu::Authentication::BASIC
|
17
|
+
end
|
18
|
+
|
19
|
+
headers = headers.merge(url_params[:headers]) unless url_params[:headers].nil?
|
20
|
+
headers["Expect"] = '' unless url_params[:headers] && url_params[:headers]["Expect"]
|
21
|
+
end
|
22
|
+
|
23
|
+
curb.headers = headers
|
24
|
+
curb.timeout = @timeout
|
25
|
+
|
26
|
+
curb
|
27
|
+
end
|
28
|
+
|
29
|
+
# Set headers to be used for every request
|
30
|
+
# * headers: hash of header names and values
|
31
|
+
def global_headers=(headers)
|
32
|
+
@global_headers = headers
|
33
|
+
end
|
34
|
+
|
35
|
+
# Headers to be used for every request
|
36
|
+
# Returns: hash of header names and values
|
37
|
+
def global_headers
|
38
|
+
@global_headers ||= {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def get(url, params = {})
|
42
|
+
curb = self.build(url, params)
|
43
|
+
curb.http_get
|
44
|
+
CurbFu::Response::Base.from_curb_response(curb)
|
45
|
+
end
|
46
|
+
|
47
|
+
def put(url, params = {})
|
48
|
+
curb = self.build(url, params)
|
49
|
+
curb.http_put("")
|
50
|
+
CurbFu::Response::Base.from_curb_response(curb)
|
51
|
+
end
|
52
|
+
|
53
|
+
def post(url, params = {})
|
54
|
+
fields = create_post_fields(params)
|
55
|
+
fields = [fields] if fields.is_a?(String)
|
56
|
+
|
57
|
+
curb = self.build(url)
|
58
|
+
curb.http_post(*fields)
|
59
|
+
CurbFu::Response::Base.from_curb_response(curb)
|
60
|
+
end
|
61
|
+
|
62
|
+
def post_file(url, params = {}, filez = {})
|
63
|
+
fields = create_post_fields(params)
|
64
|
+
fields += create_file_fields(filez)
|
65
|
+
|
66
|
+
curb = self.build(url)
|
67
|
+
curb.multipart_form_post = true
|
68
|
+
|
69
|
+
begin
|
70
|
+
curb.http_post(*fields)
|
71
|
+
rescue Curl::Err::InvalidPostFieldError => e
|
72
|
+
field_list = (params.merge(filez)).inject([]) { |list, (name, value)| list << "#{name} => #{value.to_s[0..49].inspect}"; list }
|
73
|
+
raise e, "There was an attempt to post invalid fields. The fields were:\n#{field_list.join("\n")}"
|
74
|
+
end
|
75
|
+
CurbFu::Response::Base.from_curb_response(curb)
|
76
|
+
end
|
77
|
+
|
78
|
+
def delete(url)
|
79
|
+
curb = self.build(url)
|
80
|
+
curb.http_delete
|
81
|
+
CurbFu::Response::Base.from_curb_response(curb)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
def create_post_fields(params)
|
86
|
+
return params if params.is_a? String
|
87
|
+
|
88
|
+
fields = []
|
89
|
+
params.each do |name, value|
|
90
|
+
value_string = value if value.is_a?(String)
|
91
|
+
value_string = value.join(',') if value.is_a?(Array)
|
92
|
+
value_string ||= value.to_s
|
93
|
+
|
94
|
+
fields << Curl::PostField.content(name.to_s,value_string)
|
95
|
+
end
|
96
|
+
return fields
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_put_fields(params)
|
100
|
+
return params if params.is_a? String
|
101
|
+
|
102
|
+
params.inject([]) do |list, (k,v)|
|
103
|
+
v = v.is_a?(Array) ? v.join(',') : v
|
104
|
+
list << "#{k}=#{v}"
|
105
|
+
list
|
106
|
+
end.join('&')
|
107
|
+
end
|
108
|
+
|
109
|
+
def create_file_fields(filez)
|
110
|
+
fields = []
|
111
|
+
filez.each do |name, path|
|
112
|
+
fields << Curl::PostField.file(name, path)
|
113
|
+
end
|
114
|
+
fields
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module CurbFu
|
2
|
+
class Request
|
3
|
+
module Common
|
4
|
+
def timeout=(val)
|
5
|
+
@timeout = val
|
6
|
+
end
|
7
|
+
|
8
|
+
def timeout
|
9
|
+
@timeout.nil? ? 10 : @timeout
|
10
|
+
end
|
11
|
+
|
12
|
+
def build_url(url_params, query_params = {})
|
13
|
+
if url_params.is_a? String
|
14
|
+
built_url = url_params
|
15
|
+
else
|
16
|
+
built_url = "http://#{url_params[:host]}"
|
17
|
+
built_url += ":" + url_params[:port].to_s if url_params[:port]
|
18
|
+
built_url += url_params[:path] if url_params[:path]
|
19
|
+
end
|
20
|
+
|
21
|
+
# TODO: update for use with CurbFu::Entity
|
22
|
+
if query_params.is_a? String
|
23
|
+
built_url += query_params
|
24
|
+
elsif !query_params.empty?
|
25
|
+
built_url += "?"
|
26
|
+
built_url += query_params.collect do |name, value|
|
27
|
+
CurbFu::Request::Parameter.new(name, value).to_uri_param
|
28
|
+
end.join('&')
|
29
|
+
end
|
30
|
+
built_url
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module CurbFu
|
2
|
+
class Request
|
3
|
+
class Parameter
|
4
|
+
attr_accessor :name, :value
|
5
|
+
|
6
|
+
def initialize(name, value)
|
7
|
+
self.name = name
|
8
|
+
self.value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.build_uri_params(param_hash)
|
12
|
+
param_hash.to_param
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.build_post_fields(param_hash)
|
16
|
+
param_hash.to_post_fields
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_uri_param
|
20
|
+
value.to_param(name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_curl_post_field
|
24
|
+
field_string = value.to_param(name)
|
25
|
+
fields = field_string.split('&').collect do |field_value_pair|
|
26
|
+
field_name, field_value = field_value_pair.split('=')
|
27
|
+
Curl::PostField.content(field_name, CGI::unescape(field_value))
|
28
|
+
end
|
29
|
+
fields.length == 1 ? fields[0] : fields
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'rack/test'
|
2
|
+
|
3
|
+
module CurbFu
|
4
|
+
class Request
|
5
|
+
module Test
|
6
|
+
include Common
|
7
|
+
|
8
|
+
def self.included(target)
|
9
|
+
target.extend(ClassMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def get(url, params = {})
|
14
|
+
request_options = build_request_options(url)
|
15
|
+
params = hashify_params(params) if params.is_a?(String)
|
16
|
+
respond(request_options[:interface], :get, request_options[:url],
|
17
|
+
params, request_options[:headers], request_options[:username], request_options[:password])
|
18
|
+
end
|
19
|
+
|
20
|
+
def post(url, params = {})
|
21
|
+
request_options = build_request_options(url)
|
22
|
+
respond(request_options[:interface], :post, request_options[:url],
|
23
|
+
params, request_options[:headers], request_options[:username], request_options[:password])
|
24
|
+
end
|
25
|
+
|
26
|
+
def post_file(url, params = {}, filez = {})
|
27
|
+
request_options = build_request_options(url)
|
28
|
+
uploaded_files = filez.inject({}) do |hsh, (name, path)|
|
29
|
+
hsh[name] = Rack::Test::UploadedFile.new(path)
|
30
|
+
hsh
|
31
|
+
end
|
32
|
+
respond(request_options[:interface], :post, request_options[:url],
|
33
|
+
params.merge(uploaded_files), request_options[:headers], request_options[:username], request_options[:password])
|
34
|
+
end
|
35
|
+
|
36
|
+
def put(url, params = {})
|
37
|
+
request_options = build_request_options(url)
|
38
|
+
respond(request_options[:interface], :put, request_options[:url],
|
39
|
+
params, request_options[:headers], request_options[:username], request_options[:password])
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete(url, params = {})
|
43
|
+
request_options = build_request_options(url)
|
44
|
+
params = hashify_params(params) if params.is_a?(String)
|
45
|
+
respond(request_options[:interface], :delete, request_options[:url],
|
46
|
+
params, request_options[:headers], request_options[:username], request_options[:password])
|
47
|
+
end
|
48
|
+
|
49
|
+
def hashify_params(param_string)
|
50
|
+
param_string.sub!(/^\?/,'')
|
51
|
+
param_string.split('&').inject({}) do |hsh, pair|
|
52
|
+
key, value = pair.split('=')
|
53
|
+
|
54
|
+
if key.match(/(.+)\[\]$/)
|
55
|
+
key = $1
|
56
|
+
hsh[key] ||= []
|
57
|
+
hsh[key] << value
|
58
|
+
elsif key.match(/([^\[]+)\[(.+)\]$/)
|
59
|
+
key = $1
|
60
|
+
subkey = $2
|
61
|
+
hsh[key] ||= {}
|
62
|
+
hsh[key].update( subkey => value )
|
63
|
+
else
|
64
|
+
hsh[key] = value
|
65
|
+
end
|
66
|
+
|
67
|
+
hsh
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def build_request_options(url)
|
72
|
+
options = {}
|
73
|
+
options[:headers] = (url.is_a?(String)) ? nil : url.delete(:headers)
|
74
|
+
options[:url] = build_url(url)
|
75
|
+
options[:username], options[:password] = get_auth(url)
|
76
|
+
options[:interface] = get_interface(url)
|
77
|
+
options
|
78
|
+
end
|
79
|
+
|
80
|
+
def respond(interface, operation, url, params, headers, username = nil, password = nil)
|
81
|
+
if interface.nil?
|
82
|
+
raise Curl::Err::ConnectionFailedError
|
83
|
+
else
|
84
|
+
unless headers.nil?
|
85
|
+
process_headers(headers).each do |name, value|
|
86
|
+
interface.header(name, value)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
interface.authorize(username, password) unless username.nil?
|
90
|
+
puts "sending #{operation} to #{url} with params #{params.inspect} using interface #{interface.inspect}" if CurbFu.debug?
|
91
|
+
begin
|
92
|
+
response = interface.send(operation, url, params, 'SERVER_NAME' => interface.hostname)
|
93
|
+
rescue => e
|
94
|
+
puts "Caught error: #{e}, #{e.backtrace.join("\n")}" if CurbFu.debug?
|
95
|
+
raise e
|
96
|
+
end
|
97
|
+
CurbFu::Response::Base.from_rack_response(response)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def process_headers(headers)
|
102
|
+
headers.inject({}) do |accum, (header_name, value)|
|
103
|
+
key = header_name.gsub("-", "_").upcase
|
104
|
+
key = "HTTP_" + key unless key =~ /^HTTP_/
|
105
|
+
accum[key] = value
|
106
|
+
accum
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_interface(url)
|
111
|
+
if url.is_a?(Hash)
|
112
|
+
host = url[:host]
|
113
|
+
else
|
114
|
+
host = parse_hostname(url)
|
115
|
+
end
|
116
|
+
match_host(host)
|
117
|
+
end
|
118
|
+
|
119
|
+
def parse_hostname(uri)
|
120
|
+
parsed_hostname = URI.parse(uri)
|
121
|
+
parsed_hostname.host || parsed_hostname.path
|
122
|
+
end
|
123
|
+
|
124
|
+
def match_host(host)
|
125
|
+
match = CurbFu.stubs.find do |(hostname, interface)|
|
126
|
+
hostname == host
|
127
|
+
end
|
128
|
+
match.last unless match.nil?
|
129
|
+
end
|
130
|
+
|
131
|
+
def get_auth(url)
|
132
|
+
username = password = nil
|
133
|
+
if url.is_a?(Hash) && url[:username]
|
134
|
+
username = url[:username]
|
135
|
+
password = url[:password]
|
136
|
+
end
|
137
|
+
[username, password]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class Interface
|
142
|
+
include Rack::Test::Methods
|
143
|
+
|
144
|
+
attr_accessor :app, :hostname
|
145
|
+
|
146
|
+
def initialize(app, hostname = 'example.org')
|
147
|
+
self.app = app
|
148
|
+
self.hostname = hostname
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|