nice_http 1.7.2 → 1.7.3

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.
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