nice_http 1.8.10 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +53 -3
- data/lib/nice_http/add_stats.rb +85 -0
- data/lib/nice_http/close.rb +39 -0
- data/lib/nice_http/defaults.rb +30 -0
- data/lib/nice_http/inherited.rb +8 -0
- data/lib/nice_http/initialize.rb +253 -0
- data/lib/nice_http/manage/create_stats.rb +64 -0
- data/lib/nice_http/{manage_request.rb → manage/request.rb} +16 -9
- data/lib/nice_http/{manage_response.rb → manage/response.rb} +7 -111
- data/lib/nice_http/manage/set_stats.rb +43 -0
- data/lib/nice_http/manage/wait_async_operation.rb +43 -0
- data/lib/nice_http/methods/delete.rb +108 -0
- data/lib/nice_http/methods/get.rb +159 -0
- data/lib/nice_http/methods/head.rb +72 -0
- data/lib/nice_http/methods/patch.rb +103 -0
- data/lib/nice_http/methods/post.rb +122 -0
- data/lib/nice_http/methods/put.rb +87 -0
- data/lib/nice_http/methods/send_request.rb +47 -0
- data/lib/nice_http/reset.rb +54 -0
- data/lib/nice_http/save_stats.rb +37 -0
- data/lib/nice_http/utils/basic_authentication.rb +18 -0
- data/lib/nice_http/{utils.rb → utils/get_value_xml_tag.rb} +0 -45
- data/lib/nice_http/utils/set_value_xml_tag.rb +30 -0
- data/lib/nice_http.rb +33 -475
- metadata +24 -6
- data/lib/nice_http/http_methods.rb +0 -686
@@ -0,0 +1,122 @@
|
|
1
|
+
module NiceHttpHttpMethods
|
2
|
+
|
3
|
+
######################################################
|
4
|
+
# Post data to path
|
5
|
+
# @param arguments [Hash] containing at least keys :data and :path.
|
6
|
+
# In case :data not supplied and :data_examples array supplied, it will be taken the first example as :data.
|
7
|
+
# @param arguments [Array<path, data, additional_headers>]
|
8
|
+
# path (string).
|
9
|
+
# data (json data for example).
|
10
|
+
# additional_headers (Hash key=>value).
|
11
|
+
# @return [Hash] response
|
12
|
+
# Including at least the symbol keys:
|
13
|
+
# :data = the response data body.
|
14
|
+
# :message = plain text response.
|
15
|
+
# :code = code response (200=ok,500=wrong...).
|
16
|
+
# All keys in response are lowercase.
|
17
|
+
# data, message and code can also be accessed as attributes like .message .code .data.
|
18
|
+
# In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil, data: '' }
|
19
|
+
# @example
|
20
|
+
# resp = @http.post(Requests::Customer.update_customer)
|
21
|
+
# assert resp.code == 201
|
22
|
+
# @example
|
23
|
+
# resp = http.post( {
|
24
|
+
# path: "/api/users",
|
25
|
+
# data: {name: "morpheus", job: "leader"}
|
26
|
+
# } )
|
27
|
+
# pp resp.data.json
|
28
|
+
######################################################
|
29
|
+
def post(*arguments)
|
30
|
+
begin
|
31
|
+
path, data, headers_t = manage_request(*arguments)
|
32
|
+
@start_time = Time.now if @start_time.nil?
|
33
|
+
if arguments.size > 0 and arguments[0].kind_of?(Hash)
|
34
|
+
arg = arguments[0]
|
35
|
+
if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
|
36
|
+
data = ""
|
37
|
+
if arg[:mock_response].keys.include?(:data)
|
38
|
+
data = arg[:mock_response][:data]
|
39
|
+
if data.kind_of?(Hash) #to json
|
40
|
+
begin
|
41
|
+
require "json"
|
42
|
+
data = data.to_json
|
43
|
+
rescue
|
44
|
+
@logger.fatal "There was a problem converting to json: #{data}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
@logger.warn "Pay attention!!! This is a mock response:"
|
49
|
+
@start_time_net = Time.now if @start_time_net.nil?
|
50
|
+
manage_response(arg[:mock_response], data.to_s)
|
51
|
+
return @response
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
begin
|
56
|
+
@start_time_net = Time.now if @start_time_net.nil?
|
57
|
+
if headers_t["Content-Type"] == "multipart/form-data"
|
58
|
+
require "net/http/post/multipart"
|
59
|
+
headers_t.each { |key, value|
|
60
|
+
arguments[0][:data].add_field(key, value) #add to Headers
|
61
|
+
}
|
62
|
+
resp = @http.request(arguments[0][:data])
|
63
|
+
elsif headers_t["Content-Type"].to_s.include?("application/x-www-form-urlencoded")
|
64
|
+
encoded_form = URI.encode_www_form(arguments[0][:data])
|
65
|
+
resp = @http.request_post(path, encoded_form, headers_t)
|
66
|
+
data = resp.body
|
67
|
+
else
|
68
|
+
resp = @http.post(path, data, headers_t)
|
69
|
+
#todo: do it also for forms and multipart
|
70
|
+
if (resp.code == 401 or resp.code == 408) and @headers_orig.values.map(&:class).include?(Proc)
|
71
|
+
try = false
|
72
|
+
@headers_orig.each do |k, v|
|
73
|
+
if v.is_a?(Proc) and headers_t.key?(k)
|
74
|
+
try = true
|
75
|
+
headers_t[k] = v.call
|
76
|
+
end
|
77
|
+
end
|
78
|
+
if try
|
79
|
+
@logger.warn "Not authorized. Trying to generate a new token."
|
80
|
+
resp = @http.post(path, data, headers_t)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
data = resp.body
|
84
|
+
end
|
85
|
+
rescue Exception => stack
|
86
|
+
@logger.warn stack
|
87
|
+
if !@timeout.nil? and (Time.now - @start_time_net) > @timeout
|
88
|
+
@logger.warn "The connection seems to be closed in the host machine. Timeout."
|
89
|
+
return { fatal_error: "Net::ReadTimeout", code: nil, message: nil, data: "" }
|
90
|
+
else
|
91
|
+
@logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
|
92
|
+
@http.finish()
|
93
|
+
@http.start()
|
94
|
+
@start_time_net = Time.now if @start_time_net.nil?
|
95
|
+
@headers_orig.each { |k, v| headers_t[k] = v.call if v.is_a?(Proc) and headers_t.key?(k) }
|
96
|
+
resp, data = @http.post(path, data, headers_t)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
manage_response(resp, data)
|
100
|
+
if @auto_redirect and @response[:code].to_i >= 300 and @response[:code].to_i < 400 and @response.include?(:location)
|
101
|
+
if @num_redirects <= 30
|
102
|
+
@num_redirects += 1
|
103
|
+
current_server = "http"
|
104
|
+
current_server += "s" if @ssl == true
|
105
|
+
current_server += "://#{@host}"
|
106
|
+
location = @response[:location].gsub(current_server, "")
|
107
|
+
@logger.info "(#{@num_redirects}) Redirecting NiceHttp to #{location}"
|
108
|
+
get(location)
|
109
|
+
else
|
110
|
+
@logger.fatal "(#{@num_redirects}) Maximum number of redirections for a single request reached. Be sure everything is correct, it seems there is a non ending loop"
|
111
|
+
@num_redirects = 0
|
112
|
+
end
|
113
|
+
else
|
114
|
+
@num_redirects = 0
|
115
|
+
end
|
116
|
+
return @response
|
117
|
+
rescue Exception => stack
|
118
|
+
@logger.fatal stack
|
119
|
+
return { fatal_error: stack.to_s, code: nil, message: nil, data: "" }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module NiceHttpHttpMethods
|
2
|
+
|
3
|
+
######################################################
|
4
|
+
# Put data to path
|
5
|
+
# @param arguments [Hash] containing at least keys :data and :path.
|
6
|
+
# In case :data not supplied and :data_examples array supplied, it will be taken the first example as :data.
|
7
|
+
# @param arguments [Array<path, data, additional_headers>]
|
8
|
+
# path (string).
|
9
|
+
# data (json data for example).
|
10
|
+
# additional_headers (Hash key=>value).
|
11
|
+
# @return [Hash] response
|
12
|
+
# Including at least the symbol keys:
|
13
|
+
# :data = the response data body.
|
14
|
+
# :message = plain text response.
|
15
|
+
# :code = code response (200=ok,500=wrong...).
|
16
|
+
# All keys in response are lowercase.
|
17
|
+
# data, message and code can also be accessed as attributes like .message .code .data.
|
18
|
+
# In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil, data: '' }
|
19
|
+
# @example
|
20
|
+
# resp = @http.put(Requests::Customer.remove_phone)
|
21
|
+
######################################################
|
22
|
+
def put(*arguments)
|
23
|
+
begin
|
24
|
+
path, data, headers_t = manage_request(*arguments)
|
25
|
+
@start_time = Time.now if @start_time.nil?
|
26
|
+
if arguments.size > 0 and arguments[0].kind_of?(Hash)
|
27
|
+
arg = arguments[0]
|
28
|
+
if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
|
29
|
+
data = ""
|
30
|
+
if arg[:mock_response].keys.include?(:data)
|
31
|
+
data = arg[:mock_response][:data]
|
32
|
+
if data.kind_of?(Hash) #to json
|
33
|
+
begin
|
34
|
+
require "json"
|
35
|
+
data = data.to_json
|
36
|
+
rescue
|
37
|
+
@logger.fatal "There was a problem converting to json: #{data}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
@logger.warn "Pay attention!!! This is a mock response:"
|
42
|
+
@start_time_net = Time.now if @start_time_net.nil?
|
43
|
+
manage_response(arg[:mock_response], data.to_s)
|
44
|
+
return @response
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
begin
|
49
|
+
@start_time_net = Time.now if @start_time_net.nil?
|
50
|
+
resp = @http.send_request("PUT", path, data, headers_t)
|
51
|
+
if (resp.code == 401 or resp.code == 408) and @headers_orig.values.map(&:class).include?(Proc)
|
52
|
+
try = false
|
53
|
+
@headers_orig.each do |k, v|
|
54
|
+
if v.is_a?(Proc) and headers_t.key?(k)
|
55
|
+
try = true
|
56
|
+
headers_t[k] = v.call
|
57
|
+
end
|
58
|
+
end
|
59
|
+
if try
|
60
|
+
@logger.warn "Not authorized. Trying to generate a new token."
|
61
|
+
resp = @http.send_request("PUT", path, data, headers_t)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
data = resp.body
|
65
|
+
rescue Exception => stack
|
66
|
+
@logger.warn stack
|
67
|
+
if !@timeout.nil? and (Time.now - @start_time_net) > @timeout
|
68
|
+
@logger.warn "The connection seems to be closed in the host machine. Timeout."
|
69
|
+
return { fatal_error: "Net::ReadTimeout", code: nil, message: nil, data: "" }
|
70
|
+
else
|
71
|
+
@logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
|
72
|
+
@http.finish()
|
73
|
+
@http.start()
|
74
|
+
@headers_orig.each { |k, v| headers_t[k] = v.call if v.is_a?(Proc) and headers_t.key?(k) }
|
75
|
+
@start_time_net = Time.now if @start_time_net.nil?
|
76
|
+
resp, data = @http.send_request("PUT", path, data, headers_t)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
manage_response(resp, data)
|
80
|
+
|
81
|
+
return @response
|
82
|
+
rescue Exception => stack
|
83
|
+
@logger.fatal stack
|
84
|
+
return { fatal_error: stack.to_s, code: nil, message: nil, data: "" }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module NiceHttpHttpMethods
|
2
|
+
|
3
|
+
######################################################
|
4
|
+
# It will send the request depending on the :method declared on the request hash
|
5
|
+
# Take a look at https://github.com/MarioRuiz/Request-Hash
|
6
|
+
#
|
7
|
+
# @param request_hash [Hash] containing at least key :path and :method. The methods that are accepted are: :get, :head, :post, :put, :delete, :patch
|
8
|
+
#
|
9
|
+
# @return [Hash] response
|
10
|
+
# Including at least the symbol keys:
|
11
|
+
# :data = the response data body.
|
12
|
+
# :message = plain text response.
|
13
|
+
# :code = code response (200=ok,500=wrong...).
|
14
|
+
# All keys in response are lowercase.
|
15
|
+
# data, message and code can also be accessed as attributes like .message .code .data.
|
16
|
+
# In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil, data: '' }
|
17
|
+
# @example
|
18
|
+
# resp = @http.send_request Requests::Customer.remove_session
|
19
|
+
# assert resp.code == 204
|
20
|
+
######################################################
|
21
|
+
def send_request(request_hash)
|
22
|
+
unless request_hash.is_a?(Hash) and request_hash.key?(:method) and request_hash.key?(:path) and
|
23
|
+
request_hash[:method].is_a?(Symbol) and
|
24
|
+
[:get, :head, :post, :put, :delete, :patch].include?(request_hash[:method])
|
25
|
+
message = "send_request: it needs to be supplied a Request Hash that includes a :method and :path. "
|
26
|
+
message += "Supported methods: :get, :head, :post, :put, :delete, :patch"
|
27
|
+
@logger.fatal message
|
28
|
+
return { fatal_error: message, code: nil, message: nil }
|
29
|
+
else
|
30
|
+
case request_hash[:method]
|
31
|
+
when :get
|
32
|
+
resp = get request_hash
|
33
|
+
when :post
|
34
|
+
resp = post request_hash
|
35
|
+
when :head
|
36
|
+
resp = head request_hash
|
37
|
+
when :put
|
38
|
+
resp = put request_hash
|
39
|
+
when :delete
|
40
|
+
resp = delete request_hash
|
41
|
+
when :patch
|
42
|
+
resp = patch request_hash
|
43
|
+
end
|
44
|
+
return resp
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class NiceHttp
|
2
|
+
######################################################
|
3
|
+
# to reset to the original defaults
|
4
|
+
######################################################
|
5
|
+
def self.reset!
|
6
|
+
@host = nil
|
7
|
+
@port = 80
|
8
|
+
@ssl = false
|
9
|
+
@timeout = nil
|
10
|
+
@headers = {}
|
11
|
+
@values_for = {}
|
12
|
+
@debug = false
|
13
|
+
@log = :fix_file
|
14
|
+
@log_path = ""
|
15
|
+
@log_headers = :all
|
16
|
+
@proxy_host = nil
|
17
|
+
@proxy_port = nil
|
18
|
+
@last_request = nil
|
19
|
+
@request = nil
|
20
|
+
@requests = nil
|
21
|
+
@last_response = nil
|
22
|
+
@request_id = ""
|
23
|
+
@use_mocks = false
|
24
|
+
@connections = []
|
25
|
+
@active = 0
|
26
|
+
@auto_redirect = true
|
27
|
+
@log_files = {}
|
28
|
+
@create_stats = false
|
29
|
+
@stats = {
|
30
|
+
all: {
|
31
|
+
num_requests: 0,
|
32
|
+
started: nil,
|
33
|
+
finished: nil,
|
34
|
+
real_time_elapsed: 0,
|
35
|
+
time_elapsed: {
|
36
|
+
total: 0,
|
37
|
+
maximum: 0,
|
38
|
+
minimum: 1000000,
|
39
|
+
average: 0,
|
40
|
+
},
|
41
|
+
method: {},
|
42
|
+
},
|
43
|
+
path: {},
|
44
|
+
name: {},
|
45
|
+
}
|
46
|
+
@capture = false
|
47
|
+
@captured = []
|
48
|
+
@async_wait_seconds = 0
|
49
|
+
@async_header = "location"
|
50
|
+
@async_completed = ""
|
51
|
+
@async_resource = ""
|
52
|
+
@async_status = ""
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class NiceHttp
|
2
|
+
######################################################
|
3
|
+
# It will save the NiceHttp.stats on different files, each key of the hash in a different file.
|
4
|
+
#
|
5
|
+
# @param file_name [String] path and file name to be used to store the stats.
|
6
|
+
# In case no one supplied it will be used the value in NiceHttp.log and it will be saved on YAML format.
|
7
|
+
# In case extension is .yaml will be saved on YAML format.
|
8
|
+
# In case extension is .json will be saved on JSON format.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# NiceHttp.save_stats
|
12
|
+
# NiceHttp.save_stats('./stats/my_stats.yaml')
|
13
|
+
# NiceHttp.save_stats('./stats/my_stats.json')
|
14
|
+
######################################################
|
15
|
+
def self.save_stats(file_name = "")
|
16
|
+
if file_name == ""
|
17
|
+
if self.log.is_a?(String)
|
18
|
+
file_name = self.log
|
19
|
+
else
|
20
|
+
file_name = "./#{self.log_path}nice_http.log"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
require "fileutils"
|
24
|
+
FileUtils.mkdir_p File.dirname(file_name)
|
25
|
+
if file_name.match?(/\.json$/)
|
26
|
+
require "json"
|
27
|
+
self.stats.keys.each do |key|
|
28
|
+
File.open("#{file_name.gsub(/.json$/, "_stats_")}#{key}.json", "w") { |file| file.write(self.stats[key].to_json) }
|
29
|
+
end
|
30
|
+
else
|
31
|
+
require "yaml"
|
32
|
+
self.stats.keys.each do |key|
|
33
|
+
File.open("#{file_name.gsub(/.\w+$/, "_stats_")}#{key}.yaml", "w") { |file| file.write(self.stats[key].to_yaml) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module NiceHttpUtils
|
2
|
+
##################################################
|
3
|
+
# returns the seed for Basic authentication
|
4
|
+
# @param user [String]
|
5
|
+
# @param password [String]
|
6
|
+
# @param strict [Boolean] (default: false) use strict_encode64 if true, if not encode64
|
7
|
+
# @return [String] seed string to be used on Authorization key header on a get request
|
8
|
+
####################################################
|
9
|
+
def self.basic_authentication(user:, password:, strict: false)
|
10
|
+
require "base64"
|
11
|
+
if strict
|
12
|
+
seed = "Basic " + Base64.strict_encode64(user + ":" + password)
|
13
|
+
else
|
14
|
+
seed = "Basic " + Base64.encode64(user + ":" + password)
|
15
|
+
end
|
16
|
+
return seed
|
17
|
+
end
|
18
|
+
end
|
@@ -60,49 +60,4 @@ module NiceHttpUtils
|
|
60
60
|
return ret
|
61
61
|
end
|
62
62
|
end
|
63
|
-
|
64
|
-
##################################################
|
65
|
-
# set a value on xml tag
|
66
|
-
# @param tag_name [String]
|
67
|
-
# @param xml_string [String]
|
68
|
-
# @param value [String]
|
69
|
-
# @param take_off_prefix [Boolean] (optional). true, false(default)
|
70
|
-
# @return [String] with the new value
|
71
|
-
####################################################
|
72
|
-
def self.set_value_xml_tag(tag_name, xml_string, value, take_off_prefix = false)
|
73
|
-
tag_name = tag_name.to_s
|
74
|
-
if take_off_prefix
|
75
|
-
i = tag_name.index(":")
|
76
|
-
tag_name = tag_name[i + 1..tag_name.length] unless i.nil?
|
77
|
-
end
|
78
|
-
if xml_string.to_s != ""
|
79
|
-
if take_off_prefix
|
80
|
-
old_value = NiceHttpUtils.get_value_xml_tag(tag_name, xml_string.dup, true)
|
81
|
-
xml_string.gsub!(/:#{tag_name}>#{Regexp.escape(old_value)}<\//i, ":" + tag_name + ">" + value + "</")
|
82
|
-
xml_string.gsub!(/<#{tag_name}>#{Regexp.escape(old_value)}<\//i, "<" + tag_name + ">" + value + "</")
|
83
|
-
else
|
84
|
-
xml_string.gsub!(/<#{tag_name}>.*<\/#{tag_name}>/i, "<" + tag_name + ">" + value + "</" + tag_name + ">")
|
85
|
-
end
|
86
|
-
return xml_string
|
87
|
-
else
|
88
|
-
return ""
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
##################################################
|
93
|
-
# returns the seed for Basic authentication
|
94
|
-
# @param user [String]
|
95
|
-
# @param password [String]
|
96
|
-
# @param strict [Boolean] (default: false) use strict_encode64 if true, if not encode64
|
97
|
-
# @return [String] seed string to be used on Authorization key header on a get request
|
98
|
-
####################################################
|
99
|
-
def self.basic_authentication(user:, password:, strict: false)
|
100
|
-
require "base64"
|
101
|
-
if strict
|
102
|
-
seed = "Basic " + Base64.strict_encode64(user + ":" + password)
|
103
|
-
else
|
104
|
-
seed = "Basic " + Base64.encode64(user + ":" + password)
|
105
|
-
end
|
106
|
-
return seed
|
107
|
-
end
|
108
63
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module NiceHttpUtils
|
2
|
+
|
3
|
+
##################################################
|
4
|
+
# set a value on xml tag
|
5
|
+
# @param tag_name [String]
|
6
|
+
# @param xml_string [String]
|
7
|
+
# @param value [String]
|
8
|
+
# @param take_off_prefix [Boolean] (optional). true, false(default)
|
9
|
+
# @return [String] with the new value
|
10
|
+
####################################################
|
11
|
+
def self.set_value_xml_tag(tag_name, xml_string, value, take_off_prefix = false)
|
12
|
+
tag_name = tag_name.to_s
|
13
|
+
if take_off_prefix
|
14
|
+
i = tag_name.index(":")
|
15
|
+
tag_name = tag_name[i + 1..tag_name.length] unless i.nil?
|
16
|
+
end
|
17
|
+
if xml_string.to_s != ""
|
18
|
+
if take_off_prefix
|
19
|
+
old_value = NiceHttpUtils.get_value_xml_tag(tag_name, xml_string.dup, true)
|
20
|
+
xml_string.gsub!(/:#{tag_name}>#{Regexp.escape(old_value)}<\//i, ":" + tag_name + ">" + value + "</")
|
21
|
+
xml_string.gsub!(/<#{tag_name}>#{Regexp.escape(old_value)}<\//i, "<" + tag_name + ">" + value + "</")
|
22
|
+
else
|
23
|
+
xml_string.gsub!(/<#{tag_name}>.*<\/#{tag_name}>/i, "<" + tag_name + ">" + value + "</" + tag_name + ">")
|
24
|
+
end
|
25
|
+
return xml_string
|
26
|
+
else
|
27
|
+
return ""
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|