aspera-cli 4.18.0 → 4.18.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/aspera/rest.rb CHANGED
@@ -12,7 +12,7 @@ require 'json'
12
12
  require 'base64'
13
13
  require 'cgi'
14
14
 
15
- # add cancel method to http
15
+ # Cancel method for HTTP
16
16
  class Net::HTTP::Cancel < Net::HTTPRequest # rubocop:disable Style/ClassAndModuleChildren
17
17
  METHOD = 'CANCEL'
18
18
  REQUEST_HAS_BODY = false
@@ -24,12 +24,16 @@ module Aspera
24
24
  # rest call errors are raised as exception RestCallError
25
25
  # and error are analyzed in RestErrorAnalyzer
26
26
  class Rest
27
- # global settings also valid for any subclass
27
+ # Global settings also valid for any subclass
28
+ # @param user_agent [String] HTTP request header: 'User-Agent'
29
+ # @param download_partial_suffix [String] suffix for partial download
30
+ # @param session_cb [lambda] lambda called on new HTTP session. Takes the Net::HTTP as arg. Used to change parameters on creation.
31
+ # @param progress_bar [Object] progress bar object
28
32
  @@global = { # rubocop:disable Style/ClassVars
29
- user_agent: 'Ruby', # goes to HTTP request header: 'User-Agent'
30
- download_partial_suffix: '.http_partial', # suffix for partial download
31
- session_cb: nil, # a lambda which takes the Net::HTTP as arg, use this to change parameters
32
- progress_bar: nil # progress bar object
33
+ user_agent: 'RubyAsperaRest',
34
+ download_partial_suffix: '.http_partial',
35
+ session_cb: nil,
36
+ progress_bar: nil
33
37
  }
34
38
 
35
39
  # flag for array parameters prefixed with []
@@ -44,9 +48,10 @@ module Aspera
44
48
  JSON_DECODE = ['application/json', 'application/vnd.api+json', 'application/x-javascript'].freeze
45
49
 
46
50
  class << self
51
+ # @return [String] Basic auth token
47
52
  def basic_token(user, pass); return "Basic #{Base64.strict_encode64("#{user}:#{pass}")}"; end
48
53
 
49
- # used to build a parameter list prefixed with "[]"
54
+ # Build a parameter list prefixed with "[]"
50
55
  # @param values [Array] list of values
51
56
  def array_params(values)
52
57
  return [ARRAY_PARAMS].concat(values)
@@ -56,7 +61,7 @@ module Aspera
56
61
  return values.first.eql?(ARRAY_PARAMS)
57
62
  end
58
63
 
59
- # build URI from URL and parameters and check it is http or https, encode array [] parameters
64
+ # Build URI from URL and parameters and check it is http or https, encode array [] parameters
60
65
  def build_uri(url, query_hash=nil)
61
66
  uri = URI.parse(url)
62
67
  Aspera.assert(%w[http https].include?(uri.scheme)){"REST endpoint shall be http/s not #{uri.scheme}"}
@@ -82,8 +87,16 @@ module Aspera
82
87
  return uri
83
88
  end
84
89
 
90
+ # decode query string as hash
91
+ # Does not support arrays in query string, no standard, e.g. PHP's way is p[]=1&p[]=2
92
+ # @param query [String] query string
93
+ # @return [Hash] decoded query
85
94
  def decode_query(query)
86
- URI.decode_www_form(query).each_with_object({}){|v, h|h[v.first] = v.last }
95
+ URI.decode_www_form(query).each_with_object({}) do |pair, h|
96
+ key = pair.first
97
+ raise "Array not supported in query string: #{key}" if key.include?('[]') || h.key?(key)
98
+ h[key] = pair.last
99
+ end
87
100
  end
88
101
 
89
102
  # Start a HTTP/S session, also used for web sockets
@@ -141,6 +154,17 @@ module Aspera
141
154
  def user_agent
142
155
  return @@global[:user_agent]
143
156
  end
157
+
158
+ def parse_header(header)
159
+ type, *params = header.split(/;\s*/)
160
+ parameters = params.map do |param|
161
+ one = param.split(/=\s*/)
162
+ one[0] = one[0].to_sym
163
+ one[1] = one[1].gsub(/\A"|"\z/, '')
164
+ one
165
+ end.to_h
166
+ { type: type.downcase, parameters: parameters }
167
+ end
144
168
  end
145
169
 
146
170
  private
@@ -185,8 +209,12 @@ module Aspera
185
209
  headers: nil
186
210
  )
187
211
  Aspera.assert_type(base_url, String)
188
- # base url with max one trailing slashes (note: string may be frozen)
189
- @base_url = base_url.gsub(%r{//+$}, '/')
212
+ # base url with no trailing slashes (note: string may be frozen)
213
+ @base_url = base_url.gsub(%r{/+$}, '')
214
+ # remove trailing port if it is 443 and scheme is https
215
+ @base_url = @base_url.gsub(/:443$/, '') if @base_url.start_with?('https://')
216
+ @base_url = @base_url.gsub(/:80$/, '') if @base_url.start_with?('http://')
217
+ Log.log.debug{"Rest.new(#{@base_url})"}
190
218
  # default is no auth
191
219
  @auth_params = auth.nil? ? {type: :none} : auth
192
220
  Aspera.assert_type(@auth_params, Hash)
@@ -265,7 +293,7 @@ module Aspera
265
293
  begin
266
294
  # TODO: shall we percent encode subpath (spaces) test with access key delete with space in id
267
295
  # URI.escape()
268
- separator = !['', '/'].include?(subpath) || @base_url.end_with?('/') ? '/' : ''
296
+ separator = ['', '/'].include?(subpath) ? '' : '/'
269
297
  uri = self.class.build_uri("#{@base_url}#{separator}#{subpath}", query)
270
298
  Log.log.debug{"URI=#{uri}"}
271
299
  begin
@@ -305,7 +333,7 @@ module Aspera
305
333
  # make http request (pipelined)
306
334
  http_session.request(req) do |response|
307
335
  result[:http] = response
308
- result_mime = (result[:http]['Content-Type'] || 'text/plain').split(';').first.downcase
336
+ result_mime = self.class.parse_header(result[:http]['Content-Type'] || 'text/plain')[:type]
309
337
  # JSON data needs to be parsed, in case it contains an error code
310
338
  if !save_to_file.nil? &&
311
339
  result[:http].code.to_s.start_with?('2') &&
@@ -315,8 +343,11 @@ module Aspera
315
343
  Log.log.debug('before write file')
316
344
  target_file = save_to_file
317
345
  # override user's path to path in header
318
- if !response['Content-Disposition'].nil? && (m = response['Content-Disposition'].match(/filename="([^"]+)"/))
319
- target_file = File.join(File.dirname(target_file), m[1])
346
+ if !response['Content-Disposition'].nil?
347
+ disposition = self.class.parse_header(response['Content-Disposition'])
348
+ if disposition[:parameters].key?(:filename)
349
+ target_file = File.join(File.dirname(target_file), disposition[:parameters][:filename])
350
+ end
320
351
  end
321
352
  # download with temp filename
322
353
  target_file_tmp = "#{target_file}#{@@global[:download_partial_suffix]}"
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aspera-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.18.0
4
+ version: 4.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laurent Martin
@@ -37,7 +37,7 @@ cert_chain:
37
37
  eTf9kxhVM40wGQOECVNA8UsEEZHD48eF+csUYZtAJOF5oxTI8UyV9T/o6CgO0c9/
38
38
  Gzz+Qm5ULOUcPiJLjSpaiTrkiIVYiDGnqNSr6R1Hb1c=
39
39
  -----END CERTIFICATE-----
40
- date: 2024-07-10 00:00:00.000000000 Z
40
+ date: 2024-08-21 00:00:00.000000000 Z
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: blankslate
@@ -390,6 +390,7 @@ files:
390
390
  - README.md
391
391
  - bin/ascli
392
392
  - bin/asession
393
+ - examples/build_package.sh
393
394
  - examples/dascli
394
395
  - examples/proxy.pac
395
396
  - examples/rubyc
@@ -435,6 +436,7 @@ files:
435
436
  - lib/aspera/cli/plugins/preview.rb
436
437
  - lib/aspera/cli/plugins/server.rb
437
438
  - lib/aspera/cli/plugins/shares.rb
439
+ - lib/aspera/cli/special_values.rb
438
440
  - lib/aspera/cli/sync_actions.rb
439
441
  - lib/aspera/cli/transfer_agent.rb
440
442
  - lib/aspera/cli/transfer_progress.rb
@@ -468,7 +470,6 @@ files:
468
470
  - lib/aspera/oauth/jwt.rb
469
471
  - lib/aspera/oauth/url_json.rb
470
472
  - lib/aspera/oauth/web.rb
471
- - lib/aspera/open_application.rb
472
473
  - lib/aspera/persistency_action_once.rb
473
474
  - lib/aspera/persistency_folder.rb
474
475
  - lib/aspera/preview/file_types.rb
metadata.gz.sig CHANGED
Binary file
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'aspera/log'
4
- require 'aspera/environment'
5
- require 'rbconfig'
6
- require 'singleton'
7
-
8
- module Aspera
9
- # Allows a user to open a Url
10
- # if method is "text", then URL is displayed on terminal
11
- # if method is "graphical", then the URL will be opened with the default browser.
12
- class OpenApplication
13
- include Singleton
14
- USER_INTERFACES = %i[text graphical].freeze
15
- class << self
16
- def default_gui_mode
17
- # assume not remotely connected on macos and windows
18
- return :graphical if [Environment::OS_WINDOWS, Environment::OS_X].include?(Environment.os)
19
- # unix family
20
- return :graphical if ENV.key?('DISPLAY') && !ENV['DISPLAY'].empty?
21
- return :text
22
- end
23
-
24
- # command must be non blocking
25
- def uri_graphical(uri)
26
- case Environment.os
27
- when Environment::OS_X then return system('open', uri.to_s)
28
- when Environment::OS_WINDOWS then return system('start', 'explorer', %Q{"#{uri}"})
29
- when Environment::OS_LINUX then return system('xdg-open', uri.to_s)
30
- else
31
- raise "no graphical open method for #{Environment.os}"
32
- end
33
- end
34
-
35
- def editor(file_path)
36
- if ENV.key?('EDITOR')
37
- system(ENV['EDITOR'], file_path.to_s)
38
- elsif Environment.os.eql?(Environment::OS_WINDOWS)
39
- system('notepad.exe', %Q{"#{file_path}"})
40
- else
41
- uri_graphical(file_path.to_s)
42
- end
43
- end
44
- end
45
-
46
- attr_accessor :url_method
47
-
48
- def initialize
49
- @url_method = self.class.default_gui_mode
50
- end
51
-
52
- # this is non blocking
53
- def uri(the_url)
54
- case @url_method
55
- when :graphical
56
- self.class.uri_graphical(the_url)
57
- when :text
58
- case the_url.to_s
59
- when /^http/
60
- puts "USER ACTION: please enter this url in a browser:\n#{the_url.to_s.red}\n"
61
- else
62
- puts "USER ACTION: open this:\n#{the_url.to_s.red}\n"
63
- end
64
- else
65
- raise StandardError, "unsupported url open method: #{@url_method}"
66
- end
67
- end
68
- end
69
- end