http 5.0.0.pre3 → 5.0.3

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 (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