http2 0.0.28 → 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.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +150 -28
  3. data/Rakefile +20 -31
  4. data/lib/http2.rb +105 -70
  5. data/lib/http2/base_request.rb +22 -0
  6. data/{include → lib/http2}/connection.rb +56 -28
  7. data/lib/http2/cookie.rb +22 -0
  8. data/lib/http2/errors.rb +18 -0
  9. data/lib/http2/get_request.rb +33 -0
  10. data/{include → lib/http2}/post_data_generator.rb +7 -6
  11. data/{include → lib/http2}/post_multipart_request.rb +20 -20
  12. data/{include → lib/http2}/post_request.rb +17 -22
  13. data/lib/http2/response.rb +109 -0
  14. data/{include → lib/http2}/response_reader.rb +75 -42
  15. data/{include → lib/http2}/url_builder.rb +6 -6
  16. data/lib/http2/utils.rb +52 -0
  17. data/spec/helpers.rb +27 -0
  18. data/spec/http2/cookies_spec.rb +18 -0
  19. data/spec/http2/get_request_spec.rb +11 -0
  20. data/spec/http2/post_data_generator_spec.rb +2 -1
  21. data/spec/http2/post_multipart_request_spec.rb +11 -0
  22. data/spec/http2/post_request_spec.rb +11 -0
  23. data/spec/http2/response_reader_spec.rb +12 -0
  24. data/spec/http2/response_spec.rb +73 -0
  25. data/spec/http2/url_builder_spec.rb +1 -1
  26. data/spec/http2_spec.rb +107 -89
  27. data/spec/spec_helper.rb +8 -8
  28. data/spec/spec_root/content_type_test.rhtml +4 -0
  29. data/spec/spec_root/cookie_test.rhtml +8 -0
  30. data/spec/spec_root/json_test.rhtml +9 -0
  31. data/spec/spec_root/multipart_test.rhtml +28 -0
  32. data/spec/spec_root/redirect_test.rhtml +3 -0
  33. data/spec/spec_root/unauthorized.rhtml +3 -0
  34. data/spec/spec_root/unsupported_media_type.rhtml +3 -0
  35. metadata +118 -36
  36. data/.document +0 -5
  37. data/.rspec +0 -1
  38. data/Gemfile +0 -14
  39. data/Gemfile.lock +0 -77
  40. data/VERSION +0 -1
  41. data/http2.gemspec +0 -77
  42. data/include/errors.rb +0 -11
  43. data/include/get_request.rb +0 -34
  44. data/include/response.rb +0 -73
  45. data/include/utils.rb +0 -40
  46. data/shippable.yml +0 -7
@@ -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,17 +1,20 @@
1
1
  class Http2::Connection
2
2
  def initialize(http2)
3
- @http2, @debug, @args = http2, http2.debug, http2.args
3
+ @http2 = http2
4
+ @debug = http2.debug
5
+ @args = http2.args
6
+ @nl = http2.nl
4
7
  reconnect
5
8
  end
6
9
 
7
10
  def destroy
8
- @sock.close if @sock and !@sock.closed?
11
+ @sock.close if @sock && !@sock.closed?
9
12
  @sock = nil
10
13
 
11
- @sock_plain.close if @sock_plain and !@sock_plain.closed?
14
+ @sock_plain.close if @sock_plain && !@sock_plain.closed?
12
15
  @sock_plain = nil
13
16
 
14
- @sock_ssl.close if @sock_ssl and !@sock_ssl.closed?
17
+ @sock_ssl.close if @sock_ssl && !@sock_ssl.closed?
15
18
  @sock_ssl = nil
16
19
  end
17
20
 
@@ -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,14 +64,16 @@ class Http2::Connection
61
64
  def reconnect
62
65
  puts "Http2: Reconnect." if @debug
63
66
 
64
- #Open connection.
65
- if @args[:proxy] && @args[:ssl]
66
- connect_proxy_ssl
67
- elsif @args[:proxy]
68
- connect_proxy
67
+ # Open connection.
68
+ if @args[:proxy]
69
+ if @args[:proxy][:connect]
70
+ connect_proxy_connect
71
+ else
72
+ connect_proxy
73
+ end
69
74
  else
70
- print "Http2: Opening socket connection to '#{@args[:host]}:#{@args[:port]}'.\n" if @debug
71
- @sock_plain = TCPSocket.new(@args[:host], @args[:port].to_i)
75
+ puts "Http2: Opening socket connection to '#{@http2.host}:#{@http2.port}'." if @debug
76
+ @sock_plain = TCPSocket.new(@http2.host, @http2.port)
72
77
  end
73
78
 
74
79
  if @args[:ssl]
@@ -82,7 +87,7 @@ class Http2::Connection
82
87
  #===Examples
83
88
  # puts "Socket is working." if http.socket_working?
84
89
  def socket_working?
85
- return false if !@sock or @sock.closed?
90
+ return false if !@sock || @sock.closed?
86
91
 
87
92
  if @keepalive_timeout && @request_last
