http2 0.0.23 → 0.0.24

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1ceccc4ed3d44786cd9e606a534761f41e14fac6
4
+ data.tar.gz: e89411685ce84e2fa7d2de4275a6a94fe60dfa5d
5
+ SHA512:
6
+ metadata.gz: 00e97764f4dfea2d97014bc8e88e43e4ad3644a73570775789b293c89fbd5bc991f77b9411e36548f3d3ab473291b8794e228893066b25c5e8548144a788523a
7
+ data.tar.gz: b7a44522fa4032401c88e6f552702e9f50a6c207edb357211b91f5af645e4d40e666041abda4b7c37eadaf2bd5555cc20e4e275fbf8d6e1b4297ebc47662ff0f
data/Gemfile CHANGED
@@ -2,6 +2,7 @@ source "http://rubygems.org"
2
2
  # Add dependencies required to use your gem here.
3
3
  # Example:
4
4
  # gem "activesupport", ">= 2.3.5"
5
+ gem "string-cases"
5
6
 
6
7
  # Add dependencies to develop your gem here.
7
8
  # Include everything needed to run rake, tests, features, etc.
@@ -9,5 +10,7 @@ group :development do
9
10
  gem "rspec", "~> 2.8.0"
10
11
  gem "rdoc", "~> 3.12"
11
12
  gem "bundler", ">= 1.0.0"
12
- gem "jeweler", "~> 1.8.4"
13
+ gem "jeweler", ">= 1.8.4"
13
14
  end
15
+
16
+ gem "codeclimate-test-reporter", group: :test, require: nil
data/Gemfile.lock CHANGED
@@ -1,15 +1,47 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
+ addressable (2.3.6)
5
+ builder (3.2.2)
6
+ codeclimate-test-reporter (0.3.0)
7
+ simplecov (>= 0.7.1, < 1.0.0)
4
8
  diff-lcs (1.1.3)
5
- git (1.2.5)
6
- jeweler (1.8.4)
9
+ docile (1.1.5)
10
+ faraday (0.8.9)
11
+ multipart-post (~> 1.2.0)
12
+ git (1.2.6)
13
+ github_api (0.10.1)
14
+ addressable
15
+ faraday (~> 0.8.1)
16
+ hashie (>= 1.2)
17
+ multi_json (~> 1.4)
18
+ nokogiri (~> 1.5.2)
19
+ oauth2
20
+ hashie (2.1.1)
21
+ highline (1.6.21)
22
+ jeweler (1.8.8)
23
+ builder
7
24
  bundler (~> 1.0)
8
25
  git (>= 1.2.5)
26
+ github_api (= 0.10.1)
27
+ highline (>= 1.6.15)
28
+ nokogiri (= 1.5.10)
9
29
  rake
10
30
  rdoc
11
- json (1.8.0)
12
- rake (10.1.0)
31
+ json (1.8.1)
32
+ jwt (1.0.0)
33
+ multi_json (1.10.1)
34
+ multi_xml (0.5.5)
35
+ multipart-post (1.2.0)
36
+ nokogiri (1.5.10)
37
+ oauth2 (0.9.4)
38
+ faraday (>= 0.8, < 0.10)
39
+ jwt (~> 1.0)
40
+ multi_json (~> 1.3)
41
+ multi_xml (~> 0.5)
42
+ rack (~> 1.2)
43
+ rack (1.5.2)
44
+ rake (10.3.2)
13
45
  rdoc (3.12.2)
14
46
  json (~> 1.4)
15
47
  rspec (2.8.0)
@@ -20,12 +52,20 @@ GEM
20
52
  rspec-expectations (2.8.0)
21
53
  diff-lcs (~> 1.1.2)
22
54
  rspec-mocks (2.8.0)
55
+ simplecov (0.8.2)
56
+ docile (~> 1.1.0)
57
+ multi_json
58
+ simplecov-html (~> 0.8.0)
59
+ simplecov-html (0.8.0)
60
+ string-cases (0.0.0)
23
61
 
24
62
  PLATFORMS
25
63
  ruby
26
64
 
27
65
  DEPENDENCIES
28
66
  bundler (>= 1.0.0)
29
- jeweler (~> 1.8.4)
67
+ codeclimate-test-reporter
68
+ jeweler (>= 1.8.4)
30
69
  rdoc (~> 3.12)
