Commit 7758645e authored by manas kashyap's avatar manas kashyap

New upstream version 3.3.0

parent 0b5ecdbb
AllCops:
DisplayCopNames: true
## Layout ######################################################################
Layout/DotPosition:
EnforcedStyle: trailing
Layout/SpaceAroundOperators:
AllowForAlignment: true
Layout/SpaceInsideHashLiteralBraces:
EnforcedStyle: no_space
## Metrics #####################################################################
Metrics/AbcSize:
Enabled: false
Metrics/BlockLength:
Exclude:
- spec/**/*
Metrics/BlockNesting:
Max: 2
......@@ -8,19 +28,22 @@ Metrics/ClassLength:
CountComments: false
Max: 125
Metrics/PerceivedComplexity:
# TODO: Lower to 6
Metrics/CyclomaticComplexity:
Max: 8
Metrics/CyclomaticComplexity:
Max: 8 # TODO: Lower to 6
Metrics/PerceivedComplexity:
Max: 8
# TODO: Lower to 80
Metrics/LineLength:
AllowURI: true
Max: 143 # TODO: Lower to 80
Max: 143
# TODO: Lower to 15
Metrics/MethodLength:
CountComments: false
Max: 25 # TODO: Lower to 15
Max: 25
Metrics/ModuleLength:
CountComments: false
......@@ -30,9 +53,14 @@ Metrics/ParameterLists:
Max: 5
CountKeywordArgs: true
Metrics/AbcSize:
## Performance #################################################################
# XXX: requires ruby 2.4+
Performance/RegexpMatch:
Enabled: false
## Style #######################################################################
Style/CollectionMethods:
PreferredMethods:
collect: 'map'
......@@ -43,9 +71,6 @@ Style/CollectionMethods:
Style/Documentation:
Enabled: false
Style/DotPosition:
EnforcedStyle: trailing
Style/DoubleNegation:
Enabled: false
......@@ -58,20 +83,28 @@ Style/Encoding:
Style/EmptyCaseCondition:
Enabled: false
# XXX: Lots of times it suggests making code terrible to read.
Style/GuardClause:
Enabled: false
Style/HashSyntax:
EnforcedStyle: hash_rockets
Style/Lambda:
Enabled: false
Style/SpaceAroundOperators:
AllowForAlignment: true
Style/OptionHash:
Enabled: true
Style/SpaceInsideHashLiteralBraces:
EnforcedStyle: no_space
# XXX: requires ruby 2.3+
Style/SafeNavigation:
Enabled: false
Style/StringLiterals:
EnforcedStyle: double_quotes
Style/TrivialAccessors:
Enabled: false
Style/YodaCondition:
Enabled: false
language: ruby
sudo: false
cache: bundler
before_install:
- gem update --system 2.6.10
- gem update --system
- gem --version
- gem install bundler --version 1.14.3 --no-rdoc --no-ri
- gem install bundler --no-rdoc --no-ri
- bundle --version
install: bundle _1.14.3_ install --without development doc
install: bundle install --without development doc
script: bundle _1.14.3_ exec rake
script: bundle exec rake
env:
global:
- JRUBY_OPTS="$JRUBY_OPTS --debug"
env: JRUBY_OPTS="$JRUBY_OPTS --debug"
rvm:
- jruby-9.1.7.0
- 2.0.0
- 2.1
# Include JRuby first because it takes the longest
- jruby-9.1.13.0
- 2.2
- 2.3.3
- 2.4.0
- 2.3.4
- 2.4.1
matrix:
fast_finish: true
include:
# Only run RuboCop and Yardstick metrics on the latest Ruby
- rvm: 2.4.1
env: SUITE="rubocop"
- rvm: 2.4.1
env: SUITE="yardstick"
branches:
only:
......
## 3.3.0 (2018-04-25)
This version backports some of the fixes and improvements made to development
version of the HTTP gem:
* [#458](https://github.com/httprb/http/pull/458)
Extract HTTP::Client#build_request method.
([@tycoon])
## 3.2.1 (2018-04-24)
* [#468](https://github.com/httprb/http/pull/468)
Rewind `HTTP::Request::Body#source` once `#each` is complete.
([@ixti])
## 3.2.0 (2018-04-22)
This version backports one change we missed to backport in previous release:
* Reduce memory usage when reading response body
([@janko-m])
## 3.1.0 (2018-04-22)
This version backports some of the fixes and improvements made to development
version of the HTTP gem:
* Fix for `#readpartial` to respect max length argument.
([@janko-m], [@marshall-lee])
* Fix for `HTTP::Request#headline` to allow two leading slashes in path.
([@scarfacedeb])
* Fix query string building for string with newlines.
([@mikegee])
* Deallocate temporary strings in `Response::Body#to_s`.
([@janko-m])
* Add `Request::Body#source`.
([@janko-m])
## 3.0.0 (2017-10-01)
* Drop support of Ruby `2.0` and Ruby `2.1`.
([@ixti])
* [#410](https://github.com/httprb/http/pull/410)
Infer `Host` header upon redirects.
([@janko-m])
* [#409](https://github.com/httprb/http/pull/409)
Enables request body streaming on any IO object.
([@janko-m])
* [#413](https://github.com/httprb/http/issues/413),
[#414](https://github.com/httprb/http/pull/414)
Fix encoding of body chunks.
([@janko-m])
* [#368](https://github.com/httprb/http/pull/368),
[#357](https://github.com/httprb/http/issues/357)
Fix timeout issue.
([@HoneyryderChuck])
## 2.2.2 (2017-04-27)
* [#404](https://github.com/httprb/http/issues/404),
......@@ -593,4 +663,8 @@ end
[@britishtea]: https://github.com/britishtea
[@janko-m]: https://github.com/janko-m
[@Bonias]: https://github.com/Bonias
[@nestegg]: https://github.com/nestegg
[@HoneyryderChuck]: https://github.com/HoneyryderChuck
[@marshall-lee]: https://github.com/marshall-lee
[@scarfacedeb]: https://github.com/scarfacedeb
[@mikegee]: https://github.com/mikegee
[@tycoon]: https://github.com/tycooon
# frozen_string_literal: true
source "https://rubygems.org"
ruby RUBY_VERSION
......@@ -15,15 +17,20 @@ group :development do
end
group :test do
gem "activemodel", :require => false # Used by certificate_authority
gem "certificate_authority", :require => false
gem "backports"
gem "coveralls", :require => false
gem "simplecov", ">= 0.9"
gem "rubocop", "= 0.40.0"
gem "rspec", "~> 3.0"
gem "rspec", "~> 3.0"
gem "rspec-its"
gem "rubocop", "= 0.49.1"
gem "yardstick"
gem "certificate_authority", :require => false
gem "activemodel", :require => false # Used by certificate_authority
end
group :doc do
......
# frozen_string_literal: true
# More info at https://github.com/guard/guard#readme
guard :rspec, :cmd => "GUARD_RSPEC=1 bundle exec rspec --no-profile" do
......
......@@ -6,10 +6,11 @@
[![Coverage Status](https://coveralls.io/repos/httprb/http/badge.svg?branch=master)](https://coveralls.io/r/httprb/http)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/httprb/http/blob/master/LICENSE.txt)
_NOTE: This is the 2.x **stable** branch. For the 3.x **development** branch, please see:_
_NOTE: This is the 3.x **stable** branch. For the 4.x **development** branch, please see:_
https://github.com/httprb/http/
## About
HTTP (The Gem! a.k.a. http.rb) is an easy-to-use client library for making requests
......@@ -160,10 +161,9 @@ and call `#readpartial` on it repeatedly until it returns `nil`:
This library aims to support and is [tested against][travis] the following Ruby
versions:
* Ruby 2.0.0
* Ruby 2.1.x
* Ruby 2.2.x
* Ruby 2.3.x
* Ruby 2.4.x
* JRuby 9.1.x.x
If something doesn't work on one of these versions, it's a bug.
......@@ -194,5 +194,5 @@ dropped.
## Copyright
Copyright (c) 2011-2016 Tony Arcieri, Erik Michaels-Ober, Alexey V. Zapparov, Zachary Anker.
Copyright (c) 2011-2018 Tony Arcieri, Alexey V. Zapparov, Erik Michaels-Ober, Zachary Anker.
See LICENSE.txt for further details.
#!/usr/bin/env rake
# frozen_string_literal: true
require "bundler/gem_tasks"
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new
task :test => :spec
begin
require "rubocop/rake_task"
RuboCop::RakeTask.new
rescue LoadError
task :rubocop do
$stderr.puts "RuboCop is disabled"
end
end
require "rubocop/rake_task"
RuboCop::RakeTask.new
require "yardstick/rake/measurement"
Yardstick::Rake::Measurement.new do |measurement|
......@@ -36,7 +29,7 @@ task :generate_status_codes do
code = e.xpath("xmlns:value").text.to_s
desc = e.xpath("xmlns:description").text.to_s
next a if "Unassigned" == desc || "(Unused)" == desc
next a if %w[Unassigned (Unused)].include?(desc)
a << "#{code} => #{desc.inspect}"
end
......@@ -68,4 +61,12 @@ task :generate_status_codes do
end
end
task :default => [:spec, :rubocop, :verify_measurements]
if ENV["CI"].nil?
task :default => %i[spec rubocop verify_measurements]
else
case ENV["SUITE"]
when "rubocop" then task :default => :rubocop
when "yardstick" then task :default => :verify_measurements
else task :default => :spec
end
end
# frozen_string_literal: true
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "http/version"
......@@ -23,10 +25,10 @@ Gem::Specification.new do |gem|
gem.require_paths = ["lib"]
gem.version = HTTP::VERSION
gem.required_ruby_version = ">= 2.0"
gem.required_ruby_version = ">= 2.2"
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-form_data", "~> 2.0"
gem.add_runtime_dependency "http-cookie", "~> 1.0"
gem.add_runtime_dependency "addressable", "~> 2.3"
......
# frozen_string_literal: true
require "http/parser"
require "http/errors"
......
# frozen_string_literal: true
require "base64"
require "http/headers"
......@@ -8,73 +9,82 @@ module HTTP
# Request a get sans response body
# @param uri
# @option options [Hash]
def head(uri, options = {})
def head(uri, options = {}) # rubocop:disable Style/OptionHash
request :head, uri, options
end
# Get a resource
# @param uri
# @option options [Hash]
def get(uri, options = {})
def get(uri, options = {}) # rubocop:disable Style/OptionHash
request :get, uri, options
end
# Post to a resource
# @param uri
# @option options [Hash]
def post(uri, options = {})
def post(uri, options = {}) # rubocop:disable Style/OptionHash
request :post, uri, options
end
# Put to a resource
# @param uri
# @option options [Hash]
def put(uri, options = {})
def put(uri, options = {}) # rubocop:disable Style/OptionHash
request :put, uri, options
end
# Delete a resource
# @param uri
# @option options [Hash]
def delete(uri, options = {})
def delete(uri, options = {}) # rubocop:disable Style/OptionHash
request :delete, uri, options
end
# Echo the request back to the client
# @param uri
# @option options [Hash]
def trace(uri, options = {})
def trace(uri, options = {}) # rubocop:disable Style/OptionHash
request :trace, uri, options
end
# Return the methods supported on the given URI
# @param uri
# @option options [Hash]
def options(uri, options = {})
def options(uri, options = {}) # rubocop:disable Style/OptionHash
request :options, uri, options
end
# Convert to a transparent TCP/IP tunnel
# @param uri
# @option options [Hash]
def connect(uri, options = {})
def connect(uri, options = {}) # rubocop:disable Style/OptionHash
request :connect, uri, options
end
# Apply partial modifications to a resource
# @param uri
# @option options [Hash]
def patch(uri, options = {})
def patch(uri, options = {}) # rubocop:disable Style/OptionHash
request :patch, uri, options
end
# Make an HTTP request with the given verb
# @param verb
# @param uri
# @option options [Hash]
def request(verb, uri, options = {})
def request(verb, uri, options = {}) # rubocop:disable Style/OptionHash
branch(options).request verb, uri
end
# Prepare an HTTP request with the given verb
# @param verb
# @param uri
# @option options [Hash]
def build_request(verb, uri, options = {}) # rubocop:disable Style/OptionHash
branch(options).build_request verb, uri
end
# @overload timeout(options = {})
# Syntax sugar for `timeout(:per_operation, options)`
# @overload timeout(klass, options = {})
......@@ -85,7 +95,7 @@ module HTTP
# @option options [Float] :read Read timeout
# @option options [Float] :write Write timeout
# @option options [Float] :connect Connect timeout
def timeout(klass, options = {})
def timeout(klass, options = {}) # rubocop:disable Style/OptionHash
if klass.is_a? Hash
options = klass
klass = :per_operation
......@@ -98,7 +108,7 @@ module HTTP
else raise ArgumentError, "Unsupported Timeout class: #{klass}"
end
[:read, :write, :connect].each do |k|
%i[read write connect].each do |k|
next unless options.key? k
options["#{k}_timeout".to_sym] = options.delete k
end
......@@ -170,8 +180,8 @@ module HTTP
# @param opts
# @return [HTTP::Client]
# @see Redirector#initialize
def follow(opts = {})
branch default_options.with_follow opts
def follow(options = {}) # rubocop:disable Style/OptionHash
branch default_options.with_follow options
end
# Make a request with the given headers
......
# frozen_string_literal: true
require "forwardable"
require "http/form_data"
......@@ -23,27 +24,33 @@ module HTTP
end
# Make an HTTP request
def request(verb, uri, opts = {})
def request(verb, uri, opts = {}) # rubocop:disable Style/OptionHash
opts = @default_options.merge(opts)
req = build_request(verb, uri, opts)
res = perform(req, opts)
return res unless opts.follow
Redirector.new(opts.follow).perform(req, res) do |request|
perform(request, opts)
end
end
# Prepare an HTTP request
def build_request(verb, uri, opts = {}) # rubocop:disable Style/OptionHash
opts = @default_options.merge(opts)
uri = make_request_uri(uri, opts)
headers = make_request_headers(opts)
body = make_request_body(opts, headers)
proxy = opts.proxy
req = HTTP::Request.new(
:verb => verb,
:uri => uri,
:headers => headers,
:proxy => proxy,
:body => body
HTTP::Request.new(
:verb => verb,
:uri => uri,
:headers => headers,
:proxy => proxy,
:body => body,
:auto_deflate => opts.feature(:auto_deflate)
)
res = perform(req, opts)
return res unless opts.follow
Redirector.new(opts.follow).perform(req, res) do |request|
perform(request, opts)
end
end
# @!method persistent?
......@@ -121,7 +128,7 @@ module HTTP
uri = HTTP::URI.parse uri
if opts.params && !opts.params.empty?
uri.query = [uri.query, HTTP::URI.form_encode(opts.params)].compact.join("&")
uri.query_values = uri.query_values(Array).to_a.concat(opts.params.to_a)
end
# Some proxies (seen on WEBRick) fail if URL has
......@@ -146,29 +153,30 @@ module HTTP
headers[Headers::COOKIE] = cookies
end
if (auto_deflate = opts.feature(:auto_deflate))
# 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] = auto_deflate.method
end
headers
end
# Create the request body object to send
def make_request_body(opts, headers)
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
case
when opts.body
opts.body
when opts.form
form = HTTP::FormData.create opts.form
headers[Headers::CONTENT_TYPE] ||= form.content_type
form
when opts.json
body = MimeType[:json].encode opts.json
headers[Headers::CONTENT_TYPE] ||= "application/json; charset=#{body.encoding.name}"
body
end
end
end
......
# frozen_string_literal: true
require "forwardable"
require "http/headers"
......@@ -6,21 +7,21 @@ require "http/response/parser"
module HTTP
# A connection to the HTTP server
class Connection
class Connection # rubocop: disable Metrics/ClassLength
extend Forwardable
# Allowed values for CONNECTION header
KEEP_ALIVE = "Keep-Alive".freeze
CLOSE = "close".freeze
KEEP_ALIVE = "Keep-Alive"
CLOSE = "close"
# Attempt to read this much data
BUFFER_SIZE = 16_384
# HTTP/1.0
HTTP_1_0 = "1.0".freeze
HTTP_1_0 = "1.0"
# HTTP/1.1
HTTP_1_1 = "1.1".freeze
HTTP_1_1 = "1.1"
# Returned after HTTP CONNECT (via proxy)
attr_reader :proxy_response_headers
......@@ -34,6 +35,7 @@ module HTTP
@pending_request = false
@pending_response = false
@failed_proxy_connect = false
@buffer = "".b
@parser = Response::Parser.new
......@@ -84,9 +86,11 @@ module HTTP
def readpartial(size = BUFFER_SIZE)
return unless @pending_response
finished = (read_more(size) == :eof) || @parser.finished?
chunk = @parser.chunk
chunk = @parser.read(size)
return chunk if chunk
finished = (read_more(size) == :eof) || @parser.finished?
chunk = @parser.read(size)
finish_response if finished
chunk.to_s
......@@ -209,8 +213,9 @@ module HTTP
def read_more(size)
return if @parser.finished?
value = @socket.readpartial(size)
value = @socket.readpartial(size, @buffer)
if value == :eof
@parser << ""
:eof
elsif value
@parser << value
......
# frozen_string_literal: true
module HTTP
ContentType = Struct.new(:mime_type, :charset) do
MIME_TYPE_RE = %r{^([^/]+/[^;]+)(?:$|;)}
......
# frozen_string_literal: true
module HTTP
# Generic error
class Error < StandardError; end
......@@ -18,6 +19,6 @@ module HTTP
# Generic Timeout error
class TimeoutError < Error; end
# Header name is invalid
class InvalidHeaderNameError < Error; end
# Header value is of unexpected format (similar to Net::HTTPHeaderSyntaxError)
class HeaderError < Error; end
end
# frozen_string_literal: true
module HTTP
class Feature
def initialize(opts = {})
def initialize(opts = {}) # rubocop:disable Style/OptionHash
@opts = opts
end
end
......
# frozen_string_literal: true
require "zlib"
require "tempfile"
module HTTP
module Features
......@@ -12,32 +13,88 @@ module HTTP
@method = @opts.key?(:method) ? @opts[:method].to_s : "gzip"
raise Error, "Only gzip and deflate methods are supported" unless %w(gzip deflate).include?(@method)
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)
def deflated_body(body)
case method
when "gzip"
GzippedBody.new(body)
when "deflate"