класс Net::HTTP
API клиента HTTP для Ruby.
Net::HTTP предоставляет богатую библиотеку, которая может использоваться для создания HTTP агентов пользователя. Для получения более подробной информации об HTTP см. [RFC2616](www.ietf.org/rfc/rfc2616.txt)
Net::HTTP разработан для тесного взаимодействия с URI. URI::Generic#host, URI::Generic#port и URI::HTTP#request_uri разработаны для работы с Net::HTTP.
Если вам нужно выполнить только несколько запросов GET, попробуйте OpenURI.
Простые примеры
Все примеры предполагают, что вы загрузили Net::HTTP с помощью:
require 'net/http'
Это также потребует 'uri', поэтому вам не нужно подключать его отдельно.
Методы Net::HTTP в следующем разделе не сохраняют соединения. Они не рекомендуются, если вы выполняете много HTTP запросов.
GET
Net::HTTP.get('example.com', '/index.html') # => String
GET по URI
uri = URI('http://example.com/index.html?count=10')
Net::HTTP.get(uri) # => String
GET с динамическими параметрами
uri = URI('http://example.com/index.html')
params = { :limit => 10, :page => 3 }
uri.query = URI.encode_www_form(params)
res = Net::HTTP.get_response(uri)
puts res.body if res.is_a?(Net::HTTPSuccess)
POST
uri = URI('http://www.example.com/search.cgi')
res = Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50')
puts res.body
POST с несколькими значениями
uri = URI('http://www.example.com/search.cgi')
res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50')
puts res.body
Как использовать Net::HTTP
Следующий пример кода может использоваться в качестве основы для HTTP агента пользователя, который может выполнять различные типы запросов с использованием постоянных соединений.
uri = URI('http://example.com/some_path?query=string')
Net::HTTP.start(uri.host, uri.port) do |http|
request = Net::HTTP::Get.new uri
response = http.request request # Net::HTTPResponse object
end
::start сразу создаёт соединение с HTTP сервером, которое остается открытым на все время блока. Соединение останется открытым для нескольких запросов в блоке, если сервер указывает, что поддерживает постоянные соединения.
Типы запросов, которые поддерживает Net::HTTP, перечислены ниже в разделе «Классы запросов HTTP».
Если вы хотите повторно использовать соединение для нескольких HTTP запросов без автоматического закрытия, вы можете использовать ::new вместо ::start. request автоматически откроет соединение с сервером, если оно не открыто в данный момент. Вы можете вручную закрыть соединение с помощью finish.
Для всех объектов запросов Net::HTTP и сокращённых методов запросов вы можете передать либо строку для пути запроса, либо URI, из которого Net::HTTP извлечёт путь запроса.
Данные ответа
uri = URI('http://example.com/index.html')
res = Net::HTTP.get_response(uri)
# Headers
res['Set-Cookie'] # => String
res.get_fields('set-cookie') # => Array
res.to_hash['set-cookie'] # => Array
puts "Headers: #{res.to_hash.inspect}"
# Status
puts res.code # => '200'
puts res.message # => 'OK'
puts res.class.name # => 'HTTPOK'
# Body
puts res.body if res.response_body_permitted?
Следование редиректу
Каждый объект Net::HTTPResponse принадлежит классу для своего кода ответа.
Например, все ответы 2XX являются экземплярами подкласса Net::HTTPSuccess, ответ 3XX — экземпляром подкласса Net::HTTPRedirection, а ответ 200 — экземпляром класса Net::HTTPOK. Подробности о классах ответов см. в разделе «Классы ответов HTTP» ниже.
Используя оператор case, вы можете правильно обработать различные типы ответов:
def fetch(uri_str, limit = 10)
# You should choose a better exception.
raise ArgumentError, 'too many HTTP redirects' if limit == 0
response = Net::HTTP.get_response(URI(uri_str))
case response
when Net::HTTPSuccess then
response
when Net::HTTPRedirection then
location = response['location']
warn "redirected to #{location}"
fetch(location, limit - 1)
else
response.value
end
end
print fetch('http://www.ruby-lang.org')
POST
POST можно выполнить с использованием класса запроса Net::HTTP::Post. Этот пример создаёт POST тело в формате urlencoded:
uri = URI('http://www.example.com/todo.cgi')
req = Net::HTTP::Post.new(uri)
req.set_form_data('from' => '2005-01-01', 'to' => '2005-03-31')
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(req)
end
case res
when Net::HTTPSuccess, Net::HTTPRedirection
# OK
else
res.value
end
В настоящее время Net::HTTP не поддерживает multipart/form-data. Для отправки multipart/form-data используйте Net::HTTPGenericRequest#body= и Net::HTTPRequest#content_type=:
req = Net::HTTP::Post.new(uri) req.body = multipart_data req.content_type = 'multipart/form-data'
Другие запросы, которые могут содержать тело, такие как PUT, можно создать аналогичным образом, используя соответствующий класс запроса (Net::HTTP::Put).
Установление заголовков
Следующий пример выполняет условный GET запрос с использованием заголовка If-Modified-Since. Если файлы не были изменены с момента указанного времени в заголовке, будет возвращён ответ Not Modified. Подробности см. в разделе 9.3 RFC 2616.
uri = URI('http://example.com/cached_response')
file = File.stat 'cached_response'
req = Net::HTTP::Get.new(uri)
req['If-Modified-Since'] = file.mtime.rfc2822
res = Net::HTTP.start(uri.hostname, uri.port) {|http|
http.request(req)
}
open 'cached_response', 'w' do |io|
io.write res.body
end if res.is_a?(Net::HTTPSuccess)
Аутентификация Basic
Аутентификация Basic выполняется в соответствии с [RFC2617](www.ietf.org/rfc/rfc2617.txt)
uri = URI('http://example.com/index.html?key=value')
req = Net::HTTP::Get.new(uri)
req.basic_auth 'user', 'pass'
res = Net::HTTP.start(uri.hostname, uri.port) {|http|
http.request(req)
}
puts res.body
Потоковая передача тел ответов
По умолчанию Net::HTTP считывает весь ответ в память. Если вы работаете с большими файлами или хотите реализовать индикатор прогресса, вы можете вместо этого передать тело непосредственно в IO.
uri = URI('http://example.com/large_file')
Net::HTTP.start(uri.host, uri.port) do |http|
request = Net::HTTP::Get.new uri
http.request request do |response|
open 'large_file', 'w' do |io|
response.read_body do |chunk|
io.write chunk
end
end
end
end
HTTPS
HTTPS активируется для соединения HTTP с помощью #use_ssl=.
uri = URI('https://secure.example.com/some_path?query=string')
Net::HTTP.start(uri.host, uri.port,
:use_ssl => uri.scheme == 'https') do |http|
request = Net::HTTP::Get.new uri
response = http.request request # Net::HTTPResponse object
end
В предыдущих версиях Ruby вам нужно было подключить 'net/https', чтобы использовать HTTPS. Сейчас это не нужно.
Прокси
Net::HTTP автоматически создаст прокси из переменной среды http_proxy , если она присутствует. Чтобы отключить использование http_proxy, передайте nil в качестве адреса прокси.
Вы также можете создать пользовательский прокси:
proxy_addr = 'your.proxy.host'
proxy_port = 8080
Net::HTTP.new('example.com', nil, proxy_addr, proxy_port).start { |http|
# always proxy via your.proxy.addr:8080
}
См. ::new для получения дополнительной информации и примеров, таких как прокси, требующие имени пользователя и пароля.
Сжатие
Net::HTTP автоматически добавляет Accept-Encoding для сжатия тел ответов и автоматически распаковывает ответы gzip и deflate, если не был отправлен заголовок Range.
Сжатие можно отключить, установив заголовок Accept-Encoding: identity.
Классы запросов HTTP
Вот иерархия классов запросов HTTP.
Классы ответов HTTP
Вот иерархия классов ответов HTTP. Все классы определены в модуле Net и являются подклассами Net::HTTPResponse.
- HTTPUnknownResponse
-
Для необработанных расширений HTTP
- HTTPInformation
-
1xx
- HTTPContinue
-
100
- HTTPSwitchProtocol
-
101
- HTTPSuccess
-
2xx
- HTTPOK
-
200
- HTTPCreated
-
201
- HTTPAccepted
-
202
- HTTPNonAuthoritativeInformation
-
203
- HTTPNoContent
-
204
- HTTPResetContent
-
205
- HTTPPartialContent
-
206
- HTTPMultiStatus
-
207
- HTTPIMUsed
-
226
- HTTPRedirection
-
3xx
- HTTPMultipleChoices
-
300
- HTTPMovedPermanently
-
301
- HTTPFound
-
302
- HTTPSeeOther
-
303
- HTTPNotModified
-
304
- HTTPUseProxy
-
305
- HTTPTemporaryRedirect
-
307
- HTTPClientError
-
4xx
- HTTPBadRequest
-
400
- HTTPUnauthorized
-
401
- HTTPPaymentRequired
-
402
- HTTPForbidden
-
403
- HTTPNotFound
-
404
- HTTPMethodNotAllowed
-
405
- HTTPNotAcceptable
-
406
- HTTPProxyAuthenticationRequired
-
407
- HTTPRequestTimeOut
-
408
- HTTPConflict
-
409
- HTTPGone
-
410
- HTTPLengthRequired
-
411
- HTTPPreconditionFailed
-
412
- HTTPRequestEntityTooLarge
-
413
- HTTPRequestURITooLong
-
414
- HTTPUnsupportedMediaType
-
415
- HTTPRequestedRangeNotSatisfiable
-
416
- HTTPExpectationFailed
-
417
- HTTPUnprocessableEntity
-
422
- HTTPLocked
-
423
- HTTPFailedDependency
-
424
- HTTPUpgradeRequired
-
426
- HTTPPreconditionRequired
-
428
- HTTPTooManyRequests
-
429
- HTTPRequestHeaderFieldsTooLarge
-
431
- HTTPServerError
-
5xx
- HTTPInternalServerError
-
500
- HTTPNotImplemented
-
501
- HTTPBadGateway
-
502
- HTTPServiceUnavailable
-
503
- HTTPGatewayTimeOut
-
504
- HTTPVersionNotSupported
-
505
- HTTPInsufficientStorage
-
507
- HTTPNetworkAuthenticationRequired
-
511
Также существует исключение Net::HTTPBadResponse, которое генерируется при возникновении ошибки протокола.
Константы
- SSL_ATTRIBUTES
- SSL_IVNAMES
Атрибуты
Адрес прокси-сервера. Если Net::HTTP не использует прокси, nil.
Пароль пользователя для доступа к прокси. Если Net::HTTP не использует прокси, nil.
Номер порта прокси-сервера. Если Net::HTTP не использует прокси, nil.
Имя пользователя для доступа к прокси. Если Net::HTTP не использует прокси, nil.
Имя хоста DNS или IP-адрес для подключения.
Устанавливает путь к файлу сертификатов CA в формате PEM.
Файл может содержать несколько сертификатов CA.
Устанавливает путь к каталогу сертификатов CA, содержащему сертификаты в формате PEM.
Устанавливает объект OpenSSL::X509::Certificate в качестве клиентского сертификата. (Этот метод появился в расширении Michal Rokos's OpenSSL).
Устанавливает X509::Store для проверки сертификата узла.
Устанавливает доступные шифры. См. OpenSSL::SSL::SSLContext#ciphers=
Секунды ожидания ответа 100 Continue. Если объект HTTP не получает ответа в течение этого времени, он отправляет тело запроса. Значение по умолчанию — nil.
Устанавливает объект OpenSSL::PKey::RSA или OpenSSL::PKey::DSA. (Этот метод появился в расширении Michal Rokos's OpenSSL).
Локальный хост, используемый для установления соединения.
Локальный порт, используемый для установления соединения.
Количество секунд ожидания открытия соединения. Можно использовать любое число, включая дробные значения. Если объект HTTP не может открыть соединение в течение этого времени, он генерирует исключение Net::OpenTimeout. Значение по умолчанию — 60 секунд.
Номер порта для подключения.
Количество секунд ожидания чтения одного блока (через вызов read(2)). Можно использовать любое число, включая дробные значения. Если объект HTTP не может прочитать данные в течение этого времени, он генерирует исключение Net::ReadTimeout. Значение по умолчанию — 60 секунд.
Устанавливает таймаут SSL в секундах.
Устанавливает версию SSL. См. OpenSSL::SSL::SSLContext#ssl_version=
Устанавливает обратный вызов для проверки сертификата сервера.
Устанавливает максимальную глубину проверки цепочки сертификатов.
Устанавливает флаги для проверки сертификата сервера в начале сеанса SSL/TLS.
Допустимы OpenSSL::SSL::VERIFY_NONE или OpenSSL::SSL::VERIFY_PEER.
Методы публичного класса
# File lib/net/http.rb, line 980
def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil)
return self unless p_addr
Class.new(self) {
@is_proxy_class = true
if p_addr == :ENV then
@proxy_from_env = true
@proxy_address = nil
@proxy_port = nil
else
@proxy_from_env = false
@proxy_address = p_addr
@proxy_port = p_port || default_port
end
@proxy_user = p_user
@proxy_pass = p_pass
}
end Создаёт класс прокси-сервера HTTP, который ведет себя как Net::HTTP, но выполняет все запросы через указанный прокси-сервер.
Этот класс устарел. Вы можете передать эти же параметры напрямую в ::new. Подробности об аргументах см. в ::new.
# File lib/net/http.rb, line 519 def HTTP.default_port http_default_port() end
Порт по умолчанию для запросов HTTP; по умолчанию равен 80.
# File lib/net/http.rb, line 455 def HTTP.get(uri_or_host, path = nil, port = nil) get_response(uri_or_host, path, port).body end
Отправляет запрос GET на целевой ресурс и возвращает ответ HTTP в виде строки. Цель может быть указана как (uri), или как (host, path, port = 80); таким образом:
print Net::HTTP.get(URI('http://www.example.com/index.html'))
или:
print Net::HTTP.get('www.example.com', '/index.html')
# File lib/net/http.rb, line 436
def HTTP.get_print(uri_or_host, path = nil, port = nil)
get_response(uri_or_host, path, port) {|res|
res.read_body do |chunk|
$stdout.print chunk
end
}
nil
end Получает текст тела с целевого ресурса и выводит его в $stdout. Цель может быть указана как (uri), или как (host, path, port = 80); таким образом:
Net::HTTP.get_print URI('http://www.example.com/index.html')
или:
Net::HTTP.get_print 'www.example.com', '/index.html'
# File lib/net/http.rb, line 471
def HTTP.get_response(uri_or_host, path = nil, port = nil, &block)
if path
host = uri_or_host
new(host, port || HTTP.default_port).start {|http|
return http.request_get(path, &block)
}
else
uri = uri_or_host
start(uri.hostname, uri.port,
:use_ssl => uri.scheme == 'https') {|http|
return http.request_get(uri, &block)
}
end
end Отправляет запрос GET на целевой ресурс и возвращает ответ HTTP в виде объекта Net::HTTPResponse. Цель может быть указана как (uri), или как (host, path, port = 80); таким образом:
res = Net::HTTP.get_response(URI('http://www.example.com/index.html'))
print res.body
или:
res = Net::HTTP.get_response('www.example.com', '/index.html')
print res.body
# File lib/net/http.rb, line 524 def HTTP.http_default_port 80 end
Порт по умолчанию для запросов HTTP; по умолчанию равен 80.
# File lib/net/http.rb, line 529 def HTTP.https_default_port 443 end
Порт по умолчанию для запросов HTTPS; по умолчанию равен 443.
# File lib/net/http.rb, line 634
def initialize(address, port = nil)
@address = address
@port = (port || HTTP.default_port)
@local_host = nil
@local_port = nil
@curr_http_version = HTTPVersion
@keep_alive_timeout = 2
@last_communicated = nil
@close_on_empty_response = false
@socket = nil
@started = false
@open_timeout = 60
@read_timeout = 60
@continue_timeout = nil
@debug_output = nil
@proxy_from_env = false
@proxy_uri = nil
@proxy_address = nil
@proxy_port = nil
@proxy_user = nil
@proxy_pass = nil
@use_ssl = false
@ssl_context = nil
@ssl_session = nil
@sspi_enabled = false
SSL_IVNAMES.each do |ivname|
instance_variable_set ivname, nil
end
end Создаёт новый объект Net::HTTP для указанного адреса сервера, не открывая TCP-соединение или не инициализируя сеанс HTTP. address должен быть именем хоста DNS или IP-адресом.
# File lib/net/http.rb, line 610
def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil)
http = super address, port
if proxy_class? then # from Net::HTTP::Proxy()
http.proxy_from_env = @proxy_from_env
http.proxy_address = @proxy_address
http.proxy_port = @proxy_port
http.proxy_user = @proxy_user
http.proxy_pass = @proxy_pass
elsif p_addr == :ENV then
http.proxy_from_env = true
else
http.proxy_address = p_addr
http.proxy_port = p_port || default_port
http.proxy_user = p_user
http.proxy_pass = p_pass
end
http
end Создаёт новый объект Net::HTTP без открытия TCP-соединения или HTTP-сеанса.
address должен быть именем хоста DNS или IP-адресом, port - порт работы сервера. Если port не указан, используется порт по умолчанию для HTTP или HTTPS.
Если ни один из аргументов p_ не указан, адрес и порт прокси-сервера берутся из переменной окружения http_proxy (или её аналога с заглавными буквами), если она задана. Если прокси-сервер требует аутентификацию, вы должны указать её вручную. Подробности о выявлении прокси-сервера из переменных окружения см. в URI::Generic#find_proxy. Для отключения выявления прокси-сервера установите p_addr в значение nil.
Если вы подключаетесь к пользовательскому прокси-серверу, укажите p_addr имя хоста DNS или IP-адрес прокси-сервера, p_port порт для доступа к прокси-серверу, а также p_user и p_pass имя пользователя и пароль, если для использования прокси-сервера требуется авторизация.
# File lib/net/http.rb, line 504
def HTTP.post_form(url, params)
req = Post.new(url)
req.form_data = params
req.basic_auth url.user, url.password if url.user
start(url.hostname, url.port,
:use_ssl => url.scheme == 'https' ) {|http|
http.request(req)
}
end Отправляет данные HTML-формы на указанный объект URI. Данные формы должны быть предоставлены в виде Hash, сопоставляющего строку со строкой. Пример:
{ "cmd" => "search", "q" => "ruby", "max" => "50" }
Этот метод также выполняет базовая аутентификацию, если url.user существует. Однако информация о пользователе для аутентификации устарела (RFC3986). Поэтому эта функция будет удалена.
Пример:
require 'net/http'
require 'uri'
Net::HTTP.post_form URI('http://www.example.com/search.cgi'),
{ "q" => "ruby", "max" => "50" }
# File lib/net/http.rb, line 1003 def proxy_class? defined?(@is_proxy_class) ? @is_proxy_class : false end
возвращает true, если self - это класс, созданный HTTP::Proxy.
# File lib/net/http.rb, line 567
def HTTP.start(address, *arg, &block) # :yield: +http+
arg.pop if opt = Hash.try_convert(arg[-1])
port, p_addr, p_port, p_user, p_pass = *arg
port = https_default_port if !port && opt && opt[:use_ssl]
http = new(address, port, p_addr, p_port, p_user, p_pass)
if opt
if opt[:use_ssl]
opt = {verify_mode: OpenSSL::SSL::VERIFY_PEER}.update(opt)
end
http.methods.grep(/\A(\w+)=\z/) do |meth|
key = $1.to_sym
opt.key?(key) or next
http.__send__(meth, opt[key])
end
end
http.start(&block)
end Создаёт новый объект Net::HTTP, затем дополнительно открывает TCP-соединение и HTTP-сеанс.
Аргументы:
- address
-
имя хоста или IP-адрес сервера
- port
-
порт сервера
- p_addr
-
адрес прокси-сервера
- p_port
-
порт прокси-сервера
- p_user
-
пользователь прокси-сервера
- p_pass
-
пароль прокси-сервера
- opt
-
необязательный хеш
opt устанавливает следующие значения с помощью своего доступара. Ключи — #ca_file, #ca_path, cert, #cert_store, ciphers, #close_on_empty_response, key, #open_timeout, #read_timeout, #ssl_timeout, #ssl_version, use_ssl, #verify_callback, #verify_depth и verify_mode. Если вы установите :use_ssl в значение true, вы можете использовать https, и значение #verify_mode по умолчанию устанавливается как OpenSSL::SSL::VERIFY_PEER.
Если необязательный блок указан, созданный объект Net::HTTP передаётся в него, а закрывается, когда выполняется блок. В этом случае возвращаемое значение этого метода — возвращаемое значение блока. Если блок не указан, возвращаемое значение этого метода — только что созданный объект Net::HTTP, и вызывающий метод отвечает за его закрытие по завершении, используя метод finish().
# File lib/net/http.rb, line 402 def HTTP.version_1_2 true end
Включает функции net/http 1.2 (Ruby 1.8). По умолчанию включено в Ruby 1.8 и более поздних версиях.
# File lib/net/http.rb, line 408 def HTTP.version_1_2? true end
Возвращает true, если net/http находится в режиме версии 1.2. По умолчанию true.
Методы публичного экземпляра
# File lib/net/http.rb, line 726 def continue_timeout=(sec) @socket.continue_timeout = sec if @socket @continue_timeout = sec end
Устанавливает значение атрибута #continue_timeout.
# File lib/net/http.rb, line 1252 def copy(path, initheader = nil) request(Copy.new(path, initheader)) end
Отправляет запрос COPY на path и получает ответ в виде объекта HTTPResponse.
# File lib/net/http.rb, line 1240
def delete(path, initheader = {'Depth' => 'Infinity'})
request(Delete.new(path, initheader))
end Отправляет запрос DELETE на path и получает ответ в виде объекта HTTPResponse.
# File lib/net/http.rb, line 949 def finish raise IOError, 'HTTP session not yet started' unless started? do_finish end
Завершает сеанс HTTP и закрывает TCP-соединение. Вызывает IOError, если сеанс не был начат.
# File lib/net/http.rb, line 1140
def get(path, initheader = nil, dest = nil, &block) # :yield: +body_segment+
res = nil
request(Get.new(path, initheader)) {|r|
r.read_body dest, &block
res = r
}
res
end Извлекает данные с path по подключенному хосту. Может быть строкой абсолютного пути или URI для извлечения пути.
initheader должно быть массивом-хэш, например { 'Accept' => '/', … }, и по умолчанию это пустой хэш. Если у initheader нет ключа 'accept-encoding', то используется значение «gzip;q=1.0,deflate;q=0.6,identity;q=0.3», для использования сжатия gzip в приоритете перед deflate, а deflate в приоритете перед отсутствием сжатия. Ruby не имеет библиотек для поддержки сжатия (Lempel-Ziv), поэтому оно не поддерживается. Цель заключается в сокращении пропускной способности по умолчанию. Если эта процедура настраивает сжатие, она также выполняет разархивирование, удаляя заголовок, чтобы избежать путаницы. В противном случае она оставляет тело как есть.
Этот метод возвращает объект Net::HTTPResponse.
Если используется блок, то каждая часть тела сущности передаётся в блок как строка по мере чтения из сокета. Обратите внимание, что в этом случае возвращаемый объект ответа не будет содержать тело (осмысленное).
Аргумент dest устарел. Он всё ещё работает, но его не следует использовать.
Этот метод никогда не вызывает исключения.
response = http.get('/index.html')
# using block
File.open('result.txt', 'w') {|f|
http.get('/~foo/') do |str|
f.write str
end
}
# File lib/net/http.rb, line 1162 def head(path, initheader = nil) request(Head.new(path, initheader)) end
Получает только заголовок с path по подключенному хосту. header — это хэш, например { 'Accept' => '/', … }.
Этот метод возвращает объект Net::HTTPResponse.
Этот метод никогда не вызывает исключения.
response = nil
Net::HTTP.start('some.www.server', 80) {|http|
response = http.head('/index.html')
}
p response['content-type']
# File lib/net/http.rb, line 666
def inspect
"#<#{self.class} #{@address}:#{@port} open=#{started?}>"
end # File lib/net/http.rb, line 1216 def lock(path, body, initheader = nil) request(Lock.new(path, initheader), body) end
Отправляет запрос LOCK на path и получает ответ в виде объекта HTTPResponse.
# File lib/net/http.rb, line 1258 def mkcol(path, body = nil, initheader = nil) request(Mkcol.new(path, initheader), body) end
Отправляет запрос MKCOL на path и получает ответ в виде объекта HTTPResponse.
# File lib/net/http.rb, line 1246 def move(path, initheader = nil) request(Move.new(path, initheader)) end
Отправляет запрос MOVE на path и получает ответ в виде объекта HTTPResponse.
# File lib/net/http.rb, line 1228 def options(path, initheader = nil) request(Options.new(path, initheader)) end
Отправляет запрос OPTIONS на path и получает ответ в виде объекта HTTPResponse.
# File lib/net/http.rb, line 1200 def patch(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+ send_entity(path, data, initheader, dest, Patch, &block) end
Отправляет запрос PATCH на path и получает ответ в виде объекта HTTPResponse.
# File lib/net/http.rb, line 832
def peer_cert
if not use_ssl? or not @socket
return nil
end
@socket.io.peer_cert
end Возвращает сертификаты X.509, представленные сервером.
# File lib/net/http.rb, line 1194 def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+ send_entity(path, data, initheader, dest, Post, &block) end
Отправляет data (должно быть строкой) на path. header должно быть массивом-хэш, например { 'Accept' => '/', … }.
Этот метод возвращает объект Net::HTTPResponse.
Если используется блок, то каждая часть тела сущности передаётся в блок как строка по мере чтения из сокета. Обратите внимание, что в этом случае возвращаемый объект ответа не будет содержать тело (осмысленное).
Аргумент dest устарел. Он всё ещё работает, но его не следует использовать.
Этот метод никогда не вызывает исключения.
response = http.post('/cgi-bin/search.rb', 'query=foo')
# using block
File.open('result.txt', 'w') {|f|
http.post('/cgi-bin/search.rb', 'query=foo') do |str|
f.write str
end
}
Для POST необходимо задать поле заголовка Content-Type. Если поле Content-Type не задано, по умолчанию используется «application/x-www-form-urlencoded».
# File lib/net/http.rb, line 1234
def propfind(path, body = nil, initheader = {'Depth' => '0'})
request(Propfind.new(path, initheader), body)
end Отправляет запрос PROPFIND на path и получает ответ в виде объекта HTTPResponse.
# File lib/net/http.rb, line 1210 def proppatch(path, body, initheader = nil) request(Proppatch.new(path, initheader), body) end
Отправляет запрос PROPPATCH на path и получает ответ в виде объекта HTTPResponse.
# File lib/net/http.rb, line 1022
def proxy?
!!if @proxy_from_env then
proxy_uri
else
@proxy_address
end
end Истина, если запросы для этого подключения будут проксироваться
# File lib/net/http.rb, line 1043
def proxy_address
if @proxy_from_env then
proxy_uri&.hostname
else
@proxy_address
end
end Адрес прокси-сервера, если он настроен.
# File lib/net/http.rb, line 1031 def proxy_from_env? @proxy_from_env end
Истинно, если прокси для этого соединения определяется из среды.
# File lib/net/http.rb, line 1066 def proxy_pass @proxy_pass end
Пароль прокси, если он настроен.
# File lib/net/http.rb, line 1052
def proxy_port
if @proxy_from_env then
proxy_uri&.port
else
@proxy_port
end
end Порт прокси-сервера, если он настроен.
# File lib/net/http.rb, line 1061 def proxy_user @proxy_user end
Имя пользователя прокси, если оно настроен.
# File lib/net/http.rb, line 715 def read_timeout=(sec) @socket.read_timeout = sec if @socket @read_timeout = sec end
Метод установки атрибута #read_timeout.
# File lib/net/http.rb, line 1382
def request(req, body = nil, &block) # :yield: +response+
unless started?
start {
req['connection'] ||= 'close'
return request(req, body, &block)
}
end
if proxy_user()
req.proxy_basic_auth proxy_user(), proxy_pass() unless use_ssl?
end
req.set_body_internal body
res = transport_request(req, &block)
if sspi_auth?(res)
sspi_auth(req)
res = transport_request(req, &block)
end
res
end Отправляет объект HTTPRequest req на сервер HTTP.
Если req это запрос Net::HTTP::Post или Net::HTTP::Put содержащий данные, данные также отправляются. Предоставление данных для запроса Net::HTTP::Head или Net::HTTP::Get приводит к ошибке ArgumentError.
Возвращает объект HTTPResponse.
При вызове с блоком передает объект HTTPResponse в блок. Тело ответа ещё не будет считано; блок может обработать его, используя Net::HTTPResponse#read_body, если это необходимо.
Этот метод никогда не генерирует исключения Net::*.
# File lib/net/http.rb, line 1293 def request_get(path, initheader = nil, &block) # :yield: +response+ request(Get.new(path, initheader), &block) end
Отправляет запрос GET на path. Возвращает ответ как объект Net::HTTPResponse.
При вызове с блоком передает объект HTTPResponse в блок. Тело ответа ещё не будет считано; блок может обработать его, используя Net::HTTPResponse#read_body, если это необходимо.
Возвращает ответ.
Этот метод никогда не генерирует исключения Net::*.
response = http.request_get('/index.html')
# The entity body is already read in this case.
p response['content-type']
puts response.body
# Using a block
http.request_get('/index.html') {|response|
p response['content-type']
response.read_body do |str| # read body now
print str
end
}
# File lib/net/http.rb, line 1307 def request_head(path, initheader = nil, &block) request(Head.new(path, initheader), &block) end
Отправляет запрос HEAD на path и возвращает ответ в виде объекта Net::HTTPResponse.
Возвращает ответ.
Этот метод никогда не генерирует исключения Net::*.
response = http.request_head('/index.html')
p response['content-type']
# File lib/net/http.rb, line 1337 def request_post(path, data, initheader = nil, &block) # :yield: +response+ request Post.new(path, initheader), data, &block end
Отправляет запрос POST на path.
Возвращает ответ как объект Net::HTTPResponse.
При вызове с блоком, блок получает объект HTTPResponse. Тело ответа ещё не будет считано; блок может обработать его, используя Net::HTTPResponse#read_body, если это необходимо.
Возвращает ответ.
Этот метод никогда не генерирует исключения Net::*.
# example
response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...')
p response.status
puts response.body # body is already read in this case
# using block
http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response|
p response.status
p response['content-type']
response.read_body do |str| # read body now
print str
end
}
# File lib/net/http.rb, line 1361 def send_request(name, path, data = nil, header = nil) has_response_body = name != 'HEAD' r = HTTPGenericRequest.new(name,(data ? true : false),has_response_body,path,header) request r, data end
Отправляет запрос HTTP на сервер HTTP. Также отправляет строку DATA, если data задано.
Возвращает объект Net::HTTPResponse.
Этот метод никогда не генерирует исключения Net::*.
response = http.send_request('GET', '/index.html')
puts response.body
# File lib/net/http.rb, line 679 def set_debug_output(output) warn 'Net::HTTP#set_debug_output called after HTTP started' if started? @debug_output = output end
ПРЕДУПРЕЖДЕНИЕ Этот метод создаёт серьёзную уязвимость безопасности. Никогда не используйте этот метод в рабочем коде.
Устанавливает поток вывода для отладки.
http = Net::HTTP.new(hostname)
http.set_debug_output $stderr
http.start { .... } # File lib/net/http.rb, line 848
def start # :yield: http
raise IOError, 'HTTP session already opened' if @started
if block_given?
begin
do_start
return yield(self)
ensure
do_finish
end
end
do_start
self
end Открывает TCP-соединение и сеанс HTTP.
Когда этот метод вызывается с блоком, он передаёт объект Net::HTTP в блок и закрывает TCP-соединение и сеанс HTTP после выполнения блока.
При вызове с блоком возвращает результат блока; в противном случае возвращает self.
# File lib/net/http.rb, line 738 def started? @started end
Возвращает true, если сеанс HTTP был начат.
# File lib/net/http.rb, line 1264 def trace(path, initheader = nil) request(Trace.new(path, initheader)) end
Отправляет запрос TRACE на path и получает ответ в виде объекта HTTPResponse.
# File lib/net/http.rb, line 1222 def unlock(path, body, initheader = nil) request(Unlock.new(path, initheader), body) end
Отправляет запрос UNLOCK на path и получает ответ в виде объекта HTTPResponse.
# File lib/net/http.rb, line 755
def use_ssl=(flag)
flag = flag ? true : false
if started? and @use_ssl != flag
raise IOError, "use_ssl value changed, but session already started"
end
@use_ssl = flag
end Включение/выключение SSL. Этот флаг должен быть установлен до начала сеанса. Если вы измените значение use_ssl после начала сеанса, объект Net::HTTP генерирует исключение IOError.
# File lib/net/http.rb, line 747 def use_ssl? @use_ssl end
Возвращает true, если с HTTP используется SSL/TLS.
Методы частного экземпляра
# File lib/net/http.rb, line 1551 def D(msg) return unless @debug_output @debug_output << msg @debug_output << "\n" end
# File lib/net/http.rb, line 1543
def addr_port
if use_ssl?
address() + (port == HTTP.https_default_port ? '' : ":#{port()}")
else
address() + (port == HTTP.http_default_port ? '' : ":#{port()}")
end
end utils
# File lib/net/http.rb, line 1460
def begin_transport(req)
if @socket.closed?
connect
elsif @last_communicated
if @last_communicated + @keep_alive_timeout < Process.clock_gettime(Process::CLOCK_MONOTONIC)
D 'Conn close because of keep_alive_timeout'
@socket.close
connect
elsif @socket.io.to_io.wait_readable(0) && @socket.eof?
D "Conn close because of EOF"
@socket.close
connect
end
end
if not req.response_body_permitted? and @close_on_empty_response
req['connection'] ||= 'close'
end
req.update_uri address, port, use_ssl?
req['host'] ||= addr_port()
end # File lib/net/http.rb, line 868
def connect
if proxy? then
conn_address = proxy_address
conn_port = proxy_port
else
conn_address = address
conn_port = port
end
D "opening connection to #{conn_address}:#{conn_port}..."
s = Timeout.timeout(@open_timeout, Net::OpenTimeout) {
begin
TCPSocket.open(conn_address, conn_port, @local_host, @local_port)
rescue => e
raise e, "Failed to open TCP connection to " +
"#{conn_address}:#{conn_port} (#{e.message})"
end
}
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
D "opened"
if use_ssl?
ssl_parameters = Hash.new
iv_list = instance_variables
SSL_IVNAMES.each_with_index do |ivname, i|
if iv_list.include?(ivname) and
value = instance_variable_get(ivname)
ssl_parameters[SSL_ATTRIBUTES[i]] = value if value
end
end
@ssl_context = OpenSSL::SSL::SSLContext.new
@ssl_context.set_params(ssl_parameters)
D "starting SSL for #{conn_address}:#{conn_port}..."
s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
s.sync_close = true
D "SSL established"
end
@socket = BufferedIO.new(s)
@socket.read_timeout = @read_timeout
@socket.continue_timeout = @continue_timeout
@socket.debug_output = @debug_output
if use_ssl?
begin
if proxy?
buf = "CONNECT #{@address}:#{@port} HTTP/#{HTTPVersion}\r\n"
buf << "Host: #{@address}:#{@port}\r\n"
if proxy_user
credential = ["#{proxy_user}:#{proxy_pass}"].pack('m')
credential.delete!("\r\n")
buf << "Proxy-Authorization: Basic #{credential}\r\n"
end
buf << "\r\n"
@socket.write(buf)
HTTPResponse.read_new(@socket).value
end
# Server Name Indication (SNI) RFC 3546
s.hostname = @address if s.respond_to? :hostname=
if @ssl_session and
Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout
s.session = @ssl_session if @ssl_session
end
ssl_socket_connect(s, @open_timeout)
if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
s.post_connection_check(@address)
end
@ssl_session = s.session
rescue => exception
D "Conn close because of connect error #{exception}"
@socket.close if @socket and not @socket.closed?
raise exception
end
end
on_connect
end # File lib/net/http.rb, line 954 def do_finish @started = false @socket.close if @socket and not @socket.closed? @socket = nil end
# File lib/net/http.rb, line 862 def do_start connect @started = true end
# File lib/net/http.rb, line 1085
def edit_path(path)
if proxy?
if path.start_with?("ftp://") || use_ssl?
path
else
"http://#{addr_port}#{path}"
end
else
path
end
end # File lib/net/http.rb, line 1483
def end_transport(req, res)
@curr_http_version = res.http_version
@last_communicated = nil
if @socket.closed?
D 'Conn socket closed'
elsif not res.body and @close_on_empty_response
D 'Conn close'
@socket.close
elsif keep_alive?(req, res)
D 'Conn keep-alive'
@last_communicated = Process.clock_gettime(Process::CLOCK_MONOTONIC)
else
D 'Conn close'
@socket.close
end
end # File lib/net/http.rb, line 1500
def keep_alive?(req, res)
return false if req.connection_close?
if @curr_http_version <= '1.0'
res.connection_keep_alive?
else # HTTP/1.1 or later
not res.connection_close?
end
end # File lib/net/http.rb, line 943 def on_connect end
# File lib/net/http.rb, line 1405
def send_entity(path, data, initheader, dest, type, &block)
res = nil
request(type.new(path, initheader), data) {|r|
r.read_body dest, &block
res = r
}
res
end Выполняет запрос, использующий представление, и возвращает его тело.
# File lib/net/http.rb, line 1524
def sspi_auth(req)
n = Win32::SSPI::NegotiateAuth.new
req["Proxy-Authorization"] = "Negotiate #{n.get_initial_token}"
# Some versions of ISA will close the connection if this isn't present.
req["Connection"] = "Keep-Alive"
req["Proxy-Connection"] = "Keep-Alive"
res = transport_request(req)
authphrase = res["Proxy-Authenticate"] or return res
req["Proxy-Authorization"] = "Negotiate #{n.complete_authentication(authphrase)}"
rescue => err
raise HTTPAuthenticationError.new('HTTP authentication failed', err)
end # File lib/net/http.rb, line 1509
def sspi_auth?(res)
return false unless @sspi_enabled
if res.kind_of?(HTTPProxyAuthenticationRequired) and
proxy? and res["Proxy-Authenticate"].include?("Negotiate")
begin
require 'win32/sspi'
true
rescue LoadError
false
end
else
false
end
end # File lib/net/http.rb, line 1416
def transport_request(req)
count = 0
begin
begin_transport req
res = catch(:response) {
req.exec @socket, @curr_http_version, edit_path(req.path)
begin
res = HTTPResponse.read_new(@socket)
res.decode_content = req.decode_content
end while res.kind_of?(HTTPInformation)
res.uri = req.uri
res
}
res.reading_body(@socket, req.response_body_permitted?) {
yield res if block_given?
}
rescue Net::OpenTimeout
raise
rescue Net::ReadTimeout, IOError, EOFError,
Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE,
# avoid a dependency on OpenSSL
defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : IOError,
Timeout::Error => exception
if count == 0 && IDEMPOTENT_METHODS_.include?(req.method)
count += 1
@socket.close if @socket and not @socket.closed?
D "Conn close because of error #{exception}, and retry"
retry
end
D "Conn close because of error #{exception}"
@socket.close if @socket and not @socket.closed?
raise
end
end_transport req, res
res
rescue => exception
D "Conn close because of error #{exception}"
@socket.close if @socket and not @socket.closed?
raise exception
end
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.