http 5.0.0.pre3 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +65 -0
  3. data/.gitignore +6 -10
  4. data/.rspec +0 -4
  5. data/.rubocop.yml +7 -124
  6. data/.rubocop/layout.yml +8 -0
  7. data/.rubocop/style.yml +32 -0
  8. data/.rubocop_todo.yml +192 -0
  9. data/CHANGES.md +48 -1
  10. data/Gemfile +18 -11
  11. data/README.md +13 -17
  12. data/Rakefile +2 -10
  13. data/http.gemspec +2 -2
  14. data/lib/http/chainable.rb +15 -14
  15. data/lib/http/client.rb +18 -11
  16. data/lib/http/connection.rb +7 -3
  17. data/lib/http/content_type.rb +10 -5
  18. data/lib/http/feature.rb +1 -1
  19. data/lib/http/features/instrumentation.rb +1 -1
  20. data/lib/http/features/logging.rb +19 -21
  21. data/lib/http/headers.rb +3 -3
  22. data/lib/http/mime_type/adapter.rb +2 -0
  23. data/lib/http/options.rb +2 -2
  24. data/lib/http/redirector.rb +1 -1
  25. data/lib/http/request.rb +7 -4
  26. data/lib/http/response/body.rb +1 -2
  27. data/lib/http/response/inflater.rb +1 -1
  28. data/lib/http/response/parser.rb +72 -64
  29. data/lib/http/response/status.rb +2 -2
  30. data/lib/http/timeout/global.rb +16 -28
  31. data/lib/http/timeout/null.rb +2 -1
  32. data/lib/http/timeout/per_operation.rb +31 -55
  33. data/lib/http/version.rb +1 -1
  34. data/spec/lib/http/client_spec.rb +75 -41
  35. data/spec/lib/http/features/instrumentation_spec.rb +21 -15
  36. data/spec/lib/http/features/logging_spec.rb +2 -4
  37. data/spec/lib/http/headers_spec.rb +3 -3
  38. data/spec/lib/http/response/parser_spec.rb +2 -2
  39. data/spec/lib/http_spec.rb +20 -2
  40. data/spec/spec_helper.rb +21 -21
  41. data/spec/support/black_hole.rb +1 -1
  42. data/spec/support/dummy_server.rb +1 -1
  43. data/spec/support/dummy_server/servlet.rb +14 -2
  44. data/spec/support/fuubar.rb +21 -0
  45. data/spec/support/simplecov.rb +19 -0
  46. metadata +21 -16
  47. data/.coveralls.yml +0 -1
  48. data/.travis.yml +0 -38
