http 3.0.0 → 3.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7bb69225fc9d93b71457dbff8784f4b76b91424a
4
- data.tar.gz: 7377f42f20a229e2035a7bc648e259aba64a3968
3
+ metadata.gz: 42fd5a5fec6c2e6448908c9578bf27175a291933
4
+ data.tar.gz: 03a31ca5372bf0e9e2c5dcae9a8e06d9a7e38c56
5
5
  SHA512:
6
- metadata.gz: e5652ecf755869765201df754b8642afcb350cf7676561a2487d27741cda036a9702583d903fdfbb22f7bd9da9259d2e10a43ddcc2dc495febc5b59871e317c5
7
- data.tar.gz: f5f85cb7a92f0b71a86ff40f2b64cf6a266f7381cbd3da2d12f306f8a2f748e9755bfe52b7ed1b715ace31972dee99829a0687790895fe530bd5bd9f9351e8f2
6
+ metadata.gz: abc5ea24f0d2eb18022994003ab416bfddaa8976fc58bb41169eac32431b70838255d9956d710581da1ec844e0c85243d7fa937c0603122a8d61211b47bc80ae
7
+ data.tar.gz: 4b25eb0dac0ad59a5688ec5298253975c8ff186dde6d181cb8276e8e2aeafe235305f3c1717bc8c4171a551bf965215c4e0f05cda30dc79cd1bd03f6a3aa204f
data/CHANGES.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## 3.1.0 (2018-04-22)
2
+
3
+ This version backports some of the fixes and improvements made to development
4
+ version of the HTTP gem:
5
+
6
+ * Fix for `#readpartial` to respect max length argument.
7
+ ([@janko-m], [@marshall-lee])
8
+
9
+ * Fix for `HTTP::Request#headline` to allow two leading slashes in path.
10
+ ([@scarfacedeb])
11
+
12
+ * Fix query string building for string with newlines.
13
+ ([@mikegee])
14
+
15
+ * Deallocate temporary strings in `Response::Body#to_s`.
16
+ ([@janko-m])
17
+
18
+ * Add `Request::Body#source`.
19
+ ([@janko-m])
20
+
21
+
1
22
  ## 3.0.0 (2017-10-01)
2
23
 
3
24
  * Drop support of Ruby `2.0` and Ruby `2.1`.
@@ -618,3 +639,6 @@ end
618
639
  [@janko-m]: https://github.com/janko-m
619
640
  [@Bonias]: https://github.com/Bonias
620
641
  [@HoneyryderChuck]: https://github.com/HoneyryderChuck
