aspera-cli 4.4.0 → 4.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2095 -1503
  3. data/bin/ascli +2 -1
  4. data/bin/asession +4 -5
  5. data/docs/test_env.conf +3 -0
  6. data/examples/aoc.rb +4 -3
  7. data/examples/faspex4.rb +25 -25
  8. data/examples/proxy.pac +1 -1
  9. data/examples/transfer.rb +17 -17
  10. data/lib/aspera/aoc.rb +238 -185
  11. data/lib/aspera/ascmd.rb +93 -83
  12. data/lib/aspera/ats_api.rb +11 -10
  13. data/lib/aspera/cli/basic_auth_plugin.rb +13 -14
  14. data/lib/aspera/cli/extended_value.rb +42 -33
  15. data/lib/aspera/cli/formater.rb +142 -108
  16. data/lib/aspera/cli/info.rb +17 -0
  17. data/lib/aspera/cli/listener/line_dump.rb +3 -2
  18. data/lib/aspera/cli/listener/logger.rb +2 -1
  19. data/lib/aspera/cli/listener/progress.rb +16 -18
  20. data/lib/aspera/cli/listener/progress_multi.rb +18 -21
  21. data/lib/aspera/cli/main.rb +173 -149
  22. data/lib/aspera/cli/manager.rb +163 -168
  23. data/lib/aspera/cli/plugin.rb +43 -31
  24. data/lib/aspera/cli/plugins/alee.rb +6 -6
  25. data/lib/aspera/cli/plugins/aoc.rb +405 -370
  26. data/lib/aspera/cli/plugins/ats.rb +86 -79
  27. data/lib/aspera/cli/plugins/bss.rb +14 -16
  28. data/lib/aspera/cli/plugins/config.rb +580 -362
  29. data/lib/aspera/cli/plugins/console.rb +23 -19
  30. data/lib/aspera/cli/plugins/cos.rb +18 -18
  31. data/lib/aspera/cli/plugins/faspex.rb +201 -158
  32. data/lib/aspera/cli/plugins/faspex5.rb +80 -57
  33. data/lib/aspera/cli/plugins/node.rb +183 -166
  34. data/lib/aspera/cli/plugins/orchestrator.rb +71 -67
  35. data/lib/aspera/cli/plugins/preview.rb +92 -96
  36. data/lib/aspera/cli/plugins/server.rb +79 -75
  37. data/lib/aspera/cli/plugins/shares.rb +35 -19
  38. data/lib/aspera/cli/plugins/sync.rb +20 -22
  39. data/lib/aspera/cli/transfer_agent.rb +76 -113
  40. data/lib/aspera/cli/version.rb +2 -1
  41. data/lib/aspera/colors.rb +35 -27
  42. data/lib/aspera/command_line_builder.rb +48 -34
  43. data/lib/aspera/cos_node.rb +29 -21
  44. data/lib/aspera/data_repository.rb +3 -2
  45. data/lib/aspera/environment.rb +50 -45
  46. data/lib/aspera/fasp/{manager.rb → agent_base.rb} +28 -25
  47. data/lib/aspera/fasp/{connect.rb → agent_connect.rb} +52 -43
  48. data/lib/aspera/fasp/{local.rb → agent_direct.rb} +58 -72
  49. data/lib/aspera/fasp/{http_gw.rb → agent_httpgw.rb} +37 -43
  50. data/lib/aspera/fasp/{node.rb → agent_node.rb} +35 -16
  51. data/lib/aspera/fasp/agent_trsdk.rb +104 -0
  52. data/lib/aspera/fasp/error.rb +2 -1
  53. data/lib/aspera/fasp/error_info.rb +68 -52
  54. data/lib/aspera/fasp/installation.rb +152 -124
  55. data/lib/aspera/fasp/listener.rb +1 -0
  56. data/lib/aspera/fasp/parameters.rb +87 -92
  57. data/lib/aspera/fasp/parameters.yaml +305 -249
  58. data/lib/aspera/fasp/resume_policy.rb +11 -14
  59. data/lib/aspera/fasp/transfer_spec.rb +26 -0
  60. data/lib/aspera/fasp/uri.rb +22 -21
  61. data/lib/aspera/faspex_gw.rb +55 -89
  62. data/lib/aspera/hash_ext.rb +4 -3
  63. data/lib/aspera/id_generator.rb +8 -7
  64. data/lib/aspera/keychain/encrypted_hash.rb +121 -0
  65. data/lib/aspera/keychain/macos_security.rb +90 -0
  66. data/lib/aspera/log.rb +55 -37
  67. data/lib/aspera/nagios.rb +13 -12
  68. data/lib/aspera/node.rb +30 -25
  69. data/lib/aspera/oauth.rb +175 -226
  70. data/lib/aspera/open_application.rb +4 -3
  71. data/lib/aspera/persistency_action_once.rb +6 -6
  72. data/lib/aspera/persistency_folder.rb +5 -9
  73. data/lib/aspera/preview/file_types.rb +6 -5
  74. data/lib/aspera/preview/generator.rb +25 -24
  75. data/lib/aspera/preview/options.rb +16 -14
  76. data/lib/aspera/preview/utils.rb +98 -98
  77. data/lib/aspera/{proxy_auto_config.erb.js → proxy_auto_config.js} +23 -31
  78. data/lib/aspera/proxy_auto_config.rb +111 -20
  79. data/lib/aspera/rest.rb +154 -135
  80. data/lib/aspera/rest_call_error.rb +2 -2
  81. data/lib/aspera/rest_error_analyzer.rb +23 -25
  82. data/lib/aspera/rest_errors_aspera.rb +15 -14
  83. data/lib/aspera/ssh.rb +12 -10
  84. data/lib/aspera/sync.rb +42 -41
  85. data/lib/aspera/temp_file_manager.rb +18 -14
  86. data/lib/aspera/timer_limiter.rb +2 -1
  87. data/lib/aspera/uri_reader.rb +7 -5
  88. data/lib/aspera/web_auth.rb +79 -76
  89. metadata +116 -29
  90. data/docs/Makefile +0 -66
  91. data/docs/README.erb.md +0 -3973
  92. data/docs/README.md +0 -13
  93. data/docs/diagrams.txt +0 -49
  94. data/docs/doc_tools.rb +0 -58
  95. data/lib/aspera/api_detector.rb +0 -60
  96. data/lib/aspera/cli/plugins/shares2.rb +0 -114
  97. data/lib/aspera/secrets.rb +0 -20
