http2 0.0.32 → 0.0.36
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +2 -2
- data/Rakefile +2 -4
- data/lib/http2/base_request.rb +2 -2
- data/lib/http2/connection.rb +12 -2
- data/lib/http2/errors.rb +12 -5
- data/lib/http2/post_data_generator.rb +2 -2
- data/lib/http2/post_multipart_request.rb +4 -8
- data/lib/http2/post_request.rb +1 -5
- data/lib/http2/response.rb +18 -30
- data/lib/http2/response_reader.rb +50 -36
- data/lib/http2/url_builder.rb +1 -1
- data/lib/http2/utils.rb +5 -3
- data/lib/http2.rb +22 -19
- data/spec/helpers.rb +4 -6
- data/spec/http2/cookies_spec.rb +2 -2
- data/spec/http2/post_data_generator_spec.rb +3 -2
- data/spec/http2/response_spec.rb +24 -4
- data/spec/http2/url_builder_spec.rb +1 -1
- data/spec/http2_spec.rb +33 -20
- data/spec/spec_helper.rb +2 -4
- data/spec/spec_root/unauthorized.rhtml +3 -0
- data/spec/spec_root/unsupported_media_type.rhtml +3 -0
- metadata +73 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: aaaa3b5476a0d9233f4ef5977f64d0368f009021f92bd821280058f518463bca
|
4
|
+
data.tar.gz: 24942b90af076019fc755a6abdc61ae049ec2a7178dcad6f6df5c0aba9c5a945
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb01eb1aab89764bcaa356f5c89341e926ef0ebe5818aa5b22be70e64dd0e926f6ab9807723ae15b0f33d125397be759c89d5e896ab3b25d486b312bd44e252c
|
7
|
+
data.tar.gz: 7ff240f8d775175b3b5f9fdbfe1f8a4d380c83c26729c3c31730672d5e3dbea1e8d3ccc8899f39c06f27199799243c87b8de9c6483d00ff8335fb1c279a024e5
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
[![Build Status](https://img.shields.io/shippable/540e7b9b3479c5ea8f9ec21d.svg)](https://app.shippable.com/projects/540e7b9b3479c5ea8f9ec21d/builds/latest)
|
2
1
|
[![Code Climate](https://codeclimate.com/github/kaspernj/http2.png)](https://codeclimate.com/github/kaspernj/http2)
|
3
|
-
[![
|
2
|
+
[![Build Status](https://www.peakflow.io/en/projects/http2/branch-statuses/master.svg
|
3
|
+
)](https://www.peakflow.io/en/projects/http2/build-groups)
|
4
4
|
|
5
5
|
# http2
|
6
6
|
|
data/Rakefile
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
require "rubygems"
|
4
2
|
require "bundler"
|
5
3
|
begin
|
6
4
|
Bundler.setup(:default, :development)
|
7
5
|
rescue Bundler::BundlerError => e
|
8
|
-
|
9
|
-
|
6
|
+
warn e.message
|
7
|
+
warn "Run `bundle install` to install missing gems"
|
10
8
|
exit e.status_code
|
11
9
|
end
|
12
10
|
require "rake"
|
data/lib/http2/base_request.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Http2::BaseRequest
|
2
2
|
attr_reader :http2, :args, :debug
|
3
3
|
|
4
|
-
VALID_ARGUMENTS_POST = [:post, :url, :default_headers, :headers, :json, :method, :cookies, :on_content, :content_type]
|
4
|
+
VALID_ARGUMENTS_POST = [:post, :url, :default_headers, :headers, :json, :method, :cookies, :on_content, :content_type].freeze
|
5
5
|
|
6
6
|
def initialize(http2, args)
|
7
7
|
@http2 = http2
|
@@ -9,7 +9,7 @@ class Http2::BaseRequest
|
|
9
9
|
@debug = http2.debug
|
10
10
|
@nl = http2.nl
|
11
11
|
|
12
|
-
@args.
|
12
|
+
@args.each_key do |key|
|
13
13
|
raise "Invalid key: '#{key}'." unless VALID_ARGUMENTS_POST.include?(key)
|
14
14
|
end
|
15
15
|
|
data/lib/http2/connection.rb
CHANGED
@@ -33,6 +33,7 @@ class Http2::Connection
|
|
33
33
|
def sock_write(str)
|
34
34
|
str = str.to_s
|
35
35
|
return if str.empty?
|
36
|
+
|
36
37
|
count = @sock.write(str)
|
37
38
|
raise "Couldnt write to socket: '#{count}', '#{str}'." if count <= 0
|
38
39
|
end
|
@@ -49,6 +50,7 @@ class Http2::Connection
|
|
49
50
|
|
50
51
|
begin
|
51
52
|
raise Errno::EPIPE, "The socket is closed." if !@sock || @sock.closed?
|
53
|
+
|
52
54
|
sock_write(str)
|
53
55
|
rescue Errno::EPIPE # this can also be thrown by puts.
|
54
56
|
reconnect
|
@@ -130,7 +132,8 @@ class Http2::Connection
|
|
130
132
|
@sock_plain.write(@nl)
|
131
133
|
|
132
134
|
res = @sock_plain.gets.to_s
|
133
|
-
raise "Couldn't connect through proxy: #{res}" unless res.match(/^http\/1\.(0|1)\s+200/i)
|
135
|
+
raise "Couldn't connect through proxy: #{res}" unless res.match?(/^http\/1\.(0|1)\s+200/i)
|
136
|
+
|
134
137
|
@sock_plain.gets
|
135
138
|
|
136
139
|
@proxy_connect = true
|
@@ -150,8 +153,15 @@ class Http2::Connection
|
|
150
153
|
require "openssl" unless ::Kernel.const_defined?(:OpenSSL)
|
151
154
|
|
152
155
|
ssl_context = OpenSSL::SSL::SSLContext.new
|
153
|
-
|
156
|
+
|
157
|
+
if @args[:ssl_skip_verify]
|
158
|
+
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
159
|
+
else
|
160
|
+
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
161
|
+
end
|
162
|
+
|
154
163
|
@sock_ssl = OpenSSL::SSL::SSLSocket.new(@sock_plain, ssl_context)
|
164
|
+
@sock_ssl.hostname = @http2.host
|
155
165
|
@sock_ssl.sync_close = true
|
156
166
|
@sock_ssl.connect
|
157
167
|
|
data/lib/http2/errors.rb
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
# This class holds various classes for error-handeling.
|
2
2
|
class Http2::Errors
|
3
|
-
class
|
3
|
+
class BaseError < RuntimeError
|
4
4
|
attr_accessor :response
|
5
5
|
end
|
6
6
|
|
7
|
-
class Noaccess <
|
8
|
-
|
9
|
-
class
|
10
|
-
|
7
|
+
class Noaccess < BaseError; end
|
8
|
+
|
9
|
+
class Internalserver < BaseError; end
|
10
|
+
|
11
|
+
class Notfound < BaseError; end
|
12
|
+
|
13
|
+
class Badrequest < BaseError; end
|
14
|
+
|
15
|
+
class Unauthorized < BaseError; end
|
16
|
+
|
17
|
+
class UnsupportedMediaType < BaseError; end
|
11
18
|
end
|
@@ -54,10 +54,10 @@ private
|
|
54
54
|
|
55
55
|
def generate_key_value(key, value)
|
56
56
|
if value.is_a?(Hash) || value.is_a?(Array)
|
57
|
-
|
57
|
+
::Http2::PostDataGenerator.new(value, orig_key: key).generate
|
58
58
|
else
|
59
59
|
data = ::Http2::PostDataGenerator.new(value).generate
|
60
|
-
|
60
|
+
"#{Http2::Utils.urlenc(key)}=#{Http2::Utils.urlenc(data)}"
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
@@ -56,15 +56,11 @@ private
|
|
56
56
|
|
57
57
|
def read_file(path, praw)
|
58
58
|
File.open(path, "r") do |fp|
|
59
|
-
|
60
|
-
|
61
|
-
praw << data
|
62
|
-
end
|
63
|
-
# rubocop:disable Lint/HandleExceptions
|
64
|
-
rescue EOFError
|
65
|
-
# rubocop:enable Lint/HandleExceptions
|
66
|
-
# Happens when done.
|
59
|
+
while (data = fp.sysread(4096))
|
60
|
+
praw << data
|
67
61
|
end
|
62
|
+
rescue EOFError
|
63
|
+
# Happens when done.
|
68
64
|
end
|
69
65
|
end
|
70
66
|
|
data/lib/http2/post_request.rb
CHANGED
@@ -60,11 +60,7 @@ private
|
|
60
60
|
"Content-Type" => content_type
|
61
61
|
}
|
62
62
|
headers_hash.merge! @http2.default_headers(@args)
|
63
|
-
|
64
|
-
unless headers_hash["Accept"]
|
65
|
-
headers_hash["Accept"] = "application/json" if @args[:json]
|
66
|
-
end
|
67
|
-
|
63
|
+
headers_hash["Accept"] = "application/json" if @args[:json] && !headers_hash["Accept"]
|
68
64
|
headers_hash
|
69
65
|
end
|
70
66
|
end
|
data/lib/http2/response.rb
CHANGED
@@ -1,38 +1,34 @@
|
|
1
1
|
# This object will be returned as the response for each request.
|
2
2
|
class Http2::Response
|
3
3
|
# All the data the response contains. Headers, body, cookies, requested URL and more.
|
4
|
-
attr_reader :
|
5
|
-
attr_accessor :body, :charset, :code, :
|
4
|
+
attr_reader :headers, :request, :request_args, :requested_url
|
5
|
+
attr_accessor :body, :charset, :code, :http_version
|
6
|
+
attr_writer :content_type
|
6
7
|
|
7
8
|
# This method should not be called manually.
|
8
|
-
def initialize(
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@
|
14
|
-
end
|
15
|
-
|
16
|
-
# Returns headers given from the host for the result.
|
17
|
-
#===Examples
|
18
|
-
# headers_hash = res.headers
|
19
|
-
def headers
|
20
|
-
@args.fetch(:headers)
|
9
|
+
def initialize(body: "", debug: false, headers: {}, request:)
|
10
|
+
@body = body
|
11
|
+
@debug = debug
|
12
|
+
@headers = headers
|
13
|
+
@request = request
|
14
|
+
@requested_url = request.path
|
21
15
|
end
|
22
16
|
|
23
17
|
# Returns a certain header by name or false if not found.
|
24
18
|
#===Examples
|
25
19
|
# val = res.header("content-type")
|
26
20
|
def header(key)
|
27
|
-
return false unless
|
28
|
-
|
21
|
+
return false unless headers.key?(key)
|
22
|
+
|
23
|
+
headers.fetch(key).first.to_s
|
29
24
|
end
|
30
25
|
|
31
26
|
# Returns true if a header of the given string exists.
|
32
27
|
#===Examples
|
33
28
|
# print "No content-type was given." if !http.header?("content-type")
|
34
29
|
def header?(key)
|
35
|
-
return true if
|
30
|
+
return true if headers.key?(key) && !headers[key].first.to_s.empty?
|
31
|
+
|
36
32
|
false
|
37
33
|
end
|
38
34
|
|
@@ -40,7 +36,7 @@ class Http2::Response
|
|
40
36
|
if header?("content-length")
|
41
37
|
header("content-length").to_i
|
42
38
|
elsif @body
|
43
|
-
|
39
|
+
@body.bytesize
|
44
40
|
else
|
45
41
|
raise "Couldn't calculate content-length."
|
46
42
|
end
|
@@ -48,20 +44,12 @@ class Http2::Response
|
|
48
44
|
|
49
45
|
def content_type
|
50
46
|
if header?("content-type")
|
51
|
-
|
47
|
+
header("content-type")
|
52
48
|
else
|
53
49
|
raise "No content-type was given."
|
54
50
|
end
|
55
51
|
end
|
56
52
|
|
57
|
-
# Returns the requested URL as a string.
|
58
|
-
#===Examples
|
59
|
-
# res.requested_url #=> "?show=status&action=getstatus"
|
60
|
-
def requested_url
|
61
|
-
raise "URL could not be detected." unless @args[:request_args][:url]
|
62
|
-
@args[:request_args][:url]
|
63
|
-
end
|
64
|
-
|
65
53
|
# Checks the data that has been sat on the object and raises various exceptions, if it does not validate somehow.
|
66
54
|
def validate!
|
67
55
|
puts "Http2: Validating response length." if @debug
|
@@ -70,7 +58,7 @@ class Http2::Response
|
|
70
58
|
|
71
59
|
# Returns true if the result is JSON.
|
72
60
|
def json?
|
73
|
-
content_type
|
61
|
+
content_type&.start_with?("application/json")
|
74
62
|
end
|
75
63
|
|
76
64
|
def json
|
@@ -105,7 +93,7 @@ private
|
|
105
93
|
|
106
94
|
# Checks that the length of the body is the same as the given content-length if given.
|
107
95
|
def validate_body_versus_content_length!
|
108
|
-
unless
|
96
|
+
unless header?("content-length")
|
109
97
|
puts "Http2: No content length given - skipping length validation." if @debug
|
110
98
|
return nil
|
111
99
|
end
|
@@ -1,20 +1,21 @@
|
|
1
1
|
class Http2::ResponseReader
|
2
2
|
attr_reader :response
|
3
3
|
|
4
|
-
def initialize(args)
|
4
|
+
def initialize(args:, http2:, sock:, request:)
|
5
5
|
@mode = "headers"
|
6
6
|
@transfer_encoding = nil
|
7
|
-
@
|
7
|
+
@request = request
|
8
|
+
@response = Http2::Response.new(debug: http2.debug, request: request)
|
8
9
|
@rec_count = 0
|
9
|
-
@args = args
|
10
|
-
@debug =
|
11
|
-
@http2 =
|
12
|
-
@sock =
|
10
|
+
@args = args
|
11
|
+
@debug = http2.debug
|
12
|
+
@http2 = http2
|
13
|
+
@sock = sock
|
13
14
|
@nl = @http2.nl
|
14
15
|
@conn = @http2.connection
|
15
16
|
|
16
17
|
read_headers
|
17
|
-
read_body if @length == nil || @length
|
18
|
+
read_body if @length == nil || @length.positive?
|
18
19
|
finish
|
19
20
|
end
|
20
21
|
|
@@ -26,6 +27,7 @@ class Http2::ResponseReader
|
|
26
27
|
if line == "\n" || line == "\r\n" || line == @nl
|
27
28
|
puts "Http2: Changing mode to body!" if @debug
|
28
29
|
raise "No headers was given at all? Possibly corrupt state after last request?" if @response.headers.empty?
|
30
|
+
|
29
31
|
@mode = "body"
|
30
32
|
@http2.on_content_call(@args, @nl)
|
31
33
|
break
|
@@ -53,16 +55,12 @@ class Http2::ResponseReader
|
|
53
55
|
|
54
56
|
def finish
|
55
57
|
# Check if we should reconnect based on keep-alive-max.
|
56
|
-
if @keepalive_max == 1 || @connection == "close"
|
57
|
-
@conn.close unless @conn.closed?
|
58
|
-
end
|
58
|
+
@conn.close if !@conn.closed? && (@keepalive_max == 1 || @connection == "close")
|
59
59
|
|
60
60
|
# Validate that the response is as it should be.
|
61
61
|
puts "Http2: Validating response." if @debug
|
62
62
|
|
63
|
-
unless @response.code
|
64
|
-
raise "No status-code was received from the server. Headers: '#{@response.headers}' Body: '#{@response.body}'."
|
65
|
-
end
|
63
|
+
raise "No status-code was received from the server. Headers: '#{@response.headers}' Body: '#{@response.body}'." unless @response.code
|
66
64
|
|
67
65
|
@response.validate!
|
68
66
|
check_and_decode
|
@@ -81,35 +79,41 @@ private
|
|
81
79
|
url, args = url_and_args_from_location
|
82
80
|
|
83
81
|
if redirect_using_same_connection?(args)
|
84
|
-
|
82
|
+
@http2.get(url)
|
85
83
|
else
|
86
84
|
::Http2.new(args).get(url)
|
87
85
|
end
|
88
86
|
end
|
89
87
|
end
|
90
88
|
|
91
|
-
REDIRECT_CODES = [302, 303, 307]
|
89
|
+
REDIRECT_CODES = [301, 302, 303, 307, 308].freeze
|
92
90
|
def redirect_response?
|
93
|
-
REDIRECT_CODES.include?(
|
91
|
+
REDIRECT_CODES.include?(response.code.to_i) && response.header?("location") && @http2.args[:follow_redirects]
|
94
92
|
end
|
95
93
|
|
96
94
|
def redirect_using_same_connection?(args)
|
97
95
|
if !args[:host] || args[:host] == @args[:host]
|
98
|
-
|
96
|
+
true
|
99
97
|
else
|
100
|
-
|
98
|
+
false
|
101
99
|
end
|
102
100
|
end
|
103
101
|
|
102
|
+
def url
|
103
|
+
@url ||= response.header("location")
|
104
|
+
end
|
105
|
+
|
106
|
+
SSL_AND_PORT_KEYS = [:ssl, :port].freeze
|
107
|
+
|
104
108
|
def url_and_args_from_location
|
105
|
-
uri = URI.parse(
|
109
|
+
uri = URI.parse(url)
|
106
110
|
|
107
111
|
url = uri.path
|
108
|
-
url << "?#{uri.query}"
|
109
|
-
url = url.
|
112
|
+
url << "?#{uri.query}" unless uri.query.to_s.empty?
|
113
|
+
url = url.delete_prefix("/")
|
110
114
|
|
111
115
|
args = @http2.args
|
112
|
-
.reject { |k, _v|
|
116
|
+
.reject { |k, _v| SSL_AND_PORT_KEYS.include?(k) }
|
113
117
|
.merge(host: uri.host)
|
114
118
|
|
115
119
|
args[:ssl] = true if uri.scheme == "https"
|
@@ -130,7 +134,7 @@ private
|
|
130
134
|
|
131
135
|
begin
|
132
136
|
valid_string = ic.encode("UTF-8")
|
133
|
-
rescue
|
137
|
+
rescue StandardError
|
134
138
|
valid_string = untrusted_str.force_encoding("UTF-8").encode("UTF-8", invalid: :replace, replace: "").encode("UTF-8")
|
135
139
|
end
|
136
140
|
|
@@ -141,14 +145,19 @@ private
|
|
141
145
|
def handle_errors
|
142
146
|
return unless @http2.raise_errors
|
143
147
|
|
144
|
-
|
148
|
+
case @response.code
|
149
|
+
when "500"
|
145
150
|
err = Http2::Errors::Internalserver.new("A internal server error occurred")
|
146
|
-
|
151
|
+
when "403"
|
147
152
|
err = Http2::Errors::Noaccess.new("No access")
|
148
|
-
|
153
|
+
when "400"
|
149
154
|
err = Http2::Errors::Badrequest.new("Bad request")
|
150
|
-
|
155
|
+
when "401"
|
156
|
+
err = Http2::Errors::Unauthorized.new("Unauthorized")
|
157
|
+
when "404"
|
151
158
|
err = Http2::Errors::Notfound.new("Not found")
|
159
|
+
when "415"
|
160
|
+
err = Http2::Errors::UnsupportedMediaType.new("Unsupported media type")
|
152
161
|
end
|
153
162
|
|
154
163
|
if err
|
@@ -161,7 +170,13 @@ private
|
|
161
170
|
if line
|
162
171
|
@rec_count += line.length
|
163
172
|
elsif !line && @rec_count <= 0
|
164
|
-
|
173
|
+
parts = [
|
174
|
+
"KeepAliveMax: '#{@http2.keepalive_max}'",
|
175
|
+
"Connection: '#{@connection}'",
|
176
|
+
"PID: '#{Process.pid}'"
|
177
|
+
]
|
178
|
+
|
179
|
+
raise Errno::ECONNABORTED, "Server closed the connection before being able to read anything (#{parts.join(", ")})."
|
165
180
|
end
|
166
181
|
end
|
167
182
|
|
@@ -201,7 +216,7 @@ private
|
|
201
216
|
key = match[1].downcase
|
202
217
|
set_header_special_values(key, match[2])
|
203
218
|
parse_normal_header(line, key, match[1], match[2])
|
204
|
-
elsif (match = line.match(/^HTTP\/([\d\.]+)\s+(\d+)\s+(.+)$/))
|
219
|
+
elsif (match = line.match(/^HTTP\/([\d\.]+)\s+(\d+)\s+(.+)$/)) # rubocop:disable Style/RedundantRegexpEscape
|
205
220
|
@response.code = match[2]
|
206
221
|
@response.http_version = match[1]
|
207
222
|
@http2.on_content_call(@args, line)
|
@@ -232,18 +247,16 @@ private
|
|
232
247
|
@response.headers[key] = [] unless @response.headers.key?(key)
|
233
248
|
@response.headers[key] << value
|
234
249
|
|
235
|
-
if key != "transfer-encoding" && key != "content-length" && key != "connection" && key != "keep-alive"
|
236
|
-
@http2.on_content_call(@args, line)
|
237
|
-
end
|
250
|
+
@http2.on_content_call(@args, line) if key != "transfer-encoding" && key != "content-length" && key != "connection" && key != "keep-alive"
|
238
251
|
end
|
239
252
|
|
240
253
|
# Parses the body based on given headers and saves it to the result-object.
|
241
254
|
# http.parse_body(str)
|
242
255
|
def parse_body(line)
|
243
|
-
return :break if @length
|
256
|
+
return :break if @length&.zero?
|
244
257
|
|
245
258
|
if @transfer_encoding == "chunked"
|
246
|
-
|
259
|
+
parse_body_chunked(line)
|
247
260
|
else
|
248
261
|
puts "Http2: Adding #{line.to_s.bytesize} to the body." if @debug
|
249
262
|
@response.body << line
|
@@ -255,15 +268,16 @@ private
|
|
255
268
|
def parse_body_chunked(line)
|
256
269
|
len = line.strip.hex
|
257
270
|
|
258
|
-
if len
|
271
|
+
if len.positive?
|
259
272
|
read = @conn.read(len)
|
260
273
|
return :break if read == "" || read == "\n" || read == "\r\n"
|
274
|
+
|
261
275
|
@response.body << read
|
262
276
|
@http2.on_content_call(@args, read)
|
263
277
|
end
|
264
278
|
|
265
279
|
nl = @conn.gets
|
266
|
-
if len
|
280
|
+
if len.zero?
|
267
281
|
if nl == "\n" || nl == "\r\n"
|
268
282
|
return :break
|
269
283
|
else
|
data/lib/http2/url_builder.rb
CHANGED
data/lib/http2/utils.rb
CHANGED
@@ -4,7 +4,7 @@ class Http2::Utils
|
|
4
4
|
def self.urlenc(string)
|
5
5
|
# Thanks to CGI framework
|
6
6
|
string.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/) do
|
7
|
-
"
|
7
|
+
"%#{Regexp.last_match(1).unpack("H2" * Regexp.last_match(1).bytesize).join("%").upcase}"
|
8
8
|
end.tr(" ", "+")
|
9
9
|
end
|
10
10
|
|
@@ -12,18 +12,20 @@ class Http2::Utils
|
|
12
12
|
def self.urldec(string)
|
13
13
|
# Thanks to CGI framework
|
14
14
|
string.to_s.tr("+", " ").gsub(/((?:%[0-9a-fA-F]{2})+)/) do
|
15
|
-
[
|
15
|
+
[Regexp.last_match(1).delete("%")].pack("H*")
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
# Parses a cookies-string and returns them in an array.
|
20
20
|
def self.parse_set_cookies(str)
|
21
|
-
str =
|
21
|
+
str = str.to_s
|
22
22
|
return [] if str.empty?
|
23
|
+
|
23
24
|
cookie_start_regex = /^(.+?)=(.*?)(;\s*|$)/
|
24
25
|
|
25
26
|
match = str.match(cookie_start_regex)
|
26
27
|
raise "Could not match cookie: '#{str}'" unless match
|
28
|
+
|
27
29
|
str.gsub!(cookie_start_regex, "")
|
28
30
|
|
29
31
|
cookie_data = {
|
data/lib/http2.rb
CHANGED
@@ -3,7 +3,8 @@ require "uri"
|
|
3
3
|
require "monitor" unless ::Kernel.const_defined?(:Monitor)
|
4
4
|
require "string-cases"
|
5
5
|
|
6
|
-
# This class tries to emulate a browser in Ruby without any visual stuff.
|
6
|
+
# This class tries to emulate a browser in Ruby without any visual stuff.
|
7
|
+
# Remember cookies, keep sessions alive, reset connections according to keep-alive rules and more.
|
7
8
|
#===Examples
|
8
9
|
# Http2.new(host: "www.somedomain.com", port: 80, ssl: false, debug: false) do |http|
|
9
10
|
# res = http.get("index.rhtml?show=some_page")
|
@@ -17,28 +18,28 @@ require "string-cases"
|
|
17
18
|
class Http2
|
18
19
|
# Autoloader for subclasses.
|
19
20
|
def self.const_missing(name)
|
20
|
-
|
21
|
-
Http2.const_get(name)
|
22
|
-
end
|
21
|
+
file_path = "#{File.dirname(__FILE__)}/http2/#{::StringCases.camel_to_snake(name)}.rb"
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
resp = http.get("api.php?longurl=#{url}")
|
28
|
-
return resp.body
|
23
|
+
if File.exist?(file_path)
|
24
|
+
require file_path
|
25
|
+
return Http2.const_get(name) if Http2.const_defined?(name)
|
29
26
|
end
|
27
|
+
|
28
|
+
super
|
30
29
|
end
|
31
30
|
|
32
31
|
attr_reader :autostate, :connection, :cookies, :args, :debug, :mutex, :resp, :raise_errors, :nl
|
33
32
|
attr_accessor :keepalive_max, :keepalive_timeout
|
34
33
|
|
35
|
-
VALID_ARGUMENTS_INITIALIZE = [
|
34
|
+
VALID_ARGUMENTS_INITIALIZE = [
|
35
|
+
:host, :port, :skip_port_in_host_header, :ssl, :ssl_skip_verify, :nl, :user_agent, :raise_errors,
|
36
|
+
:follow_redirects, :debug, :encoding_gzip, :autostate, :basic_auth, :extra_headers, :proxy
|
37
|
+
].freeze
|
36
38
|
def initialize(args = {})
|
37
39
|
@args = parse_init_args(args)
|
38
40
|
set_default_values
|
39
41
|
@cookies = {}
|
40
42
|
@mutex = Monitor.new
|
41
|
-
|
42
43
|
@connection = ::Http2::Connection.new(self)
|
43
44
|
|
44
45
|
if block_given?
|
@@ -86,7 +87,6 @@ class Http2
|
|
86
87
|
#===Examples
|
87
88
|
# http.destroy
|
88
89
|
def destroy
|
89
|
-
@args = nil
|
90
90
|
@cookies = nil
|
91
91
|
@debug = nil
|
92
92
|
@mutex = nil
|
@@ -124,9 +124,9 @@ class Http2
|
|
124
124
|
# Proxies the request to another method but forces the method to be "DELETE".
|
125
125
|
def delete(args)
|
126
126
|
if args[:json]
|
127
|
-
|
127
|
+
post(args.merge(method: :delete))
|
128
128
|
else
|
129
|
-
|
129
|
+
get(args.merge(method: :delete))
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
@@ -147,17 +147,18 @@ class Http2
|
|
147
147
|
|
148
148
|
if @args[:basic_auth]
|
149
149
|
require "base64" unless ::Kernel.const_defined?(:Base64)
|
150
|
-
headers["Authorization"] = "Basic #{Base64.
|
150
|
+
headers["Authorization"] = "Basic #{Base64.strict_encode64("#{@args[:basic_auth][:user]}:#{@args[:basic_auth][:passwd]}").strip}"
|
151
151
|
end
|
152
152
|
|
153
153
|
if @args[:proxy] && @args[:proxy][:user] && @args[:proxy][:passwd] && !@connection.proxy_connect?
|
154
154
|
require "base64" unless ::Kernel.const_defined?(:Base64)
|
155
155
|
puts "Http2: Adding proxy auth header to request" if @debug
|
156
|
-
headers["Proxy-Authorization"] = "Basic #{Base64.
|
156
|
+
headers["Proxy-Authorization"] = "Basic #{Base64.strict_encode64("#{@args[:proxy][:user]}:#{@args[:proxy][:passwd]}").strip}"
|
157
157
|
end
|
158
158
|
|
159
159
|
headers.merge!(@args[:extra_headers]) if @args[:extra_headers]
|
160
160
|
headers.merge!(args[:headers]) if args[:headers]
|
161
|
+
|
161
162
|
headers
|
162
163
|
end
|
163
164
|
|
@@ -177,7 +178,7 @@ class Http2
|
|
177
178
|
|
178
179
|
# Returns a header-string which normally would be used for a request in the given state.
|
179
180
|
def header_str(headers_hash)
|
180
|
-
headers_hash["Cookie"] = cookie_header_string
|
181
|
+
headers_hash["Cookie"] = cookie_header_string unless cookie_header_string.empty?
|
181
182
|
|
182
183
|
headers_str = ""
|
183
184
|
headers_hash.each do |key, val|
|
@@ -204,6 +205,7 @@ class Http2
|
|
204
205
|
def cookie(name)
|
205
206
|
name = name.to_s
|
206
207
|
return @cookies.fetch(name) if @cookies.key?(name)
|
208
|
+
|
207
209
|
raise "No cookie by that name: '#{name}' in '#{@cookies.keys.join(", ")}'"
|
208
210
|
end
|
209
211
|
|
@@ -239,7 +241,7 @@ private
|
|
239
241
|
host = args[:host] || self.host
|
240
242
|
port = args[:port] || self.port
|
241
243
|
|
242
|
-
host_header_string =
|
244
|
+
host_header_string = host.dup # Copy host string to avoid changing the original string if port has been given!
|
243
245
|
host_header_string << ":#{port}" if port && ![80, 443].include?(port.to_i) && !@args[:skip_port_in_host_header]
|
244
246
|
host_header_string
|
245
247
|
end
|
@@ -269,13 +271,14 @@ private
|
|
269
271
|
args = {host: args} if args.is_a?(String)
|
270
272
|
raise "Arguments wasnt a hash." unless args.is_a?(Hash)
|
271
273
|
|
272
|
-
args.
|
274
|
+
args.each_key do |key|
|
273
275
|
raise "Invalid key: '#{key}'." unless VALID_ARGUMENTS_INITIALIZE.include?(key)
|
274
276
|
end
|
275
277
|
|
276
278
|
args[:proxy][:connect] = true if args[:proxy] && !args[:proxy].key?(:connect) && args[:ssl]
|
277
279
|
|
278
280
|
raise "No host was given." unless args[:host]
|
281
|
+
|
279
282
|
args
|
280
283
|
end
|
281
284
|
|
data/spec/helpers.rb
CHANGED
@@ -17,13 +17,11 @@ module Helpers
|
|
17
17
|
def with_http(args = {})
|
18
18
|
with_webserver do |hayabusa|
|
19
19
|
Http2.new({host: "localhost", port: hayabusa.port, encoding_gzip: false}.merge(args)) do |http|
|
20
|
-
|
21
|
-
yield http
|
22
|
-
rescue Http2::Errors::Internalserver => e
|
23
|
-
puts "Body of error-response: #{e.response.body}"
|
24
|
-
raise e
|
25
|
-
end
|
20
|
+
yield http
|
26
21
|
end
|
22
|
+
rescue Http2::Errors::Internalserver => e
|
23
|
+
puts "Body of error-response: #{e.response.body}"
|
24
|
+
raise e
|
27
25
|
end
|
28
26
|
end
|
29
27
|
end
|
data/spec/http2/cookies_spec.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
describe Http2 do
|
3
|
+
describe Http2::Cookie do
|
4
4
|
include Helpers
|
5
5
|
|
6
|
-
it "
|
6
|
+
it "parses cookies and let them be read" do
|
7
7
|
with_http do |http|
|
8
8
|
http.get("cookie_test.rhtml")
|
9
9
|
expect(http.cookies.length).to eq 2
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Http2::PostDataGenerator do
|
4
|
-
it "
|
4
|
+
it "recursively parses post-data-hashes." do
|
5
5
|
res = Http2::PostDataGenerator.new(
|
6
6
|
"test1" => "test2"
|
7
7
|
).generate
|
@@ -19,6 +19,7 @@ describe Http2::PostDataGenerator do
|
|
19
19
|
}
|
20
20
|
}
|
21
21
|
).generate
|
22
|
-
|
22
|
+
|
23
|
+
expect(res).to eq "test1%5Border%5D%5B%5B%3ABnet_profile%2C+%22profile_id%22%5D%5D=5"
|
23
24
|
end
|
24
25
|
end
|
data/spec/http2/response_spec.rb
CHANGED
@@ -15,10 +15,30 @@ describe Http2::Response do
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
describe "#json?" do
|
19
|
+
it "returns true for 'application/json'" do
|
20
|
+
with_http do |http|
|
21
|
+
res = http.get("json_test.rhtml")
|
22
|
+
|
23
|
+
expect(res).to receive(:content_type).and_return("application/json")
|
24
|
+
expect(res.json?).to eq true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns true for 'application/json' and getting" do
|
29
|
+
with_http do |http|
|
30
|
+
res = http.get("json_test.rhtml")
|
31
|
+
expect(res.json?).to eq true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "returns true for 'application/json' and posting" do
|
36
|
+
with_http do |http|
|
37
|
+
res = http.post(url: "json_test.rhtml", post: {test: "test2"})
|
38
|
+
|
39
|
+
expect(res).to receive(:content_type).and_return("application/json; charset=utf-8")
|
40
|
+
expect(res.json?).to eq true
|
41
|
+
end
|
22
42
|
end
|
23
43
|
end
|
24
44
|
|
data/spec/http2_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require "spec_helper"
|
|
2
2
|
require "json"
|
3
3
|
|
4
4
|
describe "Http2" do
|
5
|
-
it "
|
5
|
+
it "does normal post-requests" do
|
6
6
|
# Test posting keep-alive and advanced post-data.
|
7
7
|
with_http do |http|
|
8
8
|
0.upto(5) do
|
@@ -26,7 +26,7 @@ describe "Http2" do
|
|
26
26
|
}
|
27
27
|
]
|
28
28
|
},
|
29
|
-
"val9" => [
|
29
|
+
"val9" => %w[a b d]
|
30
30
|
}
|
31
31
|
)
|
32
32
|
res = JSON.parse(resp.body)
|
@@ -44,17 +44,19 @@ describe "Http2" do
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
describe "#reconnect" do
|
48
|
+
it "reconnects" do
|
49
|
+
with_http(follow_redirects: false, encoding_gzip: false) do |http|
|
50
|
+
resp1 = http.get("multipart_test.rhtml")
|
51
|
+
http.reconnect
|
52
|
+
resp2 = http.get("multipart_test.rhtml")
|
52
53
|
|
53
|
-
|
54
|
+
expect(resp1.body).to eq resp2.body
|
55
|
+
end
|
54
56
|
end
|
55
57
|
end
|
56
58
|
|
57
|
-
it "
|
59
|
+
it "handles multipart-requests and keep-alive when using multipart." do
|
58
60
|
with_http(follow_redirects: false) do |http|
|
59
61
|
0.upto(5) do
|
60
62
|
fpath = File.realpath(__FILE__)
|
@@ -84,7 +86,7 @@ describe "Http2" do
|
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
87
|
-
it "
|
89
|
+
it "handles keep-alive correctly" do
|
88
90
|
urls = [
|
89
91
|
"content_type_test.rhtml",
|
90
92
|
"json_test.rhtml"
|
@@ -100,18 +102,13 @@ describe "Http2" do
|
|
100
102
|
end
|
101
103
|
end
|
102
104
|
|
103
|
-
it "
|
104
|
-
isgd = Http2.isgdlink("https://github.com/kaspernj/http2")
|
105
|
-
raise "Expected isgd-var to be valid but it wasnt: '#{isgd}'." unless isgd.match(/^http:\/\/is\.gd\/([A-z\d]+)$/)
|
106
|
-
end
|
107
|
-
|
108
|
-
it "should raise exception when something is not found" do
|
105
|
+
it "raises an error when something is not found" do
|
109
106
|
with_http do |http|
|
110
107
|
expect { http.get("something_that_does_not_exist.rhtml") }.to raise_error(::Http2::Errors::Notfound)
|
111
108
|
end
|
112
109
|
end
|
113
110
|
|
114
|
-
it "
|
111
|
+
it "posts json" do
|
115
112
|
with_http do |http|
|
116
113
|
res = http.post(
|
117
114
|
url: "json_test.rhtml",
|
@@ -131,7 +128,7 @@ describe "Http2" do
|
|
131
128
|
end
|
132
129
|
end
|
133
130
|
|
134
|
-
it "
|
131
|
+
it "posts custom content types" do
|
135
132
|
with_http do |http|
|
136
133
|
res = http.post(
|
137
134
|
url: "content_type_test.rhtml",
|
@@ -147,7 +144,7 @@ describe "Http2" do
|
|
147
144
|
end
|
148
145
|
end
|
149
146
|
|
150
|
-
it "
|
147
|
+
it "sets various timeouts" do
|
151
148
|
with_http do |http|
|
152
149
|
http.get("content_type_test.rhtml")
|
153
150
|
expect(http.keepalive_timeout).to eq 15
|
@@ -155,10 +152,26 @@ describe "Http2" do
|
|
155
152
|
end
|
156
153
|
end
|
157
154
|
|
158
|
-
it "
|
155
|
+
it "follows redirects" do
|
159
156
|
with_http(follow_redirects: true) do |http|
|
160
157
|
resp = http.get("redirect_test.rhtml")
|
161
158
|
expect(resp.code).to eq "200"
|
162
159
|
end
|
163
160
|
end
|
161
|
+
|
162
|
+
it "throws errors on unauhtorized" do
|
163
|
+
with_http do |http|
|
164
|
+
expect do
|
165
|
+
http.get("unauthorized.rhtml")
|
166
|
+
end.to raise_error(Http2::Errors::Unauthorized)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
it "throws errors on unsupported media type" do
|
171
|
+
with_http do |http|
|
172
|
+
expect do
|
173
|
+
http.get("unsupported_media_type.rhtml")
|
174
|
+
end.to raise_error(Http2::Errors::UnsupportedMediaType)
|
175
|
+
end
|
176
|
+
end
|
164
177
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,15 +1,13 @@
|
|
1
|
-
require "codeclimate-test-reporter"
|
2
|
-
CodeClimate::TestReporter.start
|
3
|
-
|
4
1
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
5
2
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
3
|
require "rspec"
|
7
4
|
require "http2"
|
8
5
|
require "helpers"
|
6
|
+
require "json"
|
9
7
|
|
10
8
|
# Requires supporting files with custom matchers and macros, etc,
|
11
9
|
# in ./support/ and its subdirectories.
|
12
|
-
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
10
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }
|
13
11
|
|
14
12
|
RSpec.configure do |config|
|
15
13
|
config.expect_with :rspec do |c|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: http2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.36
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kasper Johansen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: string-cases
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: best_practice_project
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -39,63 +39,77 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 1.0.0
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 1.0.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: json
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: hayabusa
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
75
|
+
version: 0.0.28
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
82
|
+
version: 0.0.28
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
84
|
+
name: rake
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
87
|
- - ">="
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
89
|
+
version: '0'
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
94
|
- - ">="
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
96
|
+
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
98
|
+
name: rdoc
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
|
-
- - "
|
101
|
+
- - ">="
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0
|
103
|
+
version: '0'
|
90
104
|
type: :development
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
|
-
- - "
|
108
|
+
- - ">="
|
95
109
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0
|
110
|
+
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
112
|
+
name: rspec
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
115
|
- - ">="
|
@@ -109,7 +123,7 @@ dependencies:
|
|
109
123
|
- !ruby/object:Gem::Version
|
110
124
|
version: '0'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
126
|
+
name: rubocop
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
114
128
|
requirements:
|
115
129
|
- - ">="
|
@@ -123,33 +137,47 @@ dependencies:
|
|
123
137
|
- !ruby/object:Gem::Version
|
124
138
|
version: '0'
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
140
|
+
name: rubocop-performance
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rubocop-rspec
|
127
155
|
requirement: !ruby/object:Gem::Requirement
|
128
156
|
requirements:
|
129
157
|
- - ">="
|
130
158
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0
|
159
|
+
version: '0'
|
132
160
|
type: :development
|
133
161
|
prerelease: false
|
134
162
|
version_requirements: !ruby/object:Gem::Requirement
|
135
163
|
requirements:
|
136
164
|
- - ">="
|
137
165
|
- !ruby/object:Gem::Version
|
138
|
-
version: 0
|
166
|
+
version: '0'
|
139
167
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
168
|
+
name: sqlite3
|
141
169
|
requirement: !ruby/object:Gem::Requirement
|
142
170
|
requirements:
|
143
|
-
- - "
|
171
|
+
- - ">="
|
144
172
|
- !ruby/object:Gem::Version
|
145
|
-
version: 0
|
173
|
+
version: '0'
|
146
174
|
type: :development
|
147
175
|
prerelease: false
|
148
176
|
version_requirements: !ruby/object:Gem::Requirement
|
149
177
|
requirements:
|
150
|
-
- - "
|
178
|
+
- - ">="
|
151
179
|
- !ruby/object:Gem::Version
|
152
|
-
version: 0
|
180
|
+
version: '0'
|
153
181
|
description: A lightweight framework for doing http-connections in Ruby. Supports
|
154
182
|
cookies, keep-alive, compressing and much more.
|
155
183
|
email: k@spernj.org
|
@@ -191,6 +219,8 @@ files:
|
|
191
219
|
- spec/spec_root/json_test.rhtml
|
192
220
|
- spec/spec_root/multipart_test.rhtml
|
193
221
|
- spec/spec_root/redirect_test.rhtml
|
222
|
+
- spec/spec_root/unauthorized.rhtml
|
223
|
+
- spec/spec_root/unsupported_media_type.rhtml
|
194
224
|
homepage: http://github.com/kaspernj/http2
|
195
225
|
licenses:
|
196
226
|
- MIT
|
@@ -210,26 +240,27 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
210
240
|
- !ruby/object:Gem::Version
|
211
241
|
version: '0'
|
212
242
|
requirements: []
|
213
|
-
|
214
|
-
rubygems_version: 2.4.0
|
243
|
+
rubygems_version: 3.1.6
|
215
244
|
signing_key:
|
216
245
|
specification_version: 4
|
217
246
|
summary: A lightweight framework for doing http-connections in Ruby. Supports cookies,
|
218
247
|
keep-alive, compressing and much more.
|
219
248
|
test_files:
|
220
|
-
- spec/
|
249
|
+
- spec/http2/post_multipart_request_spec.rb
|
250
|
+
- spec/http2/response_reader_spec.rb
|
221
251
|
- spec/http2/get_request_spec.rb
|
222
|
-
- spec/http2/post_request_spec.rb
|
223
|
-
- spec/http2/post_data_generator_spec.rb
|
224
|
-
- spec/http2/cookies_spec.rb
|
225
252
|
- spec/http2/url_builder_spec.rb
|
226
|
-
- spec/http2/
|
227
|
-
- spec/http2/
|
253
|
+
- spec/http2/cookies_spec.rb
|
254
|
+
- spec/http2/post_data_generator_spec.rb
|
255
|
+
- spec/http2/post_request_spec.rb
|
228
256
|
- spec/http2/response_spec.rb
|
229
|
-
- spec/spec_root/redirect_test.rhtml
|
230
|
-
- spec/spec_root/json_test.rhtml
|
231
|
-
- spec/spec_root/multipart_test.rhtml
|
232
|
-
- spec/spec_root/content_type_test.rhtml
|
233
|
-
- spec/spec_root/cookie_test.rhtml
|
234
257
|
- spec/helpers.rb
|
258
|
+
- spec/http2_spec.rb
|
235
259
|
- spec/spec_helper.rb
|
260
|
+
- spec/spec_root/json_test.rhtml
|
261
|
+
- spec/spec_root/unauthorized.rhtml
|
262
|
+
- spec/spec_root/redirect_test.rhtml
|
263
|
+
- spec/spec_root/cookie_test.rhtml
|
264
|
+
- spec/spec_root/content_type_test.rhtml
|
265
|
+
- spec/spec_root/multipart_test.rhtml
|
266
|
+
- spec/spec_root/unsupported_media_type.rhtml
|