nice_http 1.1.1 → 1.2.0

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