6

Sinatra アプリケーションを介してリモート ファイルをプロキシしたいと考えています。これには、ヘッダーを含む HTTP 応答をリモート ソースからクライアントにストリーミングする必要がありますが、によって提供されるブロック内でストリーミング API を使用しているときに、応答のヘッダーを設定する方法がわかりませんNet::HTTP#get_response

たとえば、これは応答ヘッダーを設定しません。

get '/file' do
  stream do |out|
    uri = URI("http://manuals.info.apple.com/en/ipad_user_guide.pdf")
    Net::HTTP.get_response(uri) do |file|
      headers 'Content-Type' => file.header['Content-Type']

      file.read_body { |chunk| out << chunk }
    end
  end
end

そして、これはエラーになります: Net::HTTPOK#read_body called twice (IOError):

get '/file' do
  response = nil
  uri = URI("http://manuals.info.apple.com/en/ipad_user_guide.pdf")
  Net::HTTP.get_response(uri) do |file|
    headers 'Content-Type' => file.header['Content-Type']

    response = stream do |out|
      file.read_body { |chunk| out << chunk }
    end
  end
  response
end
4

1 に答える 1

3

私は間違っている可能性がありますが、これについて少し考えた後、ヘルパー ブロック内から応答ヘッダーを設定すると、そのブロックの実行が実際に延期streamされているため、それらのヘッダーが応答に適用されないように思えます。したがって、おそらく、ブロックが評価され、実行が開始される前に応答ヘッダーが設定されます。

これを回避するには、ファイルのコンテンツをストリーミングする前に HEAD リクエストを発行します。

例えば:

get '/file' do
  uri = URI('http://manuals.info.apple.com/en/ipad_user_guide.pdf')

  # get only header data
  head = Net::HTTP.start(uri.host, uri.port) do |http|
    http.head(uri.request_uri)
  end

  # set headers accordingly (all that apply)
  headers 'Content-Type' => head['Content-Type']

  # stream back the contents
  stream do |out|
    Net::HTTP.get_response(uri) do |f| 
      f.read_body { |ch| out << ch }
    end
  end
end

追加のリクエストがあるため、ユースケースには理想的ではないかもしれませんが、問題(遅延)が大きくならないように十分に小さくする必要があり、そのリクエストが失敗した場合にアプリが応答してから返送できるという利点が追加されます任意のデータ。

それが役に立てば幸い。

于 2012-09-18T18:10:20.530 に答える