http 1.0.2 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8fa5394fed30d8eebd829ba4c79f6ac28b47fc16
4
- data.tar.gz: d3654335bb11728650548423d57cb402eb3d5080
3
+ metadata.gz: ce1cd065ce4c93b81a5a28a949354322222d0d78
4
+ data.tar.gz: e4b2d331cc30a7780425ec9248c14ba43fcab9ad
5
5
  SHA512:
6
- metadata.gz: 429071438fa4126402282e1d1258180626f3c181192fbc9a03517920af1284d76574d3802863fdd6ac57d42bf1ad79606932c06faee21856c89572861a98b4a9
7
- data.tar.gz: b8db2ca9014d5fc387736f0c5efaba5287fc506ef8ab0ff2433bfe9dc996254e2d7c2fabf078df449962ddefd378a2d1a4a0ae7c1eab7bccfe7e31bac8810186
6
+ metadata.gz: 7fe63ab93211bb553b5f5ec8b9d62f2a7279c0896dc30ff2ff6dc1afdda0c89efcf1193f4032232aec011f2cf77f613d75a77763dfa3ba656ce07e98b72976e0
7
+ data.tar.gz: 94cfd48b04617ab0966222097c2cf11c10c6567dcc89e7decea6dd2d927e9f199cb80f8609f759ecdfa38d57ad894def14c440367f27b0d06d3b5d2bb7b3cf98
@@ -61,17 +61,8 @@ Style/HashSyntax:
61
61
  Style/Lambda:
62
62
  Enabled: false
63
63
 
64
- Style/SingleSpaceBeforeFirstArg:
65
- Enabled: false
66
-
67
64
  Style/SpaceAroundOperators:
68
- MultiSpaceAllowedForOperators:
69
- - "="
70
- - "=>"
71
- - "||"
72
- - "||="
73
- - "&&"
74
- - "&&="
65
+ AllowForAlignment: true
75
66
 
76
67
  Style/SpaceInsideHashLiteralBraces:
77
68
  EnforcedStyle: no_space
