nice_http 1.2.0 → 1.2.1

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.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +331 -325
  3. data/lib/nice_http.rb +1188 -1195
  4. data/lib/nice_http/utils.rb +109 -109
  5. metadata +32 -5
@@ -1,1195 +1,1188 @@
1
- require "logger"
2
- require "nice_hash"
3
- require_relative "nice_http/utils"
4
-
5
- ######################################################
6
- # Attributes you can access using NiceHttp.the_attribute:
7
- # :host, :port, :ssl, :headers, :debug, :log, :proxy_host, :proxy_port,
8
- # :last_request, :last_response, :request_id, :use_mocks, :connections,
9
- # :active, :auto_redirect
10
- #
11
- # @attr [String] host The host to be accessed
12
- # @attr [Integer] port The port number
13
- # @attr [Boolean] ssl If you use ssl or not
14
- # @attr [Hash] headers Contains the headers you will be using on your connection
15
- # @attr [Boolean] debug In case true shows all the details of the communication with the host
16
- # @attr [String, Symbol] log :fix_file, :no, :screen, :file, "path and file name".
17
- # :fix_file will log the communication on nice_http.log. (default).
18
- # :no will not generate any logs.
19
- # :screen will print the logs on the screen.
20
- # :file will be generated a log file with name: nice_http_YY-mm-dd-HHMMSS.log.
21
- # String the path and file name where the logs will be stored.
22
- # @attr [String] proxy_host the proxy host to be used
23
- # @attr [Integer] proxy_port the proxy port to be used
24
- # @attr [String] last_request The last request with all the content sent
25
- # @attr [String] last_response Only in case :debug is true, the last response with all the content
26
- # @attr [String] request_id If the response includes a requestId, will be stored here
27
- # @attr [Boolean] use_mocks If true, in case the request hash includes a :mock_response key, it will be used as the response instead
28
- # @attr [Array] connections It will include all the active connections (NiceHttp instances)
29
- # @attr [Integer] active Number of active connections
30
- # @attr [Boolean] auto_redirect If true, NiceHttp will take care of the auto redirections when required by the responses
31
- # @attr [Hash] response Contains the full response hash
32
- # @attr [Integer] num_redirects Number of consecutive redirections managed
33
- # @attr [Hash] headers The updated headers of the communication
34
- # @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:
35
- # { '/' => { "cfid" => "d95adfas2550255", "amddom.settings" => "doom" } }
36
- # @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:
37
- # my_http.logger.info "add this to the log file"
38
- # @see https://ruby-doc.org/stdlib-2.5.0/libdoc/logger/rdoc/Logger.html
39
- ######################################################
40
- class NiceHttp
41
- Error = Class.new StandardError
42
-
43
- InfoMissing = Class.new Error do
44
- attr_reader :attribute
45
-
46
- def initialize(attribute)
47
- @attribute = attribute
48
- message = "It was not possible to create the http connection!!!\n"
49
- message += "Wrong #{attribute}, remember to supply http:// or https:// in case you specify an url to create the http connection, for example:\n"
50
- message += "http = NiceHttp.new('http://example.com')"
51
- super message
52
- end
53
- end
54
-
55
- class << self
56
- attr_accessor :host, :port, :ssl, :headers, :debug, :log, :proxy_host, :proxy_port,
57
- :last_request, :last_response, :request_id, :use_mocks, :connections,
58
- :active, :auto_redirect
59
- end
60
-
61
- ######################################################
62
- # to reset to the original defaults
63
- ######################################################
64
- def self.reset!
65
- @host = nil
66
- @port = 80
67
- @ssl = false
68
- @headers = {}
69
- @debug = false
70
- @log = :fix_file
71
- @proxy_host = nil
72
- @proxy_port = nil
73
- @last_request = nil
74
- @last_response = nil
75
- @request_id = ""
76
- @use_mocks = false
77
- @connections = []
78
- @active = 0
79
- @auto_redirect = true
80
- end
81
- reset!
82
-
83
- ######################################################
84
- # If inheriting from NiceHttp class
85
- ######################################################
86
- def self.inherited(subclass)
87
- subclass.reset!
88
- end
89
-
90
- attr_reader :host, :port, :ssl, :debug, :log, :proxy_host, :proxy_port, :response, :num_redirects
91
- attr_accessor :headers, :cookies, :use_mocks, :auto_redirect, :logger
92
-
93
- ######################################################
94
- # Change the default values for NiceHttp supplying a Hash
95
- #
96
- # @param par [Hash] keys: :host, :port, :ssl, :headers, :debug, :log, :proxy_host, :proxy_port, :use_mocks, :auto_redirect
97
- ######################################################
98
- def self.defaults=(par = {})
99
- @host = par[:host] if par.key?(:host)
100
- @port = par[:port] if par.key?(:port)
101
- @ssl = par[:ssl] if par.key?(:ssl)
102
- @headers = par[:headers].dup if par.key?(:headers)
103
- @debug = par[:debug] if par.key?(:debug)
104
- @log = par[:log] if par.key?(:log)
105
- @proxy_host = par[:proxy_host] if par.key?(:proxy_host)
106
- @proxy_port = par[:proxy_port] if par.key?(:proxy_port)
107
- @use_mocks = par[:use_mocks] if par.key?(:use_mocks)
108
- @auto_redirect = par[:auto_redirect] if par.key?(:auto_redirect)
109
- end
110
-
111
- ######################################################
112
- # Creates a new http connection.
113
- #
114
- # @param args [] If no parameter supplied, by default will access how is setup on defaults
115
- # @example
116
- # http = NiceHttp.new()
117
- # @param args [String]. The url to create the connection.
118
- # @example
119
- # http = NiceHttp.new("https://www.example.com")
120
- # @example
121
- # http = NiceHttp.new("example.com:8999")
122
- # @example
123
- # http = NiceHttp.new("localhost:8322")
124
- # @param args [Hash] containing these possible keys:
125
- #
126
- # host -- example.com. (default blank screen)
127
- #
128
- # port -- port for the connection. 80 (default)
129
- #
130
- # ssl -- true, false (default)
131
- #
132
- # headers -- hash with the headers
133
- #
134
- # debug -- true, false (default)
135
- #
136
- # log -- :no, :screen, :file, :fix_file (default).
137
- #
138
- # A string with a path can be supplied.
139
- #
140
- # If :fix_file: nice_http.log
141
- #
142
- # In case :file it will be generated a log file with name: nice_http_YY-mm-dd-HHMMSS.log
143
- #
144
- # proxy_host
145
- #
146
- # proxy_port
147
- # @example
148
- # http2 = NiceHttp.new( host: "reqres.in", port: 443, ssl: true )
149
- # @example
150
- # my_server = {host: "example.com",
151
- # port: 80,
152
- # headers: {"api-key": "zdDDdjkck"}
153
- # }
154
- # http3 = NiceHttp.new my_server
155
- ######################################################
156
- def initialize(args = {})
157
- require "net/http"
158
- require "net/https"
159
- @host = self.class.host
160
- @port = self.class.port
161
- @ssl = self.class.ssl
162
- @headers = self.class.headers.dup
163
- @debug = self.class.debug
164
- @log = self.class.log
165
- @proxy_host = self.class.proxy_host
166
- @proxy_port = self.class.proxy_port
167
- @use_mocks = self.class.use_mocks
168
- @auto_redirect = false #set it up at the end of initialize
169
- auto_redirect = self.class.auto_redirect
170
- @num_redirects = 0
171
-
172
- #todo: set only the cookies for the current domain
173
- #key: path, value: hash with key is the name of the cookie and value the value
174
- # we set the default value for non existing keys to empty Hash {} so in case of merge there is no problem
175
- @cookies = Hash.new { |h, k| h[k] = {} }
176
-
177
- if args.is_a?(String)
178
- uri = URI.parse(args)
179
- @host = uri.host unless uri.host.nil?
180
- @port = uri.port unless uri.port.nil?
181
- @ssl = true if !uri.scheme.nil? && (uri.scheme == "https")
182
- elsif args.is_a?(Hash) && !args.keys.empty?
183
- @host = args[:host] if args.keys.include?(:host)
184
- @port = args[:port] if args.keys.include?(:port)
185
- @ssl = args[:ssl] if args.keys.include?(:ssl)
186
- @headers = args[:headers].dup if args.keys.include?(:headers)
187
- @debug = args[:debug] if args.keys.include?(:debug)
188
- @log = args[:log] if args.keys.include?(:log)
189
- @proxy_host = args[:proxy_host] if args.keys.include?(:proxy_host)
190
- @proxy_port = args[:proxy_port] if args.keys.include?(:proxy_port)
191
- @use_mocks = args[:use_mocks] if args.keys.include?(:use_mocks)
192
- auto_redirect = args[:auto_redirect] if args.keys.include?(:auto_redirect)
193
- end
194
-
195
- begin
196
- if @log.kind_of?(String)
197
- @logger = Logger.new File.new(@log, "w")
198
- elsif @log == :fix_file
199
- @logger = Logger.new File.new("nice_http.log", "w")
200
- elsif @log == :file
201
- @logger = Logger.new File.new("nice_http_#{Time.now.strftime("%Y-%m-%d-%H%M%S")}.log", "w")
202
- elsif @log == :screen
203
- @logger = Logger.new STDOUT
204
- elsif @log == :no
205
- @logger = Logger.new nil
206
- else
207
- raise InfoMissing, :log
208
- end
209
- @logger.level = Logger::INFO
210
- rescue Exception => stack
211
- raise InfoMissing, :log
212
- @logger = Logger.new nil
213
- end
214
-
215
-
216
- if @host.to_s != "" and (@host.include?("http:") or @host.include?("https:"))
217
- uri = URI.parse(@host)
218
- @host = uri.host unless uri.host.nil?
219
- @port = uri.port unless uri.port.nil?
220
- @ssl = true if !uri.scheme.nil? && (uri.scheme == "https")
221
- end
222
-
223
- raise InfoMissing, :port if @port.to_s == ""
224
- raise InfoMissing, :host if @host.to_s == ""
225
- raise InfoMissing, :ssl unless @ssl.is_a?(TrueClass) or @ssl.is_a?(FalseClass)
226
- raise InfoMissing, :debug unless @debug.is_a?(TrueClass) or @debug.is_a?(FalseClass)
227
- raise InfoMissing, :auto_redirect unless auto_redirect.is_a?(TrueClass) or auto_redirect.is_a?(FalseClass)
228
- raise InfoMissing, :use_mocks unless @use_mocks.is_a?(TrueClass) or @use_mocks.is_a?(FalseClass)
229
- raise InfoMissing, :headers unless @headers.is_a?(Hash)
230
-
231
- begin
232
- if !@proxy_host.nil? && !@proxy_port.nil?
233
- @http = Net::HTTP::Proxy(@proxy_host, @proxy_port).new(@host, @port)
234
- @http.use_ssl = @ssl
235
- @http.set_debug_output $stderr if @debug
236
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
237
- @http.start
238
- else
239
- @http = Net::HTTP.new(@host, @port)
240
- @http.use_ssl = @ssl
241
- @http.set_debug_output $stderr if @debug
242
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
243
- @http.start
244
- end
245
-
246
- @message_server = "(#{self.object_id}):"
247
-
248
- 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()} "
249
-
250
- @logger.info(log_message)
251
- @message_server += " Http connection: "
252
- if @ssl
253
- @message_server += "https://"
254
- else
255
- @message_server += "http://"
256
- end
257
- @message_server += "#{@host}:#{@port}"
258
- if @proxy_host.to_s != ""
259
- @message_server += " proxy:#{@proxy_host}:#{@proxy_port}"
260
- end
261
- @auto_redirect = auto_redirect
262
-
263
- self.class.active += 1
264
- self.class.connections.push(self)
265
- rescue Exception => stack
266
- puts stack
267
- @logger.fatal stack
268
- end
269
- end
270
-
271
- ######################################################
272
- # Get data from path
273
- #
274
- # @param arg [Hash] containing at least key :path
275
- # @param arg [String] the path
276
- #
277
- # @return [Hash] response
278
- # Including at least the symbol keys:
279
- # :data = the response data body.
280
- # :message = plain text response.
281
- # :code = code response (200=ok,500=wrong...).
282
- # All keys in response are lowercase.
283
- # data, message and code can also be accessed as attributes like .message .code .data.
284
- # In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil, data: '' }
285
- #
286
- # @example
287
- # resp = @http.get(Requests::Customer.get_profile)
288
- # assert resp.code == 200
289
- # @example
290
- # resp = @http.get("/customers/1223")
291
- # assert resp.message == "OK"
292
- ######################################################
293
- def get(arg)
294
- begin
295
- path, data, headers_t = manage_request(arg)
296
-
297
- @start_time = Time.now if @start_time.nil?
298
- if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
299
- data = ""
300
- if arg[:mock_response].keys.include?(:data)
301
- data = arg[:mock_response][:data]
302
- if data.kind_of?(Hash) #to json
303
- begin
304
- require "json"
305
- data = data.to_json
306
- rescue
307
- @logger.fatal "There was a problem converting to json: #{data}"
308
- end
309
- end
310
- end
311
- @logger.warn "Pay attention!!! This is a mock response:"
312
- @start_time_net = Time.now if @start_time_net.nil?
313
- manage_response(arg[:mock_response], data.to_s)
314
- return @response
315
- end
316
- begin
317
- if path.start_with?("http:") or path.start_with?("https:") #server included on path problably because of a redirection to a different server
318
- require "uri"
319
- uri = URI.parse(path)
320
- ssl = false
321
- ssl = true if path.include?("https:")
322
-
323
- server = "http://"
324
- server = "https://" if path.include?("https:")
325
- if uri.port != 443
326
- server += "#{uri.host}:#{uri.port}"
327
- else
328
- server += "#{uri.host}"
329
- end
330
-
331
- http_redir = nil
332
- self.class.connections.each { |conn|
333
- if conn.host == uri.host and conn.port == uri.port
334
- http_redir = conn
335
- break
336
- end
337
- }
338
-
339
- if !http_redir.nil?
340
- path, data, headers_t = manage_request(arg)
341
- http_redir.cookies.merge!(@cookies)
342
- http_redir.headers.merge!(headers_t)
343
- resp = http_redir.get(path.gsub(server, "")) #todo: remove only the server at the begining in case in query is the server it will be replaced when it should not be
344
- @response = http_redir.response
345
- else
346
- @logger.warn "It seems like the http connection cannot redirect to #{server} because there is no active connection for that server. You need to create previously one."
347
- end
348
- else
349
- @start_time_net = Time.now if @start_time_net.nil?
350
- resp = @http.get(path, headers_t)
351
- data = resp.body
352
- manage_response(resp, data)
353
- end
354
- rescue Exception => stack
355
- @logger.warn stack
356
- @logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
357
- @http.finish()
358
- @http.start()
359
- @start_time_net = Time.now if @start_time_net.nil?
360
- resp = @http.get(path)
361
- data = resp.body
362
- manage_response(resp, data)
363
- end
364
-
365
- if @auto_redirect and @response[:code].to_i >= 300 and @response[:code].to_i < 400 and @response.include?(:location)
366
- if @num_redirects <= 30
367
- @num_redirects += 1
368
- current_server = "http"
369
- current_server += "s" if @ssl == true
370
- current_server += "://#{@host}"
371
- location = @response[:location].gsub(current_server, "")
372
- @logger.info "(#{@num_redirects}) Redirecting NiceHttp to #{location}"
373
- get(location)
374
- else
375
- @logger.fatal "(#{@num_redirects}) Maximum number of redirections for a single request reached. Be sure everything is correct, it seems there is a non ending loop"
376
- @num_redirects = 0
377
- end
378
- else
379
- @num_redirects = 0
380
- end
381
- return @response
382
- rescue Exception => stack
383
- @logger.fatal stack
384
- return {fatal_error: stack.to_s, code: nil, message: nil, data: ""}
385
- end
386
- end
387
-
388
- ######################################################
389
- # Post data to path
390
- # @param arguments [Hash] containing at least keys :data and :path.
391
- # In case :data not supplied and :data_examples array supplied, it will be taken the first example as :data.
392
- # @param arguments [Array<path, data, additional_headers>]
393
- # path (string).
394
- # data (json data for example).
395
- # additional_headers (Hash key=>value).
396
- # @return [Hash] response
397
- # Including at least the symbol keys:
398
- # :data = the response data body.
399
- # :message = plain text response.
400
- # :code = code response (200=ok,500=wrong...).
401
- # All keys in response are lowercase.
402
- # data, message and code can also be accessed as attributes like .message .code .data.
403
- # In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil, data: '' }
404
- # @example
405
- # resp = @http.post(Requests::Customer.update_customer)
406
- # assert resp.code == 201
407
- # @example
408
- # resp = http.post( {
409
- # path: "/api/users",
410
- # data: {name: "morpheus", job: "leader"}
411
- # } )
412
- # pp resp.data.json
413
- ######################################################
414
- def post(*arguments)
415
- begin
416
- path, data, headers_t = manage_request(*arguments)
417
- @start_time = Time.now if @start_time.nil?
418
- if arguments.size > 0 and arguments[0].kind_of?(Hash)
419
- arg = arguments[0]
420
- if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
421
- data = ""
422
- if arg[:mock_response].keys.include?(:data)
423
- data = arg[:mock_response][:data]
424
- if data.kind_of?(Hash) #to json
425
- begin
426
- require "json"
427
- data = data.to_json
428
- rescue
429
- @logger.fatal "There was a problem converting to json: #{data}"
430
- end
431
- end
432
- end
433
- @logger.warn "Pay attention!!! This is a mock response:"
434
- @start_time_net = Time.now if @start_time_net.nil?
435
- manage_response(arg[:mock_response], data.to_s)
436
- return @response
437
- end
438
- end
439
-
440
- begin
441
- @start_time_net = Time.now if @start_time_net.nil?
442
- if headers_t["Content-Type"] == "multipart/form-data"
443
- require "net/http/post/multipart"
444
- headers_t.each { |key, value|
445
- arguments[0][:data].add_field(key, value) #add to Headers
446
- }
447
- resp = @http.request(arguments[0][:data])
448
- else
449
- resp = @http.post(path, data, headers_t)
450
- data = resp.body
451
- end
452
- rescue Exception => stack
453
- @logger.warn stack
454
- @logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
455
- @http.finish()
456
- @http.start()
457
- @start_time_net = Time.now if @start_time_net.nil?
458
- resp, data = @http.post(path, data, headers_t)
459
- end
460
- manage_response(resp, data)
461
- if @auto_redirect and @response[:code].to_i >= 300 and @response[:code].to_i < 400 and @response.include?(:location)
462
- if @num_redirects <= 30
463
- @num_redirects += 1
464
- current_server = "http"
465
- current_server += "s" if @ssl == true
466
- current_server += "://#{@host}"
467
- location = @response[:location].gsub(current_server, "")
468
- @logger.info "(#{@num_redirects}) Redirecting NiceHttp to #{location}"
469
- get(location)
470
- else
471
- @logger.fatal "(#{@num_redirects}) Maximum number of redirections for a single request reached. Be sure everything is correct, it seems there is a non ending loop"
472
- @num_redirects = 0
473
- end
474
- else
475
- @num_redirects = 0
476
- end
477
- return @response
478
- rescue Exception => stack
479
- @logger.fatal stack
480
- return {fatal_error: stack.to_s, code: nil, message: nil, data: ""}
481
- end
482
- end
483
-
484
- ######################################################
485
- # Put data to path
486
- # @param arguments [Hash] containing at least keys :data and :path.
487
- # In case :data not supplied and :data_examples array supplied, it will be taken the first example as :data.
488
- # @param arguments [Array<path, data, additional_headers>]
489
- # path (string).
490
- # data (json data for example).
491
- # additional_headers (Hash key=>value).
492
- # @return [Hash] response
493
- # Including at least the symbol keys:
494
- # :data = the response data body.
495
- # :message = plain text response.
496
- # :code = code response (200=ok,500=wrong...).
497
- # All keys in response are lowercase.
498
- # data, message and code can also be accessed as attributes like .message .code .data.
499
- # In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil, data: '' }
500
- # @example
501
- # resp = @http.put(Requests::Customer.remove_phone)
502
- ######################################################
503
- def put(*arguments)
504
- begin
505
- path, data, headers_t = manage_request(*arguments)
506
- @start_time = Time.now if @start_time.nil?
507
- if arguments.size > 0 and arguments[0].kind_of?(Hash)
508
- arg = arguments[0]
509
- if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
510
- data = ""
511
- if arg[:mock_response].keys.include?(:data)
512
- data = arg[:mock_response][:data]
513
- if data.kind_of?(Hash) #to json
514
- begin
515
- require "json"
516
- data = data.to_json
517
- rescue
518
- @logger.fatal "There was a problem converting to json: #{data}"
519
- end
520
- end
521
- end
522
- @logger.warn "Pay attention!!! This is a mock response:"
523
- @start_time_net = Time.now if @start_time_net.nil?
524
- manage_response(arg[:mock_response], data.to_s)
525
- return @response
526
- end
527
- end
528
-
529
- begin
530
- @start_time_net = Time.now if @start_time_net.nil?
531
- resp = @http.send_request("PUT", path, data, headers_t)
532
- data = resp.body
533
- rescue Exception => stack
534
- @logger.warn stack
535
- @logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
536
- @http.finish()
537
- @http.start()
538
- @start_time_net = Time.now if @start_time_net.nil?
539
- resp, data = @http.send_request("PUT", path, data, headers_t)
540
- end
541
- manage_response(resp, data)
542
-
543
- return @response
544
- rescue Exception => stack
545
- @logger.fatal stack
546
- return {fatal_error: stack.to_s, code: nil, message: nil, data: ""}
547
- end
548
- end
549
-
550
- ######################################################
551
- # Patch data to path
552
- # @param arguments [Hash] containing at least keys :data and :path.
553
- # In case :data not supplied and :data_examples array supplied, it will be taken the first example as :data.
554
- # @param arguments [Array<path, data, additional_headers>]
555
- # path (string).
556
- # data (json data for example).
557
- # additional_headers (Hash key=>value).
558
- # @return [Hash] response
559
- # Including at least the symbol keys:
560
- # :data = the response data body.
561
- # :message = plain text response.
562
- # :code = code response (200=ok,500=wrong...).
563
- # All keys in response are lowercase.
564
- # data, message and code can also be accessed as attributes like .message .code .data.
565
- # In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil, data: '' }
566
- # @example
567
- # resp = @http.patch(Requests::Customer.unrelease_account)
568
- ######################################################
569
- def patch(*arguments)
570
- begin
571
- path, data, headers_t = manage_request(*arguments)
572
- @start_time = Time.now if @start_time.nil?
573
- if arguments.size > 0 and arguments[0].kind_of?(Hash)
574
- arg = arguments[0]
575
- if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
576
- data = ""
577
- if arg[:mock_response].keys.include?(:data)
578
- data = arg[:mock_response][:data]
579
- if data.kind_of?(Hash) #to json
580
- begin
581
- require "json"
582
- data = data.to_json
583
- rescue
584
- @logger.fatal "There was a problem converting to json: #{data}"
585
- end
586
- end
587
- end
588
- @logger.warn "Pay attention!!! This is a mock response:"
589
- @start_time_net = Time.now if @start_time_net.nil?
590
- manage_response(arg[:mock_response], data.to_s)
591
- return @response
592
- end
593
- end
594
-
595
- begin
596
- @start_time_net = Time.now if @start_time_net.nil?
597
- resp = @http.patch(path, data, headers_t)
598
- data = resp.body
599
- rescue Exception => stack
600
- @logger.warn stack
601
- @logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
602
- @http.finish()
603
- @http.start()
604
- @start_time_net = Time.now if @start_time_net.nil?
605
- resp, data = @http.patch(path, data, headers_t)
606
- end
607
- manage_response(resp, data)
608
- if @auto_redirect and @response[:code].to_i >= 300 and @response[:code].to_i < 400 and @response.include?(:location)
609
- if @num_redirects <= 30
610
- @num_redirects += 1
611
- current_server = "http"
612
- current_server += "s" if @ssl == true
613
- current_server += "://#{@host}"
614
- location = @response[:location].gsub(current_server, "")
615
- @logger.info "(#{@num_redirects}) Redirecting NiceHttp to #{location}"
616
- get(location)
617
- else
618
- @logger.fatal "(#{@num_redirects}) Maximum number of redirections for a single request reached. Be sure everything is correct, it seems there is a non ending loop"
619
- @num_redirects = 0
620
- end
621
- else
622
- @num_redirects = 0
623
- end
624
- return @response
625
- rescue Exception => stack
626
- @logger.fatal stack
627
- return {fatal_error: stack.to_s, code: nil, message: nil, data: ""}
628
- end
629
- end
630
-
631
- ######################################################
632
- # Delete an existing resource
633
- # @param arg [Hash] containing at least key :path
634
- # @param arg [String] the path
635
- #
636
- # @return [Hash] response
637
- # Including at least the symbol keys:
638
- # :data = the response data body.
639
- # :message = plain text response.
640
- # :code = code response (200=ok,500=wrong...).
641
- # All keys in response are lowercase.
642
- # data, message and code can also be accessed as attributes like .message .code .data.
643
- # In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil, data: '' }
644
- # @example
645
- # resp = @http.delete(Requests::Customer.remove_session)
646
- # assert resp.code == 204
647
- ######################################################
648
- def delete(argument)
649
- begin
650
- if argument.kind_of?(String)
651
- argument = {:path => argument}
652
- end
653
- path, data, headers_t = manage_request(argument)
654
- @start_time = Time.now if @start_time.nil?
655
- if argument.kind_of?(Hash)
656
- arg = argument
657
- if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
658
- data = ""
659
- if arg[:mock_response].keys.include?(:data)
660
- data = arg[:mock_response][:data]
661
- if data.kind_of?(Hash) #to json
662
- begin
663
- require "json"
664
- data = data.to_json
665
- rescue
666
- @logger.fatal "There was a problem converting to json: #{data}"
667
- end
668
- end
669
- end
670
- @logger.warn "Pay attention!!! This is a mock response:"
671
- @start_time_net = Time.now if @start_time_net.nil?
672
- manage_response(arg[:mock_response], data.to_s)
673
- return @response
674
- end
675
- end
676
-
677
- begin
678
- @start_time_net = Time.now if @start_time_net.nil?
679
- resp = @http.delete(path, headers_t)
680
- data = resp.body
681
- rescue Exception => stack
682
- @logger.warn stack
683
- @logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
684
- @http.finish()
685
- @http.start()
686
- @start_time_net = Time.now if @start_time_net.nil?
687
- resp, data = @http.delete(path)
688
- end
689
- manage_response(resp, data)
690
-
691
- return @response
692
- rescue Exception => stack
693
- @logger.fatal stack
694
- return {fatal_error: stack.to_s, code: nil, message: nil, data: ""}
695
- end
696
- end
697
-
698
- ######################################################
699
- # Implementation of the http HEAD method.
700
- # Asks for the response identical to the one that would correspond to a GET request, but without the response body.
701
- # This is useful for retrieving meta-information written in response headers, without having to transport the entire content.
702
- # @param arg [Hash] containing at least key :path
703
- # @param arg [String] the path
704
- #
705
- # @return [Hash] response
706
- # Including at least the symbol keys:
707
- # :message = plain text response.
708
- # :code = code response (200=ok,500=wrong...).
709
- # All keys in response are lowercase.
710
- # message and code can also be accessed as attributes like .message .code.
711
- # In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil }
712
- ######################################################
713
- def head(argument)
714
- begin
715
- if argument.kind_of?(String)
716
- argument = {:path => argument}
717
- end
718
- path, data, headers_t = manage_request(argument)
719
- @start_time = Time.now if @start_time.nil?
720
- if argument.kind_of?(Hash)
721
- arg = argument
722
- if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
723
- data = ""
724
- if arg[:mock_response].keys.include?(:data)
725
- data = arg[:mock_response][:data]
726
- if data.kind_of?(Hash) #to json
727
- begin
728
- require "json"
729
- data = data.to_json
730
- rescue
731
- @logger.fatal "There was a problem converting to json: #{data}"
732
- end
733
- end
734
- end
735
- @logger.warn "Pay attention!!! This is a mock response:"
736
- @start_time_net = Time.now if @start_time_net.nil?
737
- manage_response(arg[:mock_response], data.to_s)
738
- return @response
739
- end
740
- end
741
-
742
- begin
743
- @start_time_net = Time.now if @start_time_net.nil?
744
- resp = @http.head(path, headers_t)
745
- data = resp.body
746
- rescue Exception => stack
747
- @logger.warn stack
748
- @logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
749
- @http.finish()
750
- @http.start()
751
- @start_time_net = Time.now if @start_time_net.nil?
752
- resp, data = @http.head(path)
753
- end
754
- manage_response(resp, data)
755
- return @response
756
- rescue Exception => stack
757
- @logger.fatal stack
758
- return {fatal_error: stack.to_s, code: nil, message: nil}
759
- end
760
- end
761
-
762
- ######################################################
763
- # Close HTTP connection
764
- ######################################################
765
- def close
766
- begin
767
- pos = 0
768
- found = false
769
- self.class.connections.each { |conn|
770
- if conn.object_id == self.object_id
771
- found = true
772
- break
773
- end
774
- pos += 1
775
- }
776
- if found
777
- self.class.connections.delete_at(pos)
778
- end
779
-
780
- unless @closed
781
- if !@http.nil?
782
- @http.finish()
783
- @http = nil
784
- @logger.info "the HTTP connection was closed: #{@message_server}"
785
- else
786
- @http = nil
787
- @logger.fatal "It was not possible to close the HTTP connection: #{@message_server}"
788
- end
789
- @closed = true
790
- else
791
- @logger.warn "It was not possible to close the HTTP connection, already closed: #{@message_server}"
792
- end
793
- rescue Exception => stack
794
- @logger.fatal stack
795
- end
796
- self.class.active -= 1
797
- end
798
-
799
- ######################################################
800
- # private method to manage Request
801
- # input:
802
- # 3 args: path, data, headers
803
- # 1 arg: Hash containg at least keys :path and :data
804
- # In case :data not supplied and :data_examples array supplied, it will be taken the first example as :data.
805
- # output:
806
- # path, data, headers
807
- ######################################################
808
- def manage_request(*arguments)
809
- require "json"
810
- begin
811
- content_type_included = false
812
- path = ""
813
- data = ""
814
-
815
- @response = Hash.new()
816
- headers_t = @headers.dup()
817
- cookies_to_set_str = ""
818
- if arguments.size == 3
819
- path = arguments[0]
820
- elsif arguments.size == 1 and arguments[0].kind_of?(Hash)
821
- path = arguments[0][:path]
822
- elsif arguments.size == 1 and arguments[0].kind_of?(String)
823
- path = arguments[0].to_s()
824
- end
825
- @cookies.each { |cookie_path, cookies_hash|
826
- cookie_path = "" if cookie_path == "/"
827
- path_to_check = path
828
- if path == "/" or path[-1] != "/"
829
- path_to_check += "/"
830
- end
831
- if path_to_check.scan(/^#{cookie_path}\//).size > 0
832
- cookies_hash.each { |key, value|
833
- cookies_to_set_str += "#{key}=#{value}; "
834
- }
835
- end
836
- }
837
- headers_t["Cookie"] = cookies_to_set_str
838
-
839
- method_s = caller[0].to_s().scan(/:in `(.*)'/).join
840
-
841
- if arguments.size == 3
842
- data = arguments[1]
843
- if arguments[2].kind_of?(Hash)
844
- headers_t.merge!(arguments[2])
845
- end
846
- elsif arguments.size == 1 and arguments[0].kind_of?(Hash)
847
- if arguments[0][:data].nil?
848
- if arguments[0].keys.include?(:data)
849
- data = ""
850
- elsif arguments[0].keys.include?(:data_examples) and
851
- arguments[0][:data_examples].kind_of?(Array)
852
- data = arguments[0][:data_examples][0] #the first example by default
853
- else
854
- data = ""
855
- end
856
- else
857
- data = arguments[0][:data]
858
- end
859
- if arguments[0].include?(:headers)
860
- headers_t.merge!(arguments[0][:headers])
861
- end
862
-
863
- if headers_t["Content-Type"].to_s() == "" and headers_t["content-type"].to_s() == "" and
864
- headers_t[:"content-type"].to_s() == "" and headers_t[:"Content-Type"].to_s() == ""
865
- content_type_included = false
866
- elsif headers_t["content-type"].to_s() != ""
867
- content_type_included = true
868
- headers_t["Content-Type"] = headers_t["content-type"]
869
- elsif headers_t[:"content-type"].to_s() != ""
870
- content_type_included = true
871
- headers_t["Content-Type"] = headers_t[:"content-type"]
872
- headers_t.delete(:"content-type")
873
- elsif headers_t[:"Content-Type"].to_s() != ""
874
- content_type_included = true
875
- headers_t["Content-Type"] = headers_t[:"Content-Type"]
876
- headers_t.delete(:"Content-Type")
877
- elsif headers_t["Content-Type"].to_s() != ""
878
- content_type_included = true
879
- end
880
-
881
- if !content_type_included and data.kind_of?(Hash)
882
- headers_t["Content-Type"] = "application/json"
883
- content_type_included = true
884
- end
885
- # to be backwards compatible since before was :values
886
- if arguments[0].include?(:values) and !arguments[0].include?(:values_for)
887
- arguments[0][:values_for] = arguments[0][:values]
888
- end
889
- if content_type_included and (!headers_t["Content-Type"][/text\/xml/].nil? or
890
- !headers_t["Content-Type"]["application/soap+xml"].nil? or
891
- !headers_t["Content-Type"][/application\/jxml/].nil?)
892
- if arguments[0].include?(:values_for)
893
- arguments[0][:values_for].each { |key, value|
894
- data = NiceHttpUtils.set_value_xml_tag(key.to_s(), data, value.to_s(), true)
895
- }
896
- end
897
- elsif content_type_included and !headers_t["Content-Type"][/application\/json/].nil? and data.to_s() != ""
898
- require "json"
899
- if data.kind_of?(String)
900
- if arguments[0].include?(:values_for)
901
- arguments[0][:values_for].each { |key, value|
902
- data.gsub!(/(( *|^)"?#{key.to_s()}"? *: *")(.*)(" *, *$)/, '\1' + value + '\4') # "key":"value", or key:"value",
903
- data.gsub!(/(( *|^)"?#{key.to_s()}"? *: *")(.*)(" *$)/, '\1' + value + '\4') # "key":"value" or key:"value"
904
- data.gsub!(/(( *|^)"?#{key.to_s()}"? *: *[^"])([^"].*)([^"] *, *$)/, '\1' + value + '\4') # "key":456, or key:456,
905
- data.gsub!(/(( *|^)"?#{key.to_s()}"? *: *[^"])([^"].*)([^"] * *$)/, '\1' + value + '\4') # "key":456 or key:456
906
- }
907
- end
908
- elsif data.kind_of?(Hash)
909
- data_n = Hash.new()
910
- data.each { |key, value|
911
- data_n[key.to_s()] = value
912
- }
913
- if arguments[0].include?(:values_for)
914
- #req[:values_for][:loginName] or req[:values_for]["loginName"]
915
- new_values_hash = Hash.new()
916
- arguments[0][:values_for].each { |kv, vv|
917
- if data_n.keys.include?(kv.to_s())
918
- new_values_hash[kv.to_s()] = vv
919
- end
920
- }
921
- data_n.merge!(new_values_hash)
922
- end
923
- data = data_n.to_json()
924
- elsif data.kind_of?(Array)
925
- data_arr = Array.new()
926
- data.each_with_index { |row, indx|
927
- unless row.kind_of?(Hash)
928
- @logger.fatal("Wrong format on request application/json, be sure is a Hash, Array of Hashes or JSON string")
929
- return :error, :error, :error
930
- end
931
- data_n = Hash.new()
932
- row.each { |key, value|
933
- data_n[key.to_s()] = value
934
- }
935
- if arguments[0].include?(:values_for)
936
- #req[:values_for][:loginName] or req[:values_for]["loginName"]
937
- new_values_hash = Hash.new()
938
- if arguments[0][:values_for].kind_of?(Hash) #values[:mykey][3]
939
- arguments[0][:values_for].each { |kv, vv|
940
- if data_n.keys.include?(kv.to_s()) and !vv[indx].nil?
941
- new_values_hash[kv.to_s()] = vv[indx]
942
- end
943
- }
944
- elsif arguments[0][:values_for].kind_of?(Array) #values[5][:mykey]
945
- if !arguments[0][:values_for][indx].nil?
946
- arguments[0][:values_for][indx].each { |kv, vv|
947
- if data_n.keys.include?(kv.to_s())
948
- new_values_hash[kv.to_s()] = vv
949
- end
950
- }
951
- end
952
- else
953
- @logger.fatal("Wrong format on request application/json when supplying values, the data is an array of Hashes but the values supplied are not")
954
- return :error, :error, :error
955
- end
956
- data_n.merge!(new_values_hash)
957
- end
958
- data_arr.push(data_n)
959
- }
960
- data = data_arr.to_json()
961
- else
962
- @logger.fatal("Wrong format on request application/json, be sure is a Hash, Array of Hashes or JSON string")
963
- return :error, :error, :error
964
- end
965
- elsif content_type_included and arguments[0].include?(:values_for)
966
- if arguments[0][:values_for].kind_of?(Hash) and arguments[0][:values_for].keys.size > 0
967
- if !headers_t.nil? and headers_t.kind_of?(Hash) and headers_t["Content-Type"] != "application/x-www-form-urlencoded" and headers_t["content-type"] != "application/x-www-form-urlencoded"
968
- @logger.warn(":values_for key given without a valid content-type or data for request. No values modified on the request")
969
- end
970
- end
971
- end
972
- elsif arguments.size == 1 and arguments[0].kind_of?(String)
973
- #path=arguments[0].to_s()
974
- data = ""
975
- else
976
- @logger.fatal("Invalid number of arguments or wrong arguments in #{method_s}")
977
- return :error, :error, :error
978
- end
979
- if headers_t.keys.include?("Content-Type") and !headers_t["Content-Type"]["multipart/form-data"].nil? and headers_t["Content-Type"] != ["multipart/form-data"] #only for the case raw multipart request
980
- encoding = "UTF-8"
981
- data_s = ""
982
- else
983
- encoding = data.to_s().scan(/encoding='(.*)'/i).join
984
- if encoding.to_s() == ""
985
- encoding = data.to_s().scan(/charset='(.*)'/i).join
986
- end
987
- if encoding.to_s() == "" and headers_t.include?("Content-Type")
988
- encoding = headers_t["Content-Type"].scan(/charset='?(.*)'?/i).join
989
- if encoding.to_s() == ""
990
- encoding = headers_t["Content-Type"].scan(/encoding='?(.*)'?/i).join
991
- end
992
- end
993
-
994
- begin
995
- data_s = JSON.pretty_generate(JSON.parse(data))
996
- rescue
997
- data_s = data
998
- end
999
- data_s = data_s.to_s().gsub("<", "&lt;")
1000
- end
1001
- if headers_t.keys.include?("Accept-Encoding")
1002
- headers_t["Accept-Encoding"].gsub!("gzip", "") #removed so the response is in plain text
1003
- end
1004
-
1005
- headers_ts = ""
1006
- headers_t.each { |key, val| headers_ts += key.to_s + ":" + val.to_s() + ", " }
1007
- message = "#{method_s} REQUEST: \npath= " + path.to_s() + "\n"
1008
- message += "headers= " + headers_ts.to_s() + "\n"
1009
- message += "data= " + data_s.to_s() + "\n"
1010
- message = @message_server + "\n" + message
1011
- if path.to_s().scan(/^https?:\/\//).size > 0 and path.to_s().scan(/^https?:\/\/#{@host}/).size == 0
1012
- # the path is for another server than the current
1013
- else
1014
- self.class.last_request = message
1015
- @logger.info(message)
1016
- end
1017
-
1018
- if data.to_s() != "" and encoding.to_s().upcase != "UTF-8" and encoding != ""
1019
- data = data.to_s().encode(encoding, "UTF-8")
1020
- end
1021
- return path, data, headers_t
1022
- rescue Exception => stack
1023
- @logger.fatal(stack)
1024
- @logger.fatal("manage_request Error on method #{method_s} . path:#{path.to_s()}. data:#{data.to_s()}. headers:#{headers_t.to_s()}")
1025
- return :error
1026
- end
1027
- end
1028
-
1029
- ######################################################
1030
- # private method to manage Response
1031
- # input:
1032
- # resp
1033
- # data
1034
- # output:
1035
- # @response updated
1036
- ######################################################
1037
- def manage_response(resp, data)
1038
- require "json"
1039
- begin
1040
- if @start_time.kind_of?(Time)
1041
- @response[:time_elapsed_total] = Time.now - @start_time
1042
- @start_time = nil
1043
- else
1044
- @response[:time_elapsed_total] = nil
1045
- end
1046
- if @start_time_net.kind_of?(Time)
1047
- @response[:time_elapsed] = Time.now - @start_time_net
1048
- @start_time_net = nil
1049
- else
1050
- @response[:time_elapsed] = nil
1051
- end
1052
- begin
1053
- # this is to be able to access all keys as symbols
1054
- new_resp = Hash.new()
1055
- resp.each { |key, value|
1056
- if key.kind_of?(String)
1057
- new_resp[key.to_sym] = value
1058
- end
1059
- }
1060
- new_resp.each { |key, value|
1061
- resp[key] = value
1062
- }
1063
- rescue
1064
- end
1065
- #for mock_responses to be able to add outside of the header like content-type for example
1066
- if resp.kind_of?(Hash) and !resp.has_key?(:header)
1067
- resp[:header] = {}
1068
- end
1069
- if resp.kind_of?(Hash)
1070
- resp.each { |k, v|
1071
- if k != :code and k != :message and k != :data and k != :'set-cookie' and k != :header
1072
- resp[:header][k] = v
1073
- end
1074
- }
1075
- resp[:header].each { |k, v|
1076
- resp.delete(k) if resp.has_key?(k)
1077
- }
1078
- end
1079
-
1080
- method_s = caller[0].to_s().scan(/:in `(.*)'/).join
1081
- if resp.header.kind_of?(Hash) and (resp.header["content-type"].to_s() == "application/x-deflate" or resp.header[:"content-type"].to_s() == "application/x-deflate")
1082
- data = Zlib::Inflate.inflate(data)
1083
- end
1084
- encoding_response = ""
1085
- if resp.header.kind_of?(Hash) and (resp.header["content-type"].to_s() != "" or resp.header[:"content-type"].to_s() != "")
1086
- encoding_response = resp.header["content-type"].scan(/;charset=(.*)/i).join if resp.header.has_key?("content-type")
1087
- encoding_response = resp.header[:"content-type"].scan(/;charset=(.*)/i).join if resp.header.has_key?(:"content-type")
1088
- end
1089
- if encoding_response.to_s() == ""
1090
- encoding_response = "UTF-8"
1091
- end
1092
-
1093
- if encoding_response.to_s() != "" and encoding_response.to_s().upcase != "UTF-8"
1094
- data.encode!("UTF-8", encoding_response.to_s())
1095
- end
1096
- if encoding_response != "" and encoding_response.to_s().upcase != "UTF-8"
1097
- @response[:message] = resp.message.to_s().encode("UTF-8", encoding_response.to_s())
1098
- #todo: response data in here for example is convert into string, verify if that is correct or needs to maintain the original data type (hash, array...)
1099
- resp.each { |key, val| @response[key] = val.to_s().encode("UTF-8", encoding_response.to_s()) }
1100
- else
1101
- @response[:message] = resp.message
1102
- resp.each { |key, val| @response[key] = val }
1103
- end
1104
- if !defined?(Net::HTTP::Post::Multipart) or (defined?(Net::HTTP::Post::Multipart) and !data.kind_of?(Net::HTTP::Post::Multipart))
1105
- @response[:data] = data
1106
- else
1107
- @response[:data] = ""
1108
- end
1109
-
1110
- @response[:code] = resp.code
1111
-
1112
- unless @response.nil?
1113
- message = "\nRESPONSE: \n" + @response[:code].to_s() + ":" + @response[:message].to_s()
1114
- if @debug
1115
- self.class.last_response = message
1116
- @response.each { |key, value|
1117
- if value.to_s() != ""
1118
- value_orig = value
1119
- if key.kind_of?(Symbol)
1120
- if key == :code or key == :data or key == :header or key == :message
1121
- if key == :data
1122
- begin
1123
- JSON.parse(value_orig)
1124
- data_s = JSON.pretty_generate(JSON.parse(value_orig))
1125
- rescue
1126
- data_s = value_orig
1127
- end
1128
- self.class.last_response += "\nresponse." + key.to_s() + " = '" + data_s.gsub("<", "&lt;") + "'\n"
1129
- if value_orig != value
1130
- message += "\nresponse." + key.to_s() + " = '" + value.gsub("<", "&lt;") + "'\n"
1131
- else
1132
- message += "\nresponse." + key.to_s() + " = '" + data_s.gsub("<", "&lt;") + "'\n"
1133
- end
1134
- else
1135
- self.class.last_response += "\nresponse." + key.to_s() + " = '" + value.to_s().gsub("<", "&lt;") + "'"
1136
- message += "\nresponse." + key.to_s() + " = '" + value.to_s().gsub("<", "&lt;") + "'"
1137
- end
1138
- else
1139
- self.class.last_response += "\nresponse[:" + key.to_s() + "] = '" + value.to_s().gsub("<", "&lt;") + "'"
1140
- message += "\nresponse[:" + key.to_s() + "] = '" + value.to_s().gsub("<", "&lt;") + "'"
1141
- end
1142
- elsif !@response.include?(key.to_sym)
1143
- self.class.last_response += "\nresponse['" + key.to_s() + "'] = '" + value.to_s().gsub("<", "&lt;") + "'"
1144
- message += "\nresponse['" + key.to_s() + "'] = '" + value.to_s().gsub("<", "&lt;") + "'"
1145
- end
1146
- end
1147
- }
1148
- end
1149
- @logger.info message
1150
- if @response.kind_of?(Hash)
1151
- if @response.keys.include?(:requestid)
1152
- @headers["requestId"] = @response[:requestid]
1153
- self.class.request_id = @response[:requestid]
1154
- @logger.info "requestId was found on the response header and it has been added to the headers for the next request"
1155
- end
1156
- end
1157
- end
1158
-
1159
- if resp[:'set-cookie'].to_s() != ""
1160
- if resp.kind_of?(Hash) #mock_response
1161
- cookies_to_set = resp[:'set-cookie'].to_s().split(", ")
1162
- else #Net::Http
1163
- cookies_to_set = resp.get_fields("set-cookie")
1164
- end
1165
- cookies_to_set.each { |cookie|
1166
- cookie_pair = cookie.split("; ")[0].split("=")
1167
- cookie_path = cookie.scan(/; path=([^;]+)/i).join
1168
- @cookies[cookie_path] = Hash.new() unless @cookies.keys.include?(cookie_path)
1169
- @cookies[cookie_path][cookie_pair[0]] = cookie_pair[1]
1170
- }
1171
-
1172
- @logger.info "set-cookie added to Cookie header as required"
1173
-
1174
- if @headers.has_key?("X-CSRFToken")
1175
- csrftoken = resp[:"set-cookie"].to_s().scan(/csrftoken=([\da-z]+);/).join
1176
- if csrftoken.to_s() != ""
1177
- @headers["X-CSRFToken"] = csrftoken
1178
- @logger.info "X-CSRFToken exists on headers and has been overwritten"
1179
- end
1180
- else
1181
- csrftoken = resp[:"set-cookie"].to_s().scan(/csrftoken=([\da-z]+);/).join
1182
- if csrftoken.to_s() != ""
1183
- @headers["X-CSRFToken"] = csrftoken
1184
- @logger.info "X-CSRFToken added to header as required"
1185
- end
1186
- end
1187
- end
1188
- rescue Exception => stack
1189
- @logger.fatal stack
1190
- @logger.fatal "manage_response Error on method #{method_s} "
1191
- end
1192
- end
1193
-
1194
- private :manage_request, :manage_response
1195
- end
1
+ require "logger"
2
+ require "nice_hash"
3
+ require_relative "nice_http/utils"
4
+
5
+ ######################################################
6
+ # Attributes you can access using NiceHttp.the_attribute:
7
+ # :host, :port, :ssl, :headers, :debug, :log, :proxy_host, :proxy_port,
8
+ # :last_request, :last_response, :request_id, :use_mocks, :connections,
9
+ # :active, :auto_redirect
10
+ #
11
+ # @attr [String] host The host to be accessed
12
+ # @attr [Integer] port The port number
13
+ # @attr [Boolean] ssl If you use ssl or not
14
+ # @attr [Hash] headers Contains the headers you will be using on your connection
15
+ # @attr [Boolean] debug In case true shows all the details of the communication with the host
16
+ # @attr [String, Symbol] log :fix_file, :no, :screen, :file, "path and file name".
17
+ # :fix_file will log the communication on nice_http.log. (default).
18
+ # :no will not generate any logs.
19
+ # :screen will print the logs on the screen.
20
+ # :file will be generated a log file with name: nice_http_YY-mm-dd-HHMMSS.log.
21
+ # String the path and file name where the logs will be stored.
22
+ # @attr [String] proxy_host the proxy host to be used
23
+ # @attr [Integer] proxy_port the proxy port to be used
24
+ # @attr [String] last_request The last request with all the content sent
25
+ # @attr [String] last_response Only in case :debug is true, the last response with all the content
26
+ # @attr [String] request_id If the response includes a requestId, will be stored here
27
+ # @attr [Boolean] use_mocks If true, in case the request hash includes a :mock_response key, it will be used as the response instead
28
+ # @attr [Array] connections It will include all the active connections (NiceHttp instances)
29
+ # @attr [Integer] active Number of active connections
30
+ # @attr [Boolean] auto_redirect If true, NiceHttp will take care of the auto redirections when required by the responses
31
+ # @attr [Hash] response Contains the full response hash
32
+ # @attr [Integer] num_redirects Number of consecutive redirections managed
33
+ # @attr [Hash] headers The updated headers of the communication
34
+ # @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:
35
+ # { '/' => { "cfid" => "d95adfas2550255", "amddom.settings" => "doom" } }
36
+ # @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:
37
+ # my_http.logger.info "add this to the log file"
38
+ # @see https://ruby-doc.org/stdlib-2.5.0/libdoc/logger/rdoc/Logger.html
39
+ ######################################################
40
+ class NiceHttp
41
+ Error = Class.new StandardError
42
+
43
+ InfoMissing = Class.new Error do
44
+ attr_reader :attribute
45
+
46
+ def initialize(attribute)
47
+ @attribute = attribute
48
+ message = "It was not possible to create the http connection!!!\n"
49
+ message += "Wrong #{attribute}, remember to supply http:// or https:// in case you specify an url to create the http connection, for example:\n"
50
+ message += "http = NiceHttp.new('http://example.com')"
51
+ super message
52
+ end
53
+ end
54
+
55
+ class << self
56
+ attr_accessor :host, :port, :ssl, :headers, :debug, :log, :proxy_host, :proxy_port,
57
+ :last_request, :last_response, :request_id, :use_mocks, :connections,
58
+ :active, :auto_redirect
59
+ end
60
+
61
+ ######################################################
62
+ # to reset to the original defaults
63
+ ######################################################
64
+ def self.reset!
65
+ @host = nil
66
+ @port = 80
67
+ @ssl = false
68
+ @headers = {}
69
+ @debug = false
70
+ @log = :fix_file
71
+ @proxy_host = nil
72
+ @proxy_port = nil
73
+ @last_request = nil
74
+ @last_response = nil
75
+ @request_id = ""
76
+ @use_mocks = false
77
+ @connections = []
78
+ @active = 0
79
+ @auto_redirect = true
80
+ end
81
+ reset!
82
+
83
+ ######################################################
84
+ # If inheriting from NiceHttp class
85
+ ######################################################
86
+ def self.inherited(subclass)
87
+ subclass.reset!
88
+ end
89
+
90
+ attr_reader :host, :port, :ssl, :debug, :log, :proxy_host, :proxy_port, :response, :num_redirects
91
+ attr_accessor :headers, :cookies, :use_mocks, :auto_redirect, :logger
92
+
93
+ ######################################################
94
+ # Change the default values for NiceHttp supplying a Hash
95
+ #
96
+ # @param par [Hash] keys: :host, :port, :ssl, :headers, :debug, :log, :proxy_host, :proxy_port, :use_mocks, :auto_redirect
97
+ ######################################################
98
+ def self.defaults=(par = {})
99
+ @host = par[:host] if par.key?(:host)
100
+ @port = par[:port] if par.key?(:port)
101
+ @ssl = par[:ssl] if par.key?(:ssl)
102
+ @headers = par[:headers].dup if par.key?(:headers)
103
+ @debug = par[:debug] if par.key?(:debug)
104
+ @log = par[:log] if par.key?(:log)
105
+ @proxy_host = par[:proxy_host] if par.key?(:proxy_host)
106
+ @proxy_port = par[:proxy_port] if par.key?(:proxy_port)
107
+ @use_mocks = par[:use_mocks] if par.key?(:use_mocks)
108
+ @auto_redirect = par[:auto_redirect] if par.key?(:auto_redirect)
109
+ end
110
+
111
+ ######################################################
112
+ # Creates a new http connection.
113
+ #
114
+ # @param args [] If no parameter supplied, by default will access how is setup on defaults
115
+ # @example
116
+ # http = NiceHttp.new()
117
+ # @param args [String]. The url to create the connection.
118
+ # @example
119
+ # http = NiceHttp.new("https://www.example.com")
120
+ # @example
121
+ # http = NiceHttp.new("example.com:8999")
122
+ # @example
123
+ # http = NiceHttp.new("localhost:8322")
124
+ # @param args [Hash] containing these possible keys:
125
+ #
126
+ # host -- example.com. (default blank screen)
127
+ #
128
+ # port -- port for the connection. 80 (default)
129
+ #
130
+ # ssl -- true, false (default)
131
+ #
132
+ # headers -- hash with the headers
133
+ #
134
+ # debug -- true, false (default)
135
+ #
136
+ # log -- :no, :screen, :file, :fix_file (default).
137
+ #
138
+ # A string with a path can be supplied.
139
+ #
140
+ # If :fix_file: nice_http.log
141
+ #
142
+ # In case :file it will be generated a log file with name: nice_http_YY-mm-dd-HHMMSS.log
143
+ #
144
+ # proxy_host
145
+ #
146
+ # proxy_port
147
+ # @example
148
+ # http2 = NiceHttp.new( host: "reqres.in", port: 443, ssl: true )
149
+ # @example
150
+ # my_server = {host: "example.com",
151
+ # port: 80,
152
+ # headers: {"api-key": "zdDDdjkck"}
153
+ # }
154
+ # http3 = NiceHttp.new my_server
155
+ ######################################################
156
+ def initialize(args = {})
157
+ require "net/http"
158
+ require "net/https"
159
+ @host = self.class.host
160
+ @port = self.class.port
161
+ @ssl = self.class.ssl
162
+ @headers = self.class.headers.dup
163
+ @debug = self.class.debug
164
+ @log = self.class.log
165
+ @proxy_host = self.class.proxy_host
166
+ @proxy_port = self.class.proxy_port
167
+ @use_mocks = self.class.use_mocks
168
+ @auto_redirect = false #set it up at the end of initialize
169
+ auto_redirect = self.class.auto_redirect
170
+ @num_redirects = 0
171
+
172
+ #todo: set only the cookies for the current domain
173
+ #key: path, value: hash with key is the name of the cookie and value the value
174
+ # we set the default value for non existing keys to empty Hash {} so in case of merge there is no problem
175
+ @cookies = Hash.new { |h, k| h[k] = {} }
176
+
177
+ if args.is_a?(String)
178
+ uri = URI.parse(args)
179
+ @host = uri.host unless uri.host.nil?
180
+ @port = uri.port unless uri.port.nil?
181
+ @ssl = true if !uri.scheme.nil? && (uri.scheme == "https")
182
+ elsif args.is_a?(Hash) && !args.keys.empty?
183
+ @host = args[:host] if args.keys.include?(:host)
184
+ @port = args[:port] if args.keys.include?(:port)
185
+ @ssl = args[:ssl] if args.keys.include?(:ssl)
186
+ @headers = args[:headers].dup if args.keys.include?(:headers)
187
+ @debug = args[:debug] if args.keys.include?(:debug)
188
+ @log = args[:log] if args.keys.include?(:log)
189
+ @proxy_host = args[:proxy_host] if args.keys.include?(:proxy_host)
190
+ @proxy_port = args[:proxy_port] if args.keys.include?(:proxy_port)
191
+ @use_mocks = args[:use_mocks] if args.keys.include?(:use_mocks)
192
+ auto_redirect = args[:auto_redirect] if args.keys.include?(:auto_redirect)
193
+ end
194
+
195
+ begin
196
+ if @log.kind_of?(String)
197
+ f = File.new(@log, "w")
198
+ f.sync = true
199
+ @logger = Logger.new f
200
+ elsif @log == :fix_file
201
+ f = File.new("nice_http.log", "w")
202
+ f.sync = true
203
+ @logger = Logger.new f
204
+ elsif @log == :file
205
+ f = File.new("nice_http_#{Time.now.strftime("%Y-%m-%d-%H%M%S")}.log", "w")
206
+ f.sync = true
207
+ @logger = Logger.new f
208
+ elsif @log == :screen
209
+ @logger = Logger.new STDOUT
210
+ elsif @log == :no
211
+ @logger = Logger.new nil
212
+ else
213
+ raise InfoMissing, :log
214
+ end
215
+ @logger.level = Logger::INFO
216
+ rescue Exception => stack
217
+ raise InfoMissing, :log
218
+ @logger = Logger.new nil
219
+ end
220
+
221
+
222
+ if @host.to_s != "" and (@host.include?("http:") or @host.include?("https:"))
223
+ uri = URI.parse(@host)
224
+ @host = uri.host unless uri.host.nil?
225
+ @port = uri.port unless uri.port.nil?
226
+ @ssl = true if !uri.scheme.nil? && (uri.scheme == "https")
227
+ end
228
+
229
+ raise InfoMissing, :port if @port.to_s == ""
230
+ raise InfoMissing, :host if @host.to_s == ""
231
+ raise InfoMissing, :ssl unless @ssl.is_a?(TrueClass) or @ssl.is_a?(FalseClass)
232
+ raise InfoMissing, :debug unless @debug.is_a?(TrueClass) or @debug.is_a?(FalseClass)
233
+ raise InfoMissing, :auto_redirect unless auto_redirect.is_a?(TrueClass) or auto_redirect.is_a?(FalseClass)
234
+ raise InfoMissing, :use_mocks unless @use_mocks.is_a?(TrueClass) or @use_mocks.is_a?(FalseClass)
235
+ raise InfoMissing, :headers unless @headers.is_a?(Hash)
236
+
237
+ begin
238
+ if !@proxy_host.nil? && !@proxy_port.nil?
239
+ @http = Net::HTTP::Proxy(@proxy_host, @proxy_port).new(@host, @port)
240
+ @http.use_ssl = @ssl
241
+ @http.set_debug_output $stderr if @debug
242
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
243
+ @http.start
244
+ else
245
+ @http = Net::HTTP.new(@host, @port)
246
+ @http.use_ssl = @ssl
247
+ @http.set_debug_output $stderr if @debug
248
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
249
+ @http.start
250
+ end
251
+
252
+ @message_server = "(#{self.object_id}):"
253
+
254
+ 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()} "
255
+
256
+ @logger.info(log_message)
257
+ @message_server += " Http connection: "
258
+ if @ssl
259
+ @message_server += "https://"
260
+ else
261
+ @message_server += "http://"
262
+ end
263
+ @message_server += "#{@host}:#{@port}"
264
+ if @proxy_host.to_s != ""
265
+ @message_server += " proxy:#{@proxy_host}:#{@proxy_port}"
266
+ end
267
+ @auto_redirect = auto_redirect
268
+
269
+ self.class.active += 1
270
+ self.class.connections.push(self)
271
+ rescue Exception => stack
272
+ puts stack
273
+ @logger.fatal stack
274
+ end
275
+ end
276
+
277
+ ######################################################
278
+ # Get data from path
279
+ #
280
+ # @param arg [Hash] containing at least key :path
281
+ # @param arg [String] the path
282
+ #
283
+ # @return [Hash] response
284
+ # Including at least the symbol keys:
285
+ # :data = the response data body.
286
+ # :message = plain text response.
287
+ # :code = code response (200=ok,500=wrong...).
288
+ # All keys in response are lowercase.
289
+ # data, message and code can also be accessed as attributes like .message .code .data.
290
+ # In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil, data: '' }
291
+ #
292
+ # @example
293
+ # resp = @http.get(Requests::Customer.get_profile)
294
+ # assert resp.code == 200
295
+ # @example
296
+ # resp = @http.get("/customers/1223")
297
+ # assert resp.message == "OK"
298
+ ######################################################
299
+ def get(arg)
300
+ begin
301
+ path, data, headers_t = manage_request(arg)
302
+
303
+ @start_time = Time.now if @start_time.nil?
304
+ if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
305
+ data = ""
306
+ if arg[:mock_response].keys.include?(:data)
307
+ data = arg[:mock_response][:data]
308
+ if data.kind_of?(Hash) #to json
309
+ begin
310
+ require "json"
311
+ data = data.to_json
312
+ rescue
313
+ @logger.fatal "There was a problem converting to json: #{data}"
314
+ end
315
+ end
316
+ end
317
+ @logger.warn "Pay attention!!! This is a mock response:"
318
+ @start_time_net = Time.now if @start_time_net.nil?
319
+ manage_response(arg[:mock_response], data.to_s)
320
+ return @response
321
+ end
322
+ begin
323
+ if path.start_with?("http:") or path.start_with?("https:") #server included on path problably because of a redirection to a different server
324
+ require "uri"
325
+ uri = URI.parse(path)
326
+ ssl = false
327
+ ssl = true if path.include?("https:")
328
+
329
+ server = "http://"
330
+ server = "https://" if path.include?("https:")
331
+ if uri.port != 443
332
+ server += "#{uri.host}:#{uri.port}"
333
+ else
334
+ server += "#{uri.host}"
335
+ end
336
+
337
+ http_redir = nil
338
+ self.class.connections.each { |conn|
339
+ if conn.host == uri.host and conn.port == uri.port
340
+ http_redir = conn
341
+ break
342
+ end
343
+ }
344
+
345
+ if !http_redir.nil?
346
+ path, data, headers_t = manage_request(arg)
347
+ http_redir.cookies.merge!(@cookies)
348
+ http_redir.headers.merge!(headers_t)
349
+ resp = http_redir.get(path.gsub(server, "")) #todo: remove only the server at the begining in case in query is the server it will be replaced when it should not be
350
+ @response = http_redir.response
351
+ else
352
+ @logger.warn "It seems like the http connection cannot redirect to #{server} because there is no active connection for that server. You need to create previously one."
353
+ end
354
+ else
355
+ @start_time_net = Time.now if @start_time_net.nil?
356
+ resp = @http.get(path, headers_t)
357
+ data = resp.body
358
+ manage_response(resp, data)
359
+ end
360
+ rescue Exception => stack
361
+ @logger.warn stack
362
+ @logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
363
+ @http.finish()
364
+ @http.start()
365
+ @start_time_net = Time.now if @start_time_net.nil?
366
+ resp = @http.get(path)
367
+ data = resp.body
368
+ manage_response(resp, data)
369
+ end
370
+
371
+ if @auto_redirect and @response[:code].to_i >= 300 and @response[:code].to_i < 400 and @response.include?(:location)
372
+ if @num_redirects <= 30
373
+ @num_redirects += 1
374
+ current_server = "http"
375
+ current_server += "s" if @ssl == true
376
+ current_server += "://#{@host}"
377
+ location = @response[:location].gsub(current_server, "")
378
+ @logger.info "(#{@num_redirects}) Redirecting NiceHttp to #{location}"
379
+ get(location)
380
+ else
381
+ @logger.fatal "(#{@num_redirects}) Maximum number of redirections for a single request reached. Be sure everything is correct, it seems there is a non ending loop"
382
+ @num_redirects = 0
383
+ end
384
+ else
385
+ @num_redirects = 0
386
+ end
387
+ return @response
388
+ rescue Exception => stack
389
+ @logger.fatal stack
390
+ return {fatal_error: stack.to_s, code: nil, message: nil, data: ""}
391
+ end
392
+ end
393
+
394
+ ######################################################
395
+ # Post data to path
396
+ # @param arguments [Hash] containing at least keys :data and :path.
397
+ # In case :data not supplied and :data_examples array supplied, it will be taken the first example as :data.
398
+ # @param arguments [Array<path, data, additional_headers>]
399
+ # path (string).
400
+ # data (json data for example).
401
+ # additional_headers (Hash key=>value).
402
+ # @return [Hash] response
403
+ # Including at least the symbol keys:
404
+ # :data = the response data body.
405
+ # :message = plain text response.
406
+ # :code = code response (200=ok,500=wrong...).
407
+ # All keys in response are lowercase.
408
+ # data, message and code can also be accessed as attributes like .message .code .data.
409
+ # In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil, data: '' }
410
+ # @example
411
+ # resp = @http.post(Requests::Customer.update_customer)
412
+ # assert resp.code == 201
413
+ # @example
414
+ # resp = http.post( {
415
+ # path: "/api/users",
416
+ # data: {name: "morpheus", job: "leader"}
417
+ # } )
418
+ # pp resp.data.json
419
+ ######################################################
420
+ def post(*arguments)
421
+ begin
422
+ path, data, headers_t = manage_request(*arguments)
423
+ @start_time = Time.now if @start_time.nil?
424
+ if arguments.size > 0 and arguments[0].kind_of?(Hash)
425
+ arg = arguments[0]
426
+ if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
427
+ data = ""
428
+ if arg[:mock_response].keys.include?(:data)
429
+ data = arg[:mock_response][:data]
430
+ if data.kind_of?(Hash) #to json
431
+ begin
432
+ require "json"
433
+ data = data.to_json
434
+ rescue
435
+ @logger.fatal "There was a problem converting to json: #{data}"
436
+ end
437
+ end
438
+ end
439
+ @logger.warn "Pay attention!!! This is a mock response:"
440
+ @start_time_net = Time.now if @start_time_net.nil?
441
+ manage_response(arg[:mock_response], data.to_s)
442
+ return @response
443
+ end
444
+ end
445
+
446
+ begin
447
+ @start_time_net = Time.now if @start_time_net.nil?
448
+ if headers_t["Content-Type"] == "multipart/form-data"
449
+ require "net/http/post/multipart"
450
+ headers_t.each { |key, value|
451
+ arguments[0][:data].add_field(key, value) #add to Headers
452
+ }
453
+ resp = @http.request(arguments[0][:data])
454
+ else
455
+ resp = @http.post(path, data, headers_t)
456
+ data = resp.body
457
+ end
458
+ rescue Exception => stack
459
+ @logger.warn stack
460
+ @logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
461
+ @http.finish()
462
+ @http.start()
463
+ @start_time_net = Time.now if @start_time_net.nil?
464
+ resp, data = @http.post(path, data, headers_t)
465
+ end
466
+ manage_response(resp, data)
467
+ if @auto_redirect and @response[:code].to_i >= 300 and @response[:code].to_i < 400 and @response.include?(:location)
468
+ if @num_redirects <= 30
469
+ @num_redirects += 1
470
+ current_server = "http"
471
+ current_server += "s" if @ssl == true
472
+ current_server += "://#{@host}"
473
+ location = @response[:location].gsub(current_server, "")
474
+ @logger.info "(#{@num_redirects}) Redirecting NiceHttp to #{location}"
475
+ get(location)
476
+ else
477
+ @logger.fatal "(#{@num_redirects}) Maximum number of redirections for a single request reached. Be sure everything is correct, it seems there is a non ending loop"
478
+ @num_redirects = 0
479
+ end
480
+ else
481
+ @num_redirects = 0
482
+ end
483
+ return @response
484
+ rescue Exception => stack
485
+ @logger.fatal stack
486
+ return {fatal_error: stack.to_s, code: nil, message: nil, data: ""}
487
+ end
488
+ end
489
+
490
+ ######################################################
491
+ # Put data to path
492
+ # @param arguments [Hash] containing at least keys :data and :path.
493
+ # In case :data not supplied and :data_examples array supplied, it will be taken the first example as :data.
494
+ # @param arguments [Array<path, data, additional_headers>]
495
+ # path (string).
496
+ # data (json data for example).
497
+ # additional_headers (Hash key=>value).
498
+ # @return [Hash] response
499
+ # Including at least the symbol keys:
500
+ # :data = the response data body.
501
+ # :message = plain text response.
502
+ # :code = code response (200=ok,500=wrong...).
503
+ # All keys in response are lowercase.
504
+ # data, message and code can also be accessed as attributes like .message .code .data.
505
+ # In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil, data: '' }
506
+ # @example
507
+ # resp = @http.put(Requests::Customer.remove_phone)
508
+ ######################################################
509
+ def put(*arguments)
510
+ begin
511
+ path, data, headers_t = manage_request(*arguments)
512
+ @start_time = Time.now if @start_time.nil?
513
+ if arguments.size > 0 and arguments[0].kind_of?(Hash)
514
+ arg = arguments[0]
515
+ if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
516
+ data = ""
517
+ if arg[:mock_response].keys.include?(:data)
518
+ data = arg[:mock_response][:data]
519
+ if data.kind_of?(Hash) #to json
520
+ begin
521
+ require "json"
522
+ data = data.to_json
523
+ rescue
524
+ @logger.fatal "There was a problem converting to json: #{data}"
525
+ end
526
+ end
527
+ end
528
+ @logger.warn "Pay attention!!! This is a mock response:"
529
+ @start_time_net = Time.now if @start_time_net.nil?
530
+ manage_response(arg[:mock_response], data.to_s)
531
+ return @response
532
+ end
533
+ end
534
+
535
+ begin
536
+ @start_time_net = Time.now if @start_time_net.nil?
537
+ resp = @http.send_request("PUT", path, data, headers_t)
538
+ data = resp.body
539
+ rescue Exception => stack
540
+ @logger.warn stack
541
+ @logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
542
+ @http.finish()
543
+ @http.start()
544
+ @start_time_net = Time.now if @start_time_net.nil?
545
+ resp, data = @http.send_request("PUT", path, data, headers_t)
546
+ end
547
+ manage_response(resp, data)
548
+
549
+ return @response
550
+ rescue Exception => stack
551
+ @logger.fatal stack
552
+ return {fatal_error: stack.to_s, code: nil, message: nil, data: ""}
553
+ end
554
+ end
555
+
556
+ ######################################################
557
+ # Patch data to path
558
+ # @param arguments [Hash] containing at least keys :data and :path.
559
+ # In case :data not supplied and :data_examples array supplied, it will be taken the first example as :data.
560
+ # @param arguments [Array<path, data, additional_headers>]
561
+ # path (string).
562
+ # data (json data for example).
563
+ # additional_headers (Hash key=>value).
564
+ # @return [Hash] response
565
+ # Including at least the symbol keys:
566
+ # :data = the response data body.
567
+ # :message = plain text response.
568
+ # :code = code response (200=ok,500=wrong...).
569
+ # All keys in response are lowercase.
570
+ # data, message and code can also be accessed as attributes like .message .code .data.
571
+ # In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil, data: '' }
572
+ # @example
573
+ # resp = @http.patch(Requests::Customer.unrelease_account)
574
+ ######################################################
575
+ def patch(*arguments)
576
+ begin
577
+ path, data, headers_t = manage_request(*arguments)
578
+ @start_time = Time.now if @start_time.nil?
579
+ if arguments.size > 0 and arguments[0].kind_of?(Hash)
580
+ arg = arguments[0]
581
+ if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
582
+ data = ""
583
+ if arg[:mock_response].keys.include?(:data)
584
+ data = arg[:mock_response][:data]
585
+ if data.kind_of?(Hash) #to json
586
+ begin
587
+ require "json"
588
+ data = data.to_json
589
+ rescue
590
+ @logger.fatal "There was a problem converting to json: #{data}"
591
+ end
592
+ end
593
+ end
594
+ @logger.warn "Pay attention!!! This is a mock response:"
595
+ @start_time_net = Time.now if @start_time_net.nil?
596
+ manage_response(arg[:mock_response], data.to_s)
597
+ return @response
598
+ end
599
+ end
600
+
601
+ begin
602
+ @start_time_net = Time.now if @start_time_net.nil?
603
+ resp = @http.patch(path, data, headers_t)
604
+ data = resp.body
605
+ rescue Exception => stack
606
+ @logger.warn stack
607
+ @logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
608
+ @http.finish()
609
+ @http.start()
610
+ @start_time_net = Time.now if @start_time_net.nil?
611
+ resp, data = @http.patch(path, data, headers_t)
612
+ end
613
+ manage_response(resp, data)
614
+ if @auto_redirect and @response[:code].to_i >= 300 and @response[:code].to_i < 400 and @response.include?(:location)
615
+ if @num_redirects <= 30
616
+ @num_redirects += 1
617
+ current_server = "http"
618
+ current_server += "s" if @ssl == true
619
+ current_server += "://#{@host}"
620
+ location = @response[:location].gsub(current_server, "")
621
+ @logger.info "(#{@num_redirects}) Redirecting NiceHttp to #{location}"
622
+ get(location)
623
+ else
624
+ @logger.fatal "(#{@num_redirects}) Maximum number of redirections for a single request reached. Be sure everything is correct, it seems there is a non ending loop"
625
+ @num_redirects = 0
626
+ end
627
+ else
628
+ @num_redirects = 0
629
+ end
630
+ return @response
631
+ rescue Exception => stack
632
+ @logger.fatal stack
633
+ return {fatal_error: stack.to_s, code: nil, message: nil, data: ""}
634
+ end
635
+ end
636
+
637
+ ######################################################
638
+ # Delete an existing resource
639
+ # @param arg [Hash] containing at least key :path
640
+ # @param arg [String] the path
641
+ #
642
+ # @return [Hash] response
643
+ # Including at least the symbol keys:
644
+ # :data = the response data body.
645
+ # :message = plain text response.
646
+ # :code = code response (200=ok,500=wrong...).
647
+ # All keys in response are lowercase.
648
+ # data, message and code can also be accessed as attributes like .message .code .data.
649
+ # In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil, data: '' }
650
+ # @example
651
+ # resp = @http.delete(Requests::Customer.remove_session)
652
+ # assert resp.code == 204
653
+ ######################################################
654
+ def delete(argument)
655
+ begin
656
+ if argument.kind_of?(String)
657
+ argument = {:path => argument}
658
+ end
659
+ path, data, headers_t = manage_request(argument)
660
+ @start_time = Time.now if @start_time.nil?
661
+ if argument.kind_of?(Hash)
662
+ arg = argument
663
+ if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
664
+ data = ""
665
+ if arg[:mock_response].keys.include?(:data)
666
+ data = arg[:mock_response][:data]
667
+ if data.kind_of?(Hash) #to json
668
+ begin
669
+ require "json"
670
+ data = data.to_json
671
+ rescue
672
+ @logger.fatal "There was a problem converting to json: #{data}"
673
+ end
674
+ end
675
+ end
676
+ @logger.warn "Pay attention!!! This is a mock response:"
677
+ @start_time_net = Time.now if @start_time_net.nil?
678
+ manage_response(arg[:mock_response], data.to_s)
679
+ return @response
680
+ end
681
+ end
682
+
683
+ begin
684
+ @start_time_net = Time.now if @start_time_net.nil?
685
+ resp = @http.delete(path, headers_t)
686
+ data = resp.body
687
+ rescue Exception => stack
688
+ @logger.warn stack
689
+ @logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
690
+ @http.finish()
691
+ @http.start()
692
+ @start_time_net = Time.now if @start_time_net.nil?
693
+ resp, data = @http.delete(path)
694
+ end
695
+ manage_response(resp, data)
696
+
697
+ return @response
698
+ rescue Exception => stack
699
+ @logger.fatal stack
700
+ return {fatal_error: stack.to_s, code: nil, message: nil, data: ""}
701
+ end
702
+ end
703
+
704
+ ######################################################
705
+ # Implementation of the http HEAD method.
706
+ # Asks for the response identical to the one that would correspond to a GET request, but without the response body.
707
+ # This is useful for retrieving meta-information written in response headers, without having to transport the entire content.
708
+ # @param arg [Hash] containing at least key :path
709
+ # @param arg [String] the path
710
+ #
711
+ # @return [Hash] response
712
+ # Including at least the symbol keys:
713
+ # :message = plain text response.
714
+ # :code = code response (200=ok,500=wrong...).
715
+ # All keys in response are lowercase.
716
+ # message and code can also be accessed as attributes like .message .code.
717
+ # In case of fatal error returns { fatal_error: "the error description", code: nil, message: nil }
718
+ ######################################################
719
+ def head(argument)
720
+ begin
721
+ if argument.kind_of?(String)
722
+ argument = {:path => argument}
723
+ end
724
+ path, data, headers_t = manage_request(argument)
725
+ @start_time = Time.now if @start_time.nil?
726
+ if argument.kind_of?(Hash)
727
+ arg = argument
728
+ if @use_mocks and arg.kind_of?(Hash) and arg.keys.include?(:mock_response)
729
+ @logger.warn "Pay attention!!! This is a mock response:"
730
+ @start_time_net = Time.now if @start_time_net.nil?
731
+ manage_response(arg[:mock_response], "")
732
+ return @response
733
+ end
734
+ end
735
+
736
+ begin
737
+ @start_time_net = Time.now if @start_time_net.nil?
738
+ resp = @http.head(path, headers_t)
739
+ data = resp.body
740
+ rescue Exception => stack
741
+ @logger.warn stack
742
+ @logger.warn "The connection seems to be closed in the host machine. Trying to reconnect"
743
+ @http.finish()
744
+ @http.start()
745
+ @start_time_net = Time.now if @start_time_net.nil?
746
+ resp, data = @http.head(path)
747
+ end
748
+ manage_response(resp, data)
749
+ return @response
750
+ rescue Exception => stack
751
+ @logger.fatal stack
752
+ return {fatal_error: stack.to_s, code: nil, message: nil}
753
+ end
754
+ end
755
+
756
+ ######################################################
757
+ # Close HTTP connection
758
+ ######################################################
759
+ def close
760
+ begin
761
+ pos = 0
762
+ found = false
763
+ self.class.connections.each { |conn|
764
+ if conn.object_id == self.object_id
765
+ found = true
766
+ break
767
+ end
768
+ pos += 1
769
+ }
770
+ if found
771
+ self.class.connections.delete_at(pos)
772
+ end
773
+
774
+ unless @closed
775
+ if !@http.nil?
776
+ @http.finish()
777
+ @http = nil
778
+ @logger.info "the HTTP connection was closed: #{@message_server}"
779
+ else
780
+ @http = nil
781
+ @logger.fatal "It was not possible to close the HTTP connection: #{@message_server}"
782
+ end
783
+ @closed = true
784
+ else
785
+ @logger.warn "It was not possible to close the HTTP connection, already closed: #{@message_server}"
786
+ end
787
+ rescue Exception => stack
788
+ @logger.fatal stack
789
+ end
790
+ self.class.active -= 1
791
+ end
792
+
793
+ ######################################################
794
+ # private method to manage Request
795
+ # input:
796
+ # 3 args: path, data, headers
797
+ # 1 arg: Hash containg at least keys :path and :data
798
+ # In case :data not supplied and :data_examples array supplied, it will be taken the first example as :data.
799
+ # output:
800
+ # path, data, headers
801
+ ######################################################
802
+ def manage_request(*arguments)
803
+ require "json"
804
+ begin
805
+ content_type_included = false
806
+ path = ""
807
+ data = ""
808
+
809
+ @response = Hash.new()
810
+ headers_t = @headers.dup()
811
+ cookies_to_set_str = ""
812
+ if arguments.size == 3
813
+ path = arguments[0]
814
+ elsif arguments.size == 1 and arguments[0].kind_of?(Hash)
815
+ path = arguments[0][:path]
816
+ elsif arguments.size == 1 and arguments[0].kind_of?(String)
817
+ path = arguments[0].to_s()
818
+ end
819
+ @cookies.each { |cookie_path, cookies_hash|
820
+ cookie_path = "" if cookie_path == "/"
821
+ path_to_check = path
822
+ if path == "/" or path[-1] != "/"
823
+ path_to_check += "/"
824
+ end
825
+ if path_to_check.scan(/^#{cookie_path}\//).size > 0
826
+ cookies_hash.each { |key, value|
827
+ cookies_to_set_str += "#{key}=#{value}; "
828
+ }
829
+ end
830
+ }
831
+ headers_t["Cookie"] = cookies_to_set_str
832
+
833
+ method_s = caller[0].to_s().scan(/:in `(.*)'/).join
834
+
835
+ if arguments.size == 3
836
+ data = arguments[1]
837
+ if arguments[2].kind_of?(Hash)
838
+ headers_t.merge!(arguments[2])
839
+ end
840
+ elsif arguments.size == 1 and arguments[0].kind_of?(Hash)
841
+ if arguments[0][:data].nil?
842
+ if arguments[0].keys.include?(:data)
843
+ data = ""
844
+ elsif arguments[0].keys.include?(:data_examples) and
845
+ arguments[0][:data_examples].kind_of?(Array)
846
+ data = arguments[0][:data_examples][0] #the first example by default
847
+ else
848
+ data = ""
849
+ end
850
+ else
851
+ data = arguments[0][:data]
852
+ end
853
+ if arguments[0].include?(:headers)
854
+ headers_t.merge!(arguments[0][:headers])
855
+ end
856
+
857
+ if headers_t["Content-Type"].to_s() == "" and headers_t["content-type"].to_s() == "" and
858
+ headers_t[:"content-type"].to_s() == "" and headers_t[:"Content-Type"].to_s() == ""
859
+ content_type_included = false
860
+ elsif headers_t["content-type"].to_s() != ""
861
+ content_type_included = true
862
+ headers_t["Content-Type"] = headers_t["content-type"]
863
+ elsif headers_t[:"content-type"].to_s() != ""
864
+ content_type_included = true
865
+ headers_t["Content-Type"] = headers_t[:"content-type"]
866
+ headers_t.delete(:"content-type")
867
+ elsif headers_t[:"Content-Type"].to_s() != ""
868
+ content_type_included = true
869
+ headers_t["Content-Type"] = headers_t[:"Content-Type"]
870
+ headers_t.delete(:"Content-Type")
871
+ elsif headers_t["Content-Type"].to_s() != ""
872
+ content_type_included = true
873
+ end
874
+ if !content_type_included and data.kind_of?(Hash)
875
+ headers_t["Content-Type"] = "application/json"
876
+ content_type_included = true
877
+ end
878
+ # to be backwards compatible since before was :values
879
+ if arguments[0].include?(:values) and !arguments[0].include?(:values_for)
880
+ arguments[0][:values_for] = arguments[0][:values]
881
+ end
882
+ if content_type_included and (!headers_t["Content-Type"][/text\/xml/].nil? or
883
+ !headers_t["Content-Type"]["application/soap+xml"].nil? or
884
+ !headers_t["Content-Type"][/application\/jxml/].nil?)
885
+ if arguments[0].include?(:values_for)
886
+ arguments[0][:values_for].each { |key, value|
887
+ data = NiceHttpUtils.set_value_xml_tag(key.to_s(), data, value.to_s(), true)
888
+ }
889
+ end
890
+ elsif content_type_included and !headers_t["Content-Type"][/application\/json/].nil? and data.to_s() != ""
891
+ require "json"
892
+ if data.kind_of?(String)
893
+ if arguments[0].include?(:values_for)
894
+ arguments[0][:values_for].each { |key, value|
895
+ data.gsub!(/(( *|^)"?#{key.to_s()}"? *: *")(.*)(" *, *$)/, '\1' + value + '\4') # "key":"value", or key:"value",
896
+ data.gsub!(/(( *|^)"?#{key.to_s()}"? *: *")(.*)(" *$)/, '\1' + value + '\4') # "key":"value" or key:"value"
897
+ data.gsub!(/(( *|^)"?#{key.to_s()}"? *: *[^"])([^"].*)([^"] *, *$)/, '\1' + value + '\4') # "key":456, or key:456,
898
+ data.gsub!(/(( *|^)"?#{key.to_s()}"? *: *[^"])([^"].*)([^"] * *$)/, '\1' + value + '\4') # "key":456 or key:456
899
+ }
900
+ end
901
+ elsif data.kind_of?(Hash)
902
+ data_n = Hash.new()
903
+ data.each { |key, value|
904
+ data_n[key.to_s()] = value
905
+ }
906
+ if arguments[0].include?(:values_for)
907
+ #req[:values_for][:loginName] or req[:values_for]["loginName"]
908
+ new_values_hash = Hash.new()
909
+ arguments[0][:values_for].each { |kv, vv|
910
+ if data_n.keys.include?(kv.to_s())
911
+ new_values_hash[kv.to_s()] = vv
912
+ end
913
+ }
914
+ data_n.merge!(new_values_hash)
915
+ end
916
+ data = data_n.to_json()
917
+ elsif data.kind_of?(Array)
918
+ data_arr = Array.new()
919
+ data.each_with_index { |row, indx|
920
+ unless row.kind_of?(Hash)
921
+ @logger.fatal("Wrong format on request application/json, be sure is a Hash, Array of Hashes or JSON string")
922
+ return :error, :error, :error
923
+ end
924
+ data_n = Hash.new()
925
+ row.each { |key, value|
926
+ data_n[key.to_s()] = value
927
+ }
928
+ if arguments[0].include?(:values_for)
929
+ #req[:values_for][:loginName] or req[:values_for]["loginName"]
930
+ new_values_hash = Hash.new()
931
+ if arguments[0][:values_for].kind_of?(Hash) #values[:mykey][3]
932
+ arguments[0][:values_for].each { |kv, vv|
933
+ if data_n.keys.include?(kv.to_s()) and !vv[indx].nil?
934
+ new_values_hash[kv.to_s()] = vv[indx]
935
+ end
936
+ }
937
+ elsif arguments[0][:values_for].kind_of?(Array) #values[5][:mykey]
938
+ if !arguments[0][:values_for][indx].nil?
939
+ arguments[0][:values_for][indx].each { |kv, vv|
940
+ if data_n.keys.include?(kv.to_s())
941
+ new_values_hash[kv.to_s()] = vv
942
+ end
943
+ }
944
+ end
945
+ else
946
+ @logger.fatal("Wrong format on request application/json when supplying values, the data is an array of Hashes but the values supplied are not")
947
+ return :error, :error, :error
948
+ end
949
+ data_n.merge!(new_values_hash)
950
+ end
951
+ data_arr.push(data_n)
952
+ }
953
+ data = data_arr.to_json()
954
+ else
955
+ @logger.fatal("Wrong format on request application/json, be sure is a Hash, Array of Hashes or JSON string")
956
+ return :error, :error, :error
957
+ end
958
+ elsif content_type_included and arguments[0].include?(:values_for)
959
+ if arguments[0][:values_for].kind_of?(Hash) and arguments[0][:values_for].keys.size > 0
960
+ if !headers_t.nil? and headers_t.kind_of?(Hash) and headers_t["Content-Type"] != "application/x-www-form-urlencoded" and headers_t["content-type"] != "application/x-www-form-urlencoded"
961
+ @logger.warn(":values_for key given without a valid content-type or data for request. No values modified on the request")
962
+ end
963
+ end
964
+ end
965
+ elsif arguments.size == 1 and arguments[0].kind_of?(String)
966
+ #path=arguments[0].to_s()
967
+ data = ""
968
+ else
969
+ @logger.fatal("Invalid number of arguments or wrong arguments in #{method_s}")
970
+ return :error, :error, :error
971
+ end
972
+ if headers_t.keys.include?("Content-Type") and !headers_t["Content-Type"]["multipart/form-data"].nil? and headers_t["Content-Type"] != ["multipart/form-data"] #only for the case raw multipart request
973
+ encoding = "UTF-8"
974
+ data_s = ""
975
+ else
976
+ encoding = data.to_s().scan(/encoding='(.*)'/i).join
977
+ if encoding.to_s() == ""
978
+ encoding = data.to_s().scan(/charset='(.*)'/i).join
979
+ end
980
+ if encoding.to_s() == "" and headers_t.include?("Content-Type")
981
+ encoding = headers_t["Content-Type"].scan(/charset='?(.*)'?/i).join
982
+ if encoding.to_s() == ""
983
+ encoding = headers_t["Content-Type"].scan(/encoding='?(.*)'?/i).join
984
+ end
985
+ end
986
+
987
+ begin
988
+ data_s = JSON.pretty_generate(JSON.parse(data))
989
+ rescue
990
+ data_s = data
991
+ end
992
+ data_s = data_s.to_s().gsub("<", "&lt;")
993
+ end
994
+ if headers_t.keys.include?("Accept-Encoding")
995
+ headers_t["Accept-Encoding"].gsub!("gzip", "") #removed so the response is in plain text
996
+ end
997
+
998
+ headers_ts = ""
999
+ headers_t.each { |key, val| headers_ts += key.to_s + ":" + val.to_s() + ", " }
1000
+ message = "#{method_s} REQUEST: \npath= " + path.to_s() + "\n"
1001
+ message += "headers= " + headers_ts.to_s() + "\n"
1002
+ message += "data= " + data_s.to_s() + "\n"
1003
+ message = @message_server + "\n" + message
1004
+ if path.to_s().scan(/^https?:\/\//).size > 0 and path.to_s().scan(/^https?:\/\/#{@host}/).size == 0
1005
+ # the path is for another server than the current
1006
+ else
1007
+ self.class.last_request = message
1008
+ @logger.info(message)
1009
+ end
1010
+
1011
+ if data.to_s() != "" and encoding.to_s().upcase != "UTF-8" and encoding != ""
1012
+ data = data.to_s().encode(encoding, "UTF-8")
1013
+ end
1014
+ return path, data, headers_t
1015
+ rescue Exception => stack
1016
+ @logger.fatal(stack)
1017
+ @logger.fatal("manage_request Error on method #{method_s} . path:#{path.to_s()}. data:#{data.to_s()}. headers:#{headers_t.to_s()}")
1018
+ return :error
1019
+ end
1020
+ end
1021
+
1022
+ ######################################################
1023
+ # private method to manage Response
1024
+ # input:
1025
+ # resp
1026
+ # data
1027
+ # output:
1028
+ # @response updated
1029
+ ######################################################
1030
+ def manage_response(resp, data)
1031
+ require "json"
1032
+ begin
1033
+ if @start_time.kind_of?(Time)
1034
+ @response[:time_elapsed_total] = Time.now - @start_time
1035
+ @start_time = nil
1036
+ else
1037
+ @response[:time_elapsed_total] = nil
1038
+ end
1039
+ if @start_time_net.kind_of?(Time)
1040
+ @response[:time_elapsed] = Time.now - @start_time_net
1041
+ @start_time_net = nil
1042
+ else
1043
+ @response[:time_elapsed] = nil
1044
+ end
1045
+ begin
1046
+ # this is to be able to access all keys as symbols
1047
+ new_resp = Hash.new()
1048
+ resp.each { |key, value|
1049
+ if key.kind_of?(String)
1050
+ new_resp[key.to_sym] = value
1051
+ end
1052
+ }
1053
+ new_resp.each { |key, value|
1054
+ resp[key] = value
1055
+ }
1056
+ rescue
1057
+ end
1058
+ #for mock_responses to be able to add outside of the header like content-type for example
1059
+ if resp.kind_of?(Hash) and !resp.has_key?(:header)
1060
+ resp[:header] = {}
1061
+ end
1062
+ if resp.kind_of?(Hash)
1063
+ resp.each { |k, v|
1064
+ if k != :code and k != :message and k != :data and k != :'set-cookie' and k != :header
1065
+ resp[:header][k] = v
1066
+ end
1067
+ }
1068
+ resp[:header].each { |k, v|
1069
+ resp.delete(k) if resp.has_key?(k)
1070
+ }
1071
+ end
1072
+
1073
+ method_s = caller[0].to_s().scan(/:in `(.*)'/).join
1074
+ if resp.header.kind_of?(Hash) and (resp.header["content-type"].to_s() == "application/x-deflate" or resp.header[:"content-type"].to_s() == "application/x-deflate")
1075
+ data = Zlib::Inflate.inflate(data)
1076
+ end
1077
+ encoding_response = ""
1078
+ if resp.header.kind_of?(Hash) and (resp.header["content-type"].to_s() != "" or resp.header[:"content-type"].to_s() != "")
1079
+ encoding_response = resp.header["content-type"].scan(/;charset=(.*)/i).join if resp.header.has_key?("content-type")
1080
+ encoding_response = resp.header[:"content-type"].scan(/;charset=(.*)/i).join if resp.header.has_key?(:"content-type")
1081
+ end
1082
+ if encoding_response.to_s() == ""
1083
+ encoding_response = "UTF-8"
1084
+ end
1085
+
1086
+ if encoding_response.to_s() != "" and encoding_response.to_s().upcase != "UTF-8"
1087
+ data.encode!("UTF-8", encoding_response.to_s())
1088
+ end
1089
+ if encoding_response != "" and encoding_response.to_s().upcase != "UTF-8"
1090
+ @response[:message] = resp.message.to_s().encode("UTF-8", encoding_response.to_s())
1091
+ #todo: response data in here for example is convert into string, verify if that is correct or needs to maintain the original data type (hash, array...)
1092
+ resp.each { |key, val| @response[key] = val.to_s().encode("UTF-8", encoding_response.to_s()) }
1093
+ else
1094
+ @response[:message] = resp.message
1095
+ resp.each { |key, val| @response[key] = val }
1096
+ end
1097
+ if !defined?(Net::HTTP::Post::Multipart) or (defined?(Net::HTTP::Post::Multipart) and !data.kind_of?(Net::HTTP::Post::Multipart))
1098
+ @response[:data] = data
1099
+ else
1100
+ @response[:data] = ""
1101
+ end
1102
+
1103
+ @response[:code] = resp.code
1104
+
1105
+ unless @response.nil?
1106
+ message = "\nRESPONSE: \n" + @response[:code].to_s() + ":" + @response[:message].to_s()
1107
+ if @debug
1108
+ self.class.last_response = message
1109
+ @response.each { |key, value|
1110
+ if value.to_s() != ""
1111
+ value_orig = value
1112
+ if key.kind_of?(Symbol)
1113
+ if key == :code or key == :data or key == :header or key == :message
1114
+ if key == :data
1115
+ begin
1116
+ JSON.parse(value_orig)
1117
+ data_s = JSON.pretty_generate(JSON.parse(value_orig))
1118
+ rescue
1119
+ data_s = value_orig
1120
+ end
1121
+ self.class.last_response += "\nresponse." + key.to_s() + " = '" + data_s.gsub("<", "&lt;") + "'\n"
1122
+ if value_orig != value
1123
+ message += "\nresponse." + key.to_s() + " = '" + value.gsub("<", "&lt;") + "'\n"
1124
+ else
1125
+ message += "\nresponse." + key.to_s() + " = '" + data_s.gsub("<", "&lt;") + "'\n"
1126
+ end
1127
+ else
1128
+ self.class.last_response += "\nresponse." + key.to_s() + " = '" + value.to_s().gsub("<", "&lt;") + "'"
1129
+ message += "\nresponse." + key.to_s() + " = '" + value.to_s().gsub("<", "&lt;") + "'"
1130
+ end
1131
+ else
1132
+ self.class.last_response += "\nresponse[:" + key.to_s() + "] = '" + value.to_s().gsub("<", "&lt;") + "'"
1133
+ message += "\nresponse[:" + key.to_s() + "] = '" + value.to_s().gsub("<", "&lt;") + "'"
1134
+ end
1135
+ elsif !@response.include?(key.to_sym)
1136
+ self.class.last_response += "\nresponse['" + key.to_s() + "'] = '" + value.to_s().gsub("<", "&lt;") + "'"
1137
+ message += "\nresponse['" + key.to_s() + "'] = '" + value.to_s().gsub("<", "&lt;") + "'"
1138
+ end
1139
+ end
1140
+ }
1141
+ end
1142
+ @logger.info message
1143
+ if @response.kind_of?(Hash)
1144
+ if @response.keys.include?(:requestid)
1145
+ @headers["requestId"] = @response[:requestid]
1146
+ self.class.request_id = @response[:requestid]
1147
+ @logger.info "requestId was found on the response header and it has been added to the headers for the next request"
1148
+ end
1149
+ end
1150
+ end
1151
+
1152
+ if resp[:'set-cookie'].to_s() != ""
1153
+ if resp.kind_of?(Hash) #mock_response
1154
+ cookies_to_set = resp[:'set-cookie'].to_s().split(", ")
1155
+ else #Net::Http
1156
+ cookies_to_set = resp.get_fields("set-cookie")
1157
+ end
1158
+ cookies_to_set.each { |cookie|
1159
+ cookie_pair = cookie.split("; ")[0].split("=")
1160
+ cookie_path = cookie.scan(/; path=([^;]+)/i).join
1161
+ @cookies[cookie_path] = Hash.new() unless @cookies.keys.include?(cookie_path)
1162
+ @cookies[cookie_path][cookie_pair[0]] = cookie_pair[1]
1163
+ }
1164
+
1165
+ @logger.info "set-cookie added to Cookie header as required"
1166
+
1167
+ if @headers.has_key?("X-CSRFToken")
1168
+ csrftoken = resp[:"set-cookie"].to_s().scan(/csrftoken=([\da-z]+);/).join
1169
+ if csrftoken.to_s() != ""
1170
+ @headers["X-CSRFToken"] = csrftoken
1171
+ @logger.info "X-CSRFToken exists on headers and has been overwritten"
1172
+ end
1173
+ else
1174
+ csrftoken = resp[:"set-cookie"].to_s().scan(/csrftoken=([\da-z]+);/).join
1175
+ if csrftoken.to_s() != ""
1176
+ @headers["X-CSRFToken"] = csrftoken
1177
+ @logger.info "X-CSRFToken added to header as required"
1178
+ end
1179
+ end
1180
+ end
1181
+ rescue Exception => stack
1182
+ @logger.fatal stack
1183
+ @logger.fatal "manage_response Error on method #{method_s} "
1184
+ end
1185
+ end
1186
+
1187
+ private :manage_request, :manage_response
1188
+ end