http2 0.0.31 → 0.0.32
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/README.md +14 -1
- data/Rakefile +16 -13
- data/lib/http2.rb +25 -22
- data/lib/http2/base_request.rb +22 -0
- data/{include → lib/http2}/connection.rb +10 -9
- data/{include → lib/http2}/cookie.rb +4 -1
- data/{include → lib/http2}/errors.rb +1 -1
- data/lib/http2/get_request.rb +33 -0
- data/{include → lib/http2}/post_data_generator.rb +5 -4
- data/{include → lib/http2}/post_multipart_request.rb +19 -15
- data/{include → lib/http2}/post_request.rb +13 -25
- data/{include → lib/http2}/response.rb +40 -15
- data/{include → lib/http2}/response_reader.rb +21 -13
- data/{include → lib/http2}/url_builder.rb +4 -4
- data/{include → lib/http2}/utils.rb +9 -9
- data/spec/http2/cookies_spec.rb +6 -6
- data/spec/http2/get_request_spec.rb +11 -0
- data/spec/http2/post_multipart_request_spec.rb +11 -0
- data/spec/http2/post_request_spec.rb +11 -0
- data/spec/http2/response_reader_spec.rb +12 -0
- data/spec/http2/response_spec.rb +60 -4
- data/spec/http2/url_builder_spec.rb +1 -1
- data/spec/http2_spec.rb +66 -62
- data/spec/spec_helper.rb +6 -6
- data/spec/spec_root/redirect_test.rhtml +1 -1
- metadata +61 -24
- data/include/get_request.rb +0 -34
@@ -1,13 +1,13 @@
|
|
1
|
-
class Http2::PostRequest
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
class Http2::PostRequest < Http2::BaseRequest
|
2
|
+
def headers_string
|
3
|
+
unless @headers_string
|
4
|
+
@headers_string = "#{method} /#{@args[:url]} HTTP/1.1#{@nl}"
|
5
|
+
@headers_string << @http2.header_str(headers)
|
6
|
+
@headers_string << @nl
|
7
|
+
@headers_string << @data
|
7
8
|
end
|
8
9
|
|
9
|
-
@
|
10
|
-
@conn = @http2.connection
|
10
|
+
@headers_string
|
11
11
|
end
|
12
12
|
|
13
13
|
def execute
|
@@ -15,10 +15,9 @@ class Http2::PostRequest
|
|
15
15
|
|
16
16
|
@http2.mutex.synchronize do
|
17
17
|
puts "Http2: Doing post." if @debug
|
18
|
-
puts "Http2: Header str: #{header_string}" if @debug
|
19
18
|
|
20
|
-
@conn.write(
|
21
|
-
return @http2.read_response(@args)
|
19
|
+
@conn.write(headers_string)
|
20
|
+
return @http2.read_response(self, @args)
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
@@ -36,7 +35,7 @@ private
|
|
36
35
|
if @args[:content_type]
|
37
36
|
@args[:content_type]
|
38
37
|
elsif @args[:json]
|
39
|
-
|
38
|
+
"application/json"
|
40
39
|
else
|
41
40
|
"application/x-www-form-urlencoded"
|
42
41
|
end
|
@@ -63,20 +62,9 @@ private
|
|
63
62
|
headers_hash.merge! @http2.default_headers(@args)
|
64
63
|
|
65
64
|
unless headers_hash["Accept"]
|
66
|
-
if @args[:json]
|
67
|
-
headers_hash["Accept"] = "application/json"
|
68
|
-
end
|
65
|
+
headers_hash["Accept"] = "application/json" if @args[:json]
|
69
66
|
end
|
70
67
|
|
71
|
-
|
72
|
-
end
|
73
|
-
|
74
|
-
def header_string
|
75
|
-
header_str = "#{method} /#{@args[:url]} HTTP/1.1#{@nl}"
|
76
|
-
header_str << @http2.header_str(headers, @args)
|
77
|
-
header_str << @nl
|
78
|
-
header_str << @data
|
79
|
-
|
80
|
-
header_str
|
68
|
+
headers_hash
|
81
69
|
end
|
82
70
|
end
|
@@ -1,38 +1,39 @@
|
|
1
|
-
#This object will be returned as the response for each request.
|
1
|
+
# This object will be returned as the response for each request.
|
2
2
|
class Http2::Response
|
3
|
-
#All the data the response contains. Headers, body, cookies, requested URL and more.
|
4
|
-
attr_reader :args
|
3
|
+
# All the data the response contains. Headers, body, cookies, requested URL and more.
|
4
|
+
attr_reader :args, :request
|
5
5
|
attr_accessor :body, :charset, :code, :content_type, :http_version
|
6
6
|
|
7
|
-
#This method should not be called manually.
|
7
|
+
# This method should not be called manually.
|
8
8
|
def initialize(args = {})
|
9
9
|
@args = args
|
10
|
-
@args[:headers]
|
10
|
+
@args[:headers] ||= {}
|
11
11
|
@body = args[:body] || ""
|
12
|
-
@debug =
|
12
|
+
@debug = args[:debug]
|
13
|
+
@request = args.fetch(:request)
|
13
14
|
end
|
14
15
|
|
15
|
-
#Returns headers given from the host for the result.
|
16
|
+
# Returns headers given from the host for the result.
|
16
17
|
#===Examples
|
17
18
|
# headers_hash = res.headers
|
18
19
|
def headers
|
19
|
-
|
20
|
+
@args.fetch(:headers)
|
20
21
|
end
|
21
22
|
|
22
|
-
#Returns a certain header by name or false if not found.
|
23
|
+
# Returns a certain header by name or false if not found.
|
23
24
|
#===Examples
|
24
25
|
# val = res.header("content-type")
|
25
26
|
def header(key)
|
26
|
-
return false
|
27
|
-
|
27
|
+
return false unless @args.fetch(:headers).key?(key)
|
28
|
+
@args.fetch(:headers).fetch(key).first.to_s
|
28
29
|
end
|
29
30
|
|
30
|
-
#Returns true if a header of the given string exists.
|
31
|
+
# Returns true if a header of the given string exists.
|
31
32
|
#===Examples
|
32
33
|
# print "No content-type was given." if !http.header?("content-type")
|
33
34
|
def header?(key)
|
34
35
|
return true if @args[:headers].key?(key) && @args[:headers][key].first.to_s.length > 0
|
35
|
-
|
36
|
+
false
|
36
37
|
end
|
37
38
|
|
38
39
|
def content_length
|
@@ -53,12 +54,12 @@ class Http2::Response
|
|
53
54
|
end
|
54
55
|
end
|
55
56
|
|
56
|
-
#Returns the requested URL as a string.
|
57
|
+
# Returns the requested URL as a string.
|
57
58
|
#===Examples
|
58
59
|
# res.requested_url #=> "?show=status&action=getstatus"
|
59
60
|
def requested_url
|
60
61
|
raise "URL could not be detected." unless @args[:request_args][:url]
|
61
|
-
|
62
|
+
@args[:request_args][:url]
|
62
63
|
end
|
63
64
|
|
64
65
|
# Checks the data that has been sat on the object and raises various exceptions, if it does not validate somehow.
|
@@ -76,6 +77,30 @@ class Http2::Response
|
|
76
77
|
@json ||= JSON.parse(body)
|
77
78
|
end
|
78
79
|
|
80
|
+
def host
|
81
|
+
@request.http2.host
|
82
|
+
end
|
83
|
+
|
84
|
+
def port
|
85
|
+
@request.http2.port
|
86
|
+
end
|
87
|
+
|
88
|
+
def ssl?
|
89
|
+
@request.http2.ssl?
|
90
|
+
end
|
91
|
+
|
92
|
+
def path
|
93
|
+
@request.path
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_s
|
97
|
+
"#<Http::Response host=\"#{host}\" port=#{port} ssl=#{ssl?} path=\"#{path}\">"
|
98
|
+
end
|
99
|
+
|
100
|
+
def inspect
|
101
|
+
to_s
|
102
|
+
end
|
103
|
+
|
79
104
|
private
|
80
105
|
|
81
106
|
# Checks that the length of the body is the same as the given content-length if given.
|
@@ -4,9 +4,12 @@ class Http2::ResponseReader
|
|
4
4
|
def initialize(args)
|
5
5
|
@mode = "headers"
|
6
6
|
@transfer_encoding = nil
|
7
|
-
@response = Http2::Response.new(request_args: args, debug: @debug)
|
7
|
+
@response = Http2::Response.new(request: args.fetch(:request), request_args: args, debug: @debug)
|
8
8
|
@rec_count = 0
|
9
|
-
@args
|
9
|
+
@args = args[:args]
|
10
|
+
@debug = args[:http2].debug
|
11
|
+
@http2 = args[:http2]
|
12
|
+
@sock = args[:sock]
|
10
13
|
@nl = @http2.nl
|
11
14
|
@conn = @http2.connection
|
12
15
|
|
@@ -49,7 +52,7 @@ class Http2::ResponseReader
|
|
49
52
|
end
|
50
53
|
|
51
54
|
def finish
|
52
|
-
#Check if we should reconnect based on keep-alive-max.
|
55
|
+
# Check if we should reconnect based on keep-alive-max.
|
53
56
|
if @keepalive_max == 1 || @connection == "close"
|
54
57
|
@conn.close unless @conn.closed?
|
55
58
|
end
|
@@ -66,7 +69,7 @@ class Http2::ResponseReader
|
|
66
69
|
@http2.autostate_register(@response) if @http2.args[:autostate]
|
67
70
|
handle_errors
|
68
71
|
|
69
|
-
if response = check_and_follow_redirect
|
72
|
+
if (response = check_and_follow_redirect)
|
70
73
|
@response = response
|
71
74
|
end
|
72
75
|
end
|
@@ -100,14 +103,19 @@ private
|
|
100
103
|
|
101
104
|
def url_and_args_from_location
|
102
105
|
uri = URI.parse(@response.header("location"))
|
106
|
+
|
103
107
|
url = uri.path
|
104
108
|
url << "?#{uri.query}" if uri.query.to_s.length > 0
|
109
|
+
url = url.gsub(/\A\//, "")
|
110
|
+
|
111
|
+
args = @http2.args
|
112
|
+
.reject { |k, _v| [:ssl, :port].include? k }
|
113
|
+
.merge(host: uri.host)
|
105
114
|
|
106
|
-
args = {host: uri.host}
|
107
115
|
args[:ssl] = true if uri.scheme == "https"
|
108
116
|
args[:port] = uri.port if uri.port
|
109
117
|
|
110
|
-
|
118
|
+
[url, args]
|
111
119
|
end
|
112
120
|
|
113
121
|
def check_and_decode
|
@@ -123,7 +131,7 @@ private
|
|
123
131
|
begin
|
124
132
|
valid_string = ic.encode("UTF-8")
|
125
133
|
rescue
|
126
|
-
valid_string = untrusted_str.force_encoding("UTF-8").encode("UTF-8", :
|
134
|
+
valid_string = untrusted_str.force_encoding("UTF-8").encode("UTF-8", invalid: :replace, replace: "").encode("UTF-8")
|
127
135
|
end
|
128
136
|
|
129
137
|
@response.body = valid_string
|
@@ -131,7 +139,7 @@ private
|
|
131
139
|
end
|
132
140
|
|
133
141
|
def handle_errors
|
134
|
-
return unless
|
142
|
+
return unless @http2.raise_errors
|
135
143
|
|
136
144
|
if @response.code == "500"
|
137
145
|
err = Http2::Errors::Internalserver.new("A internal server error occurred")
|
@@ -176,7 +184,7 @@ private
|
|
176
184
|
end
|
177
185
|
|
178
186
|
def parse_content_type(content_type_line)
|
179
|
-
if match_charset = content_type_line.match(/\s*;\s*charset=(.+)/i)
|
187
|
+
if (match_charset = content_type_line.match(/\s*;\s*charset=(.+)/i))
|
180
188
|
@charset = match_charset[1].downcase
|
181
189
|
@response.charset = @charset
|
182
190
|
content_type_line.gsub!(match_charset[0], "")
|
@@ -185,15 +193,15 @@ private
|
|
185
193
|
@response.content_type = content_type_line
|
186
194
|
end
|
187
195
|
|
188
|
-
#Parse a header-line and saves it on the object.
|
196
|
+
# Parse a header-line and saves it on the object.
|
189
197
|
#===Examples
|
190
198
|
# http.parse_header("Content-Type: text/html\r\n")
|
191
199
|
def parse_header(line)
|
192
|
-
if match = line.match(/^(.+?):\s*(.+)#{@nl}$/)
|
200
|
+
if (match = line.match(/^(.+?):\s*(.+)#{@nl}$/))
|
193
201
|
key = match[1].downcase
|
194
202
|
set_header_special_values(key, match[2])
|
195
203
|
parse_normal_header(line, key, match[1], match[2])
|
196
|
-
elsif match = line.match(/^HTTP\/([\d\.]+)\s+(\d+)\s+(.+)$/)
|
204
|
+
elsif (match = line.match(/^HTTP\/([\d\.]+)\s+(\d+)\s+(.+)$/))
|
197
205
|
@response.code = match[2]
|
198
206
|
@response.http_version = match[1]
|
199
207
|
@http2.on_content_call(@args, line)
|
@@ -229,7 +237,7 @@ private
|
|
229
237
|
end
|
230
238
|
end
|
231
239
|
|
232
|
-
#Parses the body based on given headers and saves it to the result-object.
|
240
|
+
# Parses the body based on given headers and saves it to the result-object.
|
233
241
|
# http.parse_body(str)
|
234
242
|
def parse_body(line)
|
235
243
|
return :break if @length == 0
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class Http2::UrlBuilder
|
2
2
|
attr_accessor :host, :port, :protocol, :path, :params
|
3
3
|
|
4
|
-
def initialize
|
4
|
+
def initialize
|
5
5
|
@params = {}
|
6
6
|
end
|
7
7
|
|
@@ -24,7 +24,7 @@ class Http2::UrlBuilder
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
|
27
|
+
url_params
|
28
28
|
end
|
29
29
|
|
30
30
|
def build_path_and_params
|
@@ -35,7 +35,7 @@ class Http2::UrlBuilder
|
|
35
35
|
url << build_params
|
36
36
|
end
|
37
37
|
|
38
|
-
|
38
|
+
url
|
39
39
|
end
|
40
40
|
|
41
41
|
def build
|
@@ -49,7 +49,7 @@ class Http2::UrlBuilder
|
|
49
49
|
|
50
50
|
url << build_path_and_params
|
51
51
|
|
52
|
-
|
52
|
+
url
|
53
53
|
end
|
54
54
|
|
55
55
|
def params?
|
@@ -2,17 +2,17 @@
|
|
2
2
|
class Http2::Utils
|
3
3
|
# URL-encodes a string.
|
4
4
|
def self.urlenc(string)
|
5
|
-
#Thanks to CGI framework
|
5
|
+
# Thanks to CGI framework
|
6
6
|
string.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/) do
|
7
|
-
|
8
|
-
end.tr(
|
7
|
+
"%" + $1.unpack("H2" * $1.bytesize).join("%").upcase
|
8
|
+
end.tr(" ", "+")
|
9
9
|
end
|
10
10
|
|
11
11
|
# URL-decodes a string.
|
12
12
|
def self.urldec(string)
|
13
|
-
#Thanks to CGI framework
|
14
|
-
|
15
|
-
[$1.delete(
|
13
|
+
# Thanks to CGI framework
|
14
|
+
string.to_s.tr("+", " ").gsub(/((?:%[0-9a-fA-F]{2})+)/) do
|
15
|
+
[$1.delete("%")].pack("H*")
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -28,10 +28,10 @@ class Http2::Utils
|
|
28
28
|
|
29
29
|
cookie_data = {
|
30
30
|
name: urldec(match[1].to_s),
|
31
|
-
value:
|
31
|
+
value: urldec(match[2].to_s)
|
32
32
|
}
|
33
33
|
|
34
|
-
while match = str.match(/(.+?)=(.*?)(;\s*|$)/)
|
34
|
+
while (match = str.match(/(.+?)=(.*?)(;\s*|$)/))
|
35
35
|
str = str.gsub(match[0], "")
|
36
36
|
key = match[1].to_s.downcase
|
37
37
|
value = match[2].to_s
|
@@ -45,6 +45,6 @@ class Http2::Utils
|
|
45
45
|
|
46
46
|
cookie = Http2::Cookie.new(cookie_data)
|
47
47
|
|
48
|
-
|
48
|
+
[cookie]
|
49
49
|
end
|
50
50
|
end
|
data/spec/http2/cookies_spec.rb
CHANGED
@@ -5,14 +5,14 @@ describe Http2 do
|
|
5
5
|
|
6
6
|
it "should parse cookies and let them be read" do
|
7
7
|
with_http do |http|
|
8
|
-
|
9
|
-
http.cookies.length.
|
8
|
+
http.get("cookie_test.rhtml")
|
9
|
+
expect(http.cookies.length).to eq 2
|
10
10
|
|
11
11
|
cookie = http.cookie("TestCookie")
|
12
|
-
cookie.name.
|
13
|
-
cookie.value.
|
14
|
-
cookie.path.
|
15
|
-
cookie.expires.
|
12
|
+
expect(cookie.name).to eq "TestCookie"
|
13
|
+
expect(cookie.value).to eq "TestValue"
|
14
|
+
expect(cookie.path).to eq "/"
|
15
|
+
expect(cookie.expires).to be > Time.now
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Http2::GetRequest do
|
4
|
+
it "reads the headers from the request" do
|
5
|
+
with_http do |http|
|
6
|
+
res = http.get("content_type_test.rhtml")
|
7
|
+
headers = res.request.headers_string
|
8
|
+
expect(headers).to include "GET /content_type_test.rhtml"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Http2::PostMultipartRequest do
|
4
|
+
it "reads the headers from the request" do
|
5
|
+
with_http do |http|
|
6
|
+
res = http.post_multipart(url: "json_test.rhtml", post: {"test1" => "test2"})
|
7
|
+
headers = res.request.headers_string
|
8
|
+
expect(headers).to include "POST /json_test.rhtml"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Http2::PostRequest do
|
4
|
+
it "reads the headers from the request" do
|
5
|
+
with_http do |http|
|
6
|
+
res = http.post(url: "json_test.rhtml", json: {testkey: "testvalue"})
|
7
|
+
headers = res.request.headers_string
|
8
|
+
expect(headers).to include "POST /json_test.rhtml"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Http2::ResponseReader do
|
4
|
+
it "#url_and_args_from_location" do
|
5
|
+
with_http(ssl_skip_verify: true, follow_redirects: true) do |http|
|
6
|
+
response = http.get("redirect_test.rhtml?redirect_to=https://www.google.dk")
|
7
|
+
expect(response.host).to eq "www.google.dk"
|
8
|
+
expect(response.ssl?).to eq true
|
9
|
+
expect(response.port).to eq 443
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/spec/http2/response_spec.rb
CHANGED
@@ -1,17 +1,73 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Http2::Response do
|
4
|
-
it "
|
4
|
+
it "#content_type" do
|
5
5
|
with_http do |http|
|
6
6
|
res = http.get("content_type_test.rhtml")
|
7
|
-
res.content_type.
|
7
|
+
expect(res.content_type).to eq "text/html"
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
it "
|
11
|
+
it "#content_length" do
|
12
12
|
with_http do |http|
|
13
13
|
res = http.get("content_type_test.rhtml")
|
14
|
-
res.content_length.
|
14
|
+
expect(res.content_length).to be > 50
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "#json?" do
|
19
|
+
with_http do |http|
|
20
|
+
res = http.get("json_test.rhtml")
|
21
|
+
expect(res.json?).to eq true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "#host" do
|
26
|
+
with_http do |http|
|
27
|
+
res = http.get("json_test.rhtml")
|
28
|
+
expect(res.host).to eq "localhost"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "#port" do
|
33
|
+
with_http do |http|
|
34
|
+
res = http.get("json_test.rhtml")
|
35
|
+
expect(res.port).to eq http.port
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it "#ssl?" do
|
40
|
+
with_http do |http|
|
41
|
+
res = http.get("json_test.rhtml")
|
42
|
+
expect(res.ssl?).to eq false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "#path" do
|
47
|
+
with_http do |http|
|
48
|
+
res = http.get("json_test.rhtml")
|
49
|
+
expect(res.path).to eq "json_test.rhtml"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it "#header" do
|
54
|
+
with_http do |http|
|
55
|
+
res = http.get("json_test.rhtml")
|
56
|
+
expect(res.header("connection")).to eq "Keep-Alive"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "#header?" do
|
61
|
+
with_http do |http|
|
62
|
+
res = http.get("json_test.rhtml")
|
63
|
+
expect(res.header?("connection")).to eq true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "#json" do
|
68
|
+
with_http do |http|
|
69
|
+
res = http.post(url: "json_test.rhtml", post: {test: "test2"})
|
70
|
+
expect(res.json["_POST"]).to eq("test" => "test2")
|
15
71
|
end
|
16
72
|
end
|
17
73
|
end
|