@@ -1,34 +1,125 @@
1
+ # frozen_string_literal: true
1
2
  require 'uri'
2
3
  require 'resolv'
3
- require 'erb'
4
+
5
+ module URI
6
+ class Generic
7
+ # save original method that finds proxy in URI::Generic, it uses env var http_proxy
8
+ alias :find_proxy_orig find_proxy
9
+ def self.register_proxy_finder(&block)
10
+ # overload the method
11
+ define_method(:find_proxy) {|envars=ENV| block.call(to_s) || find_proxy_orig(envars)}
12
+ end
13
+ end
14
+ end
4
15
 
5
16
  module Aspera
6
- # evaluate a proxy autoconfig script
17
+ # Evaluate a proxy autoconfig script
7
18
  class ProxyAutoConfig
8
19
  # template file is read once, it contains functions that can be used in a proxy autoconf script
9
- PAC_FUNC_TEMPLATE=File.read(__FILE__.gsub(/\.rb$/,'.erb.js'))
10
- private_constant :PAC_FUNC_TEMPLATE
11
- # @param proxy_auto_config the proxy auto config script to be evaluated
12
- def initialize(proxy_auto_config)
13
- @proxy_auto_config=proxy_auto_config
14
- end
20
+ # it is similar to mozilla ascii_pac_utils.inc
21
+ PAC_FUNCTIONS_FILE=__FILE__.gsub(/\.rb$/,'.js').freeze
22
+ PAC_MAIN_FUNCTION='FindProxyForURL'
23
+ private_constant :PAC_FUNCTIONS_FILE,:PAC_MAIN_FUNCTION
24
+
25
+ private
15
26
 
16
- # execut proxy auto config script for the given URL
17
- def get_proxy(service_url)
18
- # require at runtime, in case there is no js engine
19
- require 'execjs'
20
- # variables starting with "context_" are replaced in the ERB template file
21
- # I did not find an easy way for the javascript to callback ruby
22
- # and anyway, it only needs to get DNS translation
27
+ # variables starting with "context_" are replaced in the ERB template file
28
+ # I did not find an easy way for the javascript to callback ruby
29
+ # and anyway, it only needs to get DNS translation
30
+ def pac_dns_functions(context_host)
23
31
  context_self='127.0.0.1'
