http2 0.0.32 → 0.0.33

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5a6f33cdd27db12f717a7364f0b01e003fa136f4
4
- data.tar.gz: e5db49475ea7e1d94d3637273098d3e5fa3c40ba
2
+ SHA256:
3
+ metadata.gz: 856e9e6edf6d355dc73912489a6263a6a213abbaa1ec87f6fa4b5547f727f336
4
+ data.tar.gz: f4062dda7f2970331ab68a9e38095fe7bc729f61a27305e7eaea4ddb9a2d28fe
5
5
  SHA512:
6
- metadata.gz: d1502af03635d87d6c13253a5f160fb697cf83f5489a99e0b2de1ed7ce3a59b38cc11c970a4848fedc8ab6ddb88c24af31d2d651360b3081ee23a12bf6700770
7
- data.tar.gz: f4ac8597f0752a57342e0166a1bf017fc1f320d1eda7b093e7706eec8320827a5f895b320fc6bfe9e55b19dee6e58a59c62a0710a90b9cc0a177be1d5176b5f2
6
+ metadata.gz: 8ca5c943ae78a977a5253487670755830ea8225b417f79238cbaff5cf6d4f3adabe1e572b6480325c19e6cb317f35e10d2ef491de79df9d15cc7f6f8014f2b13
7
+ data.tar.gz: aead6eadd6b8569bab500e3ebf64bea0188ebf06739c83ba78d1f1fcd70b3b3ac0a6ad2516610bdefd2751bc63acb53c352fbbc1e3e310931d41ab0b6bf72f18
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
- [![Code Climate](https://codeclimate.com/github/kaspernj/http2/coverage.png)](https://codeclimate.com/github/kaspernj/http2)
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
- $stderr.puts e.message
9
- $stderr.puts "Run `bundle install` to install missing gems"
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"
@@ -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. Remember cookies, keep sessions alive, reset connections according to keep-alive rules and more.
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
- require "#{File.dirname(__FILE__)}/http2/#{::StringCases.camel_to_snake(name)}.rb"
21
- Http2.const_get(name)
22
- end
21
+ file_path = "#{File.dirname(__FILE__)}/http2/#{::StringCases.camel_to_snake(name)}.rb"
23
22
 
24
- # Converts a URL to "is.gd"-short-URL.
25
- def self.isgdlink(url)
26
- Http2.new(host: "is.gd") do |http|
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 = [:host, :port, :skip_port_in_host_header, :ssl, :ssl_skip_verify, :nl, :user_agent, :raise_errors, :follow_redirects, :debug, :encoding_gzip, :autostate, :basic_auth, :extra_headers, :proxy]
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?
@@ -124,9 +125,9 @@ class Http2
124
125
  # Proxies the request to another method but forces the method to be "DELETE".
125
126
  def delete(args)
126
127
  if args[:json]
127
- return post(args.merge(method: :delete))
128
+ post(args.merge(method: :delete))
128
129
  else
129
- return get(args.merge(method: :delete))
130
+ get(args.merge(method: :delete))
130
131
  end
131
132
  end
132
133
 
@@ -147,17 +148,18 @@ class Http2
147
148
 
148
149
  if @args[:basic_auth]
149
150
  require "base64" unless ::Kernel.const_defined?(:Base64)
150
- headers["Authorization"] = "Basic #{Base64.encode64("#{@args[:basic_auth][:user]}:#{@args[:basic_auth][:passwd]}").strip}"
151
+ headers["Authorization"] = "Basic #{Base64.strict_encode64("#{@args[:basic_auth][:user]}:#{@args[:basic_auth][:passwd]}").strip}"
151
152
  end
152
153
 
153
154
  if @args[:proxy] && @args[:proxy][:user] && @args[:proxy][:passwd] && !@connection.proxy_connect?
154
155
  require "base64" unless ::Kernel.const_defined?(:Base64)
155
156
  puts "Http2: Adding proxy auth header to request" if @debug
156
- headers["Proxy-Authorization"] = "Basic #{Base64.encode64("#{@args[:proxy][:user]}:#{@args[:proxy][:passwd]}").strip}"
157
+ headers["Proxy-Authorization"] = "Basic #{Base64.strict_encode64("#{@args[:proxy][:user]}:#{@args[:proxy][:passwd]}").strip}"
157
158
  end
158
159
 
159
160
  headers.merge!(@args[:extra_headers]) if @args[:extra_headers]
160
161
  headers.merge!(args[:headers]) if args[:headers]
162
+
161
163
  headers
162
164
  end
163
165
 
@@ -177,7 +179,7 @@ class Http2
177
179
 
178
180
  # Returns a header-string which normally would be used for a request in the given state.
179
181
  def header_str(headers_hash)
180
- headers_hash["Cookie"] = cookie_header_string
182
+ headers_hash["Cookie"] = cookie_header_string unless cookie_header_string.empty?
181
183
 
182
184
  headers_str = ""
183
185
  headers_hash.each do |key, val|
@@ -204,6 +206,7 @@ class Http2
204
206
  def cookie(name)
205
207
  name = name.to_s
206
208
  return @cookies.fetch(name) if @cookies.key?(name)
209
+
207
210
  raise "No cookie by that name: '#{name}' in '#{@cookies.keys.join(", ")}'"
208
211
  end
209
212
 
@@ -239,7 +242,7 @@ private
239
242
  host = args[:host] || self.host
240
243
  port = args[:port] || self.port
241
244
 
242
- host_header_string = "#{host}" # Copy host string to avoid changing the original string if port has been given!
245
+ host_header_string = host.dup # Copy host string to avoid changing the original string if port has been given!
243
246
  host_header_string << ":#{port}" if port && ![80, 443].include?(port.to_i) && !@args[:skip_port_in_host_header]
244
247
  host_header_string
245
248
  end
@@ -269,13 +272,14 @@ private
269
272
  args = {host: args} if args.is_a?(String)
270
273
  raise "Arguments wasnt a hash." unless args.is_a?(Hash)
271
274
 
272
- args.each do |key, _val|
275
+ args.each_key do |key|
273
276
  raise "Invalid key: '#{key}'." unless VALID_ARGUMENTS_INITIALIZE.include?(key)
274
277
  end
275
278
 
276
279
  args[:proxy][:connect] = true if args[:proxy] && !args[:proxy].key?(:connect) && args[:ssl]
277
280
 
278
281
  raise "No host was given." unless args[:host]
282
+
279
283
  args
280
284
  end
281
285
 
@@ -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.each do |key, _val|
12
+ @args.each_key do |key|
13
13
  raise "Invalid key: '#{key}'." unless VALID_ARGUMENTS_POST.include?(key)
14
14
  end
15
15
 
@@ -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
@@ -131,6 +133,7 @@ class Http2::Connection
131
133
 
132
134
  res = @sock_plain.gets.to_s
133
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
- ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER unless @args[:ssl_skip_verify]
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
 
@@ -1,11 +1,18 @@
1
1
  # This class holds various classes for error-handeling.
2
2
  class Http2::Errors
3
- class Http2error < RuntimeError
3
+ class BaseError < RuntimeError
4
4
  attr_accessor :response
5
5
  end
6
6
 
7
- class Noaccess < Http2error; end
8
- class Internalserver < Http2error; end
9
- class Notfound < Http2error; end
10
- class Badrequest < Http2error; end
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
- return ::Http2::PostDataGenerator.new(value, orig_key: key).generate
57
+ ::Http2::PostDataGenerator.new(value, orig_key: key).generate
58
58
  else
59
59
  data = ::Http2::PostDataGenerator.new(value).generate
60
- return "#{Http2::Utils.urlenc(key)}=#{Http2::Utils.urlenc(data)}"
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
- begin
60
- while (data = fp.sysread(4096))
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
 
@@ -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
@@ -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 :args, :request
5
- attr_accessor :body, :charset, :code, :content_type, :http_version
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(args = {})
9
- @args = args
10
- @args[:headers] ||= {}
11
- @body = args[:body] || ""
12
- @debug = args[:debug]
13
- @request = args.fetch(:request)
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 @args.fetch(:headers).key?(key)
28
- @args.fetch(:headers).fetch(key).first.to_s
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 @args[:headers].key?(key) && @args[:headers][key].first.to_s.length > 0
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
- return @body.bytesize
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
- return header("content-type")
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
@@ -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 self.header?("content-length")
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
- @response = Http2::Response.new(request: args.fetch(:request), request_args: args, debug: @debug)
7
+ @request = request
8
+ @response = Http2::Response.new(debug: http2.debug, request: request)
8
9
  @rec_count = 0
9
- @args = args[:args]
10
- @debug = args[:http2].debug
11
- @http2 = args[:http2]
12
- @sock = args[: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 > 0
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,31 +79,35 @@ private
81
79
  url, args = url_and_args_from_location
82
80
 
83
81
  if redirect_using_same_connection?(args)
84
- return @http2.get(url)
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?(@response.code.to_i) && @response.header?("location") && @http2.args[:follow_redirects]
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
- return true
96
+ true
99
97
  else
100
- return false
98
+ false
101
99
  end
102
100
  end
103
101
 
102
+ def url
103
+ @url ||= response.header("location")
104
+ end
105
+
104
106
  def url_and_args_from_location
105
- uri = URI.parse(@response.header("location"))
107
+ uri = URI.parse(url)
106
108
 
107
109
  url = uri.path
108
- url << "?#{uri.query}" if uri.query.to_s.length > 0
110
+ url << "?#{uri.query}" unless uri.query.to_s.empty?
109
111
  url = url.gsub(/\A\//, "")
110
112
 
111
113
  args = @http2.args
@@ -130,7 +132,7 @@ private
130
132
 
131
133
  begin
132
134
  valid_string = ic.encode("UTF-8")
133
- rescue
135
+ rescue StandardError
134
136
  valid_string = untrusted_str.force_encoding("UTF-8").encode("UTF-8", invalid: :replace, replace: "").encode("UTF-8")
135
137
  end
136
138
 
@@ -141,14 +143,19 @@ private
141
143
  def handle_errors
142
144
  return unless @http2.raise_errors
143
145
 
144
- if @response.code == "500"
146
+ case @response.code
147
+ when "500"
145
148
  err = Http2::Errors::Internalserver.new("A internal server error occurred")
146
- elsif @response.code == "403"
149
+ when "403"
147
150
  err = Http2::Errors::Noaccess.new("No access")
148
- elsif @response.code == "400"
151
+ when "400"
149
152
  err = Http2::Errors::Badrequest.new("Bad request")
150
- elsif @response.code == "404"
153
+ when "401"
154
+ err = Http2::Errors::Unauthorized.new("Unauthorized")
155
+ when "404"
151
156
  err = Http2::Errors::Notfound.new("Not found")
157
+ when "415"
158
+ err = Http2::Errors::UnsupportedMediaType.new("Unsupported media type")
152
159
  end
153
160
 
154
161
  if err
@@ -161,7 +168,13 @@ private
161
168
  if line
162
169
  @rec_count += line.length
163
170
  elsif !line && @rec_count <= 0
164
- raise Errno::ECONNABORTED, "Server closed the connection before being able to read anything (KeepAliveMax: '#{@http2.keepalive_max}', Connection: '#{@connection}', PID: '#{Process.pid}')."
171
+ parts = [
172
+ "KeepAliveMax: '#{@http2.keepalive_max}'",
173
+ "Connection: '#{@connection}'",
174
+ "PID: '#{Process.pid}'"
175
+ ]
176
+
177
+ raise Errno::ECONNABORTED, "Server closed the connection before being able to read anything (#{parts.join(", ")})."
165
178
  end
166
179
  end
167
180
 
@@ -201,7 +214,7 @@ private
201
214
  key = match[1].downcase
202
215
  set_header_special_values(key, match[2])
203
216
  parse_normal_header(line, key, match[1], match[2])
204
- elsif (match = line.match(/^HTTP\/([\d\.]+)\s+(\d+)\s+(.+)$/))
217
+ elsif (match = line.match(/^HTTP\/([\d\.]+)\s+(\d+)\s+(.+)$/)) # rubocop:disable Style/RedundantRegexpEscape
205
218
  @response.code = match[2]
206
219
  @response.http_version = match[1]
207
220
  @http2.on_content_call(@args, line)
@@ -232,18 +245,16 @@ private
232
245
  @response.headers[key] = [] unless @response.headers.key?(key)
233
246
  @response.headers[key] << value
234
247
 
235
- if key != "transfer-encoding" && key != "content-length" && key != "connection" && key != "keep-alive"
236
- @http2.on_content_call(@args, line)
237
- end
248
+ @http2.on_content_call(@args, line) if key != "transfer-encoding" && key != "content-length" && key != "connection" && key != "keep-alive"
238
249
  end
239
250
 
240
251
  # Parses the body based on given headers and saves it to the result-object.
241
252
  # http.parse_body(str)
242
253
  def parse_body(line)
243
- return :break if @length == 0
254
+ return :break if @length&.zero?
244
255
 
245
256
  if @transfer_encoding == "chunked"
246
- return parse_body_chunked(line)
257
+ parse_body_chunked(line)
247
258
  else
248
259
  puts "Http2: Adding #{line.to_s.bytesize} to the body." if @debug
249
260
  @response.body << line
@@ -255,15 +266,16 @@ private
255
266
  def parse_body_chunked(line)
256
267
  len = line.strip.hex
257
268
 
258
- if len > 0
269
+ if len.positive?
259
270
  read = @conn.read(len)
260
271
  return :break if read == "" || read == "\n" || read == "\r\n"
272
+
261
273
  @response.body << read
262
274
  @http2.on_content_call(@args, read)
263
275
  end
264
276
 
265
277
  nl = @conn.gets
266
- if len == 0
278
+ if len.zero?
267
279
  if nl == "\n" || nl == "\r\n"
268
280
  return :break
269
281
  else
@@ -28,7 +28,7 @@ class Http2::UrlBuilder
28
28
  end
29
29
 
30
30
  def build_path_and_params
31
- url = "#{path}"
31
+ url = path.to_s
32
32
 
33
33
  if params?
34
34
  url << "?"
@@ -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
- "%" + $1.unpack("H2" * $1.bytesize).join("%").upcase
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
- [$1.delete("%")].pack("H*")
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 = "#{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 = {
@@ -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
- begin
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
@@ -19,6 +19,7 @@ describe Http2::PostDataGenerator do
19
19
  }
20
20
  }
21
21
  ).generate
22
- raise "Expected 'test1%5Border%5D%5B%5B%3ABnet_profile%2C+%22profile_id%22%5D%5D=5' but got: '#{res}'." if res != "test1%5Border%5D%5B%5B%3ABnet_profile%2C+%22profile_id%22%5D%5D=5"
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
@@ -26,7 +26,7 @@ describe "Http2" do
26
26
  }
