ibm_watson 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ibm_watson
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Max Nussbaum
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-03-13 00:00:00.000000000 Z
11
+ date: 2019-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 4.1.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: ibm_cloud_sdk_core
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.1.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.1.1
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: bundler
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -248,9 +262,7 @@ files:
248
262
  - lib/ibm_watson/assistant_v2.rb
249
263
  - lib/ibm_watson/common.rb
250
264
  - lib/ibm_watson/compare_comply_v1.rb
251
- - lib/ibm_watson/detailed_response.rb
252
265
  - lib/ibm_watson/discovery_v1.rb
253
- - lib/ibm_watson/iam_token_manager.rb
254
266
  - lib/ibm_watson/language_translator_v3.rb
255
267
  - lib/ibm_watson/natural_language_classifier_v1.rb
256
268
  - lib/ibm_watson/natural_language_understanding_v1.rb
@@ -260,8 +272,6 @@ files:
260
272
  - lib/ibm_watson/tone_analyzer_v3.rb
261
273
  - lib/ibm_watson/version.rb
262
274
  - lib/ibm_watson/visual_recognition_v3.rb
263
- - lib/ibm_watson/watson_api_exception.rb
264
- - lib/ibm_watson/watson_service.rb
265
275
  - lib/ibm_watson/websocket/recognize_callback.rb
266
276
  - lib/ibm_watson/websocket/speech_to_text_websocket_listener.rb
267
277
  - rakefile
@@ -285,7 +295,6 @@ files:
285
295
  - test/unit/test_compare_comply_v1.rb
286
296
  - test/unit/test_configure_http_client.rb
287
297
  - test/unit/test_discovery_v1.rb
288
- - test/unit/test_iam_token_manager.rb
289
298
  - test/unit/test_language_translator_v3.rb
290
299
  - test/unit/test_natural_language_classifier_v1.rb
