http2 0.0.32 → 0.0.33

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 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