24
- context_host=URI.parse(service_url).host
25
32
  context_ip=nil
26
33
  Resolv::DNS.open{|dns|dns.each_address(context_host){|r_addr|context_ip=r_addr.to_s if r_addr.is_a?(Resolv::IPv4)}}
27
34
  raise "DNS name not found: #{context_host}" if context_ip.nil?
28
- pac_functions=ERB.new(PAC_FUNC_TEMPLATE).result(binding)
29
- context = ExecJS.compile(pac_functions+@proxy_auto_config)
30
- return context.call("FindProxyForURL", service_url, context_host)
35
+ # NOTE: Javascript code here with string inclusions
36
+ javascript=<<END_OF_JAVASCRIPT
37
+ function dnsResolve(host) {
38
+ if (host == '#{context_host}' || host == '#{context_ip}')
39
+ return '#{context_ip}';
40
+ throw 'only DNS for initial host, not ' + host;
41
+ }
42
+ function myIpAddress() {
43
+ return '#{context_self}';
44
+ }
45
+ END_OF_JAVASCRIPT
46
+ return javascript
47
+ end
48
+
49
+ public
50
+
51
+ # @param proxy_auto_config the proxy auto config script to be evaluated
52
+ def initialize(proxy_auto_config)
53
+ # user provided javascript with FindProxyForURL function
54
+ @proxy_auto_config=proxy_auto_config
55
+ # avoid multiple execution, this does not support load balancing
56
+ @cache={}
57
+ @pac_functions=nil
58
+ end
59
+
60
+ def register_uri_generic
61
+ URI::Generic.register_proxy_finder{|url_str|get_proxies(url_str).first}
62
+ # allow chaining
63
+ return self
64
+ end
65
+
66
+ # execute proxy auto config script for the given URL : https://en.wikipedia.org/wiki/Proxy_auto-config
67
+ # @return either nil, or a String formated following PAC standard
68
+ def find_proxy_for_url(service_url)
69
+ uri=URI.parse(service_url)
70
+ simple_url="#{uri.scheme}://#{uri.host}"
71
+ if !@cache.has_key?(simple_url)
72
+ Log.log.debug("PAC: starting javascript for #{service_url}")
73
+ # require at runtime, in case there is no js engine
74
+ require 'execjs'
75
+ # read template lib
76
+ @pac_functions=File.read(PAC_FUNCTIONS_FILE).freeze if @pac_functions.nil?
77
+ # to be executed is dns + utils + user function
78
+ js_to_execute="#{pac_dns_functions(uri.host)}#{@pac_functions}#{@proxy_auto_config}"
79
+ executable_js = ExecJS.compile(js_to_execute)
80
+ @cache[simple_url]=executable_js.call(PAC_MAIN_FUNCTION, simple_url, uri.host)
81
+ Log.log.debug("PAC: result: #{@cache[simple_url]}")
82
+ end
83
+ return @cache[simple_url]
84
+ end
85
+
86
+ # used to replace URI::Generic.find_proxy
87
+ # @return Array of URI, possibly empty
88
+ def get_proxies(service_url)
89
+ # prepare result
90
+ uri_list=[]
91
+ # execute PAC script
92
+ proxy_list_str=find_proxy_for_url(service_url)
93
+ if !proxy_list_str.is_a?(String)
94
+ Log.log.warn("PAC: did not return a String, returned #{proxy_list_str.class}")
95
+ return uri_list
96
+ end
97
+ proxy_list_str.strip!
98
+ proxy_list_str.gsub!(/\s+/,' ')
99
+ proxy_list_str.split(';').each do |item|
100
+ # strip and split by space
101
+ parts=item.strip.split
102
+ case parts.shift
103
+ when 'DIRECT'
104
+ raise 'DIRECT has no param' unless parts.empty?
105
+ when 'PROXY'
106
+ addr_port=parts.shift
107
+ raise 'PROXY shall have one param' unless addr_port.is_a?(String) && parts.empty?
108
+ begin
109
+ # PAC proxy addresses are <host>:<port>
110
+ if addr_port.match(/:[0-9]+$/)
111
+ # we want to return URIs, so add dummy scheme
112
+ uri_list.push(URI.parse("proxy://#{addr_port}"))
113
+ else
114
+ Log.log.warn("PAC: PROXY must be <address>:<port>, ignoring #{addr_port}")
115
+ end
116
+ rescue StandardError
117
+ Log.log.warn("PAC: cannot parse #{addr_port}")
118
+ end
119
+ else Log.log.warn("PAC: ignoring proxy type #{parts.first}: not supported")
120
+ end
121
+ end
122
+ return uri_list
31
123
  end
