http2 0.0.31 → 0.0.35

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: 00dd7c4f177f2a7c84ef8f9ef463fe6f2675a400
4
- data.tar.gz: 7c28395bf02c20a50d2b8633e6ddd8156c8972f3
2
+ SHA256:
3
+ metadata.gz: ff94aecdfbfa493bb62b38edeef5a2bf49f9b5fb16ce5f023e6928ed77cb21c7
4
+ data.tar.gz: d8858f9e7daf39ed428311db9af0d561c971780d249cf0d0932a36962c921884
5
5
  SHA512:
6
- metadata.gz: bdc915bf5453cb50d0d58e298f661b833d5086f370d66bfda2e5f654d01572ea4880f340d99dd3e6853abfd20e6d3a7bed5892758add2ad4c7187087f71e59b4
7
- data.tar.gz: 54923d3a26cfe73698df0fbde92cd1dd517b61efaad45e77102fb469716d3efc4833b337bb2d915fb367d826e6370b5e068e6175a8ea7884b6b14bc9069f3024
6
+ metadata.gz: 04205c61f20290a3dec42a1c7a78b380ac749e0245ea53ba13f1a65cc983140e486a73fd26bf1e8a6ad808cde9a97d98ab9ba982f984726386d0b6db201da8d3
7
+ data.tar.gz: 413150d064f99e448cfcb0cca6380a6677b8e525ffcbbdaebfd957b5b7a6c263aeb105562ffcb6e8584603fd097a7fcfbda13327a662d7ba9927ad2e0d077afa
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- [![Build Status](https://api.shippable.com/projects/540e7b9b3479c5ea8f9ec21d/badge?branchName=master)](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
 
@@ -36,6 +36,11 @@ You can also use SSL:
36
36
  Http2.new(host: "myhost.com", ssl: true, ssl_skip_verify: true)
37
37
  ```
38
38
 
39
+ You can make it follow redirects like this:
40
+ ```ruby
41
+ Http2.new(host: "myhost.com", follow_redirects: true)
42
+ ```
43
+
39
44
  ## Get requests
40
45
  ```ruby
41
46
  res = http.get("path/to/something")
@@ -155,6 +160,14 @@ http = Http2.new(
155
160
  )
156
161
  ```
157
162
 
163
+ ## Inspect the headers that were sent
164
+
165
+ ```ruby
166
+ response = http.get("some_page")
167
+ request = response.request
168
+ request.headers_string #=> "GET /some_page\n..."
169
+ ```
170
+
158
171
  ## Contributing to http2
159
172
 
160
173
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
data/Rakefile CHANGED
@@ -1,37 +1,38 @@
1
- # encoding: utf-8
2
-
3
- require 'rubygems'
4
- require 'bundler'
1
+ require "rubygems"
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
- require 'rake'
10
+ require "rake"
13
11
 
14
- require 'rspec/core'
15
- require 'rspec/core/rake_task'
12
+ require "rspec/core"
13
+ require "rspec/core/rake_task"
16
14
  RSpec::Core::RakeTask.new(:spec) do |spec|
17
- spec.pattern = FileList['spec/**/*_spec.rb']
15
+ spec.pattern = FileList["spec/**/*_spec.rb"]
18
16
  end
19
17
 
20
18
  RSpec::Core::RakeTask.new(:rcov) do |spec|
21
- spec.pattern = 'spec/**/*_spec.rb'
19
+ spec.pattern = "spec/**/*_spec.rb"
22
20
  spec.rcov = true
23
21
  end
24
22
 
25
- task :default => :spec
23
+ task default: :spec
26
24
 
27
- require 'rdoc/task'
25
+ require "rdoc/task"
28
26
  Rake::RDocTask.new do |rdoc|
29
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
27
+ version = File.exist?("VERSION") ? File.read("VERSION") : ""
30
28
 
31
- rdoc.rdoc_dir = 'rdoc'
29
+ rdoc.rdoc_dir = "rdoc"
32
30
  rdoc.title = "http2 #{version}"
33
- rdoc.rdoc_files.include('README*')
34
- rdoc.rdoc_files.include('lib/**/*.rb')
31
+ rdoc.rdoc_files.include("README*")
32
+ rdoc.rdoc_files.include("lib/**/*.rb")
35
33
  end
36
34
 
37
35
  Bundler::GemHelper.install_tasks
36
+
37
+ require "best_practice_project"
38
+ BestPracticeProject.load_tasks
@@ -0,0 +1,22 @@
1
+ class Http2::BaseRequest
2
+ attr_reader :http2, :args, :debug
3
+
4
+ VALID_ARGUMENTS_POST = [:post, :url, :default_headers, :headers, :json, :method, :cookies, :on_content, :content_type].freeze
5
+
6
+ def initialize(http2, args)
7
+ @http2 = http2
8
+ @args = http2.parse_args(args)
9
+ @debug = http2.debug
10
+ @nl = http2.nl
11
+
12
+ @args.each_key do |key|
13
+ raise "Invalid key: '#{key}'." unless VALID_ARGUMENTS_POST.include?(key)
14
+ end
15
+
16
+ @conn = @http2.connection
17
+ end
18
+
19
+ def path
20
+ @args.fetch(:url)
21
+ end
22
+ end
@@ -1,6 +1,9 @@
1
1
  class Http2::Connection
2
2
  def initialize(http2)
3
- @http2, @debug, @args, @nl = http2, http2.debug, http2.args, http2.nl
3
+ @http2 = http2
4
+ @debug = http2.debug
5
+ @args = http2.args
6
+ @nl = http2.nl
4
7
  reconnect
5
8
  end
6
9
 
@@ -23,10 +26,6 @@ class Http2::Connection
23
26
  @sock.read(length)
24
27
  end
25
28
 
26
- def close
27
- @sock.close
28
- end
29
-
30
29
  def closed?
31
30
  @sock.closed?
32
31
  end
@@ -34,6 +33,7 @@ class Http2::Connection
34
33
  def sock_write(str)
35
34
  str = str.to_s
36
35
  return if str.empty?
36
+
37
37
  count = @sock.write(str)
38
38
  raise "Couldnt write to socket: '#{count}', '#{str}'." if count <= 0
39
39
  end
@@ -46,10 +46,13 @@ class Http2::Connection
46
46
  def write(str)
47
47
  reconnect unless socket_working?
48
48
 
49
+ puts "Http2: Writing: #{str}" if @debug
50
+
49
51
  begin
50
52
  raise Errno::EPIPE, "The socket is closed." if !@sock || @sock.closed?
53
+
51
54
  sock_write(str)
52
- rescue Errno::EPIPE #this can also be thrown by puts.
55
+ rescue Errno::EPIPE # this can also be thrown by puts.
53
56
  reconnect
54
57
  sock_write(str)
55
58
  end
@@ -61,7 +64,7 @@ class Http2::Connection
61
64
  def reconnect
62
65
  puts "Http2: Reconnect." if @debug
63
66
 
64
- #Open connection.
67
+ # Open connection.
65
68
  if @args[:proxy]
66
69
  if @args[:proxy][:connect]
67
70
  connect_proxy_connect
@@ -84,7 +87,7 @@ class Http2::Connection
84
87
  #===Examples
85
88
  # puts "Socket is working." if http.socket_working?
86
89
  def socket_working?
87
- return false if !@sock or @sock.closed?
90
+ return false if !@sock || @sock.closed?
88
91
 
89
92
  if @keepalive_timeout && @request_last
90
93
  between = Time.now.to_i - @request_last.to_i
@@ -94,7 +97,7 @@ class Http2::Connection
94
97
  end
95
98
  end
96
99
 
97
- return true
100
+ true
98
101
  end
99
102
 
100
103
  # Closes the current connection if any.
@@ -130,6 +133,7 @@ class Http2::Connection
130
133
 
131
134
  res = @sock_plain.gets.to_s
132
135
  raise "Couldn't connect through proxy: #{res}" unless res.match(/^http\/1\.(0|1)\s+200/i)
136
+
133
137
  @sock_plain.gets
134
138
 
135
139
  @proxy_connect = true
@@ -149,8 +153,15 @@ class Http2::Connection
149
153
  require "openssl" unless ::Kernel.const_defined?(:OpenSSL)
150
154
 
151
155
  ssl_context = OpenSSL::SSL::SSLContext.new
152
- 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
+
153
163
  @sock_ssl = OpenSSL::SSL::SSLSocket.new(@sock_plain, ssl_context)
164
+ @sock_ssl.hostname = @http2.host
154
165
  @sock_ssl.sync_close = true
155
166
  @sock_ssl.connect
156
167
 
@@ -2,7 +2,10 @@ class Http2::Cookie
2
2
  attr_reader :name, :value, :path, :expires_raw
3
3
 
4
4
  def initialize(args)
5
- @name, @value, @path, @expires_raw = args[:name], args[:value], args[:path], args[:expires]
5
+ @name = args[:name]
6
+ @value = args[:value]
7
+ @path = args[:path]
8
+ @expires_raw = args[:expires]
6
9
  end
7
10
 
8
11
  def inspect
@@ -0,0 +1,18 @@
1
+ # This class holds various classes for error-handeling.
2
+ class Http2::Errors
3
+ class BaseError < RuntimeError
4
+ attr_accessor :response
5
+ end
6
+
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
18
+ end
@@ -0,0 +1,33 @@
1
+ class Http2::GetRequest < Http2::BaseRequest
2
+ def execute
3
+ @http2.mutex.synchronize do
4
+ @http2.connection.write(headers_string)
5
+
6
+ puts "Http2: Reading response." if @debug
7
+ resp = @http2.read_response(self, @args)
8
+
9
+ puts "Http2: Done with get request." if @debug
10
+ return resp
11
+ end
12
+ end
13
+
14
+ def headers_string
15
+ unless @header_str
16
+ @header_str = "#{method} /#{@args[:url]} HTTP/1.1#{@nl}"
17
+ @header_str << @http2.header_str(@http2.default_headers(@args))
18
+ @header_str << @nl
19
+ end
20
+
21
+ @header_str
22
+ end
23
+
24
+ private
25
+
26
+ def method
27
+ if @args[:method]
28
+ @args[:method].to_s.upcase
29
+ else
30
+ "GET"
31
+ end
32
+ end
33
+ end
@@ -1,6 +1,7 @@
1
1
  class Http2::PostDataGenerator
2
2
  def initialize(pdata, args = {})
3
- @pdata, @args = pdata, args
3
+ @pdata = pdata
4
+ @args = args
4
5
  end
5
6
 
6
7
  def generate
@@ -14,7 +15,7 @@ class Http2::PostDataGenerator
14
15
  return @pdata.to_s
15
16
  end
16
17
 
17
- return praw
18
+ praw
18
19
  end
19
20
 
20
21
  private
@@ -22,7 +23,7 @@ private
22
23
  def generate_for_hash(hash)
23
24
  praw = ""
24
25
 
25
- @pdata.each do |key, val|
26
+ hash.each do |key, val|
26
27
  praw << "&" if praw != ""
27
28
  key = "#{@args[:orig_key]}[#{key}]" if @args[:orig_key]
28
29
  praw << generate_key_value(key, val)
@@ -35,7 +36,7 @@ private
35
36
  praw = ""
36
37
 
37
38
  count = 0
38
- @pdata.each do |val|
39
+ array.each do |val|
39
40
  praw << "&" if praw != ""
40
41
 
41
42
  if @args[:orig_key]
@@ -53,10 +54,10 @@ private
53
54
 
54
55
  def generate_key_value(key, value)
55
56
  if value.is_a?(Hash) || value.is_a?(Array)
56
- return ::Http2::PostDataGenerator.new(value, orig_key: key).generate
57
+ ::Http2::PostDataGenerator.new(value, orig_key: key).generate
57
58
  else
58
59
  data = ::Http2::PostDataGenerator.new(value).generate
59
- return "#{Http2::Utils.urlenc(key)}=#{Http2::Utils.urlenc(data)}"
60
+ "#{Http2::Utils.urlenc(key)}=#{Http2::Utils.urlenc(data)}"
60
61
  end
61
62
  end
62
63
  end
@@ -1,42 +1,44 @@
1
1
  require "tempfile"
2
2
 
3
- class Http2::PostMultipartRequest
3
+ class Http2::PostMultipartRequest < Http2::BaseRequest
4
+ attr_reader :headers_string
5
+
4
6
  def initialize(http2, *args)
5
- @http2, @nl, @args = http2, http2.nl, http2.parse_args(*args)
7
+ super
8
+
6
9
  @phash = @args[:post].clone
7
10
  @http2.autostate_set_on_post_hash(phash) if @http2.autostate
8
11
  @boundary = rand(36**50).to_s(36)
9
- @conn = @http2.connection
10
12
  end
11
13
 
12
14
  def execute
13
- generate_raw(@phash) do |helper, praw|
14
- puts "Http2: Header string: #{header_string}" if @debug
15
-
15
+ generate_raw(@phash) do |_helper, praw|
16
16
  @http2.mutex.synchronize do
17
- @conn.write(header_string(praw))
17
+ @conn.write(header_string_with_raw_post(praw))
18
18
 
19
19
  praw.rewind
20
20
  praw.each_line do |data|
21
21
  @conn.sock_write(data)
22
22
  end
23
23
 
24
- return @http2.read_response(@args)
24
+ return @http2.read_response(self, @args)
25
25
  end
26
26
  end
27
27
  end
28
28
 
29
29
  private
30
30
 
31
- def header_string(praw)
32
- header_str = "POST /#{@args[:url]} HTTP/1.1#{@nl}"
33
- header_str << @http2.header_str(@http2.default_headers(@args).merge(
31
+ def header_string_with_raw_post(praw)
32
+ @headers_string = "POST /#{@args[:url]} HTTP/1.1#{@nl}"
33
+
34
+ headers = @http2.default_headers(@args).merge(
34
35
  "Content-Type" => "multipart/form-data; boundary=#{@boundary}",
35
36
  "Content-Length" => praw.size
36
- ), @args)
37
- header_str << @nl
37
+ )
38
38
 
39
- return header_str
39
+ @headers_string << @http2.header_str(headers)
40
+ @headers_string << @nl
41
+ @headers_string
40
42
  end
41
43
 
42
44
  def generate_raw(phash)
@@ -54,13 +56,11 @@ private
54
56
 
55
57
  def read_file(path, praw)
56
58
  File.open(path, "r") do |fp|
57
- begin
58
- while data = fp.sysread(4096)
59
- praw << data
60
- end
61
- rescue EOFError
62
- # Happens when done.
59
+ while (data = fp.sysread(4096))
60
+ praw << data
63
61
  end
62
+ rescue EOFError
63
+ # Happens when done.
64
64
  end
65
65
  end
66
66
 
@@ -1,13 +1,13 @@
1
- class Http2::PostRequest
2
- VALID_ARGUMENTS_POST = [:post, :url, :default_headers, :headers, :json, :method, :cookies, :on_content, :content_type]
3
-
4
- def initialize(http2, args)
5
- args.each do |key, val|
6
- raise "Invalid key: '#{key}'." unless VALID_ARGUMENTS_POST.include?(key)
1
+ class Http2::PostRequest < Http2::BaseRequest
2
+ def headers_string
3
+ unless @headers_string
4
+ @headers_string = "#{method} /#{@args[:url]} HTTP/1.1#{@nl}"
5
+ @headers_string << @http2.header_str(headers)
6
+ @headers_string << @nl
7
+ @headers_string << @data
7
8
  end
8
9
 
9
- @http2, @args, @debug, @nl = http2, http2.parse_args(args), http2.debug, http2.nl
10
- @conn = @http2.connection
10
+ @headers_string
11
11
  end
12
12
 
13
13
  def execute
@@ -15,10 +15,9 @@ class Http2::PostRequest
15
15
 
16
16
  @http2.mutex.synchronize do
17
17
  puts "Http2: Doing post." if @debug
18
- puts "Http2: Header str: #{header_string}" if @debug
19
18
 
20
- @conn.write(header_string)
21
- return @http2.read_response(@args)
19
+ @conn.write(headers_string)
20
+ return @http2.read_response(self, @args)
22
21
  end
23
22
  end
24
23
 
@@ -36,7 +35,7 @@ private
36
35
  if @args[:content_type]
37
36
  @args[:content_type]
38
37
  elsif @args[:json]
39
- content_type = "application/json"
38
+ "application/json"
40
39
  else
41
40
  "application/x-www-form-urlencoded"
42
41
  end
@@ -61,22 +60,7 @@ private
61
60
  "Content-Type" => content_type
62
61
  }
63
62
  headers_hash.merge! @http2.default_headers(@args)
64
-
65
- unless headers_hash["Accept"]
66
- if @args[:json]
67
- headers_hash["Accept"] = "application/json"
68
- end
69
- end
70
-
71
- return headers_hash
72
- end
73
-
74
- def header_string
75
- header_str = "#{method} /#{@args[:url]} HTTP/1.1#{@nl}"
76
- header_str << @http2.header_str(headers, @args)
77
- header_str << @nl
78
- header_str << @data
79
-
80
- header_str
63
+ headers_hash["Accept"] = "application/json" if @args[:json] && !headers_hash["Accept"]
64
+ headers_hash
81
65
  end
82
66
  end
@@ -1,45 +1,42 @@
1
- #This object will be returned as the response for each request.
1
+ # This object will be returned as the response for each request.
2
2
  class Http2::Response
3
- #All the data the response contains. Headers, body, cookies, requested URL and more.
4
- attr_reader :args
5
- attr_accessor :body, :charset, :code, :content_type, :http_version
6
-
7
- #This method should not be called manually.
8
- def initialize(args = {})
9
- @args = args
10
- @args[:headers] = {} unless @args.key?(:headers)
11
- @body = args[:body] || ""
12
- @debug = @args[:debug]
13
- end
3
+ # All the data the response contains. Headers, body, cookies, requested URL and more.
4
+ attr_reader :headers, :request, :request_args, :requested_url
5
+ attr_accessor :body, :charset, :code, :http_version
6
+ attr_writer :content_type
14
7
 
15
- #Returns headers given from the host for the result.
16
- #===Examples
17
- # headers_hash = res.headers
18
- def headers
19
- return @args[:headers]
8
+ # This method should not be called manually.
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
20
15
  end
21
16
 
22
- #Returns a certain header by name or false if not found.
17
+ # Returns a certain header by name or false if not found.
23
18
  #===Examples
24
19
  # val = res.header("content-type")
25
20
  def header(key)
26
- return false if !@args[:headers].key?(key)
27
- return @args[:headers][key].first.to_s
21
+ return false unless headers.key?(key)
22
+
23
+ headers.fetch(key).first.to_s
28
24
  end
29
25
 
30
- #Returns true if a header of the given string exists.
26
+ # Returns true if a header of the given string exists.
31
27
  #===Examples
32
28
  # print "No content-type was given." if !http.header?("content-type")
33
29
  def header?(key)
34
- return true if @args[:headers].key?(key) && @args[:headers][key].first.to_s.length > 0
35
- return false
30
+ return true if headers.key?(key) && !headers[key].first.to_s.empty?
31
+
32
+ false
36
33
  end
37
34
 
38
35
  def content_length
39
36
  if header?("content-length")
40
37
  header("content-length").to_i
41
38
  elsif @body
42
- return @body.bytesize
39
+ @body.bytesize
43
40
  else
44
41
  raise "Couldn't calculate content-length."
45
42
  end
@@ -47,20 +44,12 @@ class Http2::Response
47
44
 
48
45
  def content_type
49
46
  if header?("content-type")
50
- return header("content-type")
47
+ header("content-type")
51
48
  else
52
49
  raise "No content-type was given."
53
50
  end
54
51
  end
55
52
 
56
- #Returns the requested URL as a string.
57
- #===Examples
58
- # res.requested_url #=> "?show=status&action=getstatus"
59
- def requested_url
60
- raise "URL could not be detected." unless @args[:request_args][:url]
61
- return @args[:request_args][:url]
62
- end
63
-
64
53
  # Checks the data that has been sat on the object and raises various exceptions, if it does not validate somehow.
65
54
  def validate!
66
55
  puts "Http2: Validating response length." if @debug
@@ -69,18 +58,42 @@ class Http2::Response
69
58
 
70
59
  # Returns true if the result is JSON.
71
60
  def json?
72
- content_type == "application/json"
61
+ content_type&.start_with?("application/json")
73
62
  end
74
63
 
75
64
  def json
76
65
  @json ||= JSON.parse(body)
77
66
  end
78
67
 
68
+ def host
69
+ @request.http2.host
70
+ end
71
+
72
+ def port
73
+ @request.http2.port
74
+ end
75
+
76
+ def ssl?
77
+ @request.http2.ssl?
78
+ end
79
+
80
+ def path
81
+ @request.path
82
+ end
83
+
84
+ def to_s
85
+ "#<Http::Response host=\"#{host}\" port=#{port} ssl=#{ssl?} path=\"#{path}\">"
86
+ end
87
+
88
+ def inspect
89
+ to_s
90
+ end
91
+
79
92
  private
80
93
 
81
94
  # Checks that the length of the body is the same as the given content-length if given.
82
95
  def validate_body_versus_content_length!
83
- unless self.header?("content-length")
96
+ unless header?("content-length")
84
97
  puts "Http2: No content length given - skipping length validation." if @debug
85
98
  return nil
86
99
  end