642
+ [@marshall-lee]: https://github.com/marshall-lee
643
+ [@scarfacedeb]: https://github.com/scarfacedeb
644
+ [@mikegee]: https://github.com/mikegee
data/README.md CHANGED
@@ -6,9 +6,10 @@
6
6
  [![Coverage Status](https://coveralls.io/repos/httprb/http/badge.svg?branch=master)](https://coveralls.io/r/httprb/http)
7
7
  [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/httprb/http/blob/master/LICENSE.txt)
8
8
 
9
- _NOTE: This is the 3.x **development** branch. For the 2.x **stable** branch, please see:_
9
+ _NOTE: This is the 3.x **stable** branch. For the 4.x **development** branch, please see:_
10
+
11
+ https://github.com/httprb/http/
10
12
 
11
- https://github.com/httprb/http/tree/2-x-stable
12
13
 
13
14
  ## About
14
15
 
@@ -193,5 +194,5 @@ dropped.
193
194
 
194
195
  ## Copyright
195
196
 
196
- Copyright (c) 2011-2017 Tony Arcieri, Alexey V. Zapparov, Erik Michaels-Ober, Zachary Anker.
197
+ Copyright (c) 2011-2018 Tony Arcieri, Alexey V. Zapparov, Erik Michaels-Ober, Zachary Anker.
197
198
  See LICENSE.txt for further details.
@@ -25,10 +25,10 @@ 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.0"
28
+ gem.required_ruby_version = ">= 2.2"
29
29
 
30
30
  gem.add_runtime_dependency "http_parser.rb", "~> 0.6.0"
31
- gem.add_runtime_dependency "http-form_data", ">= 2.0.0-pre2", "< 3"
31
+ gem.add_runtime_dependency "http-form_data", "~> 2.0"
32
32
  gem.add_runtime_dependency "http-cookie", "~> 1.0"
33
33
  gem.add_runtime_dependency "addressable", "~> 2.3"
34
34
 
@@ -123,7 +123,7 @@ module HTTP
123
123
  uri = HTTP::URI.parse uri
124
124
 
125
125
  if opts.params && !opts.params.empty?
126
- uri.query = [uri.query, HTTP::URI.form_encode(opts.params)].compact.join("&")
126
+ uri.query_values = uri.query_values(Array).to_a.concat(opts.params.to_a)
127
127
  end
128
128
 
129
129
  # Some proxies (seen on WEBRick) fail if URL has
@@ -7,7 +7,7 @@ require "http/response/parser"
7
7
 
8
8
  module HTTP
9
9
  # A connection to the HTTP server
10
- class Connection
10
+ class Connection # rubocop: disable Metrics/ClassLength
11
11
  extend Forwardable
12
12
 
13
13
  # Allowed values for CONNECTION header
@@ -85,9 +85,11 @@ module HTTP
85
85
  def readpartial(size = BUFFER_SIZE)
86
86
  return unless @pending_response
87
87
 
88
- finished = (read_more(size) == :eof) || @parser.finished?
89
- chunk = @parser.chunk
88
+ chunk = @parser.read(size)
89
+ return chunk if chunk
90
90
 
91
+ finished = (read_more(size) == :eof) || @parser.finished?
92
+ chunk = @parser.read(size)
91
93
  finish_response if finished
92
94
 
93
95
  chunk.to_s
@@ -151,7 +151,7 @@ module HTTP
151
151
  if using_proxy? && !uri.https?
152
152
  uri.omit(:fragment)
153
153
  else
154
- uri.omit(:scheme, :authority, :fragment)
154
+ uri.request_uri
155
155
  end
156
156
 
157
157
  "#{verb.to_s.upcase} #{request_uri} HTTP/#{version}"
@@ -3,25 +3,27 @@
3
3
  module HTTP
4
4
  class Request
5
5
  class Body
6
- def initialize(body)
7
- @body = body
6
+ attr_reader :source
8
7
 
9
- validate_body_type!
8
+ def initialize(source)
9
+ @source = source
10
+
11
+ validate_source_type!
10
12
  end
11
13
 
12
14
  # Returns size which should be used for the "Content-Length" header.
13
15
  #
14
16
  # @return [Integer]
15
17
  def size
16
- if @body.is_a?(String)
17
- @body.bytesize
18
- elsif @body.respond_to?(:read)
19
- raise RequestError, "IO object must respond to #size" unless @body.respond_to?(:size)
20
- @body.size
21
- elsif @body.nil?
18
+ if @source.is_a?(String)
19
+ @source.bytesize
20
+ elsif @source.respond_to?(:read)
21
+ raise RequestError, "IO object must respond to #size" unless @source.respond_to?(:size)
22
+ @source.size
23
+ elsif @source.nil?
22
24
  0
23
25
  else
24
- raise RequestError, "cannot determine size of body: #{@body.inspect}"
26
+ raise RequestError, "cannot determine size of body: #{@source.inspect}"
25
27
  end
26
28
  end
27
29
 
@@ -29,24 +31,24 @@ module HTTP
29
31
  #
30
32
  # @yieldparam [String]
31
33
  def each(&block)
32
- if @body.is_a?(String)
33
- yield @body
34
- elsif @body.respond_to?(:read)
35
- IO.copy_stream(@body, ProcIO.new(block))
36
- elsif @body.is_a?(Enumerable)
37
- @body.each(&block)
34
+ if @source.is_a?(String)
35
+ yield @source
36
+ elsif @source.respond_to?(:read)
37
+ IO.copy_stream(@source, ProcIO.new(block))
38
+ elsif @source.is_a?(Enumerable)
39
+ @source.each(&block)
38
40
  end
39
41
  end
40
42
 
41
43
  private
42
44
 
43
- def validate_body_type!
44
- return if @body.is_a?(String)
45
- return if @body.respond_to?(:read)
46
- return if @body.is_a?(Enumerable)
47
- return if @body.nil?
45
+ def validate_source_type!
46
+ return if @source.is_a?(String)
47
+ return if @source.respond_to?(:read)
48
+ return if @source.is_a?(Enumerable)
49
+ return if @source.nil?
48
50
 
49
- raise RequestError, "body of wrong type: #{@body.class}"
51
+ raise RequestError, "body of wrong type: #{@source.class}"
50
52
  end
51
53
 
52
54
  # This class provides a "writable IO" wrapper around a proc object, with
@@ -50,6 +50,7 @@ module HTTP
50
50
 
51
51
  while (chunk = @stream.readpartial)
52
52
  @contents << chunk.force_encoding(@encoding)
53
+ chunk.clear # deallocate string
53
54
  end
54
55
  rescue
55
56
  @contents = nil
@@ -43,9 +43,17 @@ module HTTP
43
43
  end
44
44
  end
45
45
 
46
- def chunk
47
- chunk = @chunk
48
- @chunk = nil
46
+ def read(size)
47
+ return if @chunk.nil?
48
+
49
+ if @chunk.bytesize <= size
50
+ chunk = @chunk
51
+ @chunk = nil
52
+ else
53
+ chunk = @chunk.byteslice(0, size)
54
+ @chunk[0, size] = ""
55
+ end
56
+
49
57
  chunk
50
58
  end
51
59
 
@@ -15,6 +15,7 @@ module HTTP
15
15
  def_delegators :@uri, :normalized_port, :port=
16
16
  def_delegators :@uri, :path, :normalized_path, :path=
17
17
  def_delegators :@uri, :query, :normalized_query, :query=
18
+ def_delegators :@uri, :query_values, :query_values=
18
19
  def_delegators :@uri, :request_uri, :request_uri=
19
20
  def_delegators :@uri, :fragment, :normalized_fragment, :fragment=
20
21
  def_delegators :@uri, :omit, :join, :normalize
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTP
4
- VERSION = "3.0.0"
4
+ VERSION = "3.1.0"
5
5
  end
@@ -156,6 +156,14 @@ RSpec.describe HTTP::Client do
156
156
 
157
157
  client.get("http://example.com/", :params => {:t => "1970-01-01T00:00:00Z"})
158
158
  end
159
+
160
+ it 'does not convert newlines into \r\n before encoding string values' do
161
+ expect(HTTP::Request).to receive(:new) do |opts|
162
+ expect(opts[:uri].query).to eq "foo=bar%0Abaz"
163
+ end
164
+
165
+ client.get("http://example.com/", :params => {:foo => "bar\nbaz"})
166
+ end
159
167
  end
160
168
 
161
169
  describe "passing multipart form data" do
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe HTTP::Connection do
4
+ let(:req) do
5
+ HTTP::Request.new(
6
+ :verb => :get,
7
+ :uri => "http://example.com/",
8
+ :headers => {}
9
+ )
10
+ end
11
+ let(:socket) { double(:connect => nil) }
12
+ let(:timeout_class) { double(:new => socket) }
13
+ let(:opts) { HTTP::Options.new(:timeout_class => timeout_class) }
14
+ let(:connection) { HTTP::Connection.new(req, opts) }
15
+
16
+ describe "#read_headers!" do
17
+ before do
18
+ connection.instance_variable_set(:@pending_response, true)
19
+ expect(socket).to receive(:readpartial) do
20
+ <<-RESPONSE.gsub(/^\s*\| */, "").gsub(/\n/, "\r\n")
21
+ | HTTP/1.1 200 OK
22
+ | Content-Type: text
23
+ |
24
+ RESPONSE
25
+ end
26
+ end
27
+
28
+ it "reads data in parts" do
29
+ connection.read_headers!
30
+ expect(connection.headers).to eq("Content-Type" => "text")
31
+ end
32
+ end
33
+
34
+ describe "#readpartial" do
35
+ before do
36
+ connection.instance_variable_set(:@pending_response, true)
37
+ expect(socket).to receive(:readpartial) do
38
+ <<-RESPONSE.gsub(/^\s*\| */, "").gsub(/\n/, "\r\n")
39
+ | HTTP/1.1 200 OK
40
+ | Content-Type: text
41
+ |
42
+ RESPONSE
43
+ end
44
+ expect(socket).to receive(:readpartial) { "1" }
45
+ expect(socket).to receive(:readpartial) { "23" }
46
+ expect(socket).to receive(:readpartial) { "456" }
47
+ expect(socket).to receive(:readpartial) { "78" }
48
+ expect(socket).to receive(:readpartial) { "9" }
49
+ expect(socket).to receive(:readpartial) { "0" }
50
+ expect(socket).to receive(:readpartial) { :eof }
51
+ expect(socket).to receive(:closed?) { true }
52
+ end
53
+
54
+ it "reads data in parts" do
55
+ connection.read_headers!
56
+ buffer = String.new
57
+ while (s = connection.readpartial(3))
58
+ buffer << s
59
+ end
60
+ expect(buffer).to eq "1234567890"
61
+ end
62
+ end
63
+ end
@@ -46,6 +46,12 @@ RSpec.describe HTTP::Request::Body do
46
46
  end
47
47
  end
48
48
 
49
+ describe "#source" do
50
+ it "returns the original object" do
51
+ expect(subject.source).to eq ""
52
+ end
53
+ end
54
+
49
55
  describe "#size" do
50
56
  context "when body is nil" do
51
57
  let(:body) { nil }
@@ -41,6 +41,12 @@ RSpec.describe HTTP do
41
41
  end
42
42
  end
43
43
 
44
+ context "with two leading slashes in path" do
45
+ it "is allowed" do
46
+ expect { HTTP.get "#{dummy.endpoint}//" }.not_to raise_error
47
+ end
48
+ end
49
+
44
50
  context "with headers" do
45
51
  it "is easy" do
46
52
  response = HTTP.accept("application/json").get dummy.endpoint
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: 3.0.0
4
+ version: 3.1.0
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: 2017-10-01 00:00:00.000000000 Z
14
+ date: 2018-04-22 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: http_parser.rb
@@ -31,22 +31,16 @@ dependencies:
31
31
  name: http-form_data
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  requirements:
34
- - - ">="
35
- - !ruby/object:Gem::Version
36
- version: 2.0.0.pre.pre2
37
- - - "<"
34
+ - - "~>"
38
35
  - !ruby/object:Gem::Version
39
- version: '3'
36
+ version: '2.0'
40
37
  type: :runtime
41
38
  prerelease: false
42
39
  version_requirements: !ruby/object:Gem::Requirement
43
40
  requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: 2.0.0.pre.pre2
47
- - - "<"
41
+ - - "~>"
48
42
  - !ruby/object:Gem::Version
49
- version: '3'
43
+ version: '2.0'
50
44
  - !ruby/object:Gem::Dependency
51
45
  name: http-cookie
52
46
  requirement: !ruby/object:Gem::Requirement
@@ -145,6 +139,7 @@ files:
145
139
  - lib/http/version.rb
146
140
  - logo.png
147
141
  - spec/lib/http/client_spec.rb
142
+ - spec/lib/http/connection_spec.rb
148
143
  - spec/lib/http/content_type_spec.rb
149
144
  - spec/lib/http/features/auto_deflate_spec.rb
150
145
  - spec/lib/http/features/auto_inflate_spec.rb
@@ -192,7 +187,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
192
187
  requirements:
193
188
  - - ">="
194
189
  - !ruby/object:Gem::Version
195
- version: '2.0'
190
+ version: '2.2'
196
191
  required_rubygems_version: !ruby/object:Gem::Requirement
197
192
  requirements:
198
193
  - - ">="
@@ -200,12 +195,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
200
195
  version: '0'
201
196
  requirements: []
202
197
  rubyforge_project:
203
- rubygems_version: 2.6.12
198
+ rubygems_version: 2.5.2.3
204
199
  signing_key:
205
200
  specification_version: 4
206
201
  summary: HTTP should be easy
207
202
  test_files:
208
203
  - spec/lib/http/client_spec.rb
204
+ - spec/lib/http/connection_spec.rb
209
205
  - spec/lib/http/content_type_spec.rb
210
206
  - spec/lib/http/features/auto_deflate_spec.rb
211
207
  - spec/lib/http/features/auto_inflate_spec.rb