class Aspera::Fasp::HttpGW

executes a local “ascp”, connects mgt port, equivalent of “Fasp Manager”

Constants

OK_MESSAGE

message returned by HTTP GW in case of success

UPLOAD_REFRESH_SEC

refresh rate for progress

Public Class Methods

new(params) click to toggle source
Calls superclass method
# File lib/aspera/fasp/http_gw.rb, line 180
def initialize(params)
  raise "params must be Hash" unless params.is_a?(Hash)
  params=params.symbolize_keys
  raise "must have only one param: url" unless params.keys.eql?([:url])
  super()
  @gw_api=Rest.new({:base_url => params[:url]})
  api_info = @gw_api.read('info')[:data]
  Log.log.info("#{api_info}")
  @upload_chunksize=128000 # TODO: configurable ?
end

Public Instance Methods

download(transfer_spec) click to toggle source
# File lib/aspera/fasp/http_gw.rb, line 118
def download(transfer_spec)
  transfer_spec['zip_required']||=false
  transfer_spec['source_root']||='/'
  # is normally provided by application, like package name
  if !transfer_spec.has_key?('download_name')
    # by default it is the name of first file
    dname=File.basename(transfer_spec['paths'].first['source'])
    # we remove extension
    dname=dname.gsub(/\.@gw_api.*$/,'')
    # ands add indication of number of files if there is more than one
    if transfer_spec['paths'].length > 1
      dname=dname+" #{transfer_spec['paths'].length} Files"
    end
    transfer_spec['download_name']=dname
  end
  creation=@gw_api.create('download',{'transfer_spec'=>transfer_spec})[:data]
  transfer_uuid=creation['url'].split('/').last
  if transfer_spec['zip_required'] or transfer_spec['paths'].length > 1
    # it is a zip file if zip is required or there is more than 1 file
    file_dest=transfer_spec['download_name']+'.zip'
  else
    # it is a plain file if we don't require zip and there is only one file
    file_dest=File.basename(transfer_spec['paths'].first['source'])
  end
  file_dest=File.join(transfer_spec['destination_root'],file_dest)
  @gw_api.call({:operation=>'GET',:subpath=>"download/#{transfer_uuid}",:save_to_file=>file_dest})
end
shutdown() click to toggle source

terminates monitor thread

# File lib/aspera/fasp/http_gw.rb, line 172
def shutdown
end
start_transfer(transfer_spec,options={}) click to toggle source

start FASP transfer based on transfer spec (hash table) note that it is asynchronous HTTP download only supports file list

# File lib/aspera/fasp/http_gw.rb, line 149
def start_transfer(transfer_spec,options={})
  raise "GW URL must be set" unless !@gw_api.nil?
  raise "option: must be hash (or nil)" unless options.is_a?(Hash)
  raise "paths: must be Array" unless transfer_spec['paths'].is_a?(Array)
  raise "on token based transfer is supported in GW" unless transfer_spec['token'].is_a?(String)
  transfer_spec['authentication']||='token'
  case transfer_spec['direction']
  when 'send'
    upload(transfer_spec)
  when 'receive'
    download(transfer_spec)
  else
    raise "error"
  end
end
upload(transfer_spec) click to toggle source
# File lib/aspera/fasp/http_gw.rb, line 27
def upload(transfer_spec)
  # precalculate size
  total_size=0
  # currently, files are sent flat
  source_path=[]
  transfer_spec['paths'].each do |item|
    filepath=item['source']
    item['source']=item['destination']=File.basename(filepath)
    total_size+=item['file_size']=File.size(filepath)
    source_path.push(filepath)
  end
  session_id=SecureRandom.uuid
  ws=::WebSocket::Client::Simple::Client.new
  error=nil
  received=0
  ws.on :message do |msg|
    Log.log.info("ws: message: #{msg.data}")
    message=msg.data
    if message.eql?(OK_MESSAGE)
      received+=1
    else
      message.chomp!
      if message[0].eql?('"') and message[-1].eql?('"')
        error=JSON.parse(Base64.strict_decode64(message.chomp[1..-2]))['message']
      else
        error="expecting quotes in [#{message}]"
      end
    end
  end
  ws.on :error do |e|
    error=e
  end
  ws.on :open do
    Log.log.info("ws: open")
  end
  ws.on :close do
    Log.log.info("ws: close")
  end
  # open web socket to end point
  ws.connect("#{@gw_api.params[:base_url]}/upload")
  while !ws.open? and error.nil? do
    Log.log.info("ws: wait")
    sleep(0.2)
  end
  notify_begin(session_id,total_size)
  ws_send(ws,:transfer_spec,transfer_spec)
  # current file index
  filenum=0
  # aggregate size sent
  sent_bytes=0
  # last progress event
  lastevent=Time.now-1
  transfer_spec['paths'].each do |item|
    # TODO: on destination write same path?
    destination_path=item['source']
    # TODO: get mime type?
    file_mime_type=''
    total=item['file_size']
    # compute total number of slices
    numslices=1+(total-1)/@upload_chunksize
    # current slice index
    slicenum=0
    File.open(source_path[filenum]) do |file|
      while !file.eof? do
        data=file.read(@upload_chunksize)
        slice_data={
          name: destination_path,
          type: file_mime_type,
          size: total,
          data: Base64.strict_encode64(data),
          slice: slicenum,
          total_slices: numslices,
          fileIndex: filenum
        }
        ws_send(ws,:slice_upload, slice_data)
        sent_bytes+=data.length
        currenttime=Time.now
        if (currenttime-lastevent)>UPLOAD_REFRESH_SEC
          notify_progress(session_id,sent_bytes)
          lastevent=currenttime
        end
        slicenum+=1
        raise error unless error.nil?
      end
    end
    filenum+=1
  end
  ws.close
  notify_end(session_id)
end
url=(api_url) click to toggle source
# File lib/aspera/fasp/http_gw.rb, line 175
def url=(api_url)
end
wait_for_transfers_completion() click to toggle source

wait for completion of all jobs started @return list of :success or error message

# File lib/aspera/fasp/http_gw.rb, line 167
def wait_for_transfers_completion
  return [:success]
end
ws_send(ws,type,data) click to toggle source

send message on http gw web socket

# File lib/aspera/fasp/http_gw.rb, line 23
def ws_send(ws,type,data)
  ws.send(JSON.generate({type => data}))
end