88
93
  between = Time.now.to_i - @request_last.to_i
@@ -92,7 +97,7 @@ class Http2::Connection
92
97
  end
93
98
  end
94
99
 
95
- return true
100
+ true
96
101
  end
97
102
 
98
103
  # Closes the current connection if any.
@@ -102,23 +107,40 @@ class Http2::Connection
102
107
  @sock_plain.close if @sock_plain && !@sock_plain.closed?
103
108
  end
104
109
 
105
- def connect_proxy_ssl
106
- puts "Http2: Initializing proxy stuff." if @debug
110
+ def connect_proxy_connect
111
+ puts "Http2: Initializing proxy connect to '#{@args[:host]}:#{@args[:port]}' through proxy '#{@args[:proxy][:host]}:#{@args[:proxy][:port]}'." if @debug
107
112
  @sock_plain = TCPSocket.new(@args[:proxy][:host], @args[:proxy][:port])
108
113
 
109
- @sock_plain.write("CONNECT #{@args[:host]}:#{@args[:port]} HTTP/1.0#{@nl}")
110
- @sock_plain.write("User-Agent: #{@uagent}#{@nl}")
114
+ connect = "CONNECT #{@args[:host]}:#{@args[:port]} HTTP/1.1#{@nl}"
115
+ puts "Http2: Sending connect: #{connect}" if @debug
116
+ @sock_plain.write(connect)
117
+
118
+ headers = {
119
+ "Host" => "#{@args[:host]}:#{@args[:port]}"
120
+ }
111
121
 
112
122
  if @args[:proxy][:user] && @args[:proxy][:passwd]
113
- credential = ["#{@args[:proxy][:user]}:#{@args[:proxy][:passwd]}"].pack("m")
114
- credential.delete!("\r\n")
115
- @sock_plain.write("Proxy-Authorization: Basic #{credential}#{@nl}")
123
+ headers["Proxy-Authorization"] = "Basic #{["#{@args[:proxy][:user]}:#{@args[:proxy][:passwd]}"].pack("m").chomp}"
124
+ end
125
+
126
+ headers.each do |key, value|
127
+ header = "#{key}: #{value}"
128
+ puts "Http2: Sending header to proxy: #{header}" if @debug
129
+ @sock_plain.write("#{header}#{@nl}")
116
130
  end
117
131
 
118
132
  @sock_plain.write(@nl)
119
133
 
120
- res = @sock_plain.gets
121
- raise res if res.to_s.downcase != "http/1.0 200 connection established#{@nl}"
134
+ res = @sock_plain.gets.to_s
135
+ raise "Couldn't connect through proxy: #{res}" unless res.match(/^http\/1\.(0|1)\s+200/i)
136
+
137
+ @sock_plain.gets
138
+
139
+ @proxy_connect = true
140
+ end
141
+
142
+ def proxy_connect?
143
+ @proxy_connect
122
144
  end
123
145
 
124
146
  def connect_proxy
@@ -131,9 +153,15 @@ class Http2::Connection
131
153
  require "openssl" unless ::Kernel.const_defined?(:OpenSSL)
132
154
 
133
155
  ssl_context = OpenSSL::SSL::SSLContext.new
134
- #ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
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
135
162
 
136
163
  @sock_ssl = OpenSSL::SSL::SSLSocket.new(@sock_plain, ssl_context)
164
+ @sock_ssl.hostname = @http2.host
137
165
  @sock_ssl.sync_close = true
138
166
  @sock_ssl.connect
139
167
 
@@ -0,0 +1,22 @@
1
+ class Http2::Cookie
2
+ attr_reader :name, :value, :path, :expires_raw
3
+
4
+ def initialize(args)
5
+ @name = args[:name]
6
+ @value = args[:value]
7
+ @path = args[:path]
8
+ @expires_raw = args[:expires]
9
+ end
10
+
11
+ def inspect
12
+ "#<Http2::Cookie name=#{@name} value=#{@value} path=#{@path}>"
13
+ end
14
+
15
+ def to_s
16
+ inspect
17
+ end
18
+
19
+ def expires
20
+ @expires ||= Time.parse(@expires_raw) if @expires_raw
21
+ end
22
+ end
@@ -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_str}" 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
@@ -56,16 +55,12 @@ private
56
55
  end
57
56
 
58
57
  def headers
59
- headers_hash = {"Content-Length" => @data.bytesize, "Content-Type" => content_type}
58
+ headers_hash = {
59
+ "Content-Length" => @data.bytesize,
60
+ "Content-Type" => content_type
61
+ }
60
62
  headers_hash.merge! @http2.default_headers(@args)
61
- end
62
-
63
- def header_string
64
- header_str = "#{method} /#{@args[:url]} HTTP/1.1#{@nl}"
65
- header_str << @http2.header_str(headers, @args)
66
- header_str << @nl
67
- header_str << @data
68
-
69
- header_str
63
+ headers_hash["Accept"] = "application/json" if @args[:json] && !headers_hash["Accept"]
64
+ headers_hash
70
65
  end
71
66
  end