27
27
  ]
28
28
  },
29
- "val9" => ["a", "b", "d"]
29
+ "val9" => %w[a b d]
30
30
  }
31
31
  )
32
32
  res = JSON.parse(resp.body)
@@ -100,11 +100,6 @@ describe "Http2" do
100
100
  end
101
101
  end
102
102
 
103
- it "should be able to convert URL's to 'is.gd'-short-urls" do
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
103
  it "should raise exception when something is not found" do
109
104
  with_http do |http|
110
105
  expect { http.get("something_that_does_not_exist.rhtml") }.to raise_error(::Http2::Errors::Notfound)
@@ -161,4 +156,20 @@ describe "Http2" do
161
156
  expect(resp.code).to eq "200"
162
157
  end
163
158
  end
159
+
160
+ it "throws errors on unauhtorized" do
161
+ with_http do |http|
162
+ expect do
163
+ http.get("unauthorized.rhtml")
164
+ end.to raise_error(Http2::Errors::Unauthorized)
165
+ end
166
+ end
167
+
168
+ it "throws errors on unsupported media type" do
169
+ with_http do |http|
170
+ expect do
171
+ http.get("unsupported_media_type.rhtml")
172
+ end.to raise_error(Http2::Errors::UnsupportedMediaType)
173
+ end
174
+ end
164
175
  end
