http2 0.0.32 → 0.0.36
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 +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
|
-
[](https://app.shippable.com/projects/540e7b9b3479c5ea8f9ec21d/builds/latest)
|
2
1
|
[](https://codeclimate.com/github/kaspernj/http2)
|
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
|