31
70
  rspec (~> 2.8.0)
71
+ string-cases
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # http2
2
+
3
+ [![Build Status](https://api.shippable.com/projects/53bd3eef2e23bdcb03c0df9e/badge/master)](https://www.shippable.com/projects/53bd3eef2e23bdcb03c0df9e)
4
+ [![Code Climate](https://codeclimate.com/github/kaspernj/http2.png)](https://codeclimate.com/github/kaspernj/http2)
5
+
6
+ Example of usage:
7
+
8
+ ```ruby
9
+ require "rubygems"
10
+ require "http2"
11
+
12
+ Http2.new(:host => "www.google.dk") do |http|
13
+ #Get-request.
14
+ res = http.get("path/to/something")
15
+ puts res.body
16
+ puts "All response-headers: #{res.headers}"
17
+ puts "Specific header: #{res.header("HeaderName")}"
18
+
19
+ #Post-request.
20
+ res = http.post(:url => "path/to/something", :post => {
21
+ "some_post_val" => "some_value"
22
+ })
23
+
24
+ #Post-multipart (upload).
25
+ res = http.post_multipart(:url => "path/to/something", :post => {
26
+ "test_file1" => {
27
+ :fpath => fpath,
28
+ :filename => "specfile"
29
+ }
30
+ })
31
+
32
+ puts "Cookies until now: #{http.cookies}"
33
+ end
34
+ ```
35
+
36
+ ## Contributing to http2
37
+
38
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
39
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
40
+ * Fork the project.
41
+ * Start a feature/bugfix branch.
42
+ * Commit and push until you are happy with your contribution.
43
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
44
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
45
+
46
+ ## Copyright
47
+
48
+ Copyright (c) 2012 Kasper Johansen. See LICENSE.txt for
49
+ further details.
50
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.23
1
+ 0.0.24
data/http2.gemspec CHANGED
@@ -5,16 +5,16 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "http2"
8
- s.version = "0.0.23"
8
+ s.version = "0.0.24"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Kasper Johansen"]
12
- s.date = "2013-11-15"
12
+ s.date = "2014-07-09"
13
13
  s.description = "A lightweight framework for doing http-connections in Ruby. Supports cookies, keep-alive, compressing and much more."
14
14
  s.email = "k@spernj.org"
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE.txt",
17
- "README.rdoc"
17
+ "README.md"
18
18
  ]
19
19
  s.files = [
20
20
  ".document",
@@ -22,38 +22,44 @@ Gem::Specification.new do |s|
22
22
  "Gemfile",
23
23
  "Gemfile.lock",
24
24
  "LICENSE.txt",
25
- "README.rdoc",
25
+ "README.md",
26
26
  "Rakefile",
27
27
  "VERSION",
28
28
  "http2.gemspec",
29
29
  "include/errors.rb",
30
+ "include/post_multipart_helper.rb",
30
31
  "include/response.rb",
32
+ "include/response_reader.rb",
31
33
  "include/utils.rb",
32
34
  "lib/http2.rb",
35
+ "shippable.yml",
33
36
  "spec/http2_spec.rb",
34
37
  "spec/spec_helper.rb"
35
38
  ]
36
39
  s.homepage = "http://github.com/kaspernj/http2"
37
40
  s.licenses = ["MIT"]
38
41
  s.require_paths = ["lib"]
39
- s.rubygems_version = "1.8.23"
42
+ s.rubygems_version = "2.0.14"
40
43
  s.summary = "A lightweight framework for doing http-connections in Ruby. Supports cookies, keep-alive, compressing and much more."
41
44
 
42
45
  if s.respond_to? :specification_version then
43
- s.specification_version = 3
46
+ s.specification_version = 4
44
47
 
45
48
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_runtime_dependency(%q<string-cases>, [">= 0"])
46
50
  s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
47
51
  s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
48
52
  s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
49
53
  s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
50
54
  else
55
+ s.add_dependency(%q<string-cases>, [">= 0"])
51
56
  s.add_dependency(%q<rspec>, ["~> 2.8.0"])
52
57
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
53
58
  s.add_dependency(%q<bundler>, [">= 1.0.0"])
54
59
  s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
55
60
  end
56
61
  else
62
+ s.add_dependency(%q<string-cases>, [">= 0"])
57
63
  s.add_dependency(%q<rspec>, ["~> 2.8.0"])
58
64
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
59
65
  s.add_dependency(%q<bundler>, [">= 1.0.0"])
@@ -0,0 +1,77 @@
1
+ require "tempfile"
2
+
3
+ class Http2::PostMultipartHelper
4
+ attr_reader :boundary
5
+
6
+ def initialize(http2)
7
+ @nl = http2.nl
8
+
9
+ #Generate random string.
10
+ @boundary = rand(36**50).to_s(36)
11
+ end
12
+
13
+ def generate_raw(phash)
14
+ Tempfile.open("http2_post_multipart_tmp_#{@boundary}") do |praw|
15
+ phash.each do |key, val|
16
+ praw << "--#{@boundary}#{@nl}"
17
+
18
+ if val.is_a?(Tempfile) && val.respond_to?(:original_filename)
19
+ parse_temp_file(key, val, praw)
20
+ elsif val.is_a?(Hash) && val[:filename]
21
+ parse_file(key, val, praw)
22
+ else
23
+ praw << "Content-Disposition: form-data; name=\"#{key}\";#{@nl}"
24
+ praw << "Content-Length: #{val.to_s.bytesize}#{@nl}"
25
+ end
26
+
27
+ praw << "Content-Type: text/plain#{@nl}"
28
+ praw << @nl
29
+
30
+ if val.class.name.to_s == "StringIO"
31
+ praw << val.read
32
+ elsif val.is_a?(Hash) && val[:content]
33
+ praw << val[:content].to_s
34
+ elsif val.is_a?(Hash) && val[:fpath]
35
+ read_file(val[:fpath], praw)
36
+ else
37
+ praw << val.to_s
38
+ end
39
+
40
+ praw << @nl
41
+ end
42
+
43
+ praw << "--#{@boundary}--"
44
+
45
+ yield self, praw
46
+ end
47
+ end
48
+
49
+ def read_file(path, praw)
50
+ File.open(path, "r") do |fp|
51
+ begin
52
+ while data = fp.sysread(4096)
53
+ praw << data
54
+ end
55
+ rescue EOFError
56
+ # Happens when done.
57
+ end
58
+ end
59
+ end
60
+
61
+ def parse_temp_file(key, val, praw)
62
+ praw << "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{val.original_filename}\";#{@nl}"
63
+ praw << "Content-Length: #{val.to_s.bytesize}#{@nl}"
64
+ end
65
+
66
+ def parse_file(key, val, praw)
67
+ praw << "Content-Disposition: form-data; name=\"#{key}\"; filename=\"#{val[:filename]}\";#{@nl}"
68
+
69
+ if val[:content]
70
+ praw << "Content-Length: #{val[:content].to_s.bytesize}#{@nl}"
71
+ elsif val[:fpath]
72
+ praw << "Content-Length: #{File.size(val[:fpath])}#{@nl}"
73
+ else
74
+ raise "Could not figure out where to get content from."
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,251 @@
1
+ class Http2::ResponseReader
2
+ attr_reader :response
3
+
4
+ def initialize(args)
5
+ @mode = "headers"
6
+ @transfer_encoding = nil
7
+ @response = Http2::Response.new(:request_args => args, :debug => @debug)
8
+ @rec_count = 0
9
+ @args, @debug, @http2, @sock = args[:args], args[:debug], args[:http2], args[:sock]
10
+ @nl = @http2.nl
11
+
12
+ read_headers
13
+ read_body
14
+ finish
15
+ end
16
+
17
+ def read_headers
18
+ loop do
19
+ line = @sock.gets
20
+ check_line_read(line)
21
+
22
+ if line == "\n" || line == "\r\n" || line == @nl
23
+ puts "Http2: Changing mode to body!" if @debug
24
+ raise "No headers was given at all? Possibly corrupt state after last request?" if @response.headers.empty?
25
+ break if @length == 0
26
+ @mode = "body"
27
+ @http2.on_content_call(@args, @nl)
28
+ break
29
+ end
30
+
31
+ parse_header(line)
32
+ end
33
+ end
34
+
35
+ def read_body
36
+ loop do
37
+ if @length && @length > 0
38
+ line = @sock.read(@length)
39
+ raise "Expected to get #{@length} of bytes but got #{line.bytesize}" if @length != line.bytesize
40
+ else
41
+ line = @sock.gets
42
+ end
43
+
44
+ check_line_read(line)
45
+ stat = parse_body(line)
46
+ break if stat == "break"
47
+ next if stat == "next"
48
+ end
49
+ end
50
+
51
+ def finish
52
+ #Check if we should reconnect based on keep-alive-max.
53
+ if @keepalive_max == 1 or @connection == "close"
54
+ @sock.close unless @sock.closed?
55
+ end
56
+
57
+ # Validate that the response is as it should be.
58
+ puts "Http2: Validating response." if @debug
59
+
60
+ if !@response.args[:code]
61
+ raise "No status-code was received from the server. Headers: '#{@response.headers}' Body: '#{resp.args[:body]}'."
62
+ end
63
+
64
+ @response.validate!
65
+ check_and_decode
66
+ check_and_follow_redirect
67
+ handle_errors
68
+
69
+ @http2.autostate_register(@response) if @http2.args[:autostate]
70
+ end
71
+
72
+ private
73
+
74
+ def check_and_follow_redirect
75
+ if (@response.args[:code].to_s == "302" || @response.args[:code].to_s == "307") && @response.header?("location") && (!@http2.args.key?(:follow_redirects) || @http2.args[:follow_redirects])
76
+ uri = URI.parse(@response.header("location"))
77
+ url = uri.path
78
+ url << "?#{uri.query}" if uri.query.to_s.length > 0
79
+
80
+ args = {:host => uri.host}
81
+ args[:ssl] = true if uri.scheme == "https"
82
+ args[:port] = uri.port if uri.port
83
+
84
+ puts "Http2: Redirecting from location-header to '#{url}'." if @debug
85
+
86
+ if !args[:host] or args[:host] == @args[:host]
87
+ return self.get(url)
88
+ else
89
+ http = Http2.new(args)
90
+ return http.get(url)
91
+ end
92
+ end
93
+ end
94
+
95
+ def check_and_decode
96
+ # Check if the content is gzip-encoded - if so: decode it!
97
+ if @encoding == "gzip"
98
+ puts "Http2: Decoding GZip." if @debug
99
+ require "zlib"
100
+ require "stringio"
101
+ io = StringIO.new(@response.args[:body])
102
+ gz = Zlib::GzipReader.new(io)
103
+ untrusted_str = gz.read
104
+
105
+ begin
106
+ valid_string = ic.encode("UTF-8")
107
+ rescue
108
+ valid_string = untrusted_str.force_encoding("UTF-8").encode("UTF-8", :invalid => :replace, :replace => "").encode("UTF-8")
109
+ end
110
+
111
+ @response.args[:body] = valid_string
112
+ end
113
+ end
114
+
115
+ def handle_errors
116
+ if @http2.raise_errors
117
+ if @response.args[:code].to_i == 500
118
+ err = Http2::Errors::Internalserver.new("A internal server error occurred")
119
+ elsif @response.args[:code].to_i == 403
120
+ err = Http2::Errors::Noaccess.new("No access")
121
+ elsif @response.args[:code].to_i == 400
122
+ err = Http2::Errors::Badrequest.new("Bad request")
123
+ elsif @response.args[:code].to_i == 404
124
+ err = Http2::Errors::Notfound.new("Not found")
125
+ end
126
+ end
127
+
128
+ if err
129
+ err.response = @response
130
+ raise err
131
+ end
132
+ end
133
+
134
+ def check_line_read(line)
135
+ if line
136
+ @rec_count += line.length
137
+ elsif !line && @rec_count <= 0
138
+ @sock = nil
139
+ raise Errno::ECONNABORTED, "Server closed the connection before being able to read anything (KeepAliveMax: '#{@keepalive_max}', Connection: '#{@connection}', PID: '#{Process.pid}')."
140
+ end
141
+ end
142
+
143
+ def parse_cookie(cookie_line)
144
+ ::Http2::Utils.parse_set_cookies(cookie_line).each do |cookie_data|
145
+ @http2.cookies[cookie_data["name"]] = cookie_data
146
+ end
147
+ end
148
+
149
+ def parse_keep_alive(keep_alive_line)
150
+ if ka_max = keep_alive_line.match(/max=(\d+)/)
151
+ @keepalive_max = ka_max[1].to_i
152
+ print "Http2: Keepalive-max set to: '#{@keepalive_max}'.\n" if @debug
153
+ end
154
+
155
+ if ka_timeout = keep_alive_line.match(/timeout=(\d+)/)
156
+ @keepalive_timeout = ka_timeout[1].to_i
157
+ print "Http2: Keepalive-timeout set to: '#{@keepalive_timeout}'.\n" if @debug
158
+ end
159
+ end
160
+
161
+ def parse_content_type(content_type_line)
162
+ if match_charset = content_type_line.match(/\s*;\s*charset=(.+)/i)
163
+ @charset = match_charset[1].downcase
164
+ @response.args[:charset] = @charset
165
+ content_type_line.gsub!(match_charset[0], "")
166
+ end
167
+
168
+ @ctype = content_type_line
169
+ @response.args[:contenttype] = @content_type_line
170
+ end
171
+
172
+ #Parse a header-line and saves it on the object.
173
+ #===Examples
174
+ # http.parse_header("Content-Type: text/html\r\n")
175
+ def parse_header(line)
176
+ if match = line.match(/^(.+?):\s*(.+)#{@nl}$/)
177
+ key = match[1].to_s.downcase
178
+
179
+ parse_cookie(match[2]) if key == "set-cookie"
180
+ parse_keep_alive(match[2]) if key == "keep-alive"
181
+ parse_content_type(match[2]) if key == "content-type"
182
+
183
+ if key == "connection"
184
+ @connection = match[2].to_s.downcase
185
+ elsif key == "content-encoding"
186
+ @encoding = match[2].to_s.downcase
187
+ puts "Http2: Setting encoding to #{@encoding}" if @debug
188
+ elsif key == "content-length"
189
+ @length = match[2].to_i
190
+ elsif key == "transfer-encoding"
191
+ @transfer_encoding = match[2].to_s.downcase.strip
192
+ end
193
+
194
+ puts "Http2: Parsed header: #{match[1]}: #{match[2]}" if @debug
195
+ @response.headers[key] = [] unless @response.headers.key?(key)
196
+ @response.headers[key] << match[2]
197
+
198
+ if key != "transfer-encoding" && key != "content-length" && key != "connection" && key != "keep-alive"
199
+ @http2.on_content_call(@args, line)
200
+ end
201
+ elsif match = line.match(/^HTTP\/([\d\.]+)\s+(\d+)\s+(.+)$/)
202
+ @response.args[:code] = match[2]
203
+ @response.args[:http_version] = match[1]
204
+
205
+ @http2.on_content_call(@args, line)
206
+ else
207
+ raise "Could not understand header string: '#{line}'.\n\n#{@sock.read(409600)}"
208
+ end
209
+ end
210
+
211
+ #Parses the body based on given headers and saves it to the result-object.
212
+ # http.parse_body(str)
213
+ def parse_body(line)
214
+ if @response.args[:http_version] = "1.1"
215
+ return "break" if @length == 0
216
+
217
+ if @transfer_encoding == "chunked"
218
+ parse_body_chunked(line)
219
+ else
220
+ puts "Http2: Adding #{line.to_s.bytesize} to the body." if @debug
221
+ @response.args[:body] << line.to_s
222
+ @http2.on_content_call(@args, line)
223
+ return "break" if @response.header?("content-length") && @response.args[:body].length >= @response.header("content-length").to_i
224
+ end
225
+ else
226
+ raise "Dont know how to read HTTP version: '#{@resp.args[:http_version]}'."
227
+ end
228
+ end
229
+
230
+ def parse_body_chunked(line)
231
+ len = line.strip.hex
232
+
233
+ if len > 0
234
+ read = @sock.read(len)
235
+ return "break" if read == "" or (read == "\n" || read == "\r\n")
236
+ @response.args[:body] << read
237
+ @http2.on_content_call(@args, read)
238
+ end
239
+
240
+ nl = @sock.gets
241
+ if len == 0
242
+ if nl == "\n" || nl == "\r\n"
243
+ return "break"
244
+ else
245
+ raise "Dont know what to do :'-("
246
+ end
247
+ end
248
+
249
+ raise "Should have read newline but didnt: '#{nl}'." if nl != @nl
250
+ end
251
+ end