http 5.0.0.pre3 → 5.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) 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/layout.yml +8 -0
  6. data/.rubocop/style.yml +32 -0
  7. data/.rubocop.yml +7 -124
  8. data/.rubocop_todo.yml +192 -0
  9. data/CHANGES.md +123 -1
  10. data/Gemfile +18 -11
  11. data/LICENSE.txt +1 -1
  12. data/README.md +47 -87
  13. data/Rakefile +2 -10
  14. data/http.gemspec +3 -3
  15. data/lib/http/chainable.rb +15 -14
  16. data/lib/http/client.rb +26 -15
  17. data/lib/http/connection.rb +7 -3
  18. data/lib/http/content_type.rb +10 -5
  19. data/lib/http/feature.rb +1 -1
  20. data/lib/http/features/auto_inflate.rb +0 -2
  21. data/lib/http/features/instrumentation.rb +1 -1
  22. data/lib/http/features/logging.rb +19 -21
  23. data/lib/http/headers.rb +3 -3
  24. data/lib/http/mime_type/adapter.rb +2 -0
  25. data/lib/http/options.rb +2 -2
  26. data/lib/http/redirector.rb +1 -1
  27. data/lib/http/request/writer.rb +6 -2
  28. data/lib/http/request.rb +22 -5
  29. data/lib/http/response/body.rb +5 -4
  30. data/lib/http/response/inflater.rb +1 -1
  31. data/lib/http/response/parser.rb +72 -64
  32. data/lib/http/response/status.rb +2 -2
  33. data/lib/http/response.rb +24 -6
  34. data/lib/http/timeout/global.rb +18 -30
  35. data/lib/http/timeout/null.rb +2 -1
  36. data/lib/http/timeout/per_operation.rb +31 -55
  37. data/lib/http/version.rb +1 -1
  38. data/spec/lib/http/client_spec.rb +109 -41
  39. data/spec/lib/http/features/auto_inflate_spec.rb +0 -1
  40. data/spec/lib/http/features/instrumentation_spec.rb +21 -16
  41. data/spec/lib/http/features/logging_spec.rb +2 -5
  42. data/spec/lib/http/headers_spec.rb +3 -3
  43. data/spec/lib/http/redirector_spec.rb +44 -0
  44. data/spec/lib/http/request/body_spec.rb +3 -3
  45. data/spec/lib/http/request/writer_spec.rb +12 -1
  46. data/spec/lib/http/response/body_spec.rb +5 -5
  47. data/spec/lib/http/response/parser_spec.rb +5 -5
  48. data/spec/lib/http/response_spec.rb +62 -10
  49. data/spec/lib/http_spec.rb +20 -2
  50. data/spec/spec_helper.rb +21 -21
  51. data/spec/support/black_hole.rb +1 -1
  52. data/spec/support/dummy_server/servlet.rb +14 -2
  53. data/spec/support/dummy_server.rb +1 -1
  54. data/spec/support/fuubar.rb +21 -0
  55. data/spec/support/simplecov.rb +19 -0
  56. metadata +23 -18
  57. data/.coveralls.yml +0 -1
  58. data/.travis.yml +0 -38
data/README.md CHANGED
@@ -1,86 +1,32 @@
1
- # ![http.rb](https://raw.github.com/httprb/http.rb/master/logo.png)
1
+ # ![http.rb](https://raw.github.com/httprb/http.rb/main/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)
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
- [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/httprb/http/blob/master/LICENSE.txt)
3
+ [![Gem Version][gem-image]][gem-link]
4
+ [![MIT licensed][license-image]][license-link]
5
+ [![Build Status][build-image]][build-link]
6
+ [![Code Climate][codeclimate-image]][codeclimate-link]
8
7
 
9
8
  [Documentation]
10
9
 
11
- _NOTE: This is the 5.x **development** branch. For the 4.x **stable** branch, please see:_
12
-
13
- https://github.com/httprb/http/tree/4-x-stable
14
-
15
10
  ## About
16
11
 
17
12
  HTTP (The Gem! a.k.a. http.rb) is an easy-to-use client library for making requests
18
13
  from Ruby. It uses a simple method chaining system for building requests, similar to
19
14
  Python's [Requests].
