nice_http 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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