Как отменить Ruby Net :: HTTP-запрос? - PullRequest
2 голосов
/ 02 февраля 2010

Как заставить модуль Ruby's Net :: HTTP отменить запрос?

При вызове http.finish ниже возникает ошибка. У меня создается впечатление, что объект ответа не знает, что соединение было закрыто, и все еще ожидает больше данных.

Я хочу не делать запрос HEAD. Итак, сделайте запрос GET, если тип содержимого не HTML, отмените запрос.

Net::HTTP.start(uri.host, uri.port) do |http|
  http.request_get(uri.path) do |response|
    unless response['content-type'] =~ /html/i
      http.finish
    end
  end
end

/usr/lib/ruby/1.8/net/http.rb:2241:in `stream_check': attempt to read body out of block (IOError)
    from /usr/lib/ruby/1.8/net/http.rb:2171:in `read_body'
    from /usr/lib/ruby/1.8/net/http.rb:2198:in `body'
    from /usr/lib/ruby/1.8/net/http.rb:2137:in `reading_body'
    from /usr/lib/ruby/1.8/net/http.rb:1052:in `request'
    from /usr/lib/ruby/1.8/net/http.rb:948:in `request_get'
    from net.rb:9
    from /usr/lib/ruby/1.8/net/http.rb:543:in `start'
    from /usr/lib/ruby/1.8/net/http.rb:440:in `start'
    from net.rb:7

Ответы [ 3 ]

2 голосов
/ 02 февраля 2010

Я не запускал это через локальный прокси, чтобы быть абсолютно уверенным, но скорость подсказывает мне, что он не читает тело, если его content-type не является HTML.

url = URI.parse('http://alicebobandmallory.com/')
body=""
res = Net::HTTP.start(url.host, url.port) {|http|
  http.request_get(url.path) {|response|
    break unless response['content-type'] =~ /html/i
    response.read_body {|b|
     body<<b
    }
  }
}
2 голосов
/ 02 февраля 2010

отредактировано, оригинальный ответ внизу

Я не думаю, что вы бездействуете в первом фрагменте кода от вашего pastie. Попробуйте следующее, чтобы понять, что я имею в виду:


h = Net::HTTP.new uri.host,uri.port
h.set_debug_output $stderr
h.start do |http|
  http.request_get(uri.path) do |response|
  end
end

То, что происходит, заключается в том, что при выдаче GET ваш клиент обязан прочитать весь документ из сокета, независимо от того, действительно ли вы что-то делаете с ним. Это только часть спецификации HTTP.

Если вы не вызываете response.read_body, вы запрещаете вашему коду считывать ответ в память , но блок не вернется, пока все данные не будут считаны из сокета. Ваш блок с вызовом break выходит из строя перед финальным read, который призван сделать ваш код HTTP-совместимым, даже если вы решите не читать ответ в память. Я отредактировал вашу пасту , чтобы указать, где происходит окончательное чтение.

Вы просто читаете массивный файл ISO, так что похоже, что вы простаиваете.

Краткий ответ заключается в том, что вы должны выполнить запрос HEAD, если вы не собираетесь читать весь документ, как указано в спецификации HTTP.

Сложный ответ заключается в том, что вы можете выдать частичный GET, если вы введете диапазон байтов, как указано здесь , но я не уверен, что клиентская библиотека ruby ​​http является поддерживает этот режим работы.

Вызывая http.finish, вы рано закрываете сокет tcp, который выполняет работу настолько, насколько вырывает вас из блока кода, но вызывает исключение при вызове кода, потому что вы "не должны" делать это , Вы можете позвонить по номеру finish, если хотите перехватить исключение, но не очень хорошо играете с HTTP.

оригинальный ответ

Вы не должны звонить finish, соединение будет закрыто, когда блок выходов. Документация здесь .

Исключение выдается из этот код

Если вы действительно хотите заставить сокет чтобы закрыть рано, просто поймать IOError.

Я только что заметил, что ты инициализация response для результат звонка head, но тогда ты используя его снова в качестве параметра блока.

Просто проверьте тип контента до Вы звоните request_get, при условии content_type.

0 голосов
/ 03 февраля 2010

Я прекратил использовать это решение (перехватывая исключение):

require 'net/http'


uri = URI.parse('http://mirror.globo.com/ubuntu/releases/6.06.2/ubuntu-6.06.2-server-amd64.iso')

begin
  Net::HTTP.start(uri.host, uri.port) do |http|
    http.request_get(uri.path) do |response|
      unless response['content-type'] =~ /html/i
        p response['content-type']
        p 'didnt get html, stopping transfer'
        http.finish      
        # break
      end
      response.read_body do |data|
        p 'receiving data'
      end
    end
    p 'transfer succesful!'
  end
rescue 
  p 'rescued it'
end

p 'broke out of net loop'

Я также посмотрел libcurl через curb (http://curb.rubyforge.org), но он опирается на обратные вызовы, а не блоки иобратные вызовы не передаются в экземпляре Curl, поэтому нет способа уничтожить соединение, как в Net :: HTTP.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...