20
15
 
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.
25
-
26
- [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
29
-
30
-
31
- ## Another Ruby HTTP library? Why should I care?
32
-
33
- There are a lot of HTTP libraries to choose from in the Ruby ecosystem.
34
- So why would you choose this one?
16
+ Under the hood, http.rb uses the [llhttp] parser, a fast HTTP parsing native extension.
17
+ This library isn't just yet another wrapper around `Net::HTTP`. It implements the HTTP
18
+ protocol natively and outsources the parsing to native extensions.
35
19
 
36
- Top three reasons:
20
+ ### Why http.rb?
37
21
 
38
- 1. **Clean API**: http.rb offers an easy-to-use API that should be a
22
+ - **Clean API**: http.rb offers an easy-to-use API that should be a
39
23
  breath of fresh air after using something like Net::HTTP.
40
24
 
41
- 2. **Maturity**: http.rb is one of the most mature Ruby HTTP clients, supporting
25
+ - **Maturity**: http.rb is one of the most mature Ruby HTTP clients, supporting
42
26
  features like persistent connections and fine-grained timeouts.
43
27
 
44
- 3. **Performance**: using native parsers and a clean, lightweight implementation,
45
- http.rb achieves the best performance of any Ruby HTTP library which
46
- implements the HTTP protocol in Ruby instead of C:
47
-
48
- | HTTP client | Time | Implementation |
49
- |--------------------------|--------|-----------------------|
50
- | curb (persistent) | 2.519 | libcurl wrapper |
51
- | em-http-request | 2.731 | EM + http_parser.rb |
52
- | Typhoeus | 2.851 | libcurl wrapper |
53
- | StreamlyFFI (persistent) | 2.853 | libcurl wrapper |
54
- | http.rb (persistent) | 2.970 | Ruby + http_parser.rb |
55
- | http.rb | 3.588 | Ruby + http_parser.rb |
56
- | HTTParty | 3.931 | Net::HTTP wrapper |
57
- | Net::HTTP | 3.959 | Pure Ruby |
58
- | Net::HTTP (persistent) | 4.043 | Pure Ruby |
59
- | open-uri | 4.479 | Net::HTTP wrapper |
60
- | Excon (persistent) | 4.618 | Pure Ruby |
61
- | Excon | 4.701 | Pure Ruby |
62
- | RestClient | 26.838 | Net::HTTP wrapper |
63
-
64
- Benchmarks performed using excon's benchmarking tool
65
-
66
- DISCLAIMER: Most benchmarks you find in READMEs are crap,
67
- including this one. These are out-of-date. If you care about
68
- performance, benchmark for yourself for your own use cases!
69
-
70
- ## Help and Discussion
71
-
72
- If you need help or just want to talk about the http.rb,
73
- visit the http.rb Google Group:
74
-
75
- https://groups.google.com/forum/#!forum/httprb
76
-
77
- You can join by email by sending a message to:
78
-
79
- [httprb+subscribe@googlegroups.com](mailto:httprb+subscribe@googlegroups.com)
80
-
81
- If you believe you've found a bug, please report it at:
82
-
83
- https://github.com/httprb/http/issues
28
+ - **Performance**: using native parsers and a clean, lightweight implementation,
29
+ http.rb achieves high performance while implementing HTTP in Ruby instead of C.
84
30
 
85
31
 
86
32
  ## Installation
@@ -115,10 +61,9 @@ for more detailed documentation and usage notes.
115
61
 
116
62
  The following API documentation is also available:
117
63
 
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)
64
+ - [YARD API documentation](https://www.rubydoc.info/github/httprb/http)
65
+ - [Chainable module (all chainable methods)](https://www.rubydoc.info/github/httprb/http/HTTP/Chainable)
120
66
 
121
- [documentation]: https://github.com/httprb/http/wiki
122
67
 
123
68
  ### Basic Usage
124
69
 
@@ -145,7 +90,7 @@ We can also obtain an `HTTP::Response::Body` object for this response:
145
90
  ```
146
91
 
147
92
  The response body can be streamed with `HTTP::Response::Body#readpartial`.
148
- In practice, you'll want to bind the HTTP::Response::Body to a local variable
93
+ In practice, you'll want to bind the `HTTP::Response::Body` to a local variable
149
94
  and call `#readpartial` on it repeatedly until it returns `nil`:
150
95
 
151
96
  ```ruby
@@ -162,14 +107,13 @@ and call `#readpartial` on it repeatedly until it returns `nil`:
162
107
 
163
108
  ## Supported Ruby Versions
164
109
 
165
- This library aims to support and is [tested against][travis] the following Ruby
166
- versions:
110
+ This library aims to support and is [tested against][build-link]
111
+ the following Ruby versions:
167
112
 
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
113
+ - Ruby 2.6
114
+ - Ruby 2.7
115
+ - Ruby 3.0
116
+ - JRuby 9.2
173
117
 
174
118
  If something doesn't work on one of these versions, it's a bug.
175
119
 
@@ -184,20 +128,36 @@ patches in a timely fashion. If critical issues for a particular implementation
184
128
  exist at the time of a major release, support for that Ruby version may be
185
129
  dropped.
186
130
 
187
- [travis]: http://travis-ci.org/httprb/http
188
-
189
131
 
190
132
  ## Contributing to http.rb
191
133
 
192
- * Fork http.rb on GitHub
193
- * Make your changes
194
- * Ensure all tests pass (`bundle exec rake`)
195
- * Send a pull request
196
- * If we like them we'll merge them
197
- * If we've accepted a patch, feel free to ask for commit access!
134
+ - Fork http.rb on GitHub
135
+ - Make your changes
136
+ - Ensure all tests pass (`bundle exec rake`)
137
+ - Send a pull request
138
+ - If we like them we'll merge them
139
+ - If we've accepted a patch, feel free to ask for commit access!
198
140
 
199
141
 
200
142
  ## Copyright
201
143
 
202
- Copyright (c) 2011-2019 Tony Arcieri, Alexey V. Zapparov, Erik Michaels-Ober, Zachary Anker.
144
+ Copyright © 2011-2021 Tony Arcieri, Alexey V. Zapparov, Erik Michaels-Ober, Zachary Anker.
203
145
  See LICENSE.txt for further details.
146
+
147
+
148
+ [//]: # (badges)
149
+
150
+ [gem-image]: https://img.shields.io/gem/v/http?logo=ruby
151
+ [gem-link]: https://rubygems.org/gems/http
152
+ [license-image]: https://img.shields.io/badge/license-MIT-blue.svg
153
+ [license-link]: https://github.com/httprb/http/blob/main/LICENSE.txt
154
+ [build-image]: https://github.com/httprb/http/workflows/CI/badge.svg
155
+ [build-link]: https://github.com/httprb/http/actions/workflows/ci.yml
156
+ [codeclimate-image]: https://codeclimate.com/github/httprb/http.svg?branch=main
157
+ [codeclimate-link]: https://codeclimate.com/github/httprb/http
158
+
159
+ [//]: # (links)
160
+
161
+ [documentation]: https://github.com/httprb/http/wiki
162
+ [requests]: http://docs.python-requests.org/en/latest/
163
+ [llhttp]: https://llhttp.org/
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
- gem.add_runtime_dependency "addressable", "~> 2.3"
30
+ gem.add_runtime_dependency "addressable", "~> 2.8"
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.4.0"
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,19 +25,19 @@ 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)
32
32
  return res unless opts.follow
33
33
 
34
34
  Redirector.new(opts.follow).perform(req, res) do |request|
35
- perform(request, opts)
35
+ perform(wrap_request(request, opts), opts)
36
36
  end
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)
@@ -52,9 +52,7 @@ module HTTP
52
52
  :body => body
53
53
  )
54
54
 
55
- opts.features.inject(req) do |request, (_name, feature)|
56
- feature.wrap_request(request)
57
- end
55
+ wrap_request(req, opts)
58
56
  end
59
57
 
60
58
  # @!method persistent?
@@ -68,9 +66,9 @@ module HTTP
68
66
 
69
67
  @state = :dirty
70
68
 
71
- @connection ||= HTTP::Connection.new(req, options)
72
-
73
69
  begin
70
+ @connection ||= HTTP::Connection.new(req, options)
71
+
74
72
  unless @connection.failed_proxy_connect?
75
73
  @connection.send_request(req)
76
74
  @connection.read_headers!
@@ -97,13 +95,19 @@ module HTTP
97
95
  end
98
96
 
99
97
  def close
100
- @connection.close if @connection
98
+ @connection&.close
101
99
  @connection = nil
102
100
  @state = :clean
103
101
  end
104
102
 
105
103
  private
106
104
 
105
+ def wrap_request(req, opts)
106
+ opts.features.inject(req) do |request, (_name, feature)|
107
+ feature.wrap_request(request)
108
+ end
109
+ end
110
+
107
111
  def build_response(req, options)
108
112
  Response.new(
109
113
  :status => @connection.status_code,
@@ -120,15 +124,15 @@ module HTTP
120
124
  def verify_connection!(uri)
121
125
  if default_options.persistent? && uri.origin != default_options.persistent
122
126
  raise StateError, "Persistence is enabled for #{default_options.persistent}, but we got #{uri.origin}"
127
+ end
128
+
123
129
  # We re-create the connection object because we want to let prior requests
124
130
  # lazily load the body as long as possible, and this mimics prior functionality.
125
- elsif @connection && (!@connection.keep_alive? || @connection.expired?)
126
- close
131
+ return close if @connection && (!@connection.keep_alive? || @connection.expired?)
132
+
127
133
  # If we get into a bad state (eg, Timeout.timeout ensure being killed)
128
134
  # close the connection to prevent potential for mixed responses.
129
- elsif @state == :dirty
130
- close
131
- end
135
+ return close if @state == :dirty
132
136
  end
133
137
 
134
138
  # Merges query params if needed
@@ -175,7 +179,7 @@ module HTTP
175
179
  when opts.body
176
180
  opts.body
177
181
  when opts.form
178
- form = HTTP::FormData.create opts.form
182
+ form = make_form_data(opts.form)
179
183
  headers[Headers::CONTENT_TYPE] ||= form.content_type
180
184
  form
181
185
  when opts.json
@@ -184,5 +188,12 @@ module HTTP
184
188
  body
185
189
  end
186
190
  end
191
+
192
+ def make_form_data(form)
193
+ return form if form.is_a? HTTP::FormData::Multipart
194
+ return form if form.is_a? HTTP::FormData::Urlencoded
195
+
196
+ HTTP::FormData.create(form)
197
+ end
187
198
  end
188
199
  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
data/lib/http/feature.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  module HTTP
4
4
  class Feature
5
- def initialize(opts = {}) # rubocop:disable Style/OptionHash
5
+ def initialize(opts = {})
6
6
  @opts = opts
7
7
  end
8
8
 
@@ -21,8 +21,6 @@ module HTTP
21
21
  :request => response.request
22
22
  }
23
23
 
24
- options[:uri] = response.uri if response.uri
25
-
26
24
  Response.new(options)
27
25
  end
28
26
 
@@ -29,7 +29,7 @@ module HTTP
29
29
  def wrap_request(request)
30
30
  # Emit a separate "start" event, so a logger can print the request
31
31
  # being run without waiting for a response
32
- instrumenter.instrument("start_#{name}", :request => request) {}
32
+ instrumenter.instrument("start_#{name}", :request => request)
33
33
  instrumenter.start(name, :request => request)
34
34
  request
35
35
  end
@@ -9,6 +9,20 @@ module HTTP
9
9
  # HTTP.use(logging: {logger: Logger.new(STDOUT)}).get("https://example.com/")
10
10
  #
11
11
  class Logging < Feature
12
+ HTTP::Options.register_feature(:logging, self)
13
+
14
+ class NullLogger
15
+ %w[fatal error warn info debug].each do |level|
16
+ define_method(level.to_sym) do |*_args|
17
+ nil
18
+ end
19
+
20
+ define_method(:"#{level}?") do
21
+ true
22
+ end
23
+ end
24
+ end
25
+
12
26
  attr_reader :logger
13
27
 
14
28
  def initialize(logger: NullLogger.new)
@@ -17,39 +31,23 @@ module HTTP
17
31
 
18
32
  def wrap_request(request)
19
33
  logger.info { "> #{request.verb.to_s.upcase} #{request.uri}" }
20
- logger.debug do
21
- headers = request.headers.map { |name, value| "#{name}: #{value}" }.join("\n")
22
- body = request.body.source
34
+ logger.debug { "#{stringify_headers(request.headers)}\n\n#{request.body.source}" }
23
35
 
24
- headers + "\n\n" + body.to_s
25
- end
26
36
  request
27
37
  end
28
38
 
29
39
  def wrap_response(response)
30
40
  logger.info { "< #{response.status}" }
31
- logger.debug do
32
- headers = response.headers.map { |name, value| "#{name}: #{value}" }.join("\n")
33
- body = response.body.to_s
41
+ logger.debug { "#{stringify_headers(response.headers)}\n\n#{response.body}" }
34
42
 
35
- headers + "\n\n" + body
36
- end
37
43
  response
38
44
  end
39
45
 
40
- class NullLogger
41
- %w[fatal error warn info debug].each do |level|
42
- define_method(level.to_sym) do |*_args|
43
- nil
44
- end
46
+ private
45
47
 
46
- define_method(:"#{level}?") do
47
- true
48
- end
49
- end
48
+ def stringify_headers(headers)
49
+ headers.map { |name, value| "#{name}: #{value}" }.join("\n")
50
50
  end
51
-
52
- HTTP::Options.register_feature(:logging, self)
53
51
  end
54
52
  end
55
53
  end
data/lib/http/headers.rb CHANGED
@@ -17,7 +17,7 @@ module HTTP
17
17
 
18
18
  # Matches valid header field name according to RFC.
19
19
  # @see http://tools.ietf.org/html/rfc7230#section-3.2
20
- COMPLIANT_NAME_RE = /\A[A-Za-z0-9!#\$%&'*+\-.^_`|~]+\z/.freeze
20
+ COMPLIANT_NAME_RE = /\A[A-Za-z0-9!#$%&'*+\-.^_`|~]+\z/.freeze
21
21
 
22
22
  # Class constructor.
23
23
  def initialize
@@ -111,7 +111,7 @@ module HTTP
111
111
  #
112
112
  # @return [Hash]
113
113
  def to_h
114
- Hash[keys.map { |k| [k, self[k]] }]
114
+ keys.map { |k| [k, self[k]] }.to_h
115
115
  end
116
116
  alias to_hash to_h
117
117
 
@@ -164,7 +164,7 @@ module HTTP
164
164
 
165
165
  # @!method hash
166
166
  # Compute a hash-code for this headers container.
167
- # Two conatiners with the same content will have the same hash code.
167
+ # Two containers with the same content will have the same hash code.
168
168
  #
169
169
  # @see http://www.ruby-doc.org/core/Object.html#method-i-hash
170
170
  # @return [Fixnum]
@@ -14,6 +14,7 @@ module HTTP
14
14
  def_delegators :instance, :encode, :decode
15
15
  end
16
16
 
17
+ # rubocop:disable Style/DocumentDynamicEvalDefinition
17
18
  %w[encode decode].each do |operation|
18
19
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
19
20
  def #{operation}(*)
@@ -21,6 +22,7 @@ module HTTP
21
22
  end
22
23
  RUBY
23
24
  end
25
+ # rubocop:enable Style/DocumentDynamicEvalDefinition
24
26
  end
25
27
  end
26
28
  end
data/lib/http/options.rb CHANGED
@@ -32,7 +32,7 @@ module HTTP
32
32
 
33
33
  def def_option(name, reader_only: false, &interpreter)
34
34
  defined_options << name.to_sym
35
- interpreter ||= lambda { |v| v }
35
+ interpreter ||= ->(v) { v }
36
36
 
37
37
  if reader_only
38
38
  attr_reader name
@@ -47,7 +47,7 @@ module HTTP
47
47
  end
48
48
  end
49
49
 
50
- def initialize(options = {}) # rubocop:disable Style/OptionHash
50
+ def initialize(options = {})
51
51
  defaults = {
52
52
  :response => :auto,
53
53
  :proxy => {},
@@ -39,7 +39,7 @@ module HTTP
39
39
  # @param [Hash] opts
40
40
  # @option opts [Boolean] :strict (true) redirector hops policy
41
41
  # @option opts [#to_i] :max_hops (5) maximum allowed amount of hops
42
- def initialize(opts = {}) # rubocop:disable Style/OptionHash
42
+ def initialize(opts = {})
43
43
  @strict = opts.fetch(:strict, true)
44
44
  @max_hops = opts.fetch(:max_hops, 5).to_i
45
45
  end