nice_http 1.7.3 → 1.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,414 +1,428 @@
1
- require "logger"
2
- require "nice_hash"
3
- require_relative "nice_http/utils"
4
- require_relative "nice_http/manage_request"
5
- require_relative "nice_http/manage_response"
6
- require_relative "nice_http/http_methods"
7
-
8
- ######################################################
9
- # Attributes you can access using NiceHttp.the_attribute:
10
- # :host, :port, :ssl, :headers, :debug, :log, :proxy_host, :proxy_port,
11
- # :last_request, :last_response, :request_id, :use_mocks, :connections,
12
- # :active, :auto_redirect, :values_for, :create_stats, :stats
13
- #
14
- # @attr [String] host The host to be accessed
15
- # @attr [Integer] port The port number
16
- # @attr [Boolean] ssl If you use ssl or not
17
- # @attr [Hash] headers Contains the headers you will be using on your connection
18
- # @attr [Boolean] debug In case true shows all the details of the communication with the host
19
- # @attr [String, Symbol] log :fix_file, :no, :screen, :file, "path and file name".
20
- # :fix_file, will log the communication on nice_http.log. (default).
21
- # :no, will not generate any logs.
22
- # :screen, will print the logs on the screen.
23
- # :file, will be generated a log file with name: nice_http_YY-mm-dd-HHMMSS.log.
24
- # :file_run, will generate a log file with the name where the object was created and extension .log, fex: myfile.rb.log
25
- # String the path and file name where the logs will be stored.
26
- # @attr [String] proxy_host the proxy host to be used
27
- # @attr [Integer] proxy_port the proxy port to be used
28
- # @attr [String] last_request The last request with all the content sent
29
- # @attr [String] last_response Only in case :debug is true, the last response with all the content
30
- # @attr [String] request_id If the response includes a requestId, will be stored here
31
- # @attr [Boolean] use_mocks If true, in case the request hash includes a :mock_response key, it will be used as the response instead
32
- # @attr [Array] connections It will include all the active connections (NiceHttp instances)
33
- # @attr [Integer] active Number of active connections
34
- # @attr [Boolean] auto_redirect If true, NiceHttp will take care of the auto redirections when required by the responses
35
- # @attr [Hash] response Contains the full response hash
36
- # @attr [Integer] num_redirects Number of consecutive redirections managed
37
- # @attr [Hash] headers The updated headers of the communication
38
- # @attr [Hash] cookies Cookies set. The key is the path (String) where cookies are set and the value a Hash with pairs of cookie keys and values, example:
39
- # { '/' => { "cfid" => "d95adfas2550255", "amddom.settings" => "doom" } }
40
- # @attr [Logger] logger An instance of the Logger class where logs will be stored. You can access on anytime to store specific data, for example:
41
- # my_http.logger.info "add this to the log file"
42
- # @see https://ruby-doc.org/stdlib-2.5.0/libdoc/logger/rdoc/Logger.html
43
- # @attr [Hash] values_for The default values to set on the data in case not specified others
44
- # @attr [Boolean] create_stats If true, NiceHttp will create stats of the http communication and store them on NiceHttp.stats hash
45
- # @attr [Hash] stats It contains detailed stats of the http communication
46
- ######################################################
47
- class NiceHttp
48
- include NiceHttpManageRequest
49
- include NiceHttpManageResponse
50
- include NiceHttpHttpMethods
51
-
52
- Error = Class.new StandardError
53
-
54
- InfoMissing = Class.new Error do
55
- attr_reader :attribute
56
-
57
- def initialize(attribute)
58
- @attribute = attribute
59
- message = "It was not possible to create the http connection!!!\n"
60
- message += "Wrong #{attribute}, remember to supply http:// or https:// in case you specify an url to create the http connection, for example:\n"
61
- message += "http = NiceHttp.new('http://example.com')"
62
- super message
63
- end
64
- end
65
-
66
- class << self
67
- attr_accessor :host, :port, :ssl, :headers, :debug, :log, :proxy_host, :proxy_port,
68
- :last_request, :last_response, :request_id, :use_mocks, :connections,
69
- :active, :auto_redirect, :log_files, :values_for, :create_stats, :stats
70
- end
71
-
72
- at_exit do
73
- if self.create_stats
74
- require 'yaml'
75
- self.stats.keys.each do |key|
76
- File.open("./nice_http_stats_#{key}.yaml", "w") { |file| file.write(self.stats[key].to_yaml) }
77
- end
78
- end
79
- end
80
-
81
- ######################################################
82
- # to reset to the original defaults
83
- ######################################################
84
- def self.reset!
85
- @host = nil
86
- @port = 80
87
- @ssl = false
88
- @headers = {}
89
- @values_for = {}
90
- @debug = false
91
- @log = :fix_file
92
- @proxy_host = nil
93
- @proxy_port = nil
94
- @last_request = nil
95
- @last_response = nil
96
- @request_id = ""
97
- @use_mocks = false
98
- @connections = []
99
- @active = 0
100
- @auto_redirect = true
101
- @log_files = {}
102
- @create_stats = false
103
- @stats = {
104
- all: {
105
- num_requests: 0,
106
- time_elapsed: {
107
- total: 0,
108
- maximum: 0,
109
- minimum: 100000,
110
- average: 0,
111
- },
112
- method: {},
113
- },
114
- path: {},
115
- name: {},
116
- }
117
- end
118
- reset!
119
-
120
- ######################################################
121
- # If inheriting from NiceHttp class
122
- ######################################################
123
- def self.inherited(subclass)
124
- subclass.reset!
125
- end
126
-
127
- attr_reader :host, :port, :ssl, :debug, :log, :proxy_host, :proxy_port, :response, :num_redirects
128
- attr_accessor :headers, :cookies, :use_mocks, :auto_redirect, :logger, :values_for
129
-
130
- ######################################################
131
- # Change the default values for NiceHttp supplying a Hash
132
- #
133
- # @param par [Hash] keys: :host, :port, :ssl, :headers, :debug, :log, :proxy_host, :proxy_port, :use_mocks, :auto_redirect, :values_for, :create_stats
134
- ######################################################
135
- def self.defaults=(par = {})
136
- @host = par[:host] if par.key?(:host)
137
- @port = par[:port] if par.key?(:port)
138
- @ssl = par[:ssl] if par.key?(:ssl)
139
- @headers = par[:headers].dup if par.key?(:headers)
140
- @values_for = par[:values_for].dup if par.key?(:values_for)
141
- @debug = par[:debug] if par.key?(:debug)
142
- @log = par[:log] if par.key?(:log)
143
- @proxy_host = par[:proxy_host] if par.key?(:proxy_host)
144
- @proxy_port = par[:proxy_port] if par.key?(:proxy_port)
145
- @use_mocks = par[:use_mocks] if par.key?(:use_mocks)
146
- @auto_redirect = par[:auto_redirect] if par.key?(:auto_redirect)
147
- @create_stats = par[:create_stats] if par.key?(:create_stats)
148
- end
149
-
150
- ######################################################
151
- # To add specific stats
152
- # The stats will be added to NiceHttp.stats[:specific]
153
- #
154
- # @param name [Symbol] name to group your specific stats
155
- # @param state [Symbol] state of the name supplied to group your specific stats
156
- # @param started [Time] when the process you want the stats started
157
- # @param finished [Time] when the process you want the stats finished
158
- #
159
- # @example
160
- # started = Time.now
161
- # @http.send_request Requests::Customer.add_customer
162
- # 30.times do
163
- # resp = @http.get(Requests::Customer.get_customer)
164
- # break if resp.code == 200
165
- # sleep 0.5
166
- # end
167
- # NiceHttp.add_stats(:customer, :create, started, Time.now)
168
- ######################################################
169
- def self.add_stats(name, state, started, finished)
170
- self.stats[:specific] ||= {}
171
- self.stats[:specific][name] ||= {num: 0, time_elapsed: {total:0, maximum:0, minimum:1000, average: 0}}
172
- self.stats[:specific][name][:num] += 1
173
- time_elapsed = self.stats[:specific][name][:time_elapsed]
174
- time_elapsed[:total] += finished - started
175
- time_elapsed[:maximum] = (finished - started) if time_elapsed[:maximum]<(finished-started)
176
- time_elapsed[:minimum] = (finished - started) if time_elapsed[:minimum]>(finished-started)
177
- time_elapsed[:average] = time_elapsed[:total]/self.stats[:specific][name][:num]
178
-
179
- self.stats[:specific][name][state] ||= {num: 0, time_elapsed: {total:0, maximum:0, minimum:1000, average: 0}}
180
- self.stats[:specific][name][state][:num] += 1
181
- time_elapsed = self.stats[:specific][name][state][:time_elapsed]
182
- time_elapsed[:total] += finished - started
183
- time_elapsed[:maximum] = (finished - started) if time_elapsed[:maximum]<(finished-started)
184
- time_elapsed[:minimum] = (finished - started) if time_elapsed[:minimum]>(finished-started)
185
- time_elapsed[:average] = time_elapsed[:total]/self.stats[:specific][name][state][:num]
186
- end
187
-
188
-
189
- ######################################################
190
- # Creates a new http connection.
191
- #
192
- # @param args [] If no parameter supplied, by default will access how is setup on defaults
193
- # @example
194
- # http = NiceHttp.new()
195
- # @param args [String]. The url to create the connection.
196
- # @example
197
- # http = NiceHttp.new("https://www.example.com")
198
- # @example
199
- # http = NiceHttp.new("example.com:8999")
200
- # @example
201
- # http = NiceHttp.new("localhost:8322")
202
- # @param args [Hash] containing these possible keys:
203
- #
204
- # host -- example.com. (default blank screen)
205
- #
206
- # port -- port for the connection. 80 (default)
207
- #
208
- # ssl -- true, false (default)
209
- #
210
- # headers -- hash with the headers
211
- #
212
- # values_for -- hash with the values_for
213
- #
214
- # debug -- true, false (default)
215
- #
216
- # log -- :no, :screen, :file, :fix_file (default).
217
- #
218
- # A string with a path can be supplied.
219
- #
220
- # If :fix_file: nice_http.log
221
- #
222
- # In case :file it will be generated a log file with name: nice_http_YY-mm-dd-HHMMSS.log
223
- #
224
- # proxy_host
225
- #
226
- # proxy_port
227
- # @example
228
- # http2 = NiceHttp.new( host: "reqres.in", port: 443, ssl: true )
229
- # @example
230
- # my_server = {host: "example.com",
231
- # port: 80,
232
- # headers: {"api-key": "zdDDdjkck"}
233
- # }
234
- # http3 = NiceHttp.new my_server
235
- ######################################################
236
- def initialize(args = {})
237
- require "net/http"
238
- require "net/https"
239
- @host = self.class.host
240
- @port = self.class.port
241
- @prepath = ""
242
- @ssl = self.class.ssl
243
- @headers = self.class.headers.dup
244
- @values_for = self.class.values_for.dup
245
- @debug = self.class.debug
246
- @log = self.class.log
247
- @proxy_host = self.class.proxy_host
248
- @proxy_port = self.class.proxy_port
249
- @use_mocks = self.class.use_mocks
250
- @auto_redirect = false #set it up at the end of initialize
251
- auto_redirect = self.class.auto_redirect
252
- @num_redirects = 0
253
- @create_stats = self.class.create_stats
254
-
255
- #todo: set only the cookies for the current domain
256
- #key: path, value: hash with key is the name of the cookie and value the value
257
- # we set the default value for non existing keys to empty Hash {} so in case of merge there is no problem
258
- @cookies = Hash.new { |h, k| h[k] = {} }
259
-
260
- if args.is_a?(String)
261
- uri = URI.parse(args)
262
- @host = uri.host unless uri.host.nil?
263
- @port = uri.port unless uri.port.nil?
264
- @ssl = true if !uri.scheme.nil? && (uri.scheme == "https")
265
- @prepath = uri.path unless uri.path == "/"
266
- elsif args.is_a?(Hash) && !args.keys.empty?
267
- @host = args[:host] if args.keys.include?(:host)
268
- @port = args[:port] if args.keys.include?(:port)
269
- @ssl = args[:ssl] if args.keys.include?(:ssl)
270
- @headers = args[:headers].dup if args.keys.include?(:headers)
271
- @values_for = args[:values_for].dup if args.keys.include?(:values_for)
272
- @debug = args[:debug] if args.keys.include?(:debug)
273
- @log = args[:log] if args.keys.include?(:log)
274
- @proxy_host = args[:proxy_host] if args.keys.include?(:proxy_host)
275
- @proxy_port = args[:proxy_port] if args.keys.include?(:proxy_port)
276
- @use_mocks = args[:use_mocks] if args.keys.include?(:use_mocks)
277
- auto_redirect = args[:auto_redirect] if args.keys.include?(:auto_redirect)
278
- end
279
-
280
- begin
281
- log_filename = ""
282
- if @log.kind_of?(String) or @log == :fix_file or @log == :file or @log == :file_run
283
- if @log.kind_of?(String)
284
- log_filename = @log.dup
285
- elsif @log == :fix_file
286
- log_filename = "nice_http.log"
287
- elsif @log == :file
288
- log_filename = "nice_http_#{Time.now.strftime("%Y-%m-%d-%H%M%S")}.log"
289
- elsif @log == :file_run
290
- log_filename = "#{caller.first[/[^:]+/]}.log"
291
- end
292
- if Thread.current.name.to_s!=''
293
- log_filename.gsub!(/\.log$/, "_#{Thread.current.name}.log")
294
- end
295
- if self.class.log_files.key?(log_filename)
296
- @logger = self.class.log_files[log_filename]
297
- else
298
- f = File.new(log_filename, "w")
299
- f.sync = true
300
- @logger = Logger.new f
301
- self.class.log_files[log_filename] = @logger
302
- end
303
- elsif @log == :screen
304
- @logger = Logger.new STDOUT
305
- elsif @log == :no
306
- @logger = Logger.new nil
307
- else
308
- raise InfoMissing, :log
309
- end
310
- @logger.level = Logger::INFO
311
- rescue Exception => stack
312
- @logger = Logger.new nil
313
- raise InfoMissing, :log
314
- end
315
-
316
- if @host.to_s != "" and (@host.start_with?("http:") or @host.start_with?("https:"))
317
- uri = URI.parse(@host)
318
- @host = uri.host unless uri.host.nil?
319
- @port = uri.port unless uri.port.nil?
320
- @ssl = true if !uri.scheme.nil? && (uri.scheme == "https")
321
- @prepath = uri.path unless uri.path == "/"
322
- end
323
-
324
- raise InfoMissing, :port if @port.to_s == ""
325
- raise InfoMissing, :host if @host.to_s == ""
326
- raise InfoMissing, :ssl unless @ssl.is_a?(TrueClass) or @ssl.is_a?(FalseClass)
327
- raise InfoMissing, :debug unless @debug.is_a?(TrueClass) or @debug.is_a?(FalseClass)
328
- raise InfoMissing, :auto_redirect unless auto_redirect.is_a?(TrueClass) or auto_redirect.is_a?(FalseClass)
329
- raise InfoMissing, :use_mocks unless @use_mocks.is_a?(TrueClass) or @use_mocks.is_a?(FalseClass)
330
- raise InfoMissing, :headers unless @headers.is_a?(Hash)
331
- raise InfoMissing, :values_for unless @values_for.is_a?(Hash)
332
-
333
- begin
334
- if !@proxy_host.nil? && !@proxy_port.nil?
335
- @http = Net::HTTP::Proxy(@proxy_host, @proxy_port).new(@host, @port)
336
- @http.use_ssl = @ssl
337
- @http.set_debug_output $stderr if @debug
338
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
339
- @http.start
340
- else
341
- @http = Net::HTTP.new(@host, @port)
342
- @http.use_ssl = @ssl
343
- @http.set_debug_output $stderr if @debug
344
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
345
- @http.start
346
- end
347
-
348
- @message_server = "(#{self.object_id}):"
349
-
350
- log_message = "(#{self.object_id}): Http connection created. host:#{@host}, port:#{@port}, ssl:#{@ssl}, mode:#{@mode}, proxy_host: #{@proxy_host.to_s()}, proxy_port: #{@proxy_port.to_s()} "
351
-
352
- @logger.info(log_message)
353
- @message_server += " Http connection: "
354
- if @ssl
355
- @message_server += "https://"
356
- else
357
- @message_server += "http://"
358
- end
359
- @message_server += "#{@host}:#{@port}"
360
- if @proxy_host.to_s != ""
361
- @message_server += " proxy:#{@proxy_host}:#{@proxy_port}"
362
- end
363
- @auto_redirect = auto_redirect
364
- # for the case we have headers following nice_hash implementation
365
- @headers = @headers.generate
366
-
367
- self.class.active += 1
368
- self.class.connections.push(self)
369
- rescue Exception => stack
370
- puts stack
371
- @logger.fatal stack
372
- end
373
- end
374
-
375
- ######################################################
376
- # Close HTTP connection
377
- ######################################################
378
- def close
379
- begin
380
- pos = 0
381
- found = false
382
- self.class.connections.each { |conn|
383
- if conn.object_id == self.object_id
384
- found = true
385
- break
386
- else
387
- pos += 1
388
- end
389
- }
390
- if found
391
- self.class.connections.delete_at(pos)
392
- end
393
-
394
- unless @closed
395
- if !@http.nil?
396
- @http.finish()
397
- @http = nil
398
- @logger.info "the HTTP connection was closed: #{@message_server}"
399
- else
400
- @http = nil
401
- @logger.fatal "It was not possible to close the HTTP connection: #{@message_server}"
402
- end
403
- @closed = true
404
- else
405
- @logger.warn "It was not possible to close the HTTP connection, already closed: #{@message_server}"
406
- end
407
- rescue Exception => stack
408
- @logger.fatal stack
409
- end
410
- self.class.active -= 1
411
- end
412
-
413
- private :manage_request, :manage_response
414
- end
1
+ require "logger"
2
+ require "nice_hash"
3
+ require_relative "nice_http/utils"
4
+ require_relative "nice_http/manage_request"
5
+ require_relative "nice_http/manage_response"
6
+ require_relative "nice_http/http_methods"
7
+
8
+ ######################################################
9
+ # Attributes you can access using NiceHttp.the_attribute:
10
+ # :host, :port, :ssl, :headers, :debug, :log, :proxy_host, :proxy_port,
11
+ # :last_request, :last_response, :request_id, :use_mocks, :connections,
12
+ # :active, :auto_redirect, :values_for, :create_stats, :stats
13
+ #
14
+ # @attr [String] host The host to be accessed
15
+ # @attr [Integer] port The port number
16
+ # @attr [Boolean] ssl If you use ssl or not
17
+ # @attr [Hash] headers Contains the headers you will be using on your connection
18
+ # @attr [Boolean] debug In case true shows all the details of the communication with the host
19
+ # @attr [String, Symbol] log :fix_file, :no, :screen, :file, "path and file name".
20
+ # :fix_file, will log the communication on nice_http.log. (default).
21
+ # :no, will not generate any logs.
22
+ # :screen, will print the logs on the screen.
23
+ # :file, will be generated a log file with name: nice_http_YY-mm-dd-HHMMSS.log.
24
+ # :file_run, will generate a log file with the name where the object was created and extension .log, fex: myfile.rb.log
25
+ # String the path and file name where the logs will be stored.
26
+ # @attr [String] proxy_host the proxy host to be used
27
+ # @attr [Integer] proxy_port the proxy port to be used
28
+ # @attr [String] last_request The last request with all the content sent
29
+ # @attr [String] last_response Only in case :debug is true, the last response with all the content
30
+ # @attr [String] request_id If the response includes a requestId, will be stored here
31
+ # @attr [Boolean] use_mocks If true, in case the request hash includes a :mock_response key, it will be used as the response instead
32
+ # @attr [Array] connections It will include all the active connections (NiceHttp instances)
33
+ # @attr [Integer] active Number of active connections
34
+ # @attr [Boolean] auto_redirect If true, NiceHttp will take care of the auto redirections when required by the responses
35
+ # @attr [Hash] response Contains the full response hash
36
+ # @attr [Integer] num_redirects Number of consecutive redirections managed
37
+ # @attr [Hash] headers The updated headers of the communication
38
+ # @attr [Hash] cookies Cookies set. The key is the path (String) where cookies are set and the value a Hash with pairs of cookie keys and values, example:
39
+ # { '/' => { "cfid" => "d95adfas2550255", "amddom.settings" => "doom" } }
40
+ # @attr [Logger] logger An instance of the Logger class where logs will be stored. You can access on anytime to store specific data, for example:
41
+ # my_http.logger.info "add this to the log file"
42
+ # @see https://ruby-doc.org/stdlib-2.5.0/libdoc/logger/rdoc/Logger.html
43
+ # @attr [Hash] values_for The default values to set on the data in case not specified others
44
+ # @attr [Boolean] create_stats If true, NiceHttp will create stats of the http communication and store them on NiceHttp.stats hash
45
+ # @attr [Hash] stats It contains detailed stats of the http communication
46
+ ######################################################
47
+ class NiceHttp
48
+ include NiceHttpManageRequest
49
+ include NiceHttpManageResponse
50
+ include NiceHttpHttpMethods
51
+
52
+ Error = Class.new StandardError
53
+
54
+ InfoMissing = Class.new Error do
55
+ attr_reader :attribute
56
+
57
+ def initialize(attribute, message = "")
58
+ @attribute = attribute
59
+ message += "It was not possible to create the http connection!!!\n"
60
+ message += "Wrong #{attribute}. "
61
+ message += "Remember to supply http:// or https:// in case you specify an url to create the http connection, for example:\n"
62
+ message += "http = NiceHttp.new('http://example.com')"
63
+ super message
64
+ end
65
+ end
66
+
67
+ class << self
68
+ attr_accessor :host, :port, :ssl, :headers, :debug, :log, :proxy_host, :proxy_port,
69
+ :last_request, :last_response, :request_id, :use_mocks, :connections,
70
+ :active, :auto_redirect, :log_files, :values_for, :create_stats, :stats
71
+ end
72
+
73
+ at_exit do
74
+ if self.create_stats
75
+ require "yaml"
76
+ self.stats.keys.each do |key|
77
+ File.open("./nice_http_stats_#{key}.yaml", "w") { |file| file.write(self.stats[key].to_yaml) }
78
+ end
79
+ end
80
+ end
81
+
82
+ ######################################################
83
+ # to reset to the original defaults
84
+ ######################################################
85
+ def self.reset!
86
+ @host = nil
87
+ @port = 80
88
+ @ssl = false
89
+ @headers = {}
90
+ @values_for = {}
91
+ @debug = false
92
+ @log = :fix_file
93
+ @proxy_host = nil
94
+ @proxy_port = nil
95
+ @last_request = nil
96
+ @last_response = nil
97
+ @request_id = ""
98
+ @use_mocks = false
99
+ @connections = []
100
+ @active = 0
101
+ @auto_redirect = true
102
+ @log_files = {}
103
+ @create_stats = false
104
+ @stats = {
105
+ all: {
106
+ num_requests: 0,
107
+ time_elapsed: {
108
+ total: 0,
109
+ maximum: 0,
110
+ minimum: 100000,
111
+ average: 0,
112
+ },
113
+ method: {},
114
+ },
115
+ path: {},
116
+ name: {},
117
+ }
118
+ end
119
+ reset!
120
+
121
+ ######################################################
122
+ # If inheriting from NiceHttp class
123
+ ######################################################
124
+ def self.inherited(subclass)
125
+ subclass.reset!
126
+ end
127
+
128
+ attr_reader :host, :port, :ssl, :debug, :log, :proxy_host, :proxy_port, :response, :num_redirects
129
+ attr_accessor :headers, :cookies, :use_mocks, :auto_redirect, :logger, :values_for
130
+
131
+ ######################################################
132
+ # Change the default values for NiceHttp supplying a Hash
133
+ #
134
+ # @param par [Hash] keys: :host, :port, :ssl, :headers, :debug, :log, :proxy_host, :proxy_port, :use_mocks, :auto_redirect, :values_for, :create_stats
135
+ ######################################################
136
+ def self.defaults=(par = {})
137
+ @host = par[:host] if par.key?(:host)
138
+ @port = par[:port] if par.key?(:port)
139
+ @ssl = par[:ssl] if par.key?(:ssl)
140
+ @headers = par[:headers].dup if par.key?(:headers)
141
+ @values_for = par[:values_for].dup if par.key?(:values_for)
142
+ @debug = par[:debug] if par.key?(:debug)
143
+ @log = par[:log] if par.key?(:log)
144
+ @proxy_host = par[:proxy_host] if par.key?(:proxy_host)
145
+ @proxy_port = par[:proxy_port] if par.key?(:proxy_port)
146
+ @use_mocks = par[:use_mocks] if par.key?(:use_mocks)
147
+ @auto_redirect = par[:auto_redirect] if par.key?(:auto_redirect)
148
+ @create_stats = par[:create_stats] if par.key?(:create_stats)
149
+ end
150
+
151
+ ######################################################
152
+ # To add specific stats
153
+ # The stats will be added to NiceHttp.stats[:specific]
154
+ #
155
+ # @param name [Symbol] name to group your specific stats
156
+ # @param state [Symbol] state of the name supplied to group your specific stats
157
+ # @param started [Time] when the process you want the stats started
158
+ # @param finished [Time] when the process you want the stats finished
159
+ #
160
+ # @example
161
+ # started = Time.now
162
+ # @http.send_request Requests::Customer.add_customer
163
+ # 30.times do
164
+ # resp = @http.get(Requests::Customer.get_customer)
165
+ # break if resp.code == 200
166
+ # sleep 0.5
167
+ # end
168
+ # NiceHttp.add_stats(:customer, :create, started, Time.now)
169
+ ######################################################
170
+ def self.add_stats(name, state, started, finished)
171
+ self.stats[:specific] ||= {}
172
+ self.stats[:specific][name] ||= { num: 0, time_elapsed: { total: 0, maximum: 0, minimum: 1000, average: 0 } }
173
+ self.stats[:specific][name][:num] += 1
174
+ time_elapsed = self.stats[:specific][name][:time_elapsed]
175
+ time_elapsed[:total] += finished - started
176
+ time_elapsed[:maximum] = (finished - started) if time_elapsed[:maximum] < (finished - started)
177
+ time_elapsed[:minimum] = (finished - started) if time_elapsed[:minimum] > (finished - started)
178
+ time_elapsed[:average] = time_elapsed[:total] / self.stats[:specific][name][:num]
179
+
180
+ self.stats[:specific][name][state] ||= { num: 0, time_elapsed: { total: 0, maximum: 0, minimum: 1000, average: 0 } }
181
+ self.stats[:specific][name][state][:num] += 1
182
+ time_elapsed = self.stats[:specific][name][state][:time_elapsed]
183
+ time_elapsed[:total] += finished - started
184
+ time_elapsed[:maximum] = (finished - started) if time_elapsed[:maximum] < (finished - started)
185
+ time_elapsed[:minimum] = (finished - started) if time_elapsed[:minimum] > (finished - started)
186
+ time_elapsed[:average] = time_elapsed[:total] / self.stats[:specific][name][state][:num]
187
+ end
188
+
189
+ ######################################################
190
+ # Creates a new http connection.
191
+ #
192
+ # @param args [] If no parameter supplied, by default will access how is setup on defaults
193
+ # @example
194
+ # http = NiceHttp.new()
195
+ # @param args [String]. The url to create the connection.
196
+ # @example
197
+ # http = NiceHttp.new("https://www.example.com")
198
+ # @example
199
+ # http = NiceHttp.new("example.com:8999")
200
+ # @example
201
+ # http = NiceHttp.new("localhost:8322")
202
+ # @param args [Hash] containing these possible keys:
203
+ #
204
+ # host -- example.com. (default blank screen)
205
+ #
206
+ # port -- port for the connection. 80 (default)
207
+ #
208
+ # ssl -- true, false (default)
209
+ #
210
+ # headers -- hash with the headers
211
+ #
212
+ # values_for -- hash with the values_for
213
+ #
214
+ # debug -- true, false (default)
215
+ #
216
+ # log -- :no, :screen, :file, :fix_file (default).
217
+ #
218
+ # A string with a path can be supplied.
219
+ #
220
+ # If :fix_file: nice_http.log
221
+ #
222
+ # In case :file it will be generated a log file with name: nice_http_YY-mm-dd-HHMMSS.log
223
+ #
224
+ # proxy_host
225
+ #
226
+ # proxy_port
227
+ # @example
228
+ # http2 = NiceHttp.new( host: "reqres.in", port: 443, ssl: true )
229
+ # @example
230
+ # my_server = {host: "example.com",
231
+ # port: 80,
232
+ # headers: {"api-key": "zdDDdjkck"}
233
+ # }
234
+ # http3 = NiceHttp.new my_server
235
+ ######################################################
236
+ def initialize(args = {})
237
+ require "net/http"
238
+ require "net/https"
239
+ @host = self.class.host
240
+ @port = self.class.port
241
+ @prepath = ""
242
+ @ssl = self.class.ssl
243
+ @headers = self.class.headers.dup
244
+ @values_for = self.class.values_for.dup
245
+ @debug = self.class.debug
246
+ @log = self.class.log
247
+ @proxy_host = self.class.proxy_host
248
+ @proxy_port = self.class.proxy_port
249
+ @use_mocks = self.class.use_mocks
250
+ @auto_redirect = false #set it up at the end of initialize
251
+ auto_redirect = self.class.auto_redirect
252
+ @num_redirects = 0
253
+ @create_stats = self.class.create_stats
254
+
255
+ #todo: set only the cookies for the current domain
256
+ #key: path, value: hash with key is the name of the cookie and value the value
257
+ # we set the default value for non existing keys to empty Hash {} so in case of merge there is no problem
258
+ @cookies = Hash.new { |h, k| h[k] = {} }
259
+
260
+ if args.is_a?(String)
261
+ uri = URI.parse(args)
262
+ @host = uri.host unless uri.host.nil?
263
+ @port = uri.port unless uri.port.nil?
264
+ @ssl = true if !uri.scheme.nil? && (uri.scheme == "https")
265
+ @prepath = uri.path unless uri.path == "/"
266
+ elsif args.is_a?(Hash) && !args.keys.empty?
267
+ @host = args[:host] if args.keys.include?(:host)
268
+ @port = args[:port] if args.keys.include?(:port)
269
+ @ssl = args[:ssl] if args.keys.include?(:ssl)
270
+ @headers = args[:headers].dup if args.keys.include?(:headers)
271
+ @values_for = args[:values_for].dup if args.keys.include?(:values_for)
272
+ @debug = args[:debug] if args.keys.include?(:debug)
273
+ @log = args[:log] if args.keys.include?(:log)
274
+ @proxy_host = args[:proxy_host] if args.keys.include?(:proxy_host)
275
+ @proxy_port = args[:proxy_port] if args.keys.include?(:proxy_port)
276
+ @use_mocks = args[:use_mocks] if args.keys.include?(:use_mocks)
277
+ auto_redirect = args[:auto_redirect] if args.keys.include?(:auto_redirect)
278
+ end
279
+
280
+ log_filename = ""
281
+ if @log.kind_of?(String) or @log == :fix_file or @log == :file or @log == :file_run
282
+ if @log.kind_of?(String)
283
+ log_filename = @log.dup
284
+
285
+ unless log_filename.start_with?(".")
286
+ if caller.first.start_with?(Dir.pwd)
287
+ folder = File.dirname(caller.first[/[^:]+/])
288
+ else
289
+ folder = File.dirname("#{Dir.pwd}/#{caller.first[/[^:]+/]}")
290
+ end
291
+ folder += "/" unless log_filename.start_with?("/")
292
+ log_filename = folder + log_filename
293
+ end
294
+ unless Dir.exist?(File.dirname(log_filename))
295
+ @logger = Logger.new nil
296
+ raise InfoMissing, :log, "Wrong directory specified for logs.\n"
297
+ end
298
+ elsif @log == :fix_file
299
+ log_filename = "nice_http.log"
300
+ elsif @log == :file
301
+ log_filename = "nice_http_#{Time.now.strftime("%Y-%m-%d-%H%M%S")}.log"
302
+ elsif @log == :file_run
303
+ log_filename = "#{caller.first[/[^:]+/]}.log"
304
+ end
305
+ if Thread.current.name.to_s != ""
306
+ log_filename.gsub!(/\.log$/, "_#{Thread.current.name}.log")
307
+ end
308
+ if self.class.log_files.key?(log_filename)
309
+ @logger = self.class.log_files[log_filename]
310
+ else
311
+ begin
312
+ f = File.new(log_filename, "w")
313
+ f.sync = true
314
+ @logger = Logger.new f
315
+ rescue Exception => stack
316
+ @logger = Logger.new nil
317
+ raise InfoMissing, :log
318
+ end
319
+ self.class.log_files[log_filename] = @logger
320
+ end
321
+ elsif @log == :screen
322
+ @logger = Logger.new STDOUT
323
+ elsif @log == :no
324
+ @logger = Logger.new nil
325
+ else
326
+ raise InfoMissing, :log
327
+ end
328
+ @logger.level = Logger::INFO
329
+
330
+ if @host.to_s != "" and (@host.start_with?("http:") or @host.start_with?("https:"))
331
+ uri = URI.parse(@host)
332
+ @host = uri.host unless uri.host.nil?
333
+ @port = uri.port unless uri.port.nil?
334
+ @ssl = true if !uri.scheme.nil? && (uri.scheme == "https")
335
+ @prepath = uri.path unless uri.path == "/"
336
+ end
337
+
338
+ raise InfoMissing, :port if @port.to_s == ""
339
+ raise InfoMissing, :host if @host.to_s == ""
340
+ raise InfoMissing, :ssl unless @ssl.is_a?(TrueClass) or @ssl.is_a?(FalseClass)
341
+ raise InfoMissing, :debug unless @debug.is_a?(TrueClass) or @debug.is_a?(FalseClass)
342
+ raise InfoMissing, :auto_redirect unless auto_redirect.is_a?(TrueClass) or auto_redirect.is_a?(FalseClass)
343
+ raise InfoMissing, :use_mocks unless @use_mocks.is_a?(TrueClass) or @use_mocks.is_a?(FalseClass)
344
+ raise InfoMissing, :headers unless @headers.is_a?(Hash)
345
+ raise InfoMissing, :values_for unless @values_for.is_a?(Hash)
346
+
347
+ begin
348
+ if !@proxy_host.nil? && !@proxy_port.nil?
349
+ @http = Net::HTTP::Proxy(@proxy_host, @proxy_port).new(@host, @port)
350
+ @http.use_ssl = @ssl
351
+ @http.set_debug_output $stderr if @debug
352
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
353
+ @http.start
354
+ else
355
+ @http = Net::HTTP.new(@host, @port)
356
+ @http.use_ssl = @ssl
357
+ @http.set_debug_output $stderr if @debug
358
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
359
+ @http.start
360
+ end
361
+
362
+ @message_server = "(#{self.object_id}):"
363
+
364
+ log_message = "(#{self.object_id}): Http connection created. host:#{@host}, port:#{@port}, ssl:#{@ssl}, mode:#{@mode}, proxy_host: #{@proxy_host.to_s()}, proxy_port: #{@proxy_port.to_s()} "
365
+
366
+ @logger.info(log_message)
367
+ @message_server += " Http connection: "
368
+ if @ssl
369
+ @message_server += "https://"
370
+ else
371
+ @message_server += "http://"
372
+ end
373
+ @message_server += "#{@host}:#{@port}"
374
+ if @proxy_host.to_s != ""
375
+ @message_server += " proxy:#{@proxy_host}:#{@proxy_port}"
376
+ end
377
+ @auto_redirect = auto_redirect
378
+ # for the case we have headers following nice_hash implementation
379
+ @headers = @headers.generate
380
+
381
+ self.class.active += 1
382
+ self.class.connections.push(self)
383
+ rescue Exception => stack
384
+ puts stack
385
+ @logger.fatal stack
386
+ end
387
+ end
388
+
389
+ ######################################################
390
+ # Close HTTP connection
391
+ ######################################################
392
+ def close
393
+ begin
394
+ pos = 0
395
+ found = false
396
+ self.class.connections.each { |conn|
397
+ if conn.object_id == self.object_id
398
+ found = true
399
+ break
400
+ else
401
+ pos += 1
402
+ end
403
+ }
404
+ if found
405
+ self.class.connections.delete_at(pos)
406
+ end
407
+
408
+ unless @closed
409
+ if !@http.nil?
410
+ @http.finish()
411
+ @http = nil
412
+ @logger.info "the HTTP connection was closed: #{@message_server}"
413
+ else
414
+ @http = nil
415
+ @logger.fatal "It was not possible to close the HTTP connection: #{@message_server}"
416
+ end
417
+ @closed = true
418
+ else
419
+ @logger.warn "It was not possible to close the HTTP connection, already closed: #{@message_server}"
420
+ end
421
+ rescue Exception => stack
422
+ @logger.fatal stack
423
+ end
424
+ self.class.active -= 1
425
+ end
426
+
427
+ private :manage_request, :manage_response
428
+ end