nice_http 1.7.2 → 1.7.3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/nice_http.rb CHANGED
@@ -1,411 +1,414 @@
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
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 self.class.log_files.key?(log_filename)
293
- @logger = self.class.log_files[log_filename]
294
- else
295
- f = File.new(log_filename, "w")
296
- f.sync = true
297
- @logger = Logger.new f
298
- self.class.log_files[log_filename] = @logger
299
- end
300
- elsif @log == :screen
301
- @logger = Logger.new STDOUT
302
- elsif @log == :no
303
- @logger = Logger.new nil
304
- else
305
- raise InfoMissing, :log
306
- end
307
- @logger.level = Logger::INFO
308
- rescue Exception => stack
309
- @logger = Logger.new nil
310
- raise InfoMissing, :log
311
- end
312
-
313
- if @host.to_s != "" and (@host.start_with?("http:") or @host.start_with?("https:"))
314
- uri = URI.parse(@host)
315
- @host = uri.host unless uri.host.nil?
316
- @port = uri.port unless uri.port.nil?
317
- @ssl = true if !uri.scheme.nil? && (uri.scheme == "https")
318
- @prepath = uri.path unless uri.path == "/"
319
- end
320
-
321
- raise InfoMissing, :port if @port.to_s == ""
322
- raise InfoMissing, :host if @host.to_s == ""
323
- raise InfoMissing, :ssl unless @ssl.is_a?(TrueClass) or @ssl.is_a?(FalseClass)
324
- raise InfoMissing, :debug unless @debug.is_a?(TrueClass) or @debug.is_a?(FalseClass)
325
- raise InfoMissing, :auto_redirect unless auto_redirect.is_a?(TrueClass) or auto_redirect.is_a?(FalseClass)
326
- raise InfoMissing, :use_mocks unless @use_mocks.is_a?(TrueClass) or @use_mocks.is_a?(FalseClass)
327
- raise InfoMissing, :headers unless @headers.is_a?(Hash)
328
- raise InfoMissing, :values_for unless @values_for.is_a?(Hash)
329
-
330
- begin
331
- if !@proxy_host.nil? && !@proxy_port.nil?
332
- @http = Net::HTTP::Proxy(@proxy_host, @proxy_port).new(@host, @port)
333
- @http.use_ssl = @ssl
334
- @http.set_debug_output $stderr if @debug
335
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
336
- @http.start
337
- else
338
- @http = Net::HTTP.new(@host, @port)
339
- @http.use_ssl = @ssl
340
- @http.set_debug_output $stderr if @debug
341
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
342
- @http.start
343
- end
344
-
345
- @message_server = "(#{self.object_id}):"
346
-
347
- 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()} "
348
-
349
- @logger.info(log_message)
350
- @message_server += " Http connection: "
351
- if @ssl
352
- @message_server += "https://"
353
- else
354
- @message_server += "http://"
355
- end
356
- @message_server += "#{@host}:#{@port}"
357
- if @proxy_host.to_s != ""
358
- @message_server += " proxy:#{@proxy_host}:#{@proxy_port}"
359
- end
360
- @auto_redirect = auto_redirect
361
- # for the case we have headers following nice_hash implementation
362
- @headers = @headers.generate
363
-
364
- self.class.active += 1
365
- self.class.connections.push(self)
366
- rescue Exception => stack
367
- puts stack
368
- @logger.fatal stack
369
- end
370
- end
371
-
372
- ######################################################
373
- # Close HTTP connection
374
- ######################################################
375
- def close
376
- begin
377
- pos = 0
378
- found = false
379
- self.class.connections.each { |conn|
380
- if conn.object_id == self.object_id
381
- found = true
382
- break
383
- else
384
- pos += 1
385
- end
386
- }
387
- if found
388
- self.class.connections.delete_at(pos)
389
- end
390
-
391
- unless @closed
392
- if !@http.nil?
393
- @http.finish()
394
- @http = nil
395
- @logger.info "the HTTP connection was closed: #{@message_server}"
396
- else
397
- @http = nil
398
- @logger.fatal "It was not possible to close the HTTP connection: #{@message_server}"
399
- end
400
- @closed = true
401
- else
402
- @logger.warn "It was not possible to close the HTTP connection, already closed: #{@message_server}"
403
- end
404
- rescue Exception => stack
405
- @logger.fatal stack
406
- end
407
- self.class.active -= 1
408
- end
409
-
410
- private :manage_request, :manage_response
411
- 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)
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