32
124
  end
33
125
  end
34
-
data/lib/aspera/rest.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'aspera/log'
2
3
  require 'aspera/oauth'
3
4
  require 'aspera/rest_error_analyzer'
@@ -28,52 +29,52 @@ module Aspera
28
29
  # rest call errors are raised as exception RestCallError
29
30
  # and error are analyzed in RestErrorAnalyzer
30
31
  class Rest
31
- private
32
- # set to true enables debug in HTTP class
33
- @@debug=false
34
- # true if https ignore certificate
35
- @@insecure=false
36
- @@user_agent='Ruby'
37
- @@download_partial_suffix='.http_partial'
38
-
39
- public
40
- def self.insecure=(v); @@insecure=v;Log.log.debug("insecure => #{@@insecure}".red);end
41
-
42
- def self.insecure; @@insecure;end
43
-
44
- def self.user_agent=(v); @@user_agent=v;Log.log.debug("user_agent => #{@@user_agent}".red);end
32
+ # global settings also valid for any subclass
33
+ @@global={ # rubocop:disable Style/ClassVars
34
+ debug: false,
35
+ # true if https ignore certificate
36
+ insecure: false,
37
+ user_agent: 'Ruby',
38
+ download_partial_suffix: '.http_partial',
39
+ # a lambda which takes the Net::HTTP as arg, use this to change parameters
40
+ session_cb: nil
41
+ }
45
42
 
46
- def self.user_agent; @@user_agent;end
47
-
48
- def self.debug=(flag); @@debug=flag; Log.log.debug("debug http => #{flag}"); end
43
+ class<<self
44
+ # define accessors
45
+ @@global.keys.each do |p|
46
+ define_method(p){@@global[p]}
47
+ define_method("#{p}="){|val|Log.log.debug("#{p} => #{val}".red);@@global[p]=val}
48
+ end
49
49
 
50
- def self.basic_creds(user,pass); return "Basic #{Base64.strict_encode64("#{user}:#{pass}")}";end
50
+ def basic_creds(user,pass); return "Basic #{Base64.strict_encode64("#{user}:#{pass}")}";end
51
51
 
52
- # build URI from URL and parameters and check it is http or https
53
- def self.build_uri(url,params=nil)
54
- uri=URI.parse(url)
55
- raise "REST endpoint shall be http(s)" unless ['http','https'].include?(uri.scheme)
56
- if !params.nil?
57
- # support array url params, there is no standard. Either p[]=1&p[]=2, or p=1&p=2
58
- if params.is_a?(Hash)
59
- orig=params
60
- params=[]
61
- orig.each do |k,v|
62
- case v
63
- when Array
64
- suffix=v.first.eql?('[]') ? v.shift : ''
65
- v.each do |e|
66
- params.push([k+suffix,e])
52
+ # build URI from URL and parameters and check it is http or https
53
+ def build_uri(url,params=nil)
54
+ uri=URI.parse(url)
55
+ raise 'REST endpoint shall be http(s)' unless ['http','https'].include?(uri.scheme)
56
+ if !params.nil?
57
+ # support array url params, there is no standard. Either p[]=1&p[]=2, or p=1&p=2
58
+ if params.is_a?(Hash)
59
+ orig=params
60
+ params=[]
61
+ orig.each do |k,v|
62
+ case v
63
+ when Array
64
+ suffix=v.first.eql?('[]') ? v.shift : ''
65
+ v.each do |e|
66
+ params.push([k+suffix,e])
67
+ end
68
+ else
69
+ params.push([k,v])
67
70
  end
68
- else
69
- params.push([k,v])
70
71
  end
71
72
  end
73
+ # CGI.unescape to transform back %5D into []
74
+ uri.query=CGI.unescape(URI.encode_www_form(params))
72
75
  end
73
- # CGI.unescape to transform back %5D into []
74
- uri.query=CGI.unescape(URI.encode_www_form(params))
76
+ return uri
75
77
  end
76
- return uri
77
78
  end
78
79
 
79
80
  private
@@ -85,12 +86,10 @@ module Aspera
85
86
  # this honors http_proxy env var
86
87
  @http_session=Net::HTTP.new(uri.host, uri.port)