@@ -1,6 +1,3 @@
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"
@@ -9,7 +6,7 @@ require "helpers"
9
6
 
10
7
  # Requires supporting files with custom matchers and macros, etc,
11
8
  # in ./support/ and its subdirectories.
12
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
9
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }
13
10
 
14
11
  RSpec.configure do |config|
15
12
  config.expect_with :rspec do |c|
@@ -0,0 +1,3 @@
1
+ <%
2
+ _httpsession.resp.status = 401
3
+ %>
@@ -0,0 +1,3 @@
1
+ <%
2
+ _httpsession.resp.status = 415
3
+ %>
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.32
4
+ version: 0.0.33
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kasper Johansen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-11 00:00:00.000000000 Z
11
+ date: 2020-12-05 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: rake
28
+ name: best_practice_project
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,63 +39,49 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 3.4.0
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: 3.4.0
54
+ version: 1.0.0
55
55
  - !ruby/object:Gem::Dependency
56
- name: rdoc
56
+ name: hayabusa
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.12'
61
+ version: 0.0.28
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '3.12'
68
+ version: 0.0.28
69
69
  - !ruby/object:Gem::Dependency
70
- name: bundler
70
+ name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
- version: 1.0.0
75
+ version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
- version: 1.0.0
83
- - !ruby/object:Gem::Dependency
84
- name: hayabusa
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: 0.0.25
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: 0.0.25
82
+ version: '0'
97
83
  - !ruby/object:Gem::Dependency