data/CHANGES.md CHANGED
@@ -1,4 +1,28 @@
1
- ## future is unwritten (master)
1
+ ## 5.0.0 (2021-05-12)
2
+
3
+ * [#656](https://github.com/httprb/http/pull/656)
4
+ Handle connection timeouts in `Features`
5
+ ([@semenyukdmitry])
6
+
7
+ * [#651](https://github.com/httprb/http/pull/651)
8
+ Replace `http-parser` with `llhttp`
9
+ ([@bryanp])
10
+
11
+ * [#647](https://github.com/httprb/http/pull/647)
12
+ Add support for `MKCALENDAR` HTTP verb
13
+ ([@meanphil])
14
+
15
+ * [#632](https://github.com/httprb/http/pull/632)
16
+ Respect the SSL context's `verify_hostname` value
17
+ ([@colemannugent])
18
+
19
+ * [#625](https://github.com/httprb/http/pull/625)
20
+ Fix inflator with empty responses
21
+ ([@LukaszMaslej])
22
+
23
+ * [#599](https://github.com/httprb/http/pull/599)
24
+ Allow passing `HTTP::FormData::{Multipart,UrlEncoded}` object directly.
25
+ ([@ixti])
2
26
 
3
27
  * [#593](https://github.com/httprb/http/pull/593)
4
28
  [#592](https://github.com/httprb/http/issues/592)
@@ -56,6 +80,24 @@
56
80
  ([@ixti])
57
81
 
58
82
 
83
+ ## 4.4.0 (2020-03-25)
84
+
85
+ * Backport [#587](https://github.com/httprb/http/pull/587)
86
+ Fix redirections when server responds with multiple Location headers.
87
+ ([@ixti])
88
+
89
+ * Backport [#599](https://github.com/httprb/http/pull/599)
90
+ Allow passing HTTP::FormData::{Multipart,UrlEncoded} object directly.
91
+ ([@ixti])
92
+
93
+
94
+ ## 4.3.0 (2020-01-09)
95
+
96
+ * Backport [#581](https://github.com/httprb/http/pull/581)
97
+ Add Ruby-2.7 compatibility.
98
+ ([@ixti], [@janko])
99
+
100
+
59
101
  ## 4.2.0 (2019-10-22)
60
102
 
61
103
  * Backport [#489](https://github.com/httprb/http/pull/489)
@@ -840,3 +882,8 @@ end
840
882
  [@mamoonraja]: https://github.com/mamoonraja
841
883
  [@joshuaflanagan]: https://github.com/joshuaflanagan
842
884
  [@antonvolkoff]: https://github.com/antonvolkoff
885
+ [@LukaszMaslej]: https://github.com/LukaszMaslej
886
+ [@colemannugent]: https://github.com/colemannugent
887
+ [@semenyukdmitry]: https://github.com/semenyukdmitry
888
+ [@bryanp]: https://github.com/bryanp
889
+ [@meanphil]: https://github.com/meanphil
data/Gemfile CHANGED
@@ -5,32 +5,39 @@ ruby RUBY_VERSION
5
5
 
6
6
  gem "rake"
7
7
 
8
+ # Ruby 3.0 does not ship it anymore.
9
+ # TODO: We should probably refactor specs to avoid need for it.
10
+ gem "webrick"
11
+
8
12
  group :development do
9
13
  gem "guard-rspec", :require => false
10
14
  gem "nokogiri", :require => false
11
15
  gem "pry", :require => false
12
16
 
13
- platform :ruby_20 do
14
- gem "pry-debugger", :require => false
15
- gem "pry-stack_explorer", :require => false
17
+ # RSpec formatter
18
+ gem "fuubar", :require => false
19
+
20
+ platform :mri do
21
+ gem "pry-byebug"
16
22
  end
17
23
  end
18
24
 
19
25
  group :test do
20
- gem "activemodel", :require => false # Used by certificate_authority
21
- gem "certificate_authority", :require => false
26
+ gem "certificate_authority", "~> 1.0", :require => false
22
27
 
23
28
  gem "backports"
24
29
 
25
- gem "coveralls", :require => false
26
- gem "simplecov", ">= 0.9"
30
+ gem "rubocop"
31
+ gem "rubocop-performance"
32
+ gem "rubocop-rake"
33
+ gem "rubocop-rspec"
34
+
35
+ gem "simplecov", :require => false
36
+ gem "simplecov-lcov", :require => false
27
37
 
28
- gem "rspec", "~> 3.0"
38
+ gem "rspec", "~> 3.10"
29
39
  gem "rspec-its"
30
40
 
31
- gem "rubocop", "= 0.68.1"
32
- gem "rubocop-performance"
33
-
34
41
  gem "yardstick"
35
42
  end
36
43
 
data/README.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # ![http.rb](https://raw.github.com/httprb/http.rb/master/logo.png)
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/http.svg)](https://rubygems.org/gems/http)
4
- [![Build Status](https://secure.travis-ci.org/httprb/http.svg?branch=master)](https://travis-ci.org/httprb/http)
3
+ [![Gem Version](https://img.shields.io/gem/v/http?logo=ruby)](https://rubygems.org/gems/http)
4
+ [![Build Status](https://github.com/httprb/http/workflows/CI/badge.svg)](https://github.com/httprb/http/actions?query=workflow:CI)
5
5
  [![Code Climate](https://codeclimate.com/github/httprb/http.svg?branch=master)](https://codeclimate.com/github/httprb/http)
6
- [![Coverage Status](https://coveralls.io/repos/httprb/http/badge.svg?branch=master)](https://coveralls.io/r/httprb/http)
7
6
  [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/httprb/http/blob/master/LICENSE.txt)
8
7
 
9
8
  [Documentation]
@@ -18,14 +17,12 @@ HTTP (The Gem! a.k.a. http.rb) is an easy-to-use client library for making reque
18
17
  from Ruby. It uses a simple method chaining system for building requests, similar to
19
18
  Python's [Requests].
20
19
 
21
- Under the hood, via [Ruby FFI bindings][http-parser-ffi], http.rb uses the Node.js
22
- [http-parser], a fast HTTP parsing native extension. This library
23
- isn't just yet another wrapper around Net::HTTP. It implements the HTTP protocol
24
- natively and outsources the parsing to native extensions.
20
+ Under the hood, http.rb uses the [llhttp] parser, a fast HTTP parsing native extension.
21
+ This library isn't just yet another wrapper around `Net::HTTP`. It implements the HTTP
22
+ protocol natively and outsources the parsing to native extensions.
25
23
 
26
24
  [requests]: http://docs.python-requests.org/en/latest/
27
- [http-parser]: https://github.com/nodejs/http-parser
28
- [http-parser-ffi]: https://github.com/cotag/http-parser
25
+ [llhttp]: https://llhttp.org/
29
26
 
30
27
 
31
28
  ## Another Ruby HTTP library? Why should I care?
@@ -115,8 +112,8 @@ for more detailed documentation and usage notes.
115
112
 
116
113
  The following API documentation is also available:
117
114
 
118
- * [YARD API documentation](http://www.rubydoc.info/gems/http/frames)
119
- * [Chainable module (all chainable methods)](http://www.rubydoc.info/gems/http/HTTP/Chainable)
115
+ * [YARD API documentation](https://www.rubydoc.info/github/httprb/http)
116
+ * [Chainable module (all chainable methods)](https://www.rubydoc.info/github/httprb/http/HTTP/Chainable)
120
117
 
121
118
  [documentation]: https://github.com/httprb/http/wiki
122
119
 
@@ -165,11 +162,10 @@ and call `#readpartial` on it repeatedly until it returns `nil`:
165
162
  This library aims to support and is [tested against][travis] the following Ruby
166
163
  versions:
167
164
 
168
- * Ruby 2.4.x
169
- * Ruby 2.5.x
170
- * Ruby 2.6.x
171
- * Ruby 2.7.x
172
- * JRuby 9.2.x.x
165
+ * Ruby 2.6
166
+ * Ruby 2.7
167
+ * Ruby 3.0
168
+ * JRuby 9.2
173
169
 
174
170
  If something doesn't work on one of these versions, it's a bug.
175
171
 
@@ -199,5 +195,5 @@ dropped.
199
195
 
200
196
  ## Copyright
201
197
 
202
- Copyright (c) 2011-2019 Tony Arcieri, Alexey V. Zapparov, Erik Michaels-Ober, Zachary Anker.
198
+ Copyright (c) 2011-2021 Tony Arcieri, Alexey V. Zapparov, Erik Michaels-Ober, Zachary Anker.
203
199
  See LICENSE.txt for further details.
data/Rakefile CHANGED
@@ -35,7 +35,7 @@ task :generate_status_codes do
35
35
  end
36
36
 
37
37
  File.open("./lib/http/response/status/reasons.rb", "w") do |io|
38
- io.puts <<-TPL.gsub(/^[ ]{6}/, "")
38
+ io.puts <<~TPL
39
39
  # AUTO-GENERATED FILE, DO NOT CHANGE IT MANUALLY
40
40
 
41
41
  require "delegate"
@@ -61,12 +61,4 @@ task :generate_status_codes do
61
61
  end
62
62
  end
63
63
 
64
- if ENV["CI"].nil?
65
- task :default => %i[spec rubocop verify_measurements]
66
- else
67
- case ENV["SUITE"]
68
- when "rubocop" then task :default => :rubocop
69
- when "yardstick" then task :default => :verify_measurements
70
- else task :default => :spec
71
- end
72
- end
64
+ task :default => %i[spec rubocop verify_measurements]
data/http.gemspec CHANGED
@@ -25,12 +25,12 @@ Gem::Specification.new do |gem|
25
25
  gem.require_paths = ["lib"]
26
26
  gem.version = HTTP::VERSION
27
27
 
28
- gem.required_ruby_version = ">= 2.3"
28
+ gem.required_ruby_version = ">= 2.5"
29
29
 
30
30
  gem.add_runtime_dependency "addressable", "~> 2.3"
31
31
  gem.add_runtime_dependency "http-cookie", "~> 1.0"
32
32
  gem.add_runtime_dependency "http-form_data", "~> 2.2"
33
- gem.add_runtime_dependency "http-parser", "~> 1.2.0"
33
+ gem.add_runtime_dependency "llhttp-ffi", "~> 0.0.1"
34
34
 
35
35
  gem.add_development_dependency "bundler", "~> 2.0"
36
36
 
@@ -9,63 +9,63 @@ module HTTP
9
9
  # Request a get sans response body
10
10
  # @param uri
11
11
  # @option options [Hash]
12
- def head(uri, options = {}) # rubocop:disable Style/OptionHash
12
+ def head(uri, options = {})
13
13
  request :head, uri, options
14
14
  end
15
15
 
16
16
  # Get a resource
17
17
  # @param uri
18
18
  # @option options [Hash]
19
- def get(uri, options = {}) # rubocop:disable Style/OptionHash
19
+ def get(uri, options = {})
20
20
  request :get, uri, options
21
21
  end
22
22
 
23
23
  # Post to a resource
24
24
  # @param uri
25
25
  # @option options [Hash]
26
- def post(uri, options = {}) # rubocop:disable Style/OptionHash
26
+ def post(uri, options = {})
27
27
  request :post, uri, options
28
28
  end
29
29
 
30
30
  # Put to a resource
31
31
  # @param uri
32
32
  # @option options [Hash]
33
- def put(uri, options = {}) # rubocop:disable Style/OptionHash
33
+ def put(uri, options = {})
34
34
  request :put, uri, options
35
35
  end
36
36
 
37
37
  # Delete a resource
38
38
  # @param uri
39
39
  # @option options [Hash]
40
- def delete(uri, options = {}) # rubocop:disable Style/OptionHash
40
+ def delete(uri, options = {})
41
41
  request :delete, uri, options
42
42
  end
43
43
 
44
44
  # Echo the request back to the client
45
45
  # @param uri
46
46
  # @option options [Hash]
47
- def trace(uri, options = {}) # rubocop:disable Style/OptionHash
47
+ def trace(uri, options = {})
48
48
  request :trace, uri, options
49
49
  end
50
50
 
51
51
  # Return the methods supported on the given URI
52
52
  # @param uri
53
53
  # @option options [Hash]
54
- def options(uri, options = {}) # rubocop:disable Style/OptionHash
54
+ def options(uri, options = {})
55
55
  request :options, uri, options
56
56
  end
57
57
 
58
58
  # Convert to a transparent TCP/IP tunnel
59
59
  # @param uri
60
60
  # @option options [Hash]
61
- def connect(uri, options = {}) # rubocop:disable Style/OptionHash
61
+ def connect(uri, options = {})
62
62
  request :connect, uri, options
63
63
  end
64
64
 
65
65
  # Apply partial modifications to a resource
66
66
  # @param uri
67
67
  # @option options [Hash]
68
- def patch(uri, options = {}) # rubocop:disable Style/OptionHash
68
+ def patch(uri, options = {})
69
69
  request :patch, uri, options
70
70
  end
71
71
 
@@ -148,7 +148,7 @@ module HTTP
148
148
 
149
149
  yield p_client
150
150
  ensure
151
- p_client.close if p_client
151
+ p_client&.close
152
152
  end
153
153
 
154
154
  # Make a request through an HTTP proxy
@@ -173,7 +173,7 @@ module HTTP
173
173
  # @param options
174
174
  # @return [HTTP::Client]
175
175
  # @see Redirector#initialize
176
- def follow(options = {}) # rubocop:disable Style/OptionHash
176
+ def follow(options = {})
177
177
  branch default_options.with_follow options
178
178
  end
179
179
 
@@ -211,10 +211,11 @@ module HTTP
211
211
  # @option opts [#to_s] :user
212
212
  # @option opts [#to_s] :pass
213
213
  def basic_auth(opts)
214
- user = opts.fetch :user
215
- pass = opts.fetch :pass
214
+ user = opts.fetch(:user)
215
+ pass = opts.fetch(:pass)
216
+ creds = "#{user}:#{pass}"
216
217
 
217
- auth("Basic " + Base64.strict_encode64("#{user}:#{pass}"))
218
+ auth("Basic #{Base64.strict_encode64(creds)}")
218
219
  end
219
220
 
220
221
  # Get options for HTTP
data/lib/http/client.rb CHANGED
@@ -25,7 +25,7 @@ module HTTP
25
25
  end
26
26
 
27
27
  # Make an HTTP request
28
- def request(verb, uri, opts = {}) # rubocop:disable Style/OptionHash
28
+ def request(verb, uri, opts = {})
29
29
  opts = @default_options.merge(opts)
30
30
  req = build_request(verb, uri, opts)
31
31
  res = perform(req, opts)
@@ -37,7 +37,7 @@ module HTTP
37
37
  end
38
38
 
39
39
  # Prepare an HTTP request
40
- def build_request(verb, uri, opts = {}) # rubocop:disable Style/OptionHash
40
+ def build_request(verb, uri, opts = {})
41
41
  opts = @default_options.merge(opts)
42
42
  uri = make_request_uri(uri, opts)
43
43
  headers = make_request_headers(opts)
@@ -68,9 +68,9 @@ module HTTP
68
68
 
69
69
  @state = :dirty
70
70
 
71
- @connection ||= HTTP::Connection.new(req, options)
72
-
73
71
  begin
72
+ @connection ||= HTTP::Connection.new(req, options)
73
+
74
74
  unless @connection.failed_proxy_connect?
75
75
  @connection.send_request(req)
76
76
  @connection.read_headers!
@@ -97,7 +97,7 @@ module HTTP
97
97
  end
98
98
 
99
99
  def close
100
- @connection.close if @connection
100
+ @connection&.close
101
101
  @connection = nil
102
102
  @state = :clean
103
103
  end
@@ -120,15 +120,15 @@ module HTTP
120
120
  def verify_connection!(uri)
121
121
  if default_options.persistent? && uri.origin != default_options.persistent
122
122
  raise StateError, "Persistence is enabled for #{default_options.persistent}, but we got #{uri.origin}"
123
+ end
124
+
123
125
  # We re-create the connection object because we want to let prior requests
124
126
  # lazily load the body as long as possible, and this mimics prior functionality.
125
- elsif @connection && (!@connection.keep_alive? || @connection.expired?)
126
- close
127
+ return close if @connection && (!@connection.keep_alive? || @connection.expired?)
128
+
127
129
  # If we get into a bad state (eg, Timeout.timeout ensure being killed)
128
130
  # close the connection to prevent potential for mixed responses.
129
- elsif @state == :dirty
130
- close
131
- end
131
+ return close if @state == :dirty
132
132
  end
133
133
 
134
134
  # Merges query params if needed
@@ -175,7 +175,7 @@ module HTTP
175
175
  when opts.body
176
176
  opts.body
177
177
  when opts.form
178
- form = HTTP::FormData.create opts.form
178
+ form = make_form_data(opts.form)
179
179
  headers[Headers::CONTENT_TYPE] ||= form.content_type
180
180
  form
181
181
  when opts.json
@@ -184,5 +184,12 @@ module HTTP
184
184
  body
185
185
  end
186
186
  end
187
+
188
+ def make_form_data(form)
189
+ return form if form.is_a? HTTP::FormData::Multipart
190
+ return form if form.is_a? HTTP::FormData::Urlencoded
191
+
192
+ HTTP::FormData.create(form)
193
+ end
187
194
  end
188
195
  end
@@ -3,7 +3,6 @@
3
3
  require "forwardable"
4
4
 
5
5
  require "http/headers"
6
- require "http/response/parser"
7
6
 
8
7
  module HTTP
9
8
  # A connection to the HTTP server
@@ -68,8 +67,13 @@ module HTTP
68
67
  # @param [Request] req Request to send to the server
69
68
  # @return [nil]
70
69
  def send_request(req)
71
- raise StateError, "Tried to send a request while one is pending already. Make sure you read off the body." if @pending_response
72
- raise StateError, "Tried to send a request while a response is pending. Make sure you read off the body." if @pending_request
70
+ if @pending_response
71
+ raise StateError, "Tried to send a request while one is pending already. Make sure you read off the body."
72
+ end
73
+
74
+ if @pending_request
75
+ raise StateError, "Tried to send a request while a response is pending. Make sure you read off the body."
76
+ end
73
77
 
74
78
  @pending_request = true
75
79
 
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTP
4
- ContentType = Struct.new(:mime_type, :charset) do
4
+ class ContentType
5
5
  MIME_TYPE_RE = %r{^([^/]+/[^;]+)(?:$|;)}.freeze
6
6
  CHARSET_RE = /;\s*charset=([^;]+)/i.freeze
7
7
 
8
+ attr_accessor :mime_type, :charset
9
+
8
10
  class << self
9
11
  # Parse string and return ContentType struct
10
12
  def parse(str)
@@ -15,15 +17,18 @@ module HTTP
15
17
 
16
18
  # :nodoc:
17
19
  def mime_type(str)
18
- m = str.to_s[MIME_TYPE_RE, 1]
19
- m && m.strip.downcase
20
+ str.to_s[MIME_TYPE_RE, 1]&.strip&.downcase
20
21
  end
21
22
 
22
23
  # :nodoc:
23
24
  def charset(str)
24
- m = str.to_s[CHARSET_RE, 1]
25
- m && m.strip.delete('"')
25
+ str.to_s[CHARSET_RE, 1]&.strip&.delete('"')
26
26
  end
27
27
  end
28
+
29
+ def initialize(mime_type = nil, charset = nil)
30
+ @mime_type = mime_type
31
+ @charset = charset
32
+ end
28
33
  end
29
34
  end