87
88
  @http_session.use_ssl = uri.scheme.eql?('https')
88
- Log.log.debug("insecure=#{@@insecure}")
89
- @http_session.verify_mode = OpenSSL::SSL::VERIFY_NONE if @@insecure
90
- @http_session.set_debug_output($stdout) if @@debug
91
- if @params.has_key?(:session_cb)
92
- @params[:session_cb].call(@http_session)
93
- end
89
+ @http_session.verify_mode = OpenSSL::SSL::VERIFY_NONE if self.class.insecure
90
+ @http_session.set_debug_output($stdout) if self.class.debug
91
+ # set http options in callback, such as timeout and cert. verification
92
+ self.class.session_cb.call(@http_session) unless self.class.session_cb.nil?
94
93
  # manually start session for keep alive (if supported by server, else, session is closed every time)
95
94
  @http_session.start
96
95
  end
@@ -100,33 +99,72 @@ module Aspera
100
99
  public
101
100
 
102
101
  attr_reader :params
103
- attr_reader :oauth
104
102
 
105
- # @param a_rest_params default call parameters (merged at call) and (+) authentication (:auth) :
106
- # :type (:basic, :oauth2, :url, :none)
107
- # :username [:basic]
108
- # :password [:basic]
109
- # :url_creds [:url]
110
- # :session_cb a lambda which takes @http_session as arg, use this to change parameters
111
- # :* [:oauth2] see Oauth class
103
+ def oauth
104
+ if @oauth.nil?
105
+ raise 'ERROR: no OAuth defined' unless @params[:auth][:type].eql?(:oauth2)
106
+ @oauth=Oauth.new(@params[:auth])
107
+ end
108
+ return @oauth
109
+ end
110
+
111
+ # @param a_rest_params [Hash] default call parameters (merged at call)
112
112
  def initialize(a_rest_params)
113
- raise "ERROR: expecting Hash" unless a_rest_params.is_a?(Hash)
114
- raise "ERROR: expecting base_url" unless a_rest_params[:base_url].is_a?(String)
113
+ raise 'ERROR: expecting Hash' unless a_rest_params.is_a?(Hash)
114
+ raise 'ERROR: expecting base_url' unless a_rest_params[:base_url].is_a?(String)
115
115
  @params=a_rest_params.clone
116
116
  Log.dump('REST params',@params)
117
117
  # base url without trailing slashes (note: string may be frozen)
118
118
  @params[:base_url]=@params[:base_url].gsub(/\/+$/,'')
119
119
  @http_session=nil
120
120
  # default is no auth
121
- @params[:auth]||={:type=>:none}
121
+ @params[:auth]||={type: :none}
122
122
  @params[:not_auth_codes]||=['401']
123
- @oauth=Oauth.new(@params[:auth]) if @params[:auth][:type].eql?(:oauth2)
123
+ @oauth=nil
124
124
  Log.dump('REST params(2)',@params)
125
125
  end
126
126
 
127
- def oauth_token(options={})
128
- raise "ERROR: expecting Oauth, have #{@oauth.class}" unless @oauth.is_a?(Oauth)
129
- return @oauth.get_authorization(options)
127
+ def oauth_token(force_refresh: false)
128
+ raise "ERROR: expecting boolean, have #{force_refresh}" unless [true,false].include?(force_refresh)
129
+ return oauth.get_authorization(use_refresh_token: force_refresh)
130
+ end
131
+
132
+ def build_request(call_data)
133
+ # TODO: shall we percent encode subpath (spaces) test with access key delete with space in id
134
+ # URI.escape()
135
+ uri=self.class.build_uri("#{call_data[:base_url]}#{['','/'].include?(call_data[:subpath]) ? '' : '/'}#{call_data[:subpath]}",call_data[:url_params])
136
+ Log.log.debug("URI=#{uri}")
137
+ begin
138
+ # instanciate request object based on string name
139
+ req=Net::HTTP.const_get(call_data[:operation].capitalize).new(uri)
140
+ rescue NameError
141
+ raise "unsupported operation : #{call_data[:operation]}"
142
+ end
143
+ if call_data.has_key?(:json_params) && !call_data[:json_params].nil?
144
+ req.body=JSON.generate(call_data[:json_params])
145
+ Log.dump('body JSON data',call_data[:json_params])
146
+ #Log.log.debug("body JSON data=#{JSON.pretty_generate(call_data[:json_params])}")
147
+ req['Content-Type'] = 'application/json'
148
+ #call_data[:headers]['Accept']='application/json'
149
+ end
150
+ if call_data.has_key?(:www_body_params)
151
+ req.body=URI.encode_www_form(call_data[:www_body_params])
152
+ Log.log.debug("body www data=#{req.body.chomp}")
153
+ req['Content-Type'] = 'application/x-www-form-urlencoded'
154
+ end
155
+ if call_data.has_key?(:text_body_params)
156
+ req.body=call_data[:text_body_params]
157
+ Log.log.debug("body data=#{req.body.chomp}")
158
+ end
159
+ # set headers
160
+ if call_data.has_key?(:headers)
161
+ call_data[:headers].keys.each do |key|
162
+ req[key] = call_data[:headers][key]
163
+ end
164
+ end
165
+ # :type = :basic
166
+ req.basic_auth(call_data[:auth][:username],call_data[:auth][:password]) if call_data[:auth][:type].eql?(:basic)
167
+ return req
130
168
  end