98
- name: sqlite3
84
+ name: rdoc
99
85
  requirement: !ruby/object:Gem::Requirement
100
86
  requirements:
101
87
  - - ">="
@@ -109,7 +95,7 @@ dependencies:
109
95
  - !ruby/object:Gem::Version
110
96
  version: '0'
111
97
  - !ruby/object:Gem::Dependency
112
- name: codeclimate-test-reporter
98
+ name: rspec
113
99
  requirement: !ruby/object:Gem::Requirement
114
100
  requirements:
115
101
  - - ">="
@@ -123,33 +109,33 @@ dependencies:
123
109
  - !ruby/object:Gem::Version
124
110
  version: '0'
125
111
  - !ruby/object:Gem::Dependency
126
- name: best_practice_project
112
+ name: rubocop
127
113
  requirement: !ruby/object:Gem::Requirement
128
114
  requirements:
129
115
  - - ">="
130
116
  - !ruby/object:Gem::Version
131
- version: 0.0.4
117
+ version: '0'
132
118
  type: :development
133
119
  prerelease: false
134
120
  version_requirements: !ruby/object:Gem::Requirement
135
121
  requirements:
136
122
  - - ">="
137
123
  - !ruby/object:Gem::Version
138
- version: 0.0.4
124
+ version: '0'
139
125
  - !ruby/object:Gem::Dependency
