http 5.0.0 → 5.0.4
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 +4 -4
- data/.github/workflows/ci.yml +2 -2
- data/CHANGES.md +81 -0
- data/Gemfile +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +44 -80
- data/http.gemspec +2 -2
- data/lib/http/client.rb +8 -4
- data/lib/http/features/auto_inflate.rb +0 -2
- data/lib/http/request/writer.rb +6 -2
- data/lib/http/request.rb +15 -1
- data/lib/http/response/body.rb +6 -4
- data/lib/http/response/parser.rb +1 -1
- data/lib/http/response.rb +24 -6
- data/lib/http/timeout/global.rb +2 -4
- data/lib/http/version.rb +1 -1
- data/spec/lib/http/client_spec.rb +34 -0
- data/spec/lib/http/features/auto_inflate_spec.rb +0 -1
- data/spec/lib/http/features/instrumentation_spec.rb +0 -1
- data/spec/lib/http/features/logging_spec.rb +0 -1
- data/spec/lib/http/redirector_spec.rb +44 -0
- data/spec/lib/http/request/body_spec.rb +3 -3
- data/spec/lib/http/request/writer_spec.rb +12 -1
- data/spec/lib/http/response/body_spec.rb +5 -5
- data/spec/lib/http/response/parser_spec.rb +3 -3
- data/spec/lib/http/response_spec.rb +62 -10
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92001458b2f36332dfd042ab8750595408d2be8e941ed52be9237e11c071fef7
|
4
|
+
data.tar.gz: de00f9d0a8f59f3af1605f479e503269741c751b397d62d618c8866e58929140
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63249cd08800a312ffba0ed4b9a508e3890b3088058956e5433c71a60957bfe9985070225855b926d78f5a64c3b587a122c52137788b54e10da65076b39f541f
|
7
|
+
data.tar.gz: 89605690a8dd8ac3bf23447de8a75f90bc37a77d8ff0e85d84b0748daf2cf06b69011d8e19cc4b22067e479decdfc9d8ca46f7fc0495ec90c25a433e10c1eac8
|
data/.github/workflows/ci.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,3 +1,65 @@
|
|
1
|
+
## 5.0.4 (2021-10-07)
|
2
|
+
|
3
|
+
* [#698](https://github.com/httprb/http/pull/698)
|
4
|
+
Fix `HTTP::Timeout::Global#connect_ssl`.
|
5
|
+
([@tarcieri])
|
6
|
+
|
7
|
+
## 5.0.3 (2021-10-06)
|
8
|
+
|
9
|
+
* [#695](https://github.com/httprb/http/pull/695)
|
10
|
+
Revert DNS resolving feature.
|
11
|
+
([@PhilCoggins])
|
12
|
+
|
13
|
+
* [#694](https://github.com/httprb/http/pull/694)
|
14
|
+
Fix cookies extraction.
|
15
|
+
([@flosacca])
|
16
|
+
|
17
|
+
## 5.0.2 (2021-09-10)
|
18
|
+
|
19
|
+
* [#686](https://github.com/httprb/http/pull/686)
|
20
|
+
Correctly reset the parser.
|
21
|
+
([@bryanp])
|
22
|
+
|
23
|
+
* [#684](https://github.com/httprb/http/pull/684)
|
24
|
+
Don't set Content-Length for GET, HEAD, DELETE, or CONNECT requests without a BODY.
|
25
|
+
([@jyn514])
|
26
|
+
|
27
|
+
* [#679](https://github.com/httprb/http/pull/679)
|
28
|
+
Use features on redirected requests.
|
29
|
+
([@nomis])
|
30
|
+
|
31
|
+
* [#678](https://github.com/schwern)
|
32
|
+
Restore `HTTP::Response` `:uri` option for backwards compatibility.
|
33
|
+
([@schwern])
|
34
|
+
|
35
|
+
* [#676](https://github.com/httprb/http/pull/676)
|
36
|
+
Update addressable because of CVE-2021-32740.
|
37
|
+
([@matheussilvasantos])
|
38
|
+
|
39
|
+
* [#653](https://github.com/httprb/http/pull/653)
|
40
|
+
Avoid force encodings on frozen strings.
|
41
|
+
([@bvicenzo])
|
42
|
+
|
43
|
+
* [#638](https://github.com/httprb/http/pull/638)
|
44
|
+
DNS failover handling.
|
45
|
+
([@midnight-wonderer])
|
46
|
+
|
47
|
+
|
48
|
+
## 5.0.1 (2021-06-26)
|
49
|
+
|
50
|
+
* [#670](https://github.com/httprb/http/pull/670)
|
51
|
+
Revert `Response#parse` behavior introduced in [#540].
|
52
|
+
([@DannyBen])
|
53
|
+
|
54
|
+
* [#669](https://github.com/httprb/http/pull/669)
|
55
|
+
Prevent bodies from being resubmitted when following unsafe redirects.
|
56
|
+
([@odinhb])
|
57
|
+
|
58
|
+
* [#664](https://github.com/httprb/http/pull/664)
|
59
|
+
Bump llhttp-ffi to 0.3.0.
|
60
|
+
([@bryanp])
|
61
|
+
|
62
|
+
|
1
63
|
## 5.0.0 (2021-05-12)
|
2
64
|
|
3
65
|
* [#656](https://github.com/httprb/http/pull/656)
|
@@ -53,6 +115,12 @@
|
|
53
115
|
Preserve header names casing.
|
54
116
|
([@joshuaflanagan])
|
55
117
|
|
118
|
+
* [#540](https://github.com/httprb/http/pull/540)
|
119
|
+
[#538](https://github.com/httprb/http/issues/538)
|
120
|
+
**BREAKING CHANGE**
|
121
|
+
Require explicit MIME type for Response#parse
|
122
|
+
([@ixti])
|
123
|
+
|
56
124
|
* [#532](https://github.com/httprb/http/pull/532)
|
57
125
|
Fix pipes support in request bodies.
|
58
126
|
([@ixti])
|
@@ -79,6 +147,9 @@
|
|
79
147
|
Drop Ruby 2.3.x support.
|
80
148
|
([@ixti])
|
81
149
|
|
150
|
+
* [3ed0c31](https://github.com/httprb/http/commit/3ed0c318eab6a8c390654cda17bf6df9e963c7d6)
|
151
|
+
Drop Ruby 2.4.x support.
|
152
|
+
|
82
153
|
|
83
154
|
## 4.4.0 (2020-03-25)
|
84
155
|
|
@@ -887,3 +958,13 @@ end
|
|
887
958
|
[@semenyukdmitry]: https://github.com/semenyukdmitry
|
888
959
|
[@bryanp]: https://github.com/bryanp
|
889
960
|
[@meanphil]: https://github.com/meanphil
|
961
|
+
[@odinhb]: https://github.com/odinhb
|
962
|
+
[@DannyBen]: https://github.com/DannyBen
|
963
|
+
[@jyn514]: https://github.com/jyn514
|
964
|
+
[@bvicenzo]: https://github.com/bvicenzo
|
965
|
+
[@nomis]: https://github.com/nomis
|
966
|
+
[@midnight-wonderer]: https://github.com/midnight-wonderer
|
967
|
+
[@schwern]: https://github.com/schwern
|
968
|
+
[@matheussilvasantos]: https://github.com/matheussilvasantos
|
969
|
+
[@PhilCoggins]: https://github.com/PhilCoggins
|
970
|
+
[@flosacca]: https://github.com/flosacca
|
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2011-
|
1
|
+
Copyright (c) 2011-2021 Tony Arcieri, Erik Michaels-Ober, Alexey V. Zapparov, Zachary Anker
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
a copy of this software and associated documentation files (the
|
data/README.md
CHANGED
@@ -1,16 +1,12 @@
|
|
1
|
-
# 
|
2
2
|
|
3
|
-
[![Gem Version]
|
4
|
-
[![
|
5
|
-
[![
|
6
|
-
[![
|
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]
|
7
7
|
|
8
8
|
[Documentation]
|
9
9
|
|
10
|
-
_NOTE: This is the 5.x **development** branch. For the 4.x **stable** branch, please see:_
|
11
|
-
|
12
|
-
https://github.com/httprb/http/tree/4-x-stable
|
13
|
-
|
14
10
|
## About
|
15
11
|
|
16
12
|
HTTP (The Gem! a.k.a. http.rb) is an easy-to-use client library for making requests
|
@@ -21,63 +17,16 @@ Under the hood, http.rb uses the [llhttp] parser, a fast HTTP parsing native ext
|
|
21
17
|
This library isn't just yet another wrapper around `Net::HTTP`. It implements the HTTP
|
22
18
|
protocol natively and outsources the parsing to native extensions.
|
23
19
|
|
24
|
-
|
25
|
-
[llhttp]: https://llhttp.org/
|
26
|
-
|
27
|
-
|
28
|
-
## Another Ruby HTTP library? Why should I care?
|
29
|
-
|
30
|
-
There are a lot of HTTP libraries to choose from in the Ruby ecosystem.
|
31
|
-
So why would you choose this one?
|
20
|
+
### Why http.rb?
|
32
21
|
|
33
|
-
|
34
|
-
|
35
|
-
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
|
36
23
|
breath of fresh air after using something like Net::HTTP.
|
37
24
|
|
38
|
-
|
25
|
+
- **Maturity**: http.rb is one of the most mature Ruby HTTP clients, supporting
|
39
26
|
features like persistent connections and fine-grained timeouts.
|
40
27
|
|
41
|
-
|
42
|
-
http.rb achieves
|
43
|
-
implements the HTTP protocol in Ruby instead of C:
|
44
|
-
|
45
|
-
| HTTP client | Time | Implementation |
|
46
|
-
|--------------------------|--------|-----------------------|
|
47
|
-
| curb (persistent) | 2.519 | libcurl wrapper |
|
48
|
-
| em-http-request | 2.731 | EM + http_parser.rb |
|
49
|
-
| Typhoeus | 2.851 | libcurl wrapper |
|
50
|
-
| StreamlyFFI (persistent) | 2.853 | libcurl wrapper |
|
51
|
-
| http.rb (persistent) | 2.970 | Ruby + http_parser.rb |
|
52
|
-
| http.rb | 3.588 | Ruby + http_parser.rb |
|
53
|
-
| HTTParty | 3.931 | Net::HTTP wrapper |
|
54
|
-
| Net::HTTP | 3.959 | Pure Ruby |
|
55
|
-
| Net::HTTP (persistent) | 4.043 | Pure Ruby |
|
56
|
-
| open-uri | 4.479 | Net::HTTP wrapper |
|
57
|
-
| Excon (persistent) | 4.618 | Pure Ruby |
|
58
|
-
| Excon | 4.701 | Pure Ruby |
|
59
|
-
| RestClient | 26.838 | Net::HTTP wrapper |
|
60
|
-
|
61
|
-
Benchmarks performed using excon's benchmarking tool
|
62
|
-
|
63
|
-
DISCLAIMER: Most benchmarks you find in READMEs are crap,
|
64
|
-
including this one. These are out-of-date. If you care about
|
65
|
-
performance, benchmark for yourself for your own use cases!
|
66
|
-
|
67
|
-
## Help and Discussion
|
68
|
-
|
69
|
-
If you need help or just want to talk about the http.rb,
|
70
|
-
visit the http.rb Google Group:
|
71
|
-
|
72
|
-
https://groups.google.com/forum/#!forum/httprb
|
73
|
-
|
74
|
-
You can join by email by sending a message to:
|
75
|
-
|
76
|
-
[httprb+subscribe@googlegroups.com](mailto:httprb+subscribe@googlegroups.com)
|
77
|
-
|
78
|
-
If you believe you've found a bug, please report it at:
|
79
|
-
|
80
|
-
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.
|
81
30
|
|
82
31
|
|
83
32
|
## Installation
|
@@ -112,10 +61,9 @@ for more detailed documentation and usage notes.
|
|
112
61
|
|
113
62
|
The following API documentation is also available:
|
114
63
|
|
115
|
-
|
116
|
-
|
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)
|
117
66
|
|
118
|
-
[documentation]: https://github.com/httprb/http/wiki
|
119
67
|
|
120
68
|
### Basic Usage
|
121
69
|
|
@@ -142,7 +90,7 @@ We can also obtain an `HTTP::Response::Body` object for this response:
|
|
142
90
|
```
|
143
91
|
|
144
92
|
The response body can be streamed with `HTTP::Response::Body#readpartial`.
|
145
|
-
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
|
146
94
|
and call `#readpartial` on it repeatedly until it returns `nil`:
|
147
95
|
|
148
96
|
```ruby
|
@@ -159,13 +107,13 @@ and call `#readpartial` on it repeatedly until it returns `nil`:
|
|
159
107
|
|
160
108
|
## Supported Ruby Versions
|
161
109
|
|
162
|
-
This library aims to support and is [tested against][
|
163
|
-
versions:
|
110
|
+
This library aims to support and is [tested against][build-link]
|
111
|
+
the following Ruby versions:
|
164
112
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
113
|
+
- Ruby 2.6
|
114
|
+
- Ruby 2.7
|
115
|
+
- Ruby 3.0
|
116
|
+
- JRuby 9.2
|
169
117
|
|
170
118
|
If something doesn't work on one of these versions, it's a bug.
|
171
119
|
|
@@ -180,20 +128,36 @@ patches in a timely fashion. If critical issues for a particular implementation
|
|
180
128
|
exist at the time of a major release, support for that Ruby version may be
|
181
129
|
dropped.
|
182
130
|
|
183
|
-
[travis]: http://travis-ci.org/httprb/http
|
184
|
-
|
185
131
|
|
186
132
|
## Contributing to http.rb
|
187
133
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
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!
|
194
140
|
|
195
141
|
|
196
142
|
## Copyright
|
197
143
|
|
198
|
-
Copyright
|
144
|
+
Copyright © 2011-2021 Tony Arcieri, Alexey V. Zapparov, Erik Michaels-Ober, Zachary Anker.
|
199
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/http.gemspec
CHANGED
@@ -27,10 +27,10 @@ Gem::Specification.new do |gem|
|
|
27
27
|
|
28
28
|
gem.required_ruby_version = ">= 2.5"
|
29
29
|
|
30
|
-
gem.add_runtime_dependency "addressable", "~> 2.
|
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 "llhttp-ffi", "~> 0.0
|
33
|
+
gem.add_runtime_dependency "llhttp-ffi", "~> 0.4.0"
|
34
34
|
|
35
35
|
gem.add_development_dependency "bundler", "~> 2.0"
|
36
36
|
|
data/lib/http/client.rb
CHANGED
@@ -32,7 +32,7 @@ module HTTP
|
|
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
|
|
@@ -52,9 +52,7 @@ module HTTP
|
|
52
52
|
:body => body
|
53
53
|
)
|
54
54
|
|
55
|
-
|
56
|
-
feature.wrap_request(request)
|
57
|
-
end
|
55
|
+
wrap_request(req, opts)
|
58
56
|
end
|
59
57
|
|
60
58
|
# @!method persistent?
|
@@ -104,6 +102,12 @@ module HTTP
|
|
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,
|
data/lib/http/request/writer.rb
CHANGED
@@ -47,7 +47,11 @@ module HTTP
|
|
47
47
|
# Adds the headers to the header array for the given request body we are working
|
48
48
|
# with
|
49
49
|
def add_body_type_headers
|
50
|
-
return if @headers[Headers::CONTENT_LENGTH] || chunked?
|
50
|
+
return if @headers[Headers::CONTENT_LENGTH] || chunked? || (
|
51
|
+
@body.source.nil? && %w[GET HEAD DELETE CONNECT].any? do |method|
|
52
|
+
@request_header[0].start_with?("#{method} ")
|
53
|
+
end
|
54
|
+
)
|
51
55
|
|
52
56
|
@request_header << "#{Headers::CONTENT_LENGTH}: #{@body.size}"
|
53
57
|
end
|
@@ -57,7 +61,7 @@ module HTTP
|
|
57
61
|
def join_headers
|
58
62
|
# join the headers array with crlfs, stick two on the end because
|
59
63
|
# that ends the request header
|
60
|
-
@request_header.join(CRLF) + CRLF * 2
|
64
|
+
@request_header.join(CRLF) + (CRLF * 2)
|
61
65
|
end
|
62
66
|
|
63
67
|
# Writes HTTP request data into the socket.
|
data/lib/http/request.rb
CHANGED
@@ -104,12 +104,26 @@ module HTTP
|
|
104
104
|
headers = self.headers.dup
|
105
105
|
headers.delete(Headers::HOST)
|
106
106
|
|
107
|
+
new_body = body.source
|
108
|
+
if verb == :get
|
109
|
+
# request bodies should not always be resubmitted when following a redirect
|
110
|
+
# some servers will close the connection after receiving the request headers
|
111
|
+
# which may cause Errno::ECONNRESET: Connection reset by peer
|
112
|
+
# see https://github.com/httprb/http/issues/649
|
113
|
+
# new_body = Request::Body.new(nil)
|
114
|
+
new_body = nil
|
115
|
+
# the CONTENT_TYPE header causes problems if set on a get request w/ an empty body
|
116
|
+
# the server might assume that there should be content if it is set to multipart
|
117
|
+
# rack raises EmptyContentError if this happens
|
118
|
+
headers.delete(Headers::CONTENT_TYPE)
|
119
|
+
end
|
120
|
+
|
107
121
|
self.class.new(
|
108
122
|
:verb => verb,
|
109
123
|
:uri => @uri.join(uri),
|
110
124
|
:headers => headers,
|
111
125
|
:proxy => proxy,
|
112
|
-
:body =>
|
126
|
+
:body => new_body,
|
113
127
|
:version => version,
|
114
128
|
:uri_normalizer => uri_normalizer
|
115
129
|
)
|
data/lib/http/response/body.rb
CHANGED
@@ -27,7 +27,9 @@ module HTTP
|
|
27
27
|
# (see HTTP::Client#readpartial)
|
28
28
|
def readpartial(*args)
|
29
29
|
stream!
|
30
|
-
@stream.readpartial(*args)
|
30
|
+
chunk = @stream.readpartial(*args)
|
31
|
+
|
32
|
+
String.new(chunk, :encoding => @encoding) if chunk
|
31
33
|
end
|
32
34
|
|
33
35
|
# Iterate over the body, allowing it to be enumerable
|
@@ -45,11 +47,11 @@ module HTTP
|
|
45
47
|
|
46
48
|
begin
|
47
49
|
@streaming = false
|
48
|
-
@contents = String.new(""
|
50
|
+
@contents = String.new("", :encoding => @encoding)
|
49
51
|
|
50
52
|
while (chunk = @stream.readpartial)
|
51
|
-
@contents <<
|
52
|
-
chunk
|
53
|
+
@contents << String.new(chunk, :encoding => @encoding)
|
54
|
+
chunk = nil # deallocate string
|
53
55
|
end
|
54
56
|
rescue
|
55
57
|
@contents = nil
|
data/lib/http/response/parser.rb
CHANGED
data/lib/http/response.rb
CHANGED
@@ -40,10 +40,11 @@ module HTTP
|
|
40
40
|
# @option opts [HTTP::Connection] :connection
|
41
41
|
# @option opts [String] :encoding Encoding to use when reading body
|
42
42
|
# @option opts [String] :body
|
43
|
-
# @option opts [HTTP::Request] request
|
43
|
+
# @option opts [HTTP::Request] request The request this is in response to.
|
44
|
+
# @option opts [String] :uri (DEPRECATED) used to populate a missing request
|
44
45
|
def initialize(opts)
|
45
46
|
@version = opts.fetch(:version)
|
46
|
-
@request = opts
|
47
|
+
@request = init_request(opts)
|
47
48
|
@status = HTTP::Response::Status.new(opts.fetch(:status))
|
48
49
|
@headers = HTTP::Headers.coerce(opts[:headers] || {})
|
49
50
|
@proxy_headers = HTTP::Headers.coerce(opts[:proxy_headers] || {})
|
@@ -137,8 +138,8 @@ module HTTP
|
|
137
138
|
def_delegator :content_type, :charset
|
138
139
|
|
139
140
|
def cookies
|
140
|
-
@cookies ||= headers.each_with_object CookieJar.new do |
|
141
|
-
jar.parse(v, uri)
|
141
|
+
@cookies ||= headers.get(Headers::SET_COOKIE).each_with_object CookieJar.new do |v, jar|
|
142
|
+
jar.parse(v, uri)
|
142
143
|
end
|
143
144
|
end
|
144
145
|
|
@@ -156,13 +157,30 @@ module HTTP
|
|
156
157
|
# @param type [#to_s] Parse as given MIME type.
|
157
158
|
# @raise (see MimeType.[])
|
158
159
|
# @return [Object]
|
159
|
-
def parse(type)
|
160
|
-
MimeType[type].decode to_s
|
160
|
+
def parse(type = nil)
|
161
|
+
MimeType[type || mime_type].decode to_s
|
161
162
|
end
|
162
163
|
|
163
164
|
# Inspect a response
|
164
165
|
def inspect
|
165
166
|
"#<#{self.class}/#{@version} #{code} #{reason} #{headers.to_h.inspect}>"
|
166
167
|
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
# Initialize an HTTP::Request from options.
|
172
|
+
#
|
173
|
+
# @return [HTTP::Request]
|
174
|
+
def init_request(opts)
|
175
|
+
raise ArgumentError, ":uri is for backwards compatibilty and conflicts with :request" \
|
176
|
+
if opts[:request] && opts[:uri]
|
177
|
+
|
178
|
+
# For backwards compatibilty
|
179
|
+
if opts[:uri]
|
180
|
+
HTTP::Request.new(:uri => opts[:uri], :verb => :get)
|
181
|
+
else
|
182
|
+
opts.fetch(:request)
|
183
|
+
end
|
184
|
+
end
|
167
185
|
end
|
168
186
|
end
|
data/lib/http/timeout/global.rb
CHANGED
@@ -35,12 +35,10 @@ module HTTP
|
|
35
35
|
begin
|
36
36
|
@socket.connect_nonblock
|
37
37
|
rescue IO::WaitReadable
|
38
|
-
|
39
|
-
log_time
|
38
|
+
wait_readable_or_timeout
|
40
39
|
retry
|
41
40
|
rescue IO::WaitWritable
|
42
|
-
|
43
|
-
log_time
|
41
|
+
wait_writable_or_timeout
|
44
42
|
retry
|
45
43
|
end
|
46
44
|
end
|
data/lib/http/version.rb
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
require "support/http_handling_shared"
|
5
5
|
require "support/dummy_server"
|
6
6
|
require "support/ssl_helper"
|
7
|
+
require "logger"
|
7
8
|
|
8
9
|
RSpec.describe HTTP::Client do
|
9
10
|
run_server(:dummy) { DummyServer.new }
|
@@ -115,6 +116,39 @@ RSpec.describe HTTP::Client do
|
|
115
116
|
end
|
116
117
|
end
|
117
118
|
|
119
|
+
describe "following redirects with logging" do
|
120
|
+
let(:logger) do
|
121
|
+
logger = Logger.new(logdev)
|
122
|
+
logger.formatter = ->(severity, _, _, message) { format("** %s **\n%s\n", severity, message) }
|
123
|
+
logger.level = Logger::INFO
|
124
|
+
logger
|
125
|
+
end
|
126
|
+
|
127
|
+
let(:logdev) { StringIO.new }
|
128
|
+
|
129
|
+
it "logs all requests" do
|
130
|
+
client = StubbedClient.new(:follow => true, :features => { :logging => { :logger => logger } }).stub(
|
131
|
+
"http://example.com/" => redirect_response("/1"),
|
132
|
+
"http://example.com/1" => redirect_response("/2"),
|
133
|
+
"http://example.com/2" => redirect_response("/3"),
|
134
|
+
"http://example.com/3" => simple_response("OK")
|
135
|
+
)
|
136
|
+
|
137
|
+
expect { client.get("http://example.com/") }.not_to raise_error
|
138
|
+
|
139
|
+
expect(logdev.string).to eq <<~OUTPUT
|
140
|
+
** INFO **
|
141
|
+
> GET http://example.com/
|
142
|
+
** INFO **
|
143
|
+
> GET http://example.com/1
|
144
|
+
** INFO **
|
145
|
+
> GET http://example.com/2
|
146
|
+
** INFO **
|
147
|
+
> GET http://example.com/3
|
148
|
+
OUTPUT
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
118
152
|
describe "parsing params" do
|
119
153
|
let(:client) { HTTP::Client.new }
|
120
154
|
before { allow(client).to receive :perform }
|
@@ -74,7 +74,6 @@ RSpec.describe HTTP::Features::AutoInflate do
|
|
74
74
|
:status => 200,
|
75
75
|
:headers => {:content_encoding => "gzip"},
|
76
76
|
:connection => connection,
|
77
|
-
:uri => "https://example.com",
|
78
77
|
:request => HTTP::Request.new(:verb => :get, :uri => "https://example.com")
|
79
78
|
)
|
80
79
|
end
|
@@ -396,5 +396,49 @@ RSpec.describe HTTP::Redirector do
|
|
396
396
|
end
|
397
397
|
end
|
398
398
|
end
|
399
|
+
|
400
|
+
describe "changing verbs during redirects" do
|
401
|
+
let(:options) { {:strict => false} }
|
402
|
+
let(:post_body) { HTTP::Request::Body.new("i might be way longer in real life") }
|
403
|
+
let(:cookie) { "dont eat my cookies" }
|
404
|
+
|
405
|
+
def a_dangerous_request(verb)
|
406
|
+
HTTP::Request.new(
|
407
|
+
:verb => verb, :uri => "http://example.com",
|
408
|
+
:body => post_body, :headers => {
|
409
|
+
"Content-Type" => "meme",
|
410
|
+
"Cookie" => cookie
|
411
|
+
}
|
412
|
+
)
|
413
|
+
end
|
414
|
+
|
415
|
+
def empty_body
|
416
|
+
HTTP::Request::Body.new(nil)
|
417
|
+
end
|
418
|
+
|
419
|
+
it "follows without body/content type if it has to change verb" do
|
420
|
+
req = a_dangerous_request(:post)
|
421
|
+
res = redirect_response 302, "http://example.com/1"
|
422
|
+
|
423
|
+
redirector.perform(req, res) do |prev_req, _|
|
424
|
+
expect(prev_req.body).to eq(empty_body)
|
425
|
+
expect(prev_req.headers["Cookie"]).to eq(cookie)
|
426
|
+
expect(prev_req.headers["Content-Type"]).to eq(nil)
|
427
|
+
simple_response 200
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
it "leaves body/content-type intact if it does not have to change verb" do
|
432
|
+
req = a_dangerous_request(:post)
|
433
|
+
res = redirect_response 307, "http://example.com/1"
|
434
|
+
|
435
|
+
redirector.perform(req, res) do |prev_req, _|
|
436
|
+
expect(prev_req.body).to eq(post_body)
|
437
|
+
expect(prev_req.headers["Cookie"]).to eq(cookie)
|
438
|
+
expect(prev_req.headers["Content-Type"]).to eq("meme")
|
439
|
+
simple_response 200
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|
399
443
|
end
|
400
444
|
end
|
@@ -118,10 +118,10 @@ RSpec.describe HTTP::Request::Body do
|
|
118
118
|
end
|
119
119
|
|
120
120
|
context "when body is a non-Enumerable IO" do
|
121
|
-
let(:body) { FakeIO.new("a" * 16 * 1024 + "b" * 10 * 1024) }
|
121
|
+
let(:body) { FakeIO.new(("a" * 16 * 1024) + ("b" * 10 * 1024)) }
|
122
122
|
|
123
123
|
it "yields chunks of content" do
|
124
|
-
expect(chunks.inject("", :+)).to eq "a" * 16 * 1024 + "b" * 10 * 1024
|
124
|
+
expect(chunks.inject("", :+)).to eq ("a" * 16 * 1024) + ("b" * 10 * 1024)
|
125
125
|
end
|
126
126
|
end
|
127
127
|
|
@@ -148,7 +148,7 @@ RSpec.describe HTTP::Request::Body do
|
|
148
148
|
end
|
149
149
|
|
150
150
|
context "when body is an Enumerable IO" do
|
151
|
-
let(:data) { "a" * 16 * 1024 + "b" * 10 * 1024 }
|
151
|
+
let(:data) { ("a" * 16 * 1024) + ("b" * 10 * 1024) }
|
152
152
|
let(:body) { StringIO.new data }
|
153
153
|
|
154
154
|
it "yields chunks of content" do
|
@@ -47,9 +47,20 @@ RSpec.describe HTTP::Request::Writer do
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
context "when body is
|
50
|
+
context "when body is not set" do
|
51
51
|
let(:body) { HTTP::Request::Body.new(nil) }
|
52
52
|
|
53
|
+
it "doesn't write anything to the socket and doesn't set Content-Length" do
|
54
|
+
writer.stream
|
55
|
+
expect(io.string).to eq [
|
56
|
+
"#{headerstart}\r\n\r\n"
|
57
|
+
].join
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when body is empty" do
|
62
|
+
let(:body) { HTTP::Request::Body.new("") }
|
63
|
+
|
53
64
|
it "doesn't write anything to the socket and sets Content-Length" do
|
54
65
|
writer.stream
|
55
66
|
expect(io.string).to eq [
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
RSpec.describe HTTP::Response::Body do
|
4
4
|
let(:connection) { double(:sequence_id => 0) }
|
5
|
-
let(:chunks) { [
|
5
|
+
let(:chunks) { ["Hello, ", "World!"] }
|
6
6
|
|
7
7
|
before do
|
8
8
|
allow(connection).to receive(:readpartial) { chunks.shift }
|
@@ -16,7 +16,7 @@ RSpec.describe HTTP::Response::Body do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
context "when body empty" do
|
19
|
-
let(:chunks) { [
|
19
|
+
let(:chunks) { [""] }
|
20
20
|
|
21
21
|
it "returns responds to empty? with true" do
|
22
22
|
expect(subject).to be_empty
|
@@ -45,12 +45,12 @@ RSpec.describe HTTP::Response::Body do
|
|
45
45
|
it "returns content in specified encoding" do
|
46
46
|
body = described_class.new(connection)
|
47
47
|
expect(connection).to receive(:readpartial).
|
48
|
-
and_return(String.new("content"
|
48
|
+
and_return(String.new("content", :encoding => Encoding::UTF_8))
|
49
49
|
expect(body.readpartial.encoding).to eq Encoding::BINARY
|
50
50
|
|
51
51
|
body = described_class.new(connection, :encoding => Encoding::UTF_8)
|
52
52
|
expect(connection).to receive(:readpartial).
|
53
|
-
and_return(String.new("content"
|
53
|
+
and_return(String.new("content", :encoding => Encoding::BINARY))
|
54
54
|
expect(body.readpartial.encoding).to eq Encoding::UTF_8
|
55
55
|
end
|
56
56
|
end
|
@@ -59,7 +59,7 @@ RSpec.describe HTTP::Response::Body do
|
|
59
59
|
let(:chunks) do
|
60
60
|
body = Zlib::Deflate.deflate("Hi, HTTP here ☺")
|
61
61
|
len = body.length
|
62
|
-
[
|
62
|
+
[body[0, len / 2], body[(len / 2)..-1]]
|
63
63
|
end
|
64
64
|
subject(:body) do
|
65
65
|
inflater = HTTP::Response::Inflater.new(connection)
|
@@ -46,9 +46,9 @@ RSpec.describe HTTP::Response::Parser do
|
|
46
46
|
context "when got 100 Continue response" do
|
47
47
|
let :raw_response do
|
48
48
|
"HTTP/1.1 100 Continue\r\n\r\n" \
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
"HTTP/1.1 200 OK\r\n" \
|
50
|
+
"Content-Length: 12\r\n\r\n" \
|
51
|
+
"Hello World!"
|
52
52
|
end
|
53
53
|
|
54
54
|
context "when response is feeded in one part" do
|
@@ -4,6 +4,7 @@ RSpec.describe HTTP::Response do
|
|
4
4
|
let(:body) { "Hello world!" }
|
5
5
|
let(:uri) { "http://example.com/" }
|
6
6
|
let(:headers) { {} }
|
7
|
+
let(:request) { HTTP::Request.new(:verb => :get, :uri => uri) }
|
7
8
|
|
8
9
|
subject(:response) do
|
9
10
|
HTTP::Response.new(
|
@@ -11,8 +12,7 @@ RSpec.describe HTTP::Response do
|
|
11
12
|
:version => "1.1",
|
12
13
|
:headers => headers,
|
13
14
|
:body => body,
|
14
|
-
:
|
15
|
-
:request => HTTP::Request.new(:verb => :get, :uri => "http://example.com")
|
15
|
+
:request => request
|
16
16
|
)
|
17
17
|
end
|
18
18
|
|
@@ -87,19 +87,32 @@ RSpec.describe HTTP::Response do
|
|
87
87
|
end
|
88
88
|
|
89
89
|
describe "#parse" do
|
90
|
-
let(:headers) { {"Content-Type" =>
|
90
|
+
let(:headers) { {"Content-Type" => content_type} }
|
91
91
|
let(:body) { '{"foo":"bar"}' }
|
92
92
|
|
93
|
-
|
94
|
-
|
93
|
+
context "with known content type" do
|
94
|
+
let(:content_type) { "application/json" }
|
95
|
+
it "returns parsed body" do
|
96
|
+
expect(response.parse).to eq "foo" => "bar"
|
97
|
+
end
|
95
98
|
end
|
96
99
|
|
97
|
-
|
98
|
-
|
100
|
+
context "with unknown content type" do
|
101
|
+
let(:content_type) { "application/deadbeef" }
|
102
|
+
it "raises HTTP::Error" do
|
103
|
+
expect { response.parse }.to raise_error HTTP::Error
|
104
|
+
end
|
99
105
|
end
|
100
106
|
|
101
|
-
|
102
|
-
|
107
|
+
context "with explicitly given mime type" do
|
108
|
+
let(:content_type) { "application/deadbeef" }
|
109
|
+
it "ignores mime_type of response" do
|
110
|
+
expect(response.parse("application/json")).to eq "foo" => "bar"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "supports mime type aliases" do
|
114
|
+
expect(response.parse(:json)).to eq "foo" => "bar"
|
115
|
+
end
|
103
116
|
end
|
104
117
|
end
|
105
118
|
|
@@ -154,7 +167,7 @@ RSpec.describe HTTP::Response do
|
|
154
167
|
:version => "1.1",
|
155
168
|
:status => 200,
|
156
169
|
:connection => connection,
|
157
|
-
:request =>
|
170
|
+
:request => request
|
158
171
|
)
|
159
172
|
end
|
160
173
|
|
@@ -171,4 +184,43 @@ RSpec.describe HTTP::Response do
|
|
171
184
|
end
|
172
185
|
it { is_expected.not_to be_chunked }
|
173
186
|
end
|
187
|
+
|
188
|
+
describe "backwards compatibilty with :uri" do
|
189
|
+
context "with no :verb" do
|
190
|
+
subject(:response) do
|
191
|
+
HTTP::Response.new(
|
192
|
+
:status => 200,
|
193
|
+
:version => "1.1",
|
194
|
+
:headers => headers,
|
195
|
+
:body => body,
|
196
|
+
:uri => uri
|
197
|
+
)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "defaults the uri to :uri" do
|
201
|
+
expect(response.request.uri.to_s).to eq uri
|
202
|
+
end
|
203
|
+
|
204
|
+
it "defaults to the verb to :get" do
|
205
|
+
expect(response.request.verb).to eq :get
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "with both a :request and :uri" do
|
210
|
+
subject(:response) do
|
211
|
+
HTTP::Response.new(
|
212
|
+
:status => 200,
|
213
|
+
:version => "1.1",
|
214
|
+
:headers => headers,
|
215
|
+
:body => body,
|
216
|
+
:uri => uri,
|
217
|
+
:request => request
|
218
|
+
)
|
219
|
+
end
|
220
|
+
|
221
|
+
it "raises ArgumentError" do
|
222
|
+
expect { response }.to raise_error(ArgumentError)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
174
226
|
end
|
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: 5.0.
|
4
|
+
version: 5.0.4
|
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: 2021-
|
14
|
+
date: 2021-10-07 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: addressable
|
@@ -19,14 +19,14 @@ dependencies:
|
|
19
19
|
requirements:
|
20
20
|
- - "~>"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '2.
|
22
|
+
version: '2.8'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '2.
|
29
|
+
version: '2.8'
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: http-cookie
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -61,14 +61,14 @@ dependencies:
|
|
61
61
|
requirements:
|
62
62
|
- - "~>"
|
63
63
|
- !ruby/object:Gem::Version
|
64
|
-
version: 0.0
|
64
|
+
version: 0.4.0
|
65
65
|
type: :runtime
|
66
66
|
prerelease: false
|
67
67
|
version_requirements: !ruby/object:Gem::Requirement
|
68
68
|
requirements:
|
69
69
|
- - "~>"
|
70
70
|
- !ruby/object:Gem::Version
|
71
|
-
version: 0.0
|
71
|
+
version: 0.4.0
|
72
72
|
- !ruby/object:Gem::Dependency
|
73
73
|
name: bundler
|
74
74
|
requirement: !ruby/object:Gem::Requirement
|
@@ -191,7 +191,7 @@ metadata:
|
|
191
191
|
source_code_uri: https://github.com/httprb/http
|
192
192
|
wiki_uri: https://github.com/httprb/http/wiki
|
193
193
|
bug_tracker_uri: https://github.com/httprb/http/issues
|
194
|
-
changelog_uri: https://github.com/httprb/http/blob/v5.0.
|
194
|
+
changelog_uri: https://github.com/httprb/http/blob/v5.0.4/CHANGES.md
|
195
195
|
post_install_message:
|
196
196
|
rdoc_options: []
|
197
197
|
require_paths:
|