131
169
 
132
170
  # HTTP/S REST call
@@ -139,21 +177,31 @@ module Aspera
139
177
  # :url_params
140
178
  # :www_body_params
141
179
  # :text_body_params
142
- # :save_to_file (filepath)
143
- # :return_error (bool)
180
+ # :save_to_file (filepath) default: nil
181
+ # :return_error (bool) default: nil
182
+ # :redirect_max (int) default: 0
183
+ # :not_auth_codes (array)
184
+ # ----
185
+ # authentication (:auth) :
186
+ # :type (:none, :basic, :oauth2, :url)
187
+ # :username [:basic]
188
+ # :password [:basic]
189
+ # :url_creds [:url] a hash
190
+ # :* [:oauth2] see Oauth class
144
191
  def call(call_data)
145
192
  raise "Hash call parameter is required (#{call_data.class})" unless call_data.is_a?(Hash)
193
+ call_data[:subpath]='' if call_data[:subpath].nil?
146
194
  Log.log.debug("accessing #{call_data[:subpath]}".red.bold.bg_green)
147
195
  call_data[:headers]||={}
148
- call_data[:headers]['User-Agent'] ||= @@user_agent
149
- # defaults from @params are overriden by call dataz
196
+ call_data[:headers]['User-Agent'] ||= self.class.user_agent
197
+ # defaults from @params are overriden by call data
150
198
  call_data=@params.deep_merge(call_data)
151
199
  case call_data[:auth][:type]
152
200
  when :none
153
201
  # no auth
154
202
  when :basic
155
- Log.log.debug("using Basic auth")
156
- basic_auth_data=[call_data[:auth][:username],call_data[:auth][:password]]
203
+ Log.log.debug('using Basic auth')
204
+ # done in build_req
157
205
  when :oauth2
158
206
  call_data[:headers]['Authorization']=oauth_token unless call_data[:headers].has_key?('Authorization')
159
207
  when :url
@@ -163,67 +211,33 @@ module Aspera
163
211
  end
164
212
  else raise "unsupported auth type: [#{call_data[:auth][:type]}]"
165
213
  end
166
- # TODO: shall we percent encode subpath (spaces) test with access key delete with space in id
167
- # URI.escape()
168
- uri=self.class.build_uri("#{@params[:base_url]}#{call_data[:subpath].nil? ? '' : '/'}#{call_data[:subpath]}",call_data[:url_params])
169
- Log.log.debug("URI=#{uri}")
170
- begin
171
- # instanciate request object based on string name
172
- req=Object::const_get('Net::HTTP::'+call_data[:operation].capitalize).new(uri.request_uri)
173
- rescue NameError => e
174
- raise "unsupported operation : #{call_data[:operation]}"
175
- end
176
- if call_data.has_key?(:json_params) and !call_data[:json_params].nil? then
177
- req.body=JSON.generate(call_data[:json_params])
178
- Log.dump('body JSON data',call_data[:json_params])
179
- #Log.log.debug("body JSON data=#{JSON.pretty_generate(call_data[:json_params])}")
180
- req['Content-Type'] = 'application/json'
181
- #call_data[:headers]['Accept']='application/json'
182
- end
183
- if call_data.has_key?(:www_body_params) then
184
- req.body=URI.encode_www_form(call_data[:www_body_params])
185
- Log.log.debug("body www data=#{req.body.chomp}")
186
- req['Content-Type'] = 'application/x-www-form-urlencoded'
187
- end
188
- if call_data.has_key?(:text_body_params) then
189
- req.body=call_data[:text_body_params]
190
- Log.log.debug("body data=#{req.body.chomp}")
191
- end
192
- # set headers
193
- if call_data.has_key?(:headers) then
194
- call_data[:headers].keys.each do |key|
195
- req[key] = call_data[:headers][key]
196
- end
197
- end
198
- # :type = :basic
199
- req.basic_auth(*basic_auth_data) unless basic_auth_data.nil?
200
-
214
+ req=build_request(call_data)
201
215
  Log.log.debug("call_data = #{call_data}")