140
- name: rubocop
126
+ name: sqlite3
141
127
  requirement: !ruby/object:Gem::Requirement
142
128
  requirements:
143
- - - "~>"
129
+ - - ">="
144
130
  - !ruby/object:Gem::Version
145
- version: 0.35.0
131
+ version: '0'
146
132
  type: :development
147
133
  prerelease: false
148
134
  version_requirements: !ruby/object:Gem::Requirement
149
135
  requirements:
150
- - - "~>"
136
+ - - ">="
151
137
  - !ruby/object:Gem::Version
152
- version: 0.35.0
138
+ version: '0'
153
139
  description: A lightweight framework for doing http-connections in Ruby. Supports
154
140
  cookies, keep-alive, compressing and much more.
155
141
  email: k@spernj.org
@@ -191,6 +177,8 @@ files:
191
177
  - spec/spec_root/json_test.rhtml
192
178
  - spec/spec_root/multipart_test.rhtml
193
179
  - spec/spec_root/redirect_test.rhtml
180
+ - spec/spec_root/unauthorized.rhtml
181
+ - spec/spec_root/unsupported_media_type.rhtml
194
182
  homepage: http://github.com/kaspernj/http2
195
183
  licenses:
196
184
  - MIT
@@ -210,26 +198,27 @@ required_rubygems_version: !ruby/object:Gem::Requirement
210
198
  - !ruby/object:Gem::Version
211
199
  version: '0'
212
200
  requirements: []
213
- rubyforge_project:
214
- rubygems_version: 2.4.0
201
+ rubygems_version: 3.0.4
215
202
  signing_key:
216
203
  specification_version: 4
217
204
  summary: A lightweight framework for doing http-connections in Ruby. Supports cookies,
218
205
  keep-alive, compressing and much more.
219
206
  test_files:
220
207
  - spec/http2_spec.rb
208
+ - spec/spec_root/multipart_test.rhtml
209
+ - spec/spec_root/unauthorized.rhtml
210
+ - spec/spec_root/cookie_test.rhtml
211
+ - spec/spec_root/json_test.rhtml
212
+ - spec/spec_root/unsupported_media_type.rhtml
213
+ - spec/spec_root/content_type_test.rhtml
214
+ - spec/spec_root/redirect_test.rhtml
215
+ - spec/helpers.rb
216
+ - spec/spec_helper.rb
217
+ - spec/http2/cookies_spec.rb
221
218
  - spec/http2/get_request_spec.rb
222
- - spec/http2/post_request_spec.rb
219
+ - spec/http2/response_reader_spec.rb
223
220
  - spec/http2/post_data_generator_spec.rb
224
- - spec/http2/cookies_spec.rb
225
221
  - spec/http2/url_builder_spec.rb
226
- - spec/http2/response_reader_spec.rb
227
222
  - spec/http2/post_multipart_request_spec.rb
228
223
  - 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
- - spec/helpers.rb
235
- - spec/spec_helper.rb
224
+ - spec/http2/post_request_spec.rb