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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5a6f33cdd27db12f717a7364f0b01e003fa136f4
4
- data.tar.gz: e5db49475ea7e1d94d3637273098d3e5fa3c40ba
2
+ SHA256:
3
+ metadata.gz: aaaa3b5476a0d9233f4ef5977f64d0368f009021f92bd821280058f518463bca
4
+ data.tar.gz: 24942b90af076019fc755a6abdc61ae049ec2a7178dcad6f6df5c0aba9c5a945
5
5
  SHA512:
6
- metadata.gz: d1502af03635d87d6c13253a5f160fb697cf83f5489a99e0b2de1ed7ce3a59b38cc11c970a4848fedc8ab6ddb88c24af31d2d651360b3081ee23a12bf6700770
7
- data.tar.gz: f4ac8597f0752a57342e0166a1bf017fc1f320d1eda7b093e7706eec8320827a5f895b320fc6bfe9e55b19dee6e58a59c62a0710a90b9cc0a177be1d5176b5f2
6
+ metadata.gz: eb01eb1aab89764bcaa356f5c89341e926ef0ebe5818aa5b22be70e64dd0e926f6ab9807723ae15b0f33d125397be759c89d5e896ab3b25d486b312bd44e252c
7
+ data.tar.gz: 7ff240f8d775175b3b5f9fdbfe1f8a4d380c83c26729c3c31730672d5e3dbea1e8d3ccc8899f39c06f27199799243c87b8de9c6483d00ff8335fb1c279a024e5
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- [![Build Status](https://img.shields.io/shippable/540e7b9b3479c5ea8f9ec21d.svg)](https://app.shippable.com/projects/540e7b9b3479c5ea8f9ec21d/builds/latest)
2
1
  [![Code Climate](https://codeclimate.com/github/kaspernj/http2.png)](https://codeclimate.com/github/kaspernj/http2)
3
- [![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"
@@ -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
@@ -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
- 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
 
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 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
@@ -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 == "application/json"
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 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,35 +79,41 @@ 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
+
106
+ SSL_AND_PORT_KEYS = [:ssl, :port].freeze
107
+
104
108
  def url_and_args_from_location
105
- uri = URI.parse(@response.header("location"))
109
+ uri = URI.parse(url)
106
110
 
107
111
  url = uri.path
108
- url << "?#{uri.query}" if uri.query.to_s.length > 0
109
- url = url.gsub(/\A\//, "")
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| [:ssl, :port].include? k }
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
- if @response.code == "500"
148
+ case @response.code
149
+ when "500"
145
150
  err = Http2::Errors::Internalserver.new("A internal server error occurred")
146
- elsif @response.code == "403"
151
+ when "403"
147
152
  err = Http2::Errors::Noaccess.new("No access")
148
- elsif @response.code == "400"
153
+ when "400"
149
154
  err = Http2::Errors::Badrequest.new("Bad request")
150
- elsif @response.code == "404"
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
- raise Errno::ECONNABORTED, "Server closed the connection before being able to read anything (KeepAliveMax: '#{@http2.keepalive_max}', Connection: '#{@connection}', PID: '#{Process.pid}')."
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 == 0
256
+ return :break if @length&.zero?
244
257
 
245
258
  if @transfer_encoding == "chunked"
246
- return parse_body_chunked(line)
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 > 0
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 == 0
280
+ if len.zero?
267
281
  if nl == "\n" || nl == "\r\n"
268
282
  return :break
269
283
  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 << "?"
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
- "%" + $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 = {
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. 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?
@@ -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
- return post(args.merge(method: :delete))
127
+ post(args.merge(method: :delete))
128
128
  else
129
- return get(args.merge(method: :delete))
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.encode64("#{@args[:basic_auth][:user]}:#{@args[:basic_auth][:passwd]}").strip}"
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.encode64("#{@args[:proxy][:user]}:#{@args[:proxy][:passwd]}").strip}"
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 = "#{host}" # Copy host string to avoid changing the original string if port has been given!
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.each do |key, _val|
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
- 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
@@ -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 "should parse cookies and let them be read" do
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 "should be able to recursively parse post-data-hashes." do
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
- 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
@@ -15,10 +15,30 @@ describe Http2::Response do
15
15
  end
16
16
  end
17
17
 
18
- it "#json?" do
19
- with_http do |http|
20
- res = http.get("json_test.rhtml")
21
- expect(res.json?).to eq true
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
 
@@ -1,7 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Http2::UrlBuilder do
4
- context "#build" do
4
+ describe "#build" do
5
5
  it "builds correctly" do
6
6
  ub = Http2::UrlBuilder.new
7
7
  ub.protocol = "https"
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 "should be able to do normal post-requests." do
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" => ["a", "b", "d"]
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
- it "#reconnect" do
48
- with_http(follow_redirects: false, encoding_gzip: false) do |http|
49
- resp1 = http.get("multipart_test.rhtml")
50
- http.reconnect
51
- resp2 = http.get("multipart_test.rhtml")
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
- expect(resp1.body).to eq resp2.body
54
+ expect(resp1.body).to eq resp2.body
55
+ end
54
56
  end
55
57
  end
56
58
 
57
- it "should be able to do multipart-requests and keep-alive when using multipart." do
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 "it should be able to handle keep-alive correctly" do
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 "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
- 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 "should be able to post json" do
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 "should be able to post custom content types" do
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 "should set various timeouts" do
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 "should follow redirects" do
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|
@@ -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.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: 2015-12-11 00:00:00.000000000 Z
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: rake
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: 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: 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: '3.12'
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: '3.12'
82
+ version: 0.0.28
69
83
  - !ruby/object:Gem::Dependency
70
- name: bundler
84
+ name: rake
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
87
  - - ">="
74
88
  - !ruby/object:Gem::Version
75
- version: 1.0.0
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: 1.0.0
96
+ version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
- name: hayabusa
98
+ name: rdoc
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
- - - "~>"
101
+ - - ">="
88
102
  - !ruby/object:Gem::Version
89
- version: 0.0.25
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.0.25
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
- name: sqlite3
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: codeclimate-test-reporter
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: best_practice_project
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.0.4
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.0.4
166
+ version: '0'
139
167
  - !ruby/object:Gem::Dependency
140
- name: rubocop
168
+ name: sqlite3
141
169
  requirement: !ruby/object:Gem::Requirement
142
170
  requirements:
143
- - - "~>"
171
+ - - ">="
144
172
  - !ruby/object:Gem::Version
145
- version: 0.35.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.35.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
- rubyforge_project:
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/http2_spec.rb
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/response_reader_spec.rb
227
- - spec/http2/post_multipart_request_spec.rb
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