1

I'm streaming data using the following approach:

self.response_body = Enumerator.new do |y|
    10_000_000.times do |i|
        y << "This is line #{i}\n"
    end
end

I'm trying to catch any exception generated inside Enumerator and present something nicer to the user. Right now, the app is presenting an ugly error page from Torquebox. e.g.

Torquebox/Jboss error page.

I tried rescue and redirect_to and many other ways to catch the exception (including add a middleware class for handling exceptions). Any help would be appreciated!.

(The app is made under jruby v1.7.19 and torquebox v3.1.1)

Exequiel
  • 101
  • 2
  • 5
  • [My own proposal is causing the issue](http://stackoverflow.com/a/10252798/1347377). I can see the rails error page if I remove the 'Last-Modified' header, but I cannot stream the file. So? – Exequiel Jul 31 '15 at 00:36

1 Answers1

0

I have partial solution for this issue:

  1. Check the external file headers. The app is sending a header request to the external server.

    1.1 If the status is 200 (success), then the app starts the retrieve process as before.

    1.2 If the status is any other value, then an error page is displayed with the appropriate error message.

And the code looks like this:

uri = URI.parse(link)

net_http = Net::HTTP.new(uri.host, uri.port)
net_http.open_timeout = options[:timeout]
net_http.read_timeout = options[:timeout]
net_http.use_ssl = (uri.scheme == 'https')

if net_http.use_ssl?
  net_http.cert = OpenSSL::X509::Certificate.new(File.read(options[:cert]))
  net_http.key = OpenSSL::PKey::RSA.new(File.read(options[:cert]))
  net_http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end

begin
  request = Net::HTTP::Get.new(uri.request_uri)
  res = net_http.request(request)

  if res.code.to_i == 200
    self.response.headers['Pragma'] = 'no-cache'
    self.response.headers['Cache-Control'] = 'private,max-age=0,must-revalidate,no-store'
    self.response.headers['Last-Modified'] = Time.now.ctime.to_s
    self.response.headers['Accept-Ranges'] = 'bytes'
    self.response.headers['Content-Type'] = type
    self.response.headers['Content-Disposition'] = "attachment; filename=\"#{save_as}\""
    self.response.headers['Content-Transfer-Encoding'] = 'binary'
    self.response.headers['Content-Description'] = 'File Transfer'
    self.response.headers['Content-Length'] = "#{filesize}"

    self.response_body = FileStreamer.new(link, options)
  else
    raise "The requested file is not available at the moment"
  end
rescue SocketError => se
  raise 'It seems the URL is not correct'
rescue Errno::ECONNREFUSED => cr
  raise 'It seems the server refused the connection request'
rescue Errno::EHOSTUNREACH => hu
  raise 'It seems the server cannot be reached'
rescue Errno::ENETUNREACH => nu
  raise 'It seems the network cannot be reached'
rescue Timeout::Error => te
  raise 'Request timed out while connecting to the server'
rescue StandardError => e
  raise e.message
end

The FileStreamer is a rack middleware which response to each method.

def each
  begin
    @net_http.start do |http|
      http.request_get(@uri.request_uri(), 
        {'User-Agent' => 'FileStreamer'}) do |response|
        case response
          when Net::HTTPSuccess then
            response.read_body do |segment|
              yield segment
            end
          when Net::HTTPRedirection then
            raise 'Redirection not allowed'
          else
            raise "Error trying to retrieve a file:\n" \
              "  URL: #{@uri.to_s}\n" \
              "  Error: #{response.message.to_s}"
        end
      end
    end
  rescue SocketError => se
    raise 'It seems the URL is not correct'
  rescue Errno::ECONNREFUSED => cr
    raise 'It seems the server refused the connection request'
  rescue Errno::EHOSTUNREACH => hu
    raise 'It seems the server cannot be reached'
  rescue Errno::ENETUNREACH => nu
    raise 'It seems the network cannot be reached'
  rescue Timeout::Error => te
    raise 'Request timed out while connecting to the server'
  rescue StandardError => e
    raise e.message
  end
end

Keep in mind this solution is not including errors happening in the middle of the process, I guess if an error happens in the middle, then the app will display the ugly error page.

Exequiel
  • 101
  • 2
  • 5