202
- result={:http=>nil}
216
+ result={http: nil}
203
217
  # start a block to be able to retry the actual HTTP request
204
218
  begin
205
219
  # we try the call, and will retry only if oauth, as we can, first with refresh, and then re-auth if refresh is bad
206
220
  oauth_tries ||= 2
207
- tries_remain_redirect||=4
208
- Log.log.debug("send request")
221
+ tries_remain_redirect||=call_data[:redirect_max].nil? ? 0 : call_data[:redirect_max].to_i
222
+ Log.log.debug('send request')
209
223
  # make http request (pipelined)
210
224
  http_session.request(req) do |response|
211
225
  result[:http] = response
212
- if call_data.has_key?(:save_to_file)
226
+ if !call_data[:save_to_file].nil? && result[:http].code.to_s.start_with?('2')
213
227
  total_size=result[:http]['Content-Length'].to_i
214
228
  progress=ProgressBar.create(
215
- :format => '%a %B %p%% %r KB/sec %e',
216
- :rate_scale => lambda{|rate|rate/1024},
217
- :title => 'progress',
218
- :total => total_size)
219
- Log.log.debug("before write file")
229
+ format: '%a %B %p%% %r KB/sec %e',
230
+ rate_scale: lambda{|rate|rate/1024},
231
+ title: 'progress',
232
+ total: total_size)
233
+ Log.log.debug('before write file')
220
234
  target_file=call_data[:save_to_file]
221
235
  # override user's path to path in header
222
- if !response['Content-Disposition'].nil? and m=response['Content-Disposition'].match(/filename="([^"]+)"/)
236
+ if !response['Content-Disposition'].nil? && (m=response['Content-Disposition'].match(/filename="([^"]+)"/))
223
237
  target_file=File.join(File.dirname(target_file),m[1])
224
238
  end
225
239
  # download with temp filename
226
- target_file_tmp="#{target_file}#{@@download_partial_suffix}"
240
+ target_file_tmp="#{target_file}#{self.class.download_partial_suffix}"
227
241
  Log.log.debug("saving to: #{target_file}")
228
242
  File.open(target_file_tmp, 'wb') do |file|
229
243
  result[:http].read_body do |fragment|
@@ -238,27 +252,27 @@ module Aspera
238
252
  progress=nil
239
253
  end # save_to_file
240
254
  end
241
- # sometimes there is a ITF8 char (e.g. (c) )
242
- result[:http].body.force_encoding("UTF-8") if result[:http].body.is_a?(String)
255
+ # sometimes there is a UTF8 char (e.g. (c) )
256
+ result[:http].body.force_encoding('UTF-8') if result[:http].body.is_a?(String)
243
257
  Log.log.debug("result: body=#{result[:http].body}")
244
258
  result_mime=(result[:http]['Content-Type']||'text/plain').split(';').first
245
- case result_mime
259
+ result[:data]=case result_mime
246
260
  when 'application/json','application/vnd.api+json'
247
- result[:data]=JSON.parse(result[:http].body) rescue nil
261
+ JSON.parse(result[:http].body) rescue nil
248
262
  else #when 'text/plain'
249
- result[:data]=result[:http].body
263
+ result[:http].body
250
264
  end
251
265
  Log.dump("result: parsed: #{result_mime}",result[:data])
252
266
  Log.log.debug("result: code=#{result[:http].code}")
253
- RestErrorAnalyzer.instance.raiseOnError(req,result)
267
+ RestErrorAnalyzer.instance.raise_on_error(req,result)
254
268
  rescue RestCallError => e
255
269
  # not authorized: oauth token expired
