http2 0.0.31 → 0.0.35

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