291
300
  - test/unit/test_natural_language_understanding_v1.rb
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require("json")
4
-
5
- # Custom class for objects returned from API calls
6
- class DetailedResponse
7
- attr_reader :status, :headers, :result
8
- def initialize(status: nil, headers: nil, body: nil, response: nil)
9
- if status.nil? || headers.nil? || body.nil?
10
- @status = response.code
11
- @headers = response.headers.to_h
12
- @headers = response.headers.to_hash if response.headers.respond_to?("to_hash")
13
- @result = response.body.to_s
14
- @result = JSON.parse(response.body.to_s) if !response.body.to_s.empty? && @headers.key?("Content-Type") && @headers["Content-Type"].start_with?("application/json")
15
- else
16
- @status = status
17
- @headers = headers
18
- @result = body
19
- end
20
- end
21
- end
@@ -1,152 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require("http")
4
- require("json")
5
- require("rbconfig")
6
- require_relative("./version.rb")
7
-
8
- # Class to manage IAM Token Authentication
9
- class IAMTokenManager
10
- DEFAULT_IAM_URL = "https://iam.bluemix.net/identity/token"
11
- CONTENT_TYPE = "application/x-www-form-urlencoded"
12
- ACCEPT = "application/json"
13
- DEFAULT_AUTHORIZATION = "Basic Yng6Yng="
14
- REQUEST_TOKEN_GRANT_TYPE = "urn:ibm:params:oauth:grant-type:apikey"
15
- REQUEST_TOKEN_RESPONSE_TYPE = "cloud_iam"
16
- REFRESH_TOKEN_GRANT_TYPE = "refresh_token"
17
-
18
- attr_accessor :token_info, :user_access_token
19
- def initialize(iam_apikey: nil, iam_access_token: nil, iam_url: nil)
20
- @iam_apikey = iam_apikey
21
- @user_access_token = iam_access_token
22
- @iam_url = iam_url.nil? ? DEFAULT_IAM_URL : iam_url
23
- @token_info = {
24
- "access_token" => nil,
25
- "refresh_token" => nil,
26
- "token_type" => nil,
27
- "expires_in" => nil,
28
- "expiration" => nil
29
- }
30
- end
31
-
32
- def request(method:, url:, headers: nil, params: nil, data: nil)
33
- user_agent_string = "watson-apis-ruby-sdk-" + IBMWatson::VERSION
34
- user_agent_string += " #{RbConfig::CONFIG["host"]}"
35
- user_agent_string += " #{RbConfig::CONFIG["RUBY_BASE_NAME"]}-#{RbConfig::CONFIG["RUBY_PROGRAM_VERSION"]}"
36
- headers["User-Agent"] = user_agent_string
37
- response = nil
38
- if headers.key?("Content-Type") && headers["Content-Type"] == CONTENT_TYPE
39
- response = HTTP.request(
40
- method,
41
- url,
42
- body: HTTP::URI.form_encode(data),
43
- headers: headers,
44
- params: params
45
- )
46
- end
47
- return JSON.parse(response.body.to_s) if (200..299).cover?(response.code)
48
-
49
- require_relative("./watson_api_exception.rb")
50
- raise WatsonApiException.new(response: response)
51
- end
52
-
53
- # The source of the token is determined by the following logic:
54
- # 1. If user provides their own managed access token, assume it is valid and send it
55
- # 2. If this class is managing tokens and does not yet have one, make a request for one
56
- # 3. If this class is managing tokens and the token has expired refresh it. In case the refresh token is expired, get a new one
57
- # If this class is managing tokens and has a valid token stored, send it
58
- def token
59
- return @user_access_token unless @user_access_token.nil? || (@user_access_token.respond_to?(:empty?) && @user_access_token.empty?)
60
-
61
- if @token_info.all? { |_k, v| v.nil? }
62
- token_info = request_token
63
- save_token_info(
64
- token_info: token_info
65
- )
66
- return @token_info["access_token"]
67
- elsif token_expired?
68
- token_info = refresh_token_expired? ? request_token : refresh_token
69
- save_token_info(
70
- token_info: token_info
71
- )
72
- return @token_info["access_token"]
73
- else
74
- @token_info["access_token"]
75
- end
76
- end
77
-
78
- private
79
-
80
- # Request an IAM token using an API key
81
- def request_token
82
- headers = {
83
- "Content-Type" => CONTENT_TYPE,
84
- "Authorization" => DEFAULT_AUTHORIZATION,
85
- "Accept" => ACCEPT
86
- }
87
- data = {
88
- "grant_type" => REQUEST_TOKEN_GRANT_TYPE,
89
- "apikey" => @iam_apikey,
90
- "response_type" => REQUEST_TOKEN_RESPONSE_TYPE
91
- }
92
- response = request(
93
- method: "POST",
94
- url: @iam_url,
95
- headers: headers,
96
- data: data
97
- )
98
- response
99
- end
100
-
101
- # Refresh an IAM token using a refresh token
102
- def refresh_token
103
- headers = {
104
- "Content-Type" => CONTENT_TYPE,
105
- "Authorization" => DEFAULT_AUTHORIZATION,
106
- "accept" => ACCEPT
107
- }
108
- data = {
109
- "grant_type" => REFRESH_TOKEN_GRANT_TYPE,
110
- "refresh_token" => @token_info["refresh_token"]
111
- }
112
- response = request(
113
- method: "POST",
114
- url: @iam_url,
115
- headers: headers,
116
- data: data
117
- )
118
- response
119
- end
120
-
121
- # Check if currently stored token is expired.
122
- # Using a buffer to prevent the edge case of the
123
- # token expiring before the request could be made.
124
- # The buffer will be a fraction of the total TTL. Using 80%.
125
- def token_expired?
126
- return true if @token_info["expiration"].nil? || @token_info["expires_in"].nil?
127
-
128
- fraction_of_ttl = 0.8
129
- time_to_live = @token_info["expires_in"].nil? ? 0 : @token_info["expires_in"]
130
- expire_time = @token_info["expiration"].nil? ? 0 : @token_info["expiration"]
131
- refresh_time = expire_time - (time_to_live * (1.0 - fraction_of_ttl))
132
- current_time = Time.now.to_i
133
- refresh_time < current_time
134
- end
135
-
136
- # Used as a fail-safe to prevent the condition of a refresh token expiring,
137
- # which could happen after around 30 days. This function will return true
138
- # if it has been at least 7 days and 1 hour since the last token was set
139
- def refresh_token_expired?
140
- return true if @token_info["expiration"].nil?
141
-
142
- seven_days = 7 * 24 * 3600
143
- current_time = Time.now.to_i
144
- new_token_time = @token_info["expiration"] + seven_days
145
- new_token_time < current_time
146
- end
147
-
148
- # Save the response from the IAM service request to the object's state
149
- def save_token_info(token_info:)
150
- @token_info = token_info
151
- end
152
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require("json")
4
- # Custom exception class for errors returned from Watson APIs
5
- class WatsonApiException < StandardError
6
- attr_reader :code, :error, :info, :transaction_id, :global_transaction_id
7
- # :param HTTP::Response response: The response object from the Watson API
8
- def initialize(code: nil, error: nil, info: nil, transaction_id: nil, global_transaction_id: nil, response: nil)
9
- if code.nil? || error.nil?
10
- @code = response.code
11
- @error = response.reason
12
- unless response.body.empty?
13
- body_hash = JSON.parse(response.body.to_s)
14
- @code = body_hash["code"] || body_hash["error_code"] || body_hash["status"]
15
- @error = body_hash["error"] || body_hash["error_message"] || body_hash["statusInfo"] || body_hash["description"]
16
- %w[code error_code status error error_message statusInfo description].each { |k| body_hash.delete(k) }
17
- @info = body_hash
18
- end
19
- @transaction_id = transaction_id
20
- @global_transaction_id = global_transaction_id
21
- @transaction_id = response.headers["X-DP-Watson-Tran-ID"] if response.headers.include?("X-DP-Watson-Tran-ID")
22
- @global_transaction_id = response.headers["X-Global-Transaction-ID"] if response.headers.include?("X-Global-Transaction-ID")
23
- else
24
- # :nocov:
25
- @code = code
26
- @error = error
27
- @info = info
28
- @transaction_id = transaction_id
29
- @global_transaction_id = global_transaction_id
30
- # :nocov:
31
- end
32
- end
33
-
34
- def to_s
35
- msg = "Error: #{@error}, Code: #{@code}"
36
- msg += ", Information: #{@info}" unless @info.nil?
37
- msg += ", X-dp-watson-tran-id: #{@transaction_id}" unless @transaction_id.nil?
38
- msg += ", X-global-transaction-id: #{@global_transaction_id}" unless @global_transaction_id.nil?
39
- msg
40
- end
41
- end
@@ -1,285 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require("http")
4
- require("rbconfig")
5
- require("stringio")
6
- require("json")
7
- require_relative("./detailed_response.rb")
8
- require_relative("./watson_api_exception.rb")
9
- require_relative("./iam_token_manager.rb")
10
- require_relative("./version.rb")
11
- require_relative("./common.rb")
12
- # require("httplog")
13
- # HttpLog.configure do |config|
14
- # config.log_connect = true
15
- # config.log_request = true
16
- # config.log_headers = true
17
- # config.log_data = true
18
- # config.log_status = true
19
- # config.log_response = true
20
- # end
21
-
22
- DEFAULT_CREDENTIALS_FILE_NAME = "ibm-credentials.env"
23
- NORMALIZER = lambda do |uri| # Custom URI normalizer when using HTTP Client
24
- HTTP::URI.parse uri
25
- end
26
-
27
- # Class for interacting with the Watson API
28
- class WatsonService
29
- attr_accessor :password, :url, :username, :display_name
30
- attr_reader :conn, :token_manager
31
- def initialize(vars)
32
- defaults = {
33
- vcap_services_name: nil,
34
- username: nil,
35
- password: nil,
36
- use_vcap_services: true,
37
- iam_apikey: nil,
38
- iam_access_token: nil,
39
- iam_url: nil,
40
- display_name: nil
41
- }
42
- vars = defaults.merge(vars)
43
- @url = vars[:url]
44
- @username = nil
45
- @password = nil
46
- @iam_apikey = nil
47
- @token_manager = nil
48
- @temp_headers = nil
49
- @icp_prefix = vars[:password]&.start_with?("icp-") ? true : false
50
- @disable_ssl = false
51
- @display_name = vars[:display_name]
52
-
53
- if !vars[:iam_access_token].nil? || !vars[:iam_apikey].nil?
54
- set_token_manager(iam_apikey: vars[:iam_apikey], iam_access_token: vars[:iam_access_token], iam_url: vars[:iam_url])
55
- elsif !vars[:username].nil? && !vars[:password].nil?
56
- if vars[:username] == "apikey" && !@icp_prefix
57
- iam_apikey(iam_apikey: vars[:password])
58
- else
59
- @username = vars[:username]
60
- @password = vars[:password]
61
- end
62
- end
63
-
64
- if @display_name && !@username && !@iam_apikey
65
- service_name = @display_name.sub(" ", "_").downcase
66
- load_from_credential_file(service_name)
67
- end
68
-
69
- if vars[:use_vcap_services] && !@username && !@iam_apikey
70
- @vcap_service_credentials = load_from_vcap_services(service_name: vars[:vcap_services_name])
71
- if !@vcap_service_credentials.nil? && @vcap_service_credentials.instance_of?(Hash)
72
- @url = @vcap_service_credentials["url"]
73
- @username = @vcap_service_credentials["username"] if @vcap_service_credentials.key?("username")
74
- @password = @vcap_service_credentials["password"] if @vcap_service_credentials.key?("password")
75
- @iam_apikey = @vcap_service_credentials["iam_apikey"] if @vcap_service_credentials.key?("iam_apikey")
76
- @iam_access_token = @vcap_service_credentials["iam_access_token"] if @vcap_service_credentials.key?("iam_access_token")
77
- @iam_url = @vcap_service_credentials["iam_url"] if @vcap_service_credentials.key?("iam_url")
78
- end
79
- end
80
-
81
- raise ArgumentError.new('The username shouldn\'t start or end with curly brackets or quotes. Be sure to remove any {} and \" characters surrounding your username') if check_bad_first_or_last_char(@username)
82
- raise ArgumentError.new('The password shouldn\'t start or end with curly brackets or quotes. Be sure to remove any {} and \" characters surrounding your password') if check_bad_first_or_last_char(@password)
83
- raise ArgumentError.new('The url shouldn\'t start or end with curly brackets or quotes. Be sure to remove any {} and \" characters surrounding your url') if check_bad_first_or_last_char(@url)
84
- raise ArgumentError.new('The apikey shouldn\'t start or end with curly brackets or quotes. Be sure to remove any {} and \" characters surrounding your apikey') if check_bad_first_or_last_char(@iam_apikey)
85
-
86
- @conn = HTTP::Client.new(
87
- headers: {}
88
- ).use normalize_uri: { normalizer: NORMALIZER }
89
- end
90
-
91
- # Initiates the credentials based on the credential file
92
- def load_from_credential_file(service_name, separator = "=")
93
- credential_file_path = ENV["IBM_CREDENTIALS_FILE"]
94
-
95
- # Home directory
96
- if credential_file_path.nil?
97
- file_path = ENV["HOME"] + DEFAULT_CREDENTIALS_FILE_NAME
98
- credential_file_path = file_path if File.exist?(file_path)
99
- end
100
-
101
- # Top-level directory of the project
102
- if credential_file_path.nil?
103
- file_path = File.join(File.dirname(__FILE__), "/../../" + DEFAULT_CREDENTIALS_FILE_NAME)
104
- credential_file_path = file_path if File.exist?(file_path)
105
- end
106
-
107
- return if credential_file_path.nil?
108
-
109
- file_contents = File.open(credential_file_path, "r")
110
- file_contents.each_line do |line|
111
- key_val = line.strip.split(separator)
112
- set_credential_based_on_type(service_name, key_val[0].downcase, key_val[1].downcase) if key_val.length == 2
113
- end
114
- end
115
-
116
- def load_from_vcap_services(service_name:)
117
- vcap_services = ENV["VCAP_SERVICES"]
118
- unless vcap_services.nil?
119
- services = JSON.parse(vcap_services)
120
- return services[service_name][0]["credentials"] if services.key?(service_name)
121
- end
122
- nil
123
- end
124
-
125
- def add_default_headers(headers: {})
126
- raise TypeError unless headers.instance_of?(Hash)
127
-
128
- headers.each_pair { |k, v| @conn.default_options.headers.add(k, v) }
129
- end
130
-
131
- def iam_access_token(iam_access_token:)
132
- @token_manager = IAMTokenManager.new(iam_access_token: iam_access_token) if @token_manager.nil?
133
- @iam_access_token = iam_access_token
134
- end
135
-
136
- def iam_apikey(iam_apikey:)
137
- @token_manager = IAMTokenManager.new(iam_apikey: iam_apikey) if @token_manager.nil?
138
- @iam_apikey = iam_apikey
139
- end
140
-
141
- # @return [DetailedResponse]
142
- def request(args)
143
- defaults = { method: nil, url: nil, accept_json: false, headers: nil, params: nil, json: {}, data: nil }
144
- args = defaults.merge(args)
145
- args[:data].delete_if { |_k, v| v.nil? } if args[:data].instance_of?(Hash)
146
- args[:json] = args[:data].merge(args[:json]) if args[:data].respond_to?(:merge)
147
- args[:json] = args[:data] if args[:json].empty? || (args[:data].instance_of?(String) && !args[:data].empty?)
148
- args[:json].delete_if { |_k, v| v.nil? } if args[:json].instance_of?(Hash)
149
- args[:headers]["Accept"] = "application/json" if args[:accept_json] && args[:headers]["Accept"].nil?
150
- args[:headers]["Content-Type"] = "application/json" unless args[:headers].key?("Content-Type")
151
- args[:json] = args[:json].to_json if args[:json].instance_of?(Hash)
152
- args[:headers].delete_if { |_k, v| v.nil? } if args[:headers].instance_of?(Hash)
153
- args[:params].delete_if { |_k, v| v.nil? } if args[:params].instance_of?(Hash)
154
- args[:form].delete_if { |_k, v| v.nil? } if args.key?(:form)
155
- args.delete_if { |_, v| v.nil? }
156
- args[:headers].delete("Content-Type") if args.key?(:form) || args[:json].nil?
157
-
158
- if @username == "apikey" && !@icp_prefix
159
- iam_apikey(iam_apikey: @password)
160
- @username = nil
161
- end
162
-
163
- conn = @conn
164
- if !@token_manager.nil?
165
- access_token = @token_manager.token
166
- args[:headers]["Authorization"] = "Bearer #{access_token}"
167
- elsif !@username.nil? && !@password.nil?
168
- conn = @conn.basic_auth(user: @username, pass: @password)
169
- end
170
-
171
- args[:headers] = args[:headers].merge(@temp_headers) unless @temp_headers.nil?
172
- @temp_headers = nil unless @temp_headers.nil?
173
-
174
- if args.key?(:form)
175
- response = conn.follow.request(
176
- args[:method],
177
- HTTP::URI.parse(@url + args[:url]),
178
- headers: conn.default_options.headers.merge(HTTP::Headers.coerce(args[:headers])),
179
- params: args[:params],
180
- form: args[:form]
181
- )
182
- else
183
- response = conn.follow.request(
184
- args[:method],
185
- HTTP::URI.parse(@url + args[:url]),
186
- headers: conn.default_options.headers.merge(HTTP::Headers.coerce(args[:headers])),
187
- body: args[:json],
188
- params: args[:params]
189
- )
190
- end
191
- return DetailedResponse.new(response: response) if (200..299).cover?(response.code)
192
-
193
- raise WatsonApiException.new(response: response)
194
- end
195
-
196
- # @note Chainable
197
- # @param headers [Hash] Custom headers to be sent with the request
198
- # @return [self]
199
- def headers(headers)
200
- raise TypeError("Expected Hash type, received #{headers.class}") unless headers.instance_of?(Hash)
201
-
202
- @temp_headers = headers
203
- self
204
- end
205
-
206
- # @!method configure_http_client(proxy: {}, timeout: {}, disable_ssl: false)
207
- # Sets the http client config, currently works with timeout and proxies
208
- # @param proxy [Hash] The hash of proxy configurations
209
- # @option proxy address [String] The address of the proxy
210
- # @option proxy port [Integer] The port of the proxy
211
- # @option proxy username [String] The username of the proxy, if authentication is needed
212
- # @option proxy password [String] The password of the proxy, if authentication is needed
213
- # @option proxy headers [Hash] The headers to be used with the proxy
214
- # @param timeout [Hash] The hash for configuring timeouts. `per_operation` has priority over `global`
215
- # @option timeout per_operation [Hash] Timeouts per operation. Requires `read`, `write`, `connect`
216
- # @option timeout global [Integer] Upper bound on total request time
217
- # @param disable_ssl [Boolean] Disable the SSL verification (Note that this has serious security implications - only do this if you really mean to!)
218
- def configure_http_client(proxy: {}, timeout: {}, disable_ssl: false)
219
- raise TypeError("proxy parameter must be a Hash") unless proxy.empty? || proxy.instance_of?(Hash)
220
-
221
- raise TypeError("timeout parameter must be a Hash") unless timeout.empty? || timeout.instance_of?(Hash)
222
-
223
- @disable_ssl = disable_ssl
224
- if disable_ssl
225
- ssl_context = OpenSSL::SSL::SSLContext.new
226
- ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
227
- @conn.default_options = { ssl_context: ssl_context }
228
- end
229
- add_proxy(proxy) unless proxy.empty? || !proxy.dig(:address).is_a?(String) || !proxy.dig(:port).is_a?(Integer)
230
- add_timeout(timeout) unless timeout.empty? || (!timeout.key?(:per_operation) && !timeout.key?(:global))
231
- end
232
-
233
- private
234
-
235
- def set_credential_based_on_type(service_name, key, value)
236
- return unless key.include?(service_name)
237
-
238
- @iam_apikey = value if key.include?("iam_apikey")
239
- @iam_url = value if key.include?("iam_url")
240
- @url = value if key.include?("url")
241
- @username = value if key.include?("username")
242
- @password = value if key.include?("password")
243
- end
244
-
245
- def check_bad_first_or_last_char(str)
246
- return str.start_with?("{", "\"") || str.end_with?("}", "\"") unless str.nil?
247
- end
248
-
249
- def set_token_manager(iam_apikey: nil, iam_access_token: nil, iam_url: nil)
250
- @iam_apikey = iam_apikey
251
- @iam_access_token = iam_access_token
252
- @iam_url = iam_url
253
- @token_manager = IAMTokenManager.new(iam_apikey: iam_apikey, iam_access_token: iam_access_token, iam_url: iam_url)
254
- end
255
-
256
- def add_timeout(timeout)
257
- if timeout.key?(:per_operation)
258
- raise TypeError("per_operation in timeout must be a Hash") unless timeout[:per_operation].instance_of?(Hash)
259
-
260
- defaults = {
261
- write: 0,
262
- connect: 0,
263
- read: 0
264
- }
265
- time = defaults.merge(timeout[:per_operation])
266
- @conn = @conn.timeout(write: time[:write], connect: time[:connect], read: time[:read])
267
- else
268
- raise TypeError("global in timeout must be an Integer") unless timeout[:global].is_a?(Integer)
269
-
270
- @conn = @conn.timeout(timeout[:global])
271
- end
272
- end
273
-
274
- def add_proxy(proxy)
275
- if (proxy[:username].nil? || proxy[:password].nil?) && proxy[:headers].nil?
276
- @conn = @conn.via(proxy[:address], proxy[:port])
277
- elsif !proxy[:username].nil? && !proxy[:password].nil? && proxy[:headers].nil?
278
- @conn = @conn.via(proxy[:address], proxy[:port], proxy[:username], proxy[:password])
279
- elsif !proxy[:headers].nil? && (proxy[:username].nil? || proxy[:password].nil?)
280
- @conn = @conn.via(proxy[:address], proxy[:port], proxy[:headers])
281
- else
282
- @conn = @conn.via(proxy[:address], proxy[:port], proxy[:username], proxy[:password], proxy[:headers])
283
- end
284
- end
285
- end