256
- if @params[:not_auth_codes].include?(result[:http].code.to_s) and call_data[:auth][:type].eql?(:oauth2)
270
+ if call_data[:not_auth_codes].include?(result[:http].code.to_s) && call_data[:auth][:type].eql?(:oauth2)
257
271
  begin
258
272
  # try to use refresh token
259
- req['Authorization']=oauth_token(refresh: true)
273
+ req['Authorization']=oauth_token(force_refresh: true)
260
274
  rescue RestCallError => e
261
- Log.log.error("refresh failed".bg_red)
275
+ Log.log.error('refresh failed'.bg_red)
262
276
  # regenerate a brand new token
263
277
  req['Authorization']=oauth_token
264
278
  end
@@ -266,25 +280,30 @@ module Aspera
266
280
  retry unless (oauth_tries -= 1).zero?
267
281
  end # if oauth
268
282
  # moved ?
269
- if e.response.is_a?(Net::HTTPRedirection)
270
- if tries_remain_redirect > 0
271
- tries_remain_redirect-=1
272
- Log.log.info("URL is moved: #{e.response['location']}")
273
- raise e
274
- # TODO: rebuild request with new location
275
- #retry
283
+ raise e unless e.response.is_a?(Net::HTTPRedirection)
284
+ if tries_remain_redirect.positive?
285
+ tries_remain_redirect-=1
286
+ Log.log.info("URL is moved: #{e.response['location']}")
287
+ current_uri=URI.parse(call_data[:base_url])
288
+ redir_uri=URI.parse(e.response['location'])
289
+ call_data[:base_url]=e.response['location']
290
+ call_data[:subpath]=''
291
+ if current_uri.host.eql?(redir_uri.host) && current_uri.port.eql?(redir_uri.port)
292
+ req=build_request(call_data)
293
+ retry
276
294
  else
277
- raise "too many redirect"
295
+ # change host
296
+ Log.log.info("Redirect changes host: #{current_uri.host} -> #{redir_uri.host}")
297
+ return self.class.new(call_data).call(call_data)
278
298
  end
279
299
  else
280
- raise e
300
+ raise e unless call_data[:return_error]
281
301
  end
282
302
  # raise exception if could not retry and not return error in result
283
303
  raise e unless call_data[:return_error]
284
304
  end # begin request
285
305
  Log.log.debug("result=#{result}")
286
306
  return result
287
-
288
307
  end
289
308
 
290
309
  #
@@ -293,23 +312,23 @@ module Aspera
293
312
 
294
313
  # @param encoding : one of: :json_params, :url_params
295
314
  def create(subpath,params,encoding=:json_params)
296
- return call({:operation=>'POST',:subpath=>subpath,:headers=>{'Accept'=>'application/json'},encoding=>params})
315
+ return call({operation: 'POST',subpath: subpath,headers: {'Accept'=>'application/json'},encoding=>params})
297
316
  end
298
317
 
299
318
  def read(subpath,args=nil)
300
- return call({:operation=>'GET',:subpath=>subpath,:headers=>{'Accept'=>'application/json'},:url_params=>args})
319
+ return call({operation: 'GET',subpath: subpath,headers: {'Accept'=>'application/json'},url_params: args})
301
320
  end
302
321
 
303
322
  def update(subpath,params)
304
- return call({:operation=>'PUT',:subpath=>subpath,:headers=>{'Accept'=>'application/json'},:json_params=>params})
323
+ return call({operation: 'PUT',subpath: subpath,headers: {'Accept'=>'application/json'},json_params: params})
305
324
  end
306
325
 
307
326
  def delete(subpath)
308
- return call({:operation=>'DELETE',:subpath=>subpath,:headers=>{'Accept'=>'application/json'}})
327
+ return call({operation: 'DELETE',subpath: subpath,headers: {'Accept'=>'application/json'}})
309
328
  end
310
329
 
311
330
  def cancel(subpath)
312
- return call({:operation=>'CANCEL',:subpath=>subpath,:headers=>{'Accept'=>'application/json'}})
331
+ return call({operation: 'CANCEL',subpath: subpath,headers: {'Accept'=>'application/json'}})
313
332
  end
314
333
  end
315
334
  end #module Aspera
@@ -1,8 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  module Aspera
2
3
  # raised on error after REST call
3
4
  class RestCallError < StandardError
4
- attr_accessor :request
5
- attr_accessor :response
5
+ attr_accessor :request, :response
6
6
  # @param http response
7
7
  def initialize(req,resp,msg)
8
8
  @request = req