Commit a223cef5 authored by Praveen Arimbrathodiyil's avatar Praveen Arimbrathodiyil

Updated version 2.2.2 from 'upstream/2.2.2'

with Debian dir 6c0a51a54104f7b35b5ecfcbafcfb7fc815ce8e7
parents 31913f44 0b5ecdbb
......@@ -20,7 +20,7 @@ Metrics/LineLength:
Metrics/MethodLength:
CountComments: false
Max: 22 # TODO: Lower to 15
Max: 25 # TODO: Lower to 15
Metrics/ModuleLength:
CountComments: false
......@@ -55,23 +55,17 @@ Style/EachWithObject:
Style/Encoding:
Enabled: false
Style/EmptyCaseCondition:
Enabled: false
Style/HashSyntax:
EnforcedStyle: hash_rockets
Style/Lambda:
Enabled: false
Style/SingleSpaceBeforeFirstArg:
Enabled: false
Style/SpaceAroundOperators:
MultiSpaceAllowedForOperators:
- "="
- "=>"
- "||"
- "||="
- "&&"
- "&&="
AllowForAlignment: true
Style/SpaceInsideHashLiteralBraces:
EnforcedStyle: no_space
......
bundler_args: --without development doc
language: ruby
sudo: false
before_install:
- gem update --system 2.6.10
- gem --version
- gem install bundler --version 1.14.3 --no-rdoc --no-ri
- bundle --version
install: bundle _1.14.3_ install --without development doc
script: bundle _1.14.3_ exec rake
env:
global:
- JRUBY_OPTS="$JRUBY_OPTS --debug"
language: ruby
rvm:
- 1.9.3
- jruby-9.1.7.0
- 2.0.0
- 2.1
- 2.2
- 2.3.0
- jruby
- jruby-head
- ruby-head
- rbx-2
- 2.3.3
- 2.4.0
matrix:
allow_failures:
- rvm: jruby-head
- rvm: ruby-head
- rvm: rbx-2
fast_finish: true
sudo: false
branches:
only:
- master
## 2.2.2 (2017-04-27)
* [#404](https://github.com/httprb/http/issues/404),
[#405](https://github.com/httprb/http/pull/405)
Make keepalive timeout configurable.
([@nestegg])
## 2.2.1 (2017-02-06)
* [#395](https://github.com/httprb/http/issues/395)
Fix regression of API, that broke webmock integration.
([@ixti])
## 2.2.0 (2017-02-03)
* [#375](https://github.com/httprb/http/pull/375)
Add support for automatic Gzip/Inflate
([@Bonias])
* [#390](https://github.com/httprb/http/pull/390)
Add REPORT to the list of valid HTTP verbs
([@ixti])
## 2.1.0 (2016-11-08)
* [#370](https://github.com/httprb/http/issues/370)
Add Headers#include?
([@ixti])
* [#364](https://github.com/httprb/http/issues/364)
Add HTTP::Response#connection
([@janko-m])
* [#362](https://github.com/httprb/http/issues/362)
connect_ssl uses connect_timeout (Closes #359)
([@TiagoCardoso1983])
## 2.0.3 (2016-08-03)
* [#365](https://github.com/httprb/http/issues/365)
Add `HTTP::Response#content_length`
([@janko-m])
* [#335](https://github.com/httprb/http/issues/335),
[#360](https://github.com/httprb/http/pull/360)
Set `Content-Length: 0` header for `nil` bodies.
([@britishtea])
## 2.0.2 (2016-06-24)
* [#353](https://github.com/httprb/http/pull/353)
Avoid a dependency cycle between Client and Connection classes.
([@jhbabon])
## 2.0.1 (2016-05-12)
* [#341](https://github.com/httprb/http/pull/341)
Refactor some string manipulations so they are more performant
(up to 3-4x faster) and more concise.
([@tonyta])
* [#339](https://github.com/httprb/http/pull/341)
Always use byte methods when writing/slicing the write buffer.
([@zanker])
## 2.0.0 (2016-04-23)
* [#333](https://github.com/httprb/http/pull/333)
Fix HTTPS request headline when sent via proxy.
([@Connorhd])
* [#331](https://github.com/httprb/http/pull/331)
Add `#informational?`, `#success?`, `#redirect?`, `#client_error?` and
`#server_error?` helpers to `Response::Status`.
([@mwitek])
* [#330](https://github.com/httprb/http/pull/330)
Support custom CONNECT headers (request/response) during HTTPS proxy requests.
([@smudge])
* [#319](https://github.com/httprb/http/pull/319)
Drop Ruby 1.9.x support.
([@ixti])
## 1.0.4 (2016-03-19)
* [#320](https://github.com/httprb/http/pull/320)
Fix timeout regression.
([@tarcieri])
## 1.0.3 (2016-03-16)
* [#314](https://github.com/httprb/http/pull/314)
Validate charset before forcing encoding.
([@kylekyle])
* [#318](https://github.com/httprb/http/pull/318)
Remove redundant string allocations upon header names normalization.
([@ixti])
## 1.0.2 (2016-01-15)
* [#295](https://github.com/httprb/http/pull/295):
......@@ -62,6 +172,17 @@
([@tarcieri])
## 0.9.9 (2016-03-16)
* *BACKPORT* [#318](https://github.com/httprb/http/pull/318)
Remove redundant string allocations upon header names normalization.
([@ixti])
* *BACKPORT* [#295](https://github.com/httprb/http/pull/295):
Fix redirect following when used with persistent mode.
([@ixti])
## 0.9.8 (2015-09-29)
* [#260](https://github.com/httprb/http/pull/260):
......@@ -464,3 +585,12 @@ end
[@hundredwatt]: https://github.com/hundredwatt
[@jwinter]: https://github.com/jwinter
[@nerdrew]: https://github.com/nerdrew
[@kylekyle]: https://github.com/kylekyle
[@smudge]: https://github.com/smudge
[@mwitek]: https://github.com/mwitek
[@tonyta]: https://github.com/tonyta
[@jhbabon]: https://github.com/jhbabon
[@britishtea]: https://github.com/britishtea
[@janko-m]: https://github.com/janko-m
[@Bonias]: https://github.com/Bonias
[@nestegg]: https://github.com/nestegg
......@@ -23,3 +23,4 @@ In any case, specify following info in description of your issue:
- What actually happened
- The exception backtrace(s), if any
- Version of gem or commit ref you are using
- Version of ruby you are using
source "https://rubygems.org"
ruby RUBY_VERSION
gem "rake"
gem "rack-cache", "~> 1.2"
group :development do
gem "celluloid-io"
gem "guard"
gem "guard-rspec", :require => false
gem "nokogiri", :require => false
gem "pry"
gem "nokogiri", :require => false
gem "pry", :require => false
platforms :ruby_19, :ruby_20 do
gem "pry-debugger"
gem "pry-stack_explorer"
platform :ruby_20 do
gem "pry-debugger", :require => false
gem "pry-stack_explorer", :require => false
end
end
group :test do
gem "backports"
gem "coveralls"
gem "coveralls", :require => false
gem "simplecov", ">= 0.9"
gem "json", ">= 1.8.1"
gem "rubocop", "= 0.35.1"
gem "rubocop", "= 0.40.0"
gem "rspec", "~> 3.0"
gem "rspec-its"
gem "yardstick"
gem "certificate_authority"
gem "certificate_authority", :require => false
gem "activemodel", :require => false # Used by certificate_authority
end
group :doc do
......
This diff is collapsed.
......@@ -13,7 +13,7 @@ Gem::Specification.new do |gem|
DESCRIPTION
gem.summary = "HTTP should be easy"
gem.homepage = "https://github.com/httprb/http.rb"
gem.homepage = "https://github.com/httprb/http"
gem.licenses = ["MIT"]
gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
......@@ -23,6 +23,8 @@ Gem::Specification.new do |gem|
gem.require_paths = ["lib"]
gem.version = HTTP::VERSION
gem.required_ruby_version = ">= 2.0"
gem.add_runtime_dependency "http_parser.rb", "~> 0.6.0"
gem.add_runtime_dependency "http-form_data", "~> 1.0.1"
gem.add_runtime_dependency "http-cookie", "~> 1.0"
......
# frozen_string_literal: true
require "http/parser"
require "http/errors"
......@@ -20,6 +21,6 @@ module HTTP
class << self
# HTTP[:accept => 'text/html'].get(...)
alias_method :[], :headers
alias [] headers
end
end
# frozen_string_literal: true
require "base64"
require "http/headers"
......@@ -74,10 +75,12 @@ module HTTP
branch(options).request verb, uri
end
# @overload(options = {})
# @overload timeout(options = {})
# Syntax sugar for `timeout(:per_operation, options)`
# @overload(klass, options = {})
# @overload timeout(klass, options = {})
# Adds a timeout to the request.
# @param [#to_sym] klass
# either :null, :global, or :per_operation
# @param [Hash] options
# @option options [Float] :read Read timeout
# @option options [Float] :write Write timeout
......@@ -92,7 +95,7 @@ module HTTP
when :null then HTTP::Timeout::Null
when :global then HTTP::Timeout::Global
when :per_operation then HTTP::Timeout::PerOperation
else fail ArgumentError, "Unsupported Timeout class: #{klass}"
else raise ArgumentError, "Unsupported Timeout class: #{klass}"
end
[:read, :write, :connect].each do |k|
......@@ -106,12 +109,13 @@ module HTTP
)
end
# @overload persistent(host)
# @overload persistent(host, timeout: 5)
# Flags as persistent
# @param [String] host
# @raise [Request::Error] if Host is invalid
# @param [String] host
# @option [Integer] timeout Keep alive timeout
# @raise [Request::Error] if Host is invalid
# @return [HTTP::Client] Persistent client
# @overload persistent(host, &block)
# @overload persistent(host, timeout: 5, &block)
# Executes given block with persistent client and automatically closes
# connection at the end of execution.
#
......@@ -135,12 +139,13 @@ module HTTP
#
# @yieldparam [HTTP::Client] client Persistent client
# @return [Object] result of last expression in the block
def persistent(host)
p_client = branch default_options.with_persistent host
def persistent(host, timeout: 5)
options = {:keep_alive_timeout => timeout}
p_client = branch default_options.merge(options).with_persistent host
return p_client unless block_given?
yield p_client
ensure
p_client.close
p_client.close if p_client
end
# Make a request through an HTTP proxy
......@@ -152,14 +157,14 @@ module HTTP
proxy_hash[:proxy_port] = proxy[1] if proxy[1].is_a?(Integer)
proxy_hash[:proxy_username] = proxy[2] if proxy[2].is_a?(String)
proxy_hash[:proxy_password] = proxy[3] if proxy[3].is_a?(String)
proxy_hash[:proxy_headers] = proxy[2] if proxy[2].is_a?(Hash)
proxy_hash[:proxy_headers] = proxy[4] if proxy[4].is_a?(Hash)
if [2, 4].include?(proxy_hash.keys.size)
branch default_options.with_proxy(proxy_hash)
else
fail(RequestError, "invalid HTTP proxy: #{proxy_hash}")
end
raise(RequestError, "invalid HTTP proxy: #{proxy_hash}") unless (2..5).cover?(proxy_hash.keys.size)
branch default_options.with_proxy(proxy_hash)
end
alias_method :through, :via
alias through via
# Make client follow redirects.
# @param opts
......@@ -206,7 +211,7 @@ module HTTP
user = opts.fetch :user
pass = opts.fetch :pass
auth("Basic " << Base64.strict_encode64("#{user}:#{pass}"))
auth("Basic " + Base64.strict_encode64("#{user}:#{pass}"))
end
# Get options for HTTP
......@@ -227,6 +232,14 @@ module HTTP
branch default_options.with_nodelay(true)
end
# Turn on given features. Available features are:
# * auto_inflate
# * auto_deflate
# @param features
def use(*features)
branch default_options.with_features(features)
end
private
# :nodoc:
......
# frozen_string_literal: true
require "forwardable"
require "http/form_data"
......@@ -13,10 +14,7 @@ module HTTP
extend Forwardable
include Chainable
KEEP_ALIVE = "Keep-Alive".freeze
CLOSE = "close".freeze
HTTP_OR_HTTPS_RE = %r{^https?://}i
HTTP_OR_HTTPS_RE = %r{^https?://}i
def initialize(default_options = {})
@default_options = HTTP::Options.new(default_options)
......@@ -67,12 +65,14 @@ module HTTP
end
res = Response.new(
:status => @connection.status_code,
:version => @connection.http_version,
:headers => @connection.headers,
:connection => @connection,
:encoding => options.encoding,
:uri => req.uri
:status => @connection.status_code,
:version => @connection.http_version,
:headers => @connection.headers,
:proxy_headers => @connection.proxy_response_headers,
:connection => @connection,
:encoding => options.encoding,
:auto_inflate => options.feature(:auto_inflate),
:uri => req.uri
)
@connection.finish_response if req.verb == :head
......@@ -95,7 +95,7 @@ module HTTP
# Verify our request isn't going to be made against another URI
def verify_connection!(uri)
if default_options.persistent? && uri.origin != default_options.persistent
fail StateError, "Persistence is enabled for #{default_options.persistent}, but we got #{uri.origin}"
raise StateError, "Persistence is enabled for #{default_options.persistent}, but we got #{uri.origin}"
# We re-create the connection object because we want to let prior requests
# lazily load the body as long as possible, and this mimics prior functionality.
elsif @connection && (!@connection.keep_alive? || @connection.expired?)
......@@ -137,13 +137,10 @@ module HTTP
headers = opts.headers
# Tell the server to keep the conn open
if default_options.persistent?
headers[Headers::CONNECTION] = KEEP_ALIVE
else
headers[Headers::CONNECTION] = CLOSE
end
headers[Headers::CONNECTION] = default_options.persistent? ? Connection::KEEP_ALIVE : Connection::CLOSE
cookies = opts.cookies.values
unless cookies.empty?
cookies = opts.headers.get(Headers::COOKIE).concat(cookies).join("; ")
headers[Headers::COOKIE] = cookies
......@@ -154,17 +151,24 @@ module HTTP
# Create the request body object to send
def make_request_body(opts, headers)
case
when opts.body
opts.body
when opts.form
form = HTTP::FormData.create opts.form
headers[Headers::CONTENT_TYPE] ||= form.content_type
headers[Headers::CONTENT_LENGTH] ||= form.content_length
form.to_s
when opts.json
headers[Headers::CONTENT_TYPE] ||= "application/json"
MimeType[:json].encode opts.json
request_body =
case
when opts.body
opts.body
when opts.form
form = HTTP::FormData.create opts.form
headers[Headers::CONTENT_TYPE] ||= form.content_type
headers[Headers::CONTENT_LENGTH] ||= form.content_length
form.to_s
when opts.json
body = MimeType[:json].encode opts.json
headers[Headers::CONTENT_TYPE] ||= "application/json; charset=#{body.encoding.name}"
body
end
if (auto_deflate = opts.feature(:auto_deflate))
auto_deflate.deflate(headers, request_body)
else
request_body
end
end
end
......
# frozen_string_literal: true
require "forwardable"
require "http/client"
require "http/headers"
require "http/response/parser"
......@@ -9,6 +9,10 @@ module HTTP
class Connection
extend Forwardable
# Allowed values for CONNECTION header
KEEP_ALIVE = "Keep-Alive".freeze
CLOSE = "close".freeze
# Attempt to read this much data
BUFFER_SIZE = 16_384
......@@ -18,6 +22,9 @@ module HTTP
# HTTP/1.1
HTTP_1_1 = "1.1".freeze
# Returned after HTTP CONNECT (via proxy)
attr_reader :proxy_response_headers
# @param [HTTP::Request] req
# @param [HTTP::Options] options
# @raise [HTTP::ConnectionError] when failed to connect
......@@ -36,8 +43,8 @@ module HTTP
send_proxy_connect_request(req)
start_tls(req, options)
reset_timer
rescue SocketError, SystemCallError => e
raise ConnectionError, "failed to connect: #{e}"
rescue IOError, SocketError, SystemCallError => ex
raise ConnectionError, "failed to connect: #{ex}", ex.backtrace
end
# @see (HTTP::Response::Parser#status_code)
......@@ -59,11 +66,8 @@ module HTTP
# @param [Request] req Request to send to the server
# @return [nil]
def send_request(req)
if @pending_response
fail StateError, "Tried to send a request while one is pending already. Make sure you read off the body."
elsif @pending_request
fail StateError, "Tried to send a request while a response is pending. Make sure you've fully read the body from the request."
end
raise StateError, "Tried to send a request while one is pending already. Make sure you read off the body." if @pending_response
raise StateError, "Tried to send a request while a response is pending. Make sure you read off the body." if @pending_request
@pending_request = true
......@@ -80,13 +84,8 @@ module HTTP
def readpartial(size = BUFFER_SIZE)
return unless @pending_response
if read_more(size) == :eof
finished = true
else
finished = @parser.finished?
end
chunk = @parser.chunk
finished = (read_more(size) == :eof) || @parser.finished?
chunk = @parser.chunk
finish_response if finished
......@@ -98,16 +97,14 @@ module HTTP
def read_headers!
loop do
if read_more(BUFFER_SIZE) == :eof
fail EOFError unless @parser.headers?
raise ConnectionError, "couldn't read response headers" unless @parser.headers?
break
elsif @parser.headers?
break
else
break if @parser.headers?
end
end
set_keep_alive
rescue IOError, Errno::ECONNRESET, Errno::EPIPE => e
raise ConnectionError, "failed to read headers: #{e}"
end
# Callback for when we've reached the end of a response
......@@ -169,18 +166,19 @@ module HTTP
req.connect_using_proxy @socket
@pending_request = false
@pending_request = false
@pending_response = true
read_headers!
@proxy_response_headers = @parser.headers
if @parser.status_code == 200
@parser.reset
@pending_response = false
if @parser.status_code != 200
@failed_proxy_connect = true
return
end
@failed_proxy_connect = true
@parser.reset
@pending_response = false
end
# Resets expiration of persistent connection.
......@@ -195,14 +193,15 @@ module HTTP
def set_keep_alive
return @keep_alive = false unless @persistent
case @parser.http_version
when HTTP_1_0 # HTTP/1.0 requires opt in for Keep Alive
@keep_alive = @parser.headers[Headers::CONNECTION] == Client::KEEP_ALIVE
when HTTP_1_1 # HTTP/1.1 is opt-out
@keep_alive = @parser.headers[Headers::CONNECTION] != Client::CLOSE
else # Anything else we assume doesn't supportit
@keep_alive = false
end
@keep_alive =
case @parser.http_version
when HTTP_1_0 # HTTP/1.0 requires opt in for Keep Alive
@parser.headers[Headers::CONNECTION] == KEEP_ALIVE
when HTTP_1_1 # HTTP/1.1 is opt-out
@parser.headers[Headers::CONNECTION] != CLOSE
else # Anything else we assume doesn't supportit
false
end
end
# Feeds some more data into parser
......@@ -216,6 +215,8 @@ module HTTP
elsif value
@parser << value
end
rescue IOError, SocketError, SystemCallError => ex
raise ConnectionError, "error reading from socket: #{ex}", ex.backtrace
end
end
end
# frozen_string_literal: true
module HTTP
ContentType = Struct.new(:mime_type, :charset) do
MIME_TYPE_RE = %r{^([^/]+/[^;]+)(?:$|;)}
......@@ -13,14 +14,14 @@ module HTTP
# :nodoc:
def mime_type(str)
md = str.to_s.match MIME_TYPE_RE
md && md[1].to_s.strip.downcase
m = str.to_s[MIME_TYPE_RE, 1]
m && m.strip.downcase
end
# :nodoc:
def charset(str)
md = str.to_s.match CHARSET_RE
md && md[1].to_s.strip.gsub(/^"|"$/, "")
m = str.to_s[CHARSET_RE, 1]
m && m.strip.delete('"')
end
end
end
......
# frozen_string_literal: true
module HTTP
# Generic error
class Error < StandardError; end
......
# frozen_string_literal: true
module HTTP
class Feature
def initialize(opts = {})
@opts = opts
end
end
end
# frozen_string_literal: true
require "zlib"
module HTTP
module Features
class AutoDeflate < Feature
attr_reader :method
def initialize(*)
super
@method = @opts.key?(:method) ? @opts[:method].to_s : "gzip"
raise Error, "Only gzip and deflate methods are supported" unless %w(gzip deflate).include?(@method)
end
def deflate(headers, body)
return body unless body
return body unless body.is_a?(String)
# We need to delete Content-Length header. It will be set automatically
# by HTTP::Request::Writer
headers.delete(Headers::CONTENT_LENGTH)
headers[Headers::CONTENT_ENCODING] = method
case method
when "gzip" then
StringIO.open do |out|
Zlib::GzipWriter.wrap(out) do |gz|
gz.write body
gz.finish
out.tap(&:rewind).read
end
end