data/CHANGES.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 1.0.3 (2016-03-16)
2
+
3
+ * [#314](https://github.com/httprb/http/pull/314)
4
+ Validate charset before forcing encoding.
5
+ ([@kylekyle])
6
+
7
+
8
+ * [#318](https://github.com/httprb/http/pull/318)
9
+ Remove redundant string allocations upon header names normalization.
10
+ ([@ixti])
11
+
12
+
1
13
  ## 1.0.2 (2016-01-15)
2
14
 
3
15
  * [#295](https://github.com/httprb/http/pull/295):
@@ -464,3 +476,4 @@ end
464
476
  [@hundredwatt]: https://github.com/hundredwatt
465
477
  [@jwinter]: https://github.com/jwinter
466
478
  [@nerdrew]: https://github.com/nerdrew
479
+ [@kylekyle]: https://github.com/kylekyle
data/Gemfile CHANGED
@@ -22,7 +22,7 @@ group :test do
22
22
  gem "coveralls"
23
23
  gem "simplecov", ">= 0.9"
24
24
  gem "json", ">= 1.8.1"
25
- gem "rubocop", "= 0.35.1"
25
+ gem "rubocop", "= 0.36.0"
26
26
  gem "rspec", "~> 3.0"
27
27
  gem "rspec-its"
28
28
  gem "yardstick"
data/README.md CHANGED
@@ -38,21 +38,21 @@ Top three reasons:
38
38
  http.rb achieves the best performance of any Ruby HTTP library which
39
39
  implements the HTTP protocol in Ruby instead of C:
40
40
 
41
- | HTTP client | time |
42
- |--------------------------|-----------|
43
- | curb (persistent) | 2.519088 |
44
- | em-http-request | 2.731645 |
45
- | Typhoeus | 2.851911 |
46
- | StreamlyFFI (persistent) | 2.853786 |
47
- | http.rb (persistent) | 2.970702 |
48
- | http.rb | 3.588964 |
49
- | HTTParty | 3.931913 |
50
- | Net::HTTP | 3.959342 |
51
- | Net::HTTP (persistent) | 4.043674 |
52
- | open-uri | 4.479817 |
53
- | Excon (persistent) | 4.618361 |
54
- | Excon | 4.701262 |
55
- | RestClient | 26.832668 |
41
+ | HTTP client | Time | Implementation |
42
+ |--------------------------|--------|-----------------------|
43
+ | curb (persistent) | 2.519 | libcurl wrapper |
44
+ | em-http-request | 2.731 | EM + http_parser.rb |
45
+ | Typhoeus | 2.851 | libcurl wrapper |
46
+ | StreamlyFFI (persistent) | 2.853 | libcurl wrapper |
47
+ | http.rb (persistent) | 2.970 | Ruby + http_parser.rb |
48
+ | http.rb | 3.588 | Ruby + http_parser.rb |
49
+ | HTTParty | 3.931 | Net::HTTP wrapper |
50
+ | Net::HTTP | 3.959 | Pure Ruby |
51
+ | Net::HTTP (persistent) | 4.043 | Pure Ruby |
52
+ | open-uri | 4.479 | Net::HTTP wrapper |
53
+ | Excon (persistent) | 4.618 | Pure Ruby |
54
+ | Excon | 4.701 | Pure Ruby |
55
+ | RestClient | 26.838 | Net::HTTP wrapper |
56
56
 
57
57
  Benchmarks performed using excon's benchmarking tool
58
58
 
@@ -79,20 +79,24 @@ https://github.com/httprb/http/issues
79
79
  ## Installation
80
80
 
81
81
  Add this line to your application's Gemfile:
82
-
83
- gem "http"
82
+ ```ruby
83
+ gem "http"
84
+ ```
84
85
 
85
86
  And then execute:
86
-
87
- $ bundle
87
+ ```bash
88
+ $ bundle
89
+ ```
88
90
 
89
91
  Or install it yourself as:
90
-
91
- $ gem install http
92
+ ```bash
93
+ $ gem install http
94
+ ```
92
95
 
93
96
  Inside of your Ruby program do:
94
-
95
- require "http"
97
+ ```ruby
98
+ require "http"
99
+ ```
96
100
 
97
101
  ...to pull it in as a dependency.
98
102
 
@@ -102,13 +106,14 @@ Inside of your Ruby program do:
102
106
  [Please see the http.rb wiki](https://github.com/httprb/http/wiki)
103
107
  for more detailed documentation and usage notes.
104
108
 
109
+ The following API documentation is also available:
105
110
 
106
- ## Basic Usage
111
+ * [YARD API documentation](http://www.rubydoc.info/gems/http/frames)
112
+ * [Chainable module (all chainable methods)](http://www.rubydoc.info/gems/http/HTTP/Chainable)
107
113
 
108
- Here's some simple examples to get you started:
114
+ ### Basic Usage
109
115
 
110
-
111
- ### GET requests
116
+ Here's some simple examples to get you started:
112
117
 
113
118
  ```ruby
114
119
  >> HTTP.get("https://github.com").to_s
@@ -141,216 +146,6 @@ The response body can be streamed with `HTTP::Response::Body#readpartial`:
141
146
  In practice you'll want to bind the HTTP::Response::Body to a local variable (e.g.
142
147
  "body") and call readpartial on it repeatedly until it returns `nil`.
143
148
 
144
-
145
- ### POST requests
146
-
147
- Making POST requests is simple too. Want to POST a form?
148
-
149
- ```ruby
150
- HTTP.post("http://example.com/resource", :form => {:foo => "42"})
151
- ```
152
- Making GET requests with query string parameters is as simple.
153
-
154
- ```ruby
155
- HTTP.get("http://example.com/resource", :params => {:foo => "bar"})
156
- ```
157
-
158
- Want to POST with a specific body, JSON for instance?
159
-
160
- ```ruby
161
- HTTP.post("http://example.com/resource", :json => { :foo => "42" })
162
- ```
163
-
164
- Or just a plain body?
165
-
166
- ```ruby
167
- HTTP.post("http://example.com/resource", :body => "foo=42&bar=baz")
168
- ```
169
-
170
- Posting a file?
171
-
172
- ``` ruby
173
- HTTP.post("http://examplc.com/resource", :form => {
174
- :username => "ixti",
175
- :avatar => HTTP::FormData::File.new("/home/ixit/avatar.png")
176
- })
177
- ```
178
-
179
- It's easy!
180
-
181
-
182
- ### Proxy Support
183
-
184
- Making request behind proxy is as simple as making them directly. Just specify
185
- hostname (or IP address) of your proxy server and its port, and here you go:
186
-
187
- ```ruby
188
- HTTP.via("proxy-hostname.local", 8080)
189
- .get("http://example.com/resource")
190
- ```
191
-
192
- Proxy needs authentication? No problem:
193
-
194
- ```ruby
195
- HTTP.via("proxy-hostname.local", 8080, "username", "password")
196
- .get("http://example.com/resource")
197
- ```
198
-
199
-
200
- ### Adding Headers
201
-
202
- The HTTP gem uses the concept of chaining to simplify requests. Let's say
203
- you want to get the latest commit of this library from GitHub in JSON format.
204
- One way we could do this is by tacking a filename on the end of the URL:
205
-
206
- ```ruby
207
- HTTP.get("https://github.com/httprb/http/commit/HEAD.json")
208
- ```
209
-
210
- The GitHub API happens to support this approach, but really this is a bit of a
211
- hack that makes it easy for people typing URLs into the address bars of
212
- browsers to perform the act of content negotiation. Since we have access to
213
- the full, raw power of HTTP, we can perform content negotiation the way HTTP
214
- intends us to, by using the Accept header:
215
-
216
- ```ruby
217
- HTTP.headers(:accept => "application/json")
218
- .get("https://github.com/httprb/http/commit/HEAD")
219
- ```
220
-
221
- This requests JSON from GitHub. GitHub is smart enough to understand our
222
- request and returns a response with `Content-Type: application/json`.
223
-
224
- Shorter alias exists for `HTTP.headers`:
225
-
226
- ```ruby
227
- HTTP[:accept => "application/json"]
228
- .get("https://github.com/httprb/http/commit/HEAD")
229
- ```
230
-
231
-
232
- ### Authorization Header
233
-
234
- With [HTTP Basic Authentication](http://tools.ietf.org/html/rfc2617) using
235
- a username and password:
236
-
237
- ```ruby
238
- HTTP.basic_auth(:user => "user", :pass => "pass")
239
- # <HTTP::Headers {"Authorization"=>"Basic dXNlcjpwYXNz"}>
240
- ```
241
-
242
- Or with plain as-is value:
243
-
244
- ```ruby
245
- HTTP.auth("Bearer VGhlIEhUVFAgR2VtLCBST0NLUw")
246
- # <HTTP::Headers {"Authorization"=>"Bearer VGhlIEhUVFAgR2VtLCBST0NLUw"}>
247
- ```
248
-
249
- And Chain all together!
250
-
251
- ```ruby
252
- HTTP.basic_auth(:user => "user", :pass => "pass")
253
- .headers("Cookie" => "9wq3w")
254
- .get("https://example.com")
255
- ```
256
-
257
-
258
- ### Content Negotiation
259
-
260
- As important a concept as content negotiation is to HTTP, it sure should be easy,
261
- right? But usually it's not, and so we end up adding ".json" onto the ends of
262
- our URLs because the existing mechanisms make it too hard. It should be easy:
263
-
264
- ```ruby
265
- HTTP.accept(:json).get("https://github.com/httprb/http/commit/HEAD")
266
- ```
267
-
268
- This adds the appropriate Accept header for retrieving a JSON response for the
269
- given resource.
270
-
271
-
272
- ### Reuse HTTP connection: HTTP Keep-Alive
273
-
274
- If you need to make many successive requests against the same host,
275
- you can create client with persistent connection to the host:
276
-
277
- ``` ruby
278
- begin
279
- # create HTTP client with persistent connection to api.icndb.com:
280
- http = HTTP.persistent "http://api.icndb.com"
281
-
282
- # issue multiple requests using same connection:
283
- jokes = 100.times.map { http.get("/jokes/random").to_s }
284
- ensure
285
- # close underlying connection when you don't need it anymore
286
- http.close if http
287
- end
288
-
289
- ```
290
-
291
- If the optional code block is given, it will be passed the client with
292
- persistent connection to the host as an argument and `client.close` will be
293
- automatically called when the block terminates.
294
- The value of the block will be returned:
295
-
296
- ``` ruby
297
- jokes = HTTP.persistent "http://api.icndb.com" do |http|
298
- 100.times.map { http.get("/jokes/random").to_s }
299
- end
300
- ```
301
-
302
- ##### NOTICE
303
-
304
- You must consume response before sending next request via persistent connection.
305
- That means you need to call `#to_s`, `#parse` or `#flush` on response object.
306
- In the example above we used `http.get("/jokes/random").to_s` to get response
307
- bodies. That works perfectly fine, because `#to_s` reads off the response.
308
-
309
- Sometimes you don't need response body, or need whole response object to
310
- access it's status, headers etc instead. You can either call `#to_s` to
311
- make sure response was flushed and then use response object itself, or use
312
- `#flush` (syntax sugar for `#tap(&:to_s)` that will do that for you:
313
-
314
-
315
- ``` ruby
316
- contents = HTTP.persistent "http://en.wikipedia.org" do |http|
317
- %w(Hypertext_Transfer_Protocol Git GitHub Linux Hurd).map do
318
- http.get("/wiki/#{target}").flush
319
- end
320
- end
321
- ```
322
-
323
-
324
- ### Timeouts
325
-
326
- By default, HTTP does not timeout on a request. You can enable per operation
327
- (each read/write/connect call) or global (sum of all read/write/connect calls).
328
-
329
- Per operation timeouts are what `Net::HTTP` and the majority of HTTP clients do:
330
-
331
- ``` ruby
332
- HTTP.timeout(:per_operation, :write => 2, :connect => 5, :read => 10)
333
- .get "http://example.com"
334
-
335
- # For convinience, you can omit timeout type in this case. So following has
336
- # same result as the above:
337
-
338
- HTTP.timeout(:write => 2, :connect => 5, :read => 10).get "http://example.com"
339
- ```
340
-
341
- Global timeouts let you set an upper bound of how long a request can take,
342
- without having to rely on `Timeout.timeout`:
343
-
344
- ``` ruby
345
- HTTP.timeout(:global, :write => 1, :connect => 1, :read => 1)
346
- .get "http://example.com"
347
- ```
348
-
349
- Uses a timeout of 3 seconds, for the entire `get` call.
350
-
351
- *Warning!* You cannot use Celluloid::IO with timeouts currently.
352
-
353
-
354
149
  ## Supported Ruby Versions
355
150
 
356
151
  This library aims to support and is [tested against][travis] the following Ruby
@@ -13,7 +13,7 @@ Gem::Specification.new do |gem|
13
13
  DESCRIPTION
14
14
 
15
15
  gem.summary = "HTTP should be easy"
16
- gem.homepage = "https://github.com/httprb/http.rb"
16
+ gem.homepage = "https://github.com/httprb/http"
17
17
  gem.licenses = ["MIT"]
18
18
 
19
19
  gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
@@ -20,6 +20,6 @@ module HTTP
20
20
 
21
21
  class << self
22
22
  # HTTP[:accept => 'text/html'].get(...)
23
- alias_method :[], :headers
23
+ alias [] headers
24
24
  end
25
25
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "base64"
2
3
 
3
4
  require "http/headers"
@@ -153,13 +154,11 @@ module HTTP
153
154
  proxy_hash[:proxy_username] = proxy[2] if proxy[2].is_a?(String)
154
155
  proxy_hash[:proxy_password] = proxy[3] if proxy[3].is_a?(String)
155
156
 
156
- if [2, 4].include?(proxy_hash.keys.size)
157
- branch default_options.with_proxy(proxy_hash)
158
- else
159
- fail(RequestError, "invalid HTTP proxy: #{proxy_hash}")
160
- end
157
+ fail(RequestError, "invalid HTTP proxy: #{proxy_hash}") unless [2, 4].include?(proxy_hash.keys.size)
158
+
159
+ branch default_options.with_proxy(proxy_hash)
161
160
  end
162
- alias_method :through, :via
161
+ alias through via
163
162
 
164
163
  # Make client follow redirects.
165
164
  # @param opts
@@ -206,7 +205,7 @@ module HTTP
206
205
  user = opts.fetch :user
207
206
  pass = opts.fetch :pass
208
207
 
209
- auth("Basic " << Base64.strict_encode64("#{user}:#{pass}"))
208
+ auth("Basic " + Base64.strict_encode64("#{user}:#{pass}"))
210
209
  end
211
210
 
212
211
  # Get options for HTTP
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "forwardable"
2
3
 
3
4
  require "http/form_data"
@@ -137,13 +138,10 @@ module HTTP
137
138
  headers = opts.headers
138
139
 
139
140
  # Tell the server to keep the conn open
140
- if default_options.persistent?
141
- headers[Headers::CONNECTION] = KEEP_ALIVE
142
- else
143
- headers[Headers::CONNECTION] = CLOSE
144
- end
141
+ headers[Headers::CONNECTION] = default_options.persistent? ? KEEP_ALIVE : CLOSE
145
142
 
146
143
  cookies = opts.cookies.values
144
+
147
145
  unless cookies.empty?
148
146
  cookies = opts.headers.get(Headers::COOKIE).concat(cookies).join("; ")
149
147
  headers[Headers::COOKIE] = cookies
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "forwardable"
2
3
 
3
4
  require "http/client"
@@ -80,13 +81,8 @@ module HTTP
80
81
  def readpartial(size = BUFFER_SIZE)
81
82
  return unless @pending_response
82
83
 
83
- if read_more(size) == :eof
84
- finished = true
85
- else
86
- finished = @parser.finished?
87
- end
88
-
89
- chunk = @parser.chunk
84
+ finished = (read_more(size) == :eof) || @parser.finished?
85
+ chunk = @parser.chunk
90
86
 
91
87
  finish_response if finished
92
88
 
@@ -100,8 +96,8 @@ module HTTP
100
96
  if read_more(BUFFER_SIZE) == :eof
101
97
  fail EOFError unless @parser.headers?
102
98
  break
103
- else
104
- break if @parser.headers?
99
+ elsif @parser.headers?
100
+ break
105
101
  end
106
102
  end
107
103
 
@@ -169,18 +165,18 @@ module HTTP
169
165
 
170
166
  req.connect_using_proxy @socket
171
167
 
172
- @pending_request = false
168
+ @pending_request = false
173
169
  @pending_response = true
174
170
 
175
171
  read_headers!
176
172
 
177
- if @parser.status_code == 200
178
- @parser.reset
179
- @pending_response = false
173
+ if @parser.status_code != 200
174
+ @failed_proxy_connect = true
180
175
  return
181
176
  end
182
177
 
183
- @failed_proxy_connect = true
178
+ @parser.reset
179
+ @pending_response = false
184
180
  end
185
181
 
186
182
  # Resets expiration of persistent connection.
@@ -195,14 +191,15 @@ module HTTP
195
191
  def set_keep_alive
196
192
  return @keep_alive = false unless @persistent
197
193
 
198
- case @parser.http_version
199
- when HTTP_1_0 # HTTP/1.0 requires opt in for Keep Alive
200
- @keep_alive = @parser.headers[Headers::CONNECTION] == Client::KEEP_ALIVE
201
- when HTTP_1_1 # HTTP/1.1 is opt-out
202
- @keep_alive = @parser.headers[Headers::CONNECTION] != Client::CLOSE
203
- else # Anything else we assume doesn't supportit
204
- @keep_alive = false
205
- end
194
+ @keep_alive =
195
+ case @parser.http_version
196
+ when HTTP_1_0 # HTTP/1.0 requires opt in for Keep Alive
197
+ @parser.headers[Headers::CONNECTION] == Client::KEEP_ALIVE
198
+ when HTTP_1_1 # HTTP/1.1 is opt-out
199
+ @parser.headers[Headers::CONNECTION] != Client::CLOSE
200
+ else # Anything else we assume doesn't supportit
201
+ false
202
+ end
206
203
  end
207
204
 
208
205
  # Feeds some more data into parser
@@ -11,11 +11,11 @@ module HTTP
11
11
  include Enumerable
12
12
 
13
13
  # Matches HTTP header names when in "Canonical-Http-Format"
14
- CANONICAL_HEADER = /^[A-Z][a-z]*(-[A-Z][a-z]*)*$/
14
+ CANONICAL_NAME_RE = /^[A-Z][a-z]*(?:-[A-Z][a-z]*)*$/
15
15
 
16
16
  # Matches valid header field name according to RFC.
17
17
  # @see http://tools.ietf.org/html/rfc7230#section-3.2
18
- HEADER_NAME_RE = /^[A-Za-z0-9!#\$%&'*+\-.^_`|~]+$/
18
+ COMPLIANT_NAME_RE = /^[A-Za-z0-9!#\$%&'*+\-.^_`|~]+$/
19
19
 
20
20
  # Class constructor.
21
21
  def initialize
@@ -30,7 +30,7 @@ module HTTP
30
30
  delete(name)
31
31
  add(name, value)
32
32
  end
33
- alias_method :[]=, :set
33
+ alias []= set
34
34
 
35
35
  # Removes header.
36
36
  #
@@ -80,7 +80,7 @@ module HTTP
80
80
  def to_h
81
81
  Hash[keys.map { |k| [k, self[k]] }]
82
82
  end
83
- alias_method :to_hash, :to_h
83
+ alias to_hash to_h
84
84
 
85
85
  # Returns headers key/value pairs.
86
86
  #
@@ -179,7 +179,7 @@ module HTTP
179
179
  object.each { |k, v| headers.add k, v }
180
180
  headers
181
181
  end
182
- alias_method :[], :coerce
182
+ alias [] coerce
183
183
  end
184
184
 
185
185
  private
@@ -191,10 +191,11 @@ module HTTP
191
191
  # match {HEADER_NAME_RE}
192
192
  # @return [String] canonical HTTP header name
193
193
  def normalize_header(name)
194
- normalized = name[CANONICAL_HEADER]
195
- normalized ||= name.split(/[\-_]/).map(&:capitalize).join("-")
194
+ return name if name =~ CANONICAL_NAME_RE
196
195
 
197
- return normalized if normalized =~ HEADER_NAME_RE
196
+ normalized = name.split(/[\-_]/).each(&:capitalize!).join("-")
197
+
198
+ return normalized if normalized =~ COMPLIANT_NAME_RE
198
199
 
199
200
  fail InvalidHeaderNameError, "Invalid HTTP header field name: #{name.inspect}"
200
201
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module HTTP
2
3
  class Headers
3
4
  # Content-Types that are acceptable for the response.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "forwardable"
2
3
  require "base64"
3
4
  require "time"
@@ -23,26 +24,28 @@ module HTTP
23
24
  # Default User-Agent header value
24
25
  USER_AGENT = "http.rb/#{HTTP::VERSION}".freeze
25
26
 
26
- # RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1
27
- METHODS = [:options, :get, :head, :post, :put, :delete, :trace, :connect]
27
+ METHODS = [
28
+ # RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1
29
+ :options, :get, :head, :post, :put, :delete, :trace, :connect,
28
30
 
29
- # RFC 2518: HTTP Extensions for Distributed Authoring -- WEBDAV
30
- METHODS.concat [:propfind, :proppatch, :mkcol, :copy, :move, :lock, :unlock]
31
+ # RFC 2518: HTTP Extensions for Distributed Authoring -- WEBDAV
32
+ :propfind, :proppatch, :mkcol, :copy, :move, :lock, :unlock,
31
33
 
32
- # RFC 3648: WebDAV Ordered Collections Protocol
33
- METHODS.concat [:orderpatch]
34
+ # RFC 3648: WebDAV Ordered Collections Protocol
35
+ :orderpatch,
34
36
 
35
- # RFC 3744: WebDAV Access Control Protocol
36
- METHODS.concat [:acl]
37
+ # RFC 3744: WebDAV Access Control Protocol
38
+ :acl,
37
39
 
38
- # draft-dusseault-http-patch: PATCH Method for HTTP
39
- METHODS.concat [:patch]
40
+ # draft-dusseault-http-patch: PATCH Method for HTTP
41
+ :patch,
40
42
 
41
- # draft-reschke-webdav-search: WebDAV Search
42
- METHODS.concat [:search]
43
+ # draft-reschke-webdav-search: WebDAV Search
44
+ :search
45
+ ].freeze
43
46
 
44
47
  # Allowed schemes
45
- SCHEMES = [:http, :https, :ws, :wss]
48
+ SCHEMES = [:http, :https, :ws, :wss].freeze
46
49
 
47
50
  # Default ports of supported schemes
48
51
  PORTS = {
@@ -50,7 +53,7 @@ module HTTP
50
53
  :https => 443,
51
54
  :ws => 80,
52
55
  :wss => 443
53
- }
56
+ }.freeze
54
57
 
55
58
  # Method is given as a lowercase symbol e.g. :get, :post
56
59
  attr_reader :verb
@@ -71,7 +74,7 @@ module HTTP
71
74
  # @option opts [String] :body
72
75
  def initialize(opts)
73
76
  @verb = opts.fetch(:verb).to_s.downcase.to_sym
74
- @uri = normalize_uri(opts.fetch :uri)
77
+ @uri = normalize_uri(opts.fetch(:uri))
75
78
  @scheme = @uri.scheme.to_s.downcase.to_sym if @uri.scheme
76
79
 
77
80
  fail(UnsupportedMethodError, "unknown method: #{verb}") unless METHODS.include?(@verb)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "http/headers"
2
3
 
3
4
  module HTTP
@@ -16,7 +17,7 @@ module HTTP
16
17
  CHUNKED_END = "#{ZERO}#{CRLF}#{CRLF}".freeze
17
18
 
18
19
  # Types valid to be used as body source
19
- VALID_BODY_TYPES = [String, NilClass, Enumerable]
20
+ VALID_BODY_TYPES = [String, NilClass, Enumerable].freeze
20
21
 
21
22
  def initialize(socket, body, headers, headline)
22
23
  @body = body
@@ -62,7 +63,7 @@ module HTTP
62
63
  def join_headers
63
64
  # join the headers array with crlfs, stick two on the end because
64
65
  # that ends the request header
65
- @request_header.join(CRLF) + (CRLF) * 2
66
+ @request_header.join(CRLF) + CRLF * 2
66
67
  end
67
68
 
68
69
  def send_request
@@ -94,11 +95,8 @@ module HTTP
94
95
  def write(data)
95
96
  until data.empty?
96
97
  length = @socket.write(data)
97
- if data.length > length
98
- data = data[length..-1]
99
- else
100
- break
101
- end
98
+ break unless data.length > length
99
+ data = data[length..-1]
102
100
  end
103
101
  end
104
102
 
@@ -34,8 +34,8 @@ module HTTP
34
34
  # @option opts [String] :uri
35
35
  def initialize(opts)
36
36
  @version = opts.fetch(:version)
37
- @uri = HTTP::URI.parse(opts.fetch :uri) if opts.include? :uri
38
- @status = HTTP::Response::Status.new(opts.fetch :status)
37
+ @uri = HTTP::URI.parse(opts.fetch(:uri)) if opts.include? :uri
38
+ @status = HTTP::Response::Status.new(opts.fetch(:status))
39
39
  @headers = HTTP::Headers.coerce(opts[:headers] || {})
40
40
 
41
41
  if opts.include?(:connection)
@@ -59,7 +59,7 @@ module HTTP
59
59
  # @!method to_s
60
60
  # (see HTTP::Response::Body#to_s)
61
61
  def_delegator :body, :to_s
62
- alias_method :to_str, :to_s
62
+ alias to_str to_s
63
63
 
64
64
  # @!method readpartial
65
65
  # (see HTTP::Response::Body#readpartial)
@@ -35,11 +35,18 @@ module HTTP
35
35
 
36
36
  fail StateError, "body is being streamed" unless @streaming.nil?
37
37
 
38
+ # see issue 312
39
+ begin
40
+ encoding = Encoding.find @encoding
41
+ rescue ArgumentError
42
+ encoding = Encoding::BINARY
43
+ end
44
+
38
45
  begin
39
46
  @streaming = false
40
- @contents = "".force_encoding(@encoding)
47
+ @contents = "".force_encoding(encoding)
41
48
  while (chunk = @client.readpartial)
42
- @contents << chunk.force_encoding(@encoding)
49
+ @contents << chunk.force_encoding(encoding)
43
50
  end
44
51
  rescue
45
52
  @contents = nil
@@ -48,7 +55,7 @@ module HTTP
48
55
 
49
56
  @contents
50
57
  end
51
- alias_method :to_str, :to_s
58
+ alias to_str to_s
52
59
 
53
60
  # Assert that the body is actively being streamed
54
61
  def stream!
@@ -11,7 +11,7 @@ module HTTP
11
11
  def add(data)
12
12
  @parser << data
13
13
  end
14
- alias_method :<<, :add
14
+ alias << add
15
15
 
16
16
  def headers?
17
17
  !!@headers
@@ -28,7 +28,7 @@ module HTTP
28
28
 
29
29
  fail Error, "Can't coerce #{object.class}(#{object}) to #{self}"
30
30
  end
31
- alias_method :[], :coerce
31
+ alias [] coerce
32
32
 
33
33
  private
34
34
 
@@ -32,13 +32,13 @@ module HTTP
32
32
  reset_timer
33
33
 
34
34
  begin
35
- socket.connect_nonblock
35
+ @socket.connect_nonblock
36
36
  rescue IO::WaitReadable
37
- IO.select([socket], nil, nil, time_left)
37
+ IO.select([@socket], nil, nil, time_left)
38
38
  log_time
39
39
  retry
40
40
  rescue IO::WaitWritable
41
- IO.select(nil, [socket], nil, time_left)
41
+ IO.select(nil, [@socket], nil, time_left)
42
42
  log_time
43
43
  retry
44
44
  end
@@ -54,7 +54,7 @@ module HTTP
54
54
  perform_io { write_nonblock(data) }
55
55
  end
56
56
 
57
- alias_method :<<, :write
57
+ alias << write
58
58
 
59
59
  private
60
60
 
@@ -48,7 +48,7 @@ module HTTP
48
48
  def write(data)
49
49
  @socket.write(data)
50
50
  end
51
- alias_method :<<, :write
51
+ alias << write
52
52
 
53
53
  # These cops can be re-eanbled after we go Ruby 2.0+ only
54
54
  # rubocop:disable Lint/UselessAccessModifier, Metrics/BlockNesting
@@ -60,7 +60,7 @@ module HTTP
60
60
  def rescue_readable
61
61
  yield
62
62
  rescue IO::WaitReadable
63
- retry if IO.select([socket], nil, nil, read_timeout)
63
+ retry if IO.select([@socket], nil, nil, read_timeout)
64
64
  raise TimeoutError, "Read timed out after #{read_timeout} seconds"
65
65
  end
66
66
 
@@ -68,7 +68,7 @@ module HTTP
68
68
  def rescue_writable
69
69
  yield
70
70
  rescue IO::WaitWritable
71
- retry if IO.select(nil, [socket], nil, write_timeout)
71
+ retry if IO.select(nil, [@socket], nil, write_timeout)
72
72
  raise TimeoutError, "Write timed out after #{write_timeout} seconds"
73
73
  end
74
74
  else
@@ -78,7 +78,7 @@ module HTTP
78
78
  def rescue_readable
79
79
  yield
80
80
  rescue IO::WaitReadable
81
- retry if socket.to_io.wait_readable(read_timeout)
81
+ retry if @socket.to_io.wait_readable(read_timeout)
82
82
  raise TimeoutError, "Read timed out after #{read_timeout} seconds"
83
83
  end
84
84
 
@@ -86,7 +86,7 @@ module HTTP
86
86
  def rescue_writable
87
87
  yield
88
88
  rescue IO::WaitWritable
89
- retry if socket.to_io.wait_writable(write_timeout)
89
+ retry if @socket.to_io.wait_writable(write_timeout)
90
90
  raise TimeoutError, "Write timed out after #{write_timeout} seconds"
91
91
  end
92
92
  end
@@ -39,7 +39,7 @@ module HTTP
39
39
  # Read data from the socket
40
40
  def readpartial(size)
41
41
  rescue_readable do
42
- socket.read_nonblock(size)
42
+ @socket.read_nonblock(size)
43
43
  end
44
44
  rescue EOFError
45
45
  :eof
@@ -48,7 +48,7 @@ module HTTP
48
48
  # Write data to the socket
49
49
  def write(data)
50
50
  rescue_writable do
51
- socket.write_nonblock(data)
51
+ @socket.write_nonblock(data)
52
52
  end
53
53
  rescue EOFError
54
54
  :eof
@@ -59,19 +59,14 @@ module HTTP
59
59
  # Read data from the socket
60
60
  def readpartial(size)
61
61
  loop do
62
- # JRuby may still raise exceptions on SSL sockets even though
63
- # we explicitly specify `:exception => false`
64
- result = rescue_readable do
65
- socket.read_nonblock(size, :exception => false)
66
- end
67
-
62
+ result = @socket.read_nonblock(size, :exception => false)
68
63
  if result.nil?
69
64
  return :eof
70
65
  elsif result != :wait_readable
71
66
  return result
72
67
  end
73
68
 
74
- unless socket.to_io.wait_readable(read_timeout)
69
+ unless @socket.to_io.wait_readable(read_timeout)
75
70
  fail TimeoutError, "Read timed out after #{read_timeout} seconds"
76
71
  end
77
72
  end
@@ -80,16 +75,11 @@ module HTTP
80
75
  # Write data to the socket
81
76
  def write(data)
82
77
  loop do
83
- # JRuby may still raise exceptions on SSL sockets even though
84
- # we explicitly specify `:exception => false`
85
- result = rescue_writable do
86
- socket.write_nonblock(data, :exception => false)
87
- end
88
-
78
+ result = @socket.write_nonblock(data, :exception => false)
89
79
  return result unless result == :wait_writable
90
80
 
91
- unless socket.to_io.wait_writable(write_timeout)
92
- fail TimeoutError, "Read timed out after #{write_timeout} seconds"
81
+ unless @socket.to_io.wait_writable(write_timeout)
82
+ fail TimeoutError, "Write timed out after #{write_timeout} seconds"
93
83
  end
94
84
  end
95
85
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "addressable/uri"
2
3
 
3
4
  module HTTP
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module HTTP
2
- VERSION = "1.0.2".freeze
3
+ VERSION = "1.0.3".freeze
3
4
  end
@@ -110,7 +110,7 @@ RSpec.describe HTTP::Client do
110
110
 
111
111
  it "accepts params within the provided URL" do
112
112
  expect(HTTP::Request).to receive(:new) do |opts|
113
- expect(CGI.parse opts[:uri].query).to eq("foo" => %w(bar))
113
+ expect(CGI.parse(opts[:uri].query)).to eq("foo" => %w(bar))
114
114
  end
115
115
 
116
116
  client.get("http://example.com/?foo=bar")
@@ -118,7 +118,7 @@ RSpec.describe HTTP::Client do
118
118
 
119
119
  it "combines GET params from the URI with the passed in params" do
120
120
  expect(HTTP::Request).to receive(:new) do |opts|
121
- expect(CGI.parse opts[:uri].query).to eq("foo" => %w(bar), "baz" => %w(quux))
121
+ expect(CGI.parse(opts[:uri].query)).to eq("foo" => %w(bar), "baz" => %w(quux))
122
122
  end
123
123
 
124
124
  client.get("http://example.com/?foo=bar", :params => {:baz => "quux"})
@@ -142,7 +142,7 @@ RSpec.describe HTTP::Client do
142
142
 
143
143
  it "does not corrupts index-less arrays" do
144
144
  expect(HTTP::Request).to receive(:new) do |opts|
145
- expect(CGI.parse opts[:uri].query).to eq "a[]" => %w(b c), "d" => %w(e)
145
+ expect(CGI.parse(opts[:uri].query)).to eq "a[]" => %w(b c), "d" => %w(e)
146
146
  end
147
147
 
148
148
  client.get("http://example.com/?a[]=b&a[]=c", :params => {:d => "e"})
@@ -111,7 +111,7 @@ RSpec.describe HTTP::Headers do
111
111
  end
112
112
 
113
113
  it "fails with empty header name" do
114
- expect { headers.add "", "foobar" }.
114
+ expect { headers.add("", "foobar") }.
115
115
  to raise_error HTTP::InvalidHeaderNameError
116
116
  end
117
117
 
@@ -122,29 +122,29 @@ RSpec.describe HTTP::Headers do
122
122
  end
123
123
 
124
124
  describe "#get" do
125
- before { headers.set "Content-Type", "application/json" }
125
+ before { headers.set("Content-Type", "application/json") }
126
126
 
127
127
  it "returns array of associated values" do
128
- expect(headers.get "Content-Type").to eq %w(application/json)
128
+ expect(headers.get("Content-Type")).to eq %w(application/json)
129
129
  end
130
130
 
131
131
  it "normalizes header name" do
132
- expect(headers.get :content_type).to eq %w(application/json)
132
+ expect(headers.get(:content_type)).to eq %w(application/json)
133
133
  end
134
134
 
135
135
  context "when header does not exists" do
136
136
  it "returns empty array" do
137
- expect(headers.get :accept).to eq []
137
+ expect(headers.get(:accept)).to eq []
138
138
  end
139
139
  end
140
140
 
141
141
  it "fails with empty header name" do
142
- expect { headers.get "" }.
142
+ expect { headers.get("") }.
143
143
  to raise_error HTTP::InvalidHeaderNameError
144
144
  end
145
145
 
146
146
  it "fails with invalid header name" do
147
- expect { headers.get "foo bar" }.
147
+ expect { headers.get("foo bar") }.
148
148
  to raise_error HTTP::InvalidHeaderNameError
149
149
  end
150
150
  end
@@ -453,15 +453,18 @@ RSpec.describe HTTP::Headers do
453
453
  let(:headers) { {"Set-Cookie" => "hoo=ray", "set-cookie" => "woo=hoo"} }
454
454
 
455
455
  it "adds all headers" do
456
- expect(described_class.coerce(headers).to_a).to match_array([
457
- %w(Set-Cookie hoo=ray),
458
- %w(Set-Cookie woo=hoo)
459
- ])
456
+ expect(described_class.coerce(headers).to_a).
457
+ to match_array(
458
+ [
459
+ %w(Set-Cookie hoo=ray),
460
+ %w(Set-Cookie woo=hoo)
461
+ ]
462
+ )
460
463
  end
461
464
  end
462
465
 
463
466
  it "is aliased as .[]" do
464
- expect(described_class.method :coerce).to eq described_class.method(:[])
467
+ expect(described_class.method(:coerce)).to eq described_class.method(:[])
465
468
  end
466
469
  end
467
470
  end
@@ -96,7 +96,7 @@ RSpec.describe HTTP::Response::Status do
96
96
  describe ".coerce" do
97
97
  context "with String" do
98
98
  it "coerces reasons" do
99
- expect(described_class.coerce "Bad request").to eq described_class.new 400
99
+ expect(described_class.coerce("Bad request")).to eq described_class.new 400
100
100
  end
101
101
 
102
102
  it "fails when reason is unknown" do
@@ -106,26 +106,26 @@ RSpec.describe HTTP::Response::Status do
106
106
 
107
107
  context "with Symbol" do
108
108
  it "coerces symbolized reasons" do
109
- expect(described_class.coerce :bad_request).to eq described_class.new 400
109
+ expect(described_class.coerce(:bad_request)).to eq described_class.new 400
110
110
  end
111
111
 
112
112
  it "fails when symbolized reason is unknown" do
113
- expect { described_class.coerce :foobar }.to raise_error HTTP::Error
113
+ expect { described_class.coerce(:foobar) }.to raise_error HTTP::Error
114
114
  end
115
115
  end
116
116
 
117
117
  context "with Numeric" do
118
118
  it "coerces as Fixnum code" do
119
- expect(described_class.coerce 200.1).to eq described_class.new 200
119
+ expect(described_class.coerce(200.1)).to eq described_class.new 200
120
120
  end
121
121
  end
122
122
 
123
123
  it "fails if coercion failed" do
124
- expect { described_class.coerce true }.to raise_error HTTP::Error
124
+ expect { described_class.coerce(true) }.to raise_error HTTP::Error
125
125
  end
126
126
 
127
127
  it "is aliased as `.[]`" do
128
- expect(described_class.method :coerce).to eq described_class.method :[]
128
+ expect(described_class.method(:coerce)).to eq described_class.method :[]
129
129
  end
130
130
  end
131
131
  end
@@ -86,11 +86,11 @@ RSpec.describe HTTP::Response do
86
86
  context "with explicitly given mime type" do
87
87
  let(:content_type) { "application/deadbeef" }
88
88
  it "ignores mime_type of response" do
89
- expect(response.parse "application/json").to eq "foo" => "bar"
89
+ expect(response.parse("application/json")).to eq "foo" => "bar"
90
90
  end
91
91
 
92
92
  it "supports MIME type aliases" do
93
- expect(response.parse :json).to eq "foo" => "bar"
93
+ expect(response.parse(:json)).to eq "foo" => "bar"
94
94
  end
95
95
  end
96
96
  end
@@ -57,7 +57,7 @@ RSpec.describe HTTP do
57
57
 
58
58
  let(:characters) { ("A".."Z").to_a }
59
59
  let(:request_body) do
60
- (size + fuzzer).times.map { |i| characters[i % characters.length] }.join
60
+ Array.new(size + fuzzer) { |i| characters[i % characters.length] }.join
61
61
  end
62
62
 
63
63
  it "returns a large body" do
@@ -2,12 +2,12 @@ require "spec_helper"
2
2
 
3
3
  RSpec.describe "Regression testing" do
4
4
  describe "#248" do
5
- it "does not fails with github" do
5
+ it "does not fail with github" do
6
6
  github_uri = "http://github.com/"
7
7
  expect { HTTP.get(github_uri).to_s }.not_to raise_error
8
8
  end
9
9
 
10
- it "does not failes ith googleapis" do
10
+ it "does not fail with googleapis" do
11
11
  google_uri = "https://www.googleapis.com/oauth2/v1/userinfo?alt=json"
12
12
  expect { HTTP.get(google_uri).to_s }.not_to raise_error
13
13
  end
@@ -3,10 +3,12 @@
3
3
  require "simplecov"
4
4
  require "coveralls"
5
5
 
6
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
7
- SimpleCov::Formatter::HTMLFormatter,
8
- Coveralls::SimpleCov::Formatter
9
- ])
6
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
7
+ [
8
+ SimpleCov::Formatter::HTMLFormatter,
9
+ Coveralls::SimpleCov::Formatter
10
+ ]
11
+ )
10
12
 
11
13
  SimpleCov.start do
12
14
  add_filter "/spec/"
@@ -1,7 +1,7 @@
1
1
  module ServerRunner
2
- def run_server(name, &block)
2
+ def run_server(name)
3
3
  let! name do
4
- server = block.call
4
+ server = yield
5
5
 
6
6
  Thread.new { server.start }
7
7
 
@@ -6,7 +6,7 @@ module SSLHelper
6
6
  CERTS_PATH = Pathname.new File.expand_path("../../../tmp/certs", __FILE__)
7
7
 
8
8
  class RootCertificate < ::CertificateAuthority::Certificate
9
- EXTENSIONS = {"keyUsage" => {"usage" => %w(critical keyCertSign)}}
9
+ EXTENSIONS = {"keyUsage" => {"usage" => %w(critical keyCertSign)}}.freeze
10
10
 
11
11
  def initialize
12
12
  super()
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: http
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Arcieri
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2016-01-15 00:00:00.000000000 Z
14
+ date: 2016-03-16 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: http_parser.rb
@@ -162,7 +162,7 @@ files:
162
162
  - spec/support/servers/config.rb
163
163
  - spec/support/servers/runner.rb
164
164
  - spec/support/ssl_helper.rb
165
- homepage: https://github.com/httprb/http.rb
165
+ homepage: https://github.com/httprb/http
166
166
  licenses:
167
167
  - MIT
168
168
  metadata: {}