Module: Aspera::Rest

Defined in:
lib/aspera/rest/util.rb,
lib/aspera/rest/list.rb,
lib/aspera/rest/call_error.rb,
lib/aspera/rest/parameters.rb,
lib/aspera/rest/aspera_errors.rb,
lib/aspera/rest/error_analyzer.rb,
lib/aspera/rest/client.rb

Overview

HTTP REST client and helpers

Defined Under Namespace

Modules: List Classes: AsperaErrors, CallError, Client, ErrorAnalyzer, Parameters

Class Method Summary collapse

Class Method Details

.basic_authorization(user, pass) ⇒ String

Build a Basic authentication header value

Parameters:

Returns:

  • (String) —

    Basic auth token



19
# File 'lib/aspera/rest/util.rb', line 19

def basic_authorization(user, pass) = "Basic #{Base64.strict_encode64("#{user}:#{pass}")}"

.build_uri(url, query) ⇒ URI

Build URI from URL and parameters and check it is http or https. Check if php style is specified. nil values in query result in key without value, e.g. ?a, while empty string values result in ?a=.

Parameters:

  • url (String) —

    The URL without query.

  • query (Hash, Array, String) —

    The query parameters.

Returns:

  • (URI) —

    The built URI.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/aspera/rest/util.rb', line 36

def build_uri(url, query)
  uri = URI.parse(url)
  Aspera.assert_values(uri.scheme, %w[http https]) { 'URI scheme' }
  return uri if query.nil? || query.respond_to?(:empty?) && query.empty?
  Log.dump(:query, query)
  uri.query =
    case query
    when String
      query
    when Hash
      URI.encode_www_form(h_to_query_array(query))
    when Array
      Aspera.assert(query.all? { |i| i.is_a?(Array) && i.length.eql?(2) }, 'Query must be array of arrays of 2 elements')
      URI.encode_www_form(query) # remove nil values
    else Aspera.error_unexpected_value(query.class) { 'query type' }
    end.gsub('%5B%5D=', '[]=')
  # [] is allowed in url parameters
  uri
end

.h_to_query_array(query) ⇒ Array<Array>

Support array for query parameter, there is no standard. Either p=1&p=2 (default) or p=1&p=2 (if :x_array_php_style is set to true in query)

Parameters:

  • query (Hash) —

    HTTP query as hash

Returns:

  • (Array<Array>) —

    Array of [key, value] pairs suitable for URI.encode_www_form



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/aspera/rest/util.rb', line 61

def h_to_query_array(query)
  Aspera.assert_type(query, Hash)
  suffix = query[:x_array_php_style] ? '[]' : nil
  query.each_with_object([]) do |(k, v), query_array|
    next if k.eql?(:x_array_php_style)
    case v
    when Array
      v.each do |e|
        query_array.push(["#{k}#{suffix}", e])
      end
    else
      query_array.push([k, v])
    end
  end
end

.io_http_session(http_session) ⇒ Net::BufferedIO

get Net::HTTP underlying socket i/o little hack, handy because HTTP debug, proxy, etc... will be available used implement web sockets after start_http_session

Parameters:

  • http_session (Net::HTTP) —

    the session object

Returns:

  • (Net::BufferedIO) —

    The underlying socket i/o



120
121
122
123
124
125
126
# File 'lib/aspera/rest/util.rb', line 120

def io_http_session(http_session)
  Aspera.assert_type(http_session, Net::HTTP)
  # Net::BufferedIO in net/protocol.rb
  result = http_session.instance_variable_get(:@socket)
  Aspera.assert(!result.nil?) { "no socket for #{http_session}" }
  return result
end

.parse_header(header) ⇒ Hash

Parses an HTTP Content-Type header string into its media type and parameters according to RFC 9110 and RFC 6838. TODO: use gem: content_type

Parameters:

  • header (String) —

    The Content-Type header string, e.g., "application/json; charset=utf-8"

Returns:

  • (Hash) —

    A hash with :type and :parameters keys. Example:

    {
    type: "application/json",
    parameters: {
      charset: "utf-8",
      version: "1.0"
    }
    }


162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/aspera/rest/util.rb', line 162

def parse_header(header)
  parts = header.split(';').map(&:strip)
  media_type = parts.shift.downcase
  parameters = parts.filter_map do |param|
    key, value = param.split('=', 2)
    next unless key && value
    key = key.strip.downcase.to_sym
    value = value.strip.gsub(/\A"|"\z/, '')
    [key, value]
  end.to_h
  {type: media_type, parameters: parameters}
end

.php_style(query) ⇒ Hash

Indicate that the given Hash query uses php style for array parameters

Parameters:

  • query (Hash) —

    A key can have Array value and result will use PHP format: a=1&a=2

Returns:

  • (Hash) —

    The query parameters.



24
25
26
27
28
# File 'lib/aspera/rest/util.rb', line 24

def php_style(query)
  Aspera.assert_type(query, Hash) { 'query' }
  query[:x_array_php_style] = true
  query
end

.query_to_h(query) ⇒ Hash

Decode query string as Hash if parameter is only once, then it's a scalar if a parameter is several, then it's array if parameter has [] then it's an array, and [] is removed Support arrays in query string, e.g. PHP's way is p=1&p=2

Parameters:

  • query (String) —

    query string as in URI.query

Returns:

  • (Hash) —

    decoded query



84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/aspera/rest/util.rb', line 84

def query_to_h(query)
  URI.decode_www_form(query).each_with_object({}) do |(key, value), h|
    if key.end_with?('[]')
      key = key[..-3]
      h[key] = [] unless h.key?(key)
    end
    if h.key?(key)
      h[key] = [h[key]] if !h[key].is_a?(Array)
      h[key].push(value)
    else
      h[key] = value
    end
  end
end

.remote_certificate_chain(url, as_string: true) ⇒ String, Array<OpenSSL::X509::Certificate>

Get certificate chain of remote server

Parameters:

  • url (String) —

    URL of server

  • as_string (Boolean) (defaults to: true) —

    true to return PEM string, false for certificate objects

Returns:

  • (String, Array<OpenSSL::X509::Certificate>) —

    Certificates of remote server



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/aspera/rest/util.rb', line 132

def remote_certificate_chain(url, as_string: true)
  result = []
  # initiate a session to retrieve remote certificate
  http_session = Rest.start_http_session(url)
  begin
    # retrieve underlying openssl socket
    result = Rest.io_http_session(http_session).io.peer_cert_chain
  rescue
    result = http_session.peer_cert
  ensure
    http_session.finish
  end
  result = result.map(&:to_pem).join("\n") if as_string
  return result
end

.start_http_session(base_url) ⇒ Net::HTTP

Start a HTTP/S session, also used for web sockets

Parameters:

  • base_url (String) —

    Base url of HTTP/S session

Returns:

  • (Net::HTTP) —

    A started HTTP session



102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/aspera/rest/util.rb', line 102

def start_http_session(base_url)
  uri = URI.parse(base_url)
  Aspera.assert_values(uri.scheme, %w[http https]) { 'URI scheme' }
  # This honors http_proxy env var
  http_session = Net::HTTP.new(uri.host, uri.port)
  http_session.use_ssl = uri.scheme.eql?('https')
  # Set http options in callback, such as timeout and cert. verification
  Parameters.instance.session_cb&.call(http_session)
  # Manually start session for keep alive (if supported by server, else, session is closed every time)
  http_session.start
  return http_session
end