em-http-request 0.2.13 → 0.2.14

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.

Potentially problematic release.


This version of em-http-request might be problematic. Click here for more details.

data/Changelog.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.14 / 2010-10-06
4
+
5
+ - bugfix: form-encode keys/values of ruby objects passed in as body
6
+
3
7
  ## 0.2.13 / 2010-09-25
4
8
 
5
9
  - added SOCKS5 proxy support
data/README.md CHANGED
@@ -29,6 +29,7 @@ Libraries & Applications using em-http
29
29
  - [RDaneel](http://github.com/hasmanydevelopers/RDaneel) - Ruby crawler which respects robots.txt
30
30
  - [rsolr-async](http://github.com/mwmitchell/rsolr-async) - An asynchronus connection adapter for RSolr
31
31
  - [PubSubHubbub](http://github.com/igrigorik/PubSubHubbub) - Asynchronous PubSubHubbub ruby client
32
+ - [Firering](http://github.com/EmmanuelOga/firering) - Eventmachine powered Campfire API
32
33
  - and many others.. drop me a link if you want yours included!
33
34
 
34
35
  Simple client example
data/Rakefile CHANGED
@@ -19,9 +19,9 @@ task :default => :compile
19
19
  Rake::RDocTask.new(:rdoc) do |task|
20
20
  task.rdoc_dir = 'doc'
21
21
  task.title = 'EventMachine::HttpRequest'
22
- task.options = %w(--title HttpRequest --main README --line-numbers)
22
+ task.options = %w(--title HttpRequest --main README.md --line-numbers)
23
23
  task.rdoc_files.include(['lib/**/*.rb'])
24
- task.rdoc_files.include(['README', 'LICENSE'])
24
+ task.rdoc_files.include(['README.md', 'LICENSE'])
25
25
  end
26
26
 
27
27
  # Rebuild parser Ragel
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.13
1
+ 0.2.14
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{em-http-request}
8
- s.version = "0.2.13"
8
+ s.version = "0.2.14"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Ilya Grigorik"]
12
- s.date = %q{2010-09-25}
12
+ s.date = %q{2010-10-06}
13
13
  s.description = %q{EventMachine based, async HTTP Request interface}
14
14
  s.email = %q{ilya@igvita.com}
15
15
  s.extensions = ["ext/buffer/extconf.rb", "ext/http11_client/extconf.rb"]
@@ -43,14 +43,13 @@ Gem::Specification.new do |s|
43
43
  "lib/em-http.rb",
44
44
  "lib/em-http/client.rb",
45
45
  "lib/em-http/core_ext/bytesize.rb",
46
- "lib/em-http/core_ext/hash.rb",
47
46
  "lib/em-http/decoders.rb",
48
47
  "lib/em-http/http_options.rb",
49
48
  "lib/em-http/mock.rb",
50
49
  "lib/em-http/multi.rb",
51
50
  "lib/em-http/request.rb",
51
+ "spec/encoding_spec.rb",
52
52
  "spec/fixtures/google.ca",
53
- "spec/hash_spec.rb",
54
53
  "spec/helper.rb",
55
54
  "spec/mock_spec.rb",
56
55
  "spec/multi_spec.rb",
@@ -67,7 +66,7 @@ Gem::Specification.new do |s|
67
66
  s.rubygems_version = %q{1.3.7}
68
67
  s.summary = %q{EventMachine based, async HTTP Request interface}
69
68
  s.test_files = [
70
- "spec/hash_spec.rb",
69
+ "spec/encoding_spec.rb",
71
70
  "spec/helper.rb",
72
71
  "spec/mock_spec.rb",
73
72
  "spec/multi_spec.rb",
@@ -1 +1 @@
1
- require 'em-http'
1
+ require 'em-http'
data/lib/em-http.rb CHANGED
@@ -10,7 +10,6 @@ require 'socket'
10
10
  require File.dirname(__FILE__) + '/http11_client'
11
11
  require File.dirname(__FILE__) + '/em_buffer'
12
12
 
13
- require File.dirname(__FILE__) + '/em-http/core_ext/hash'
14
13
  require File.dirname(__FILE__) + '/em-http/core_ext/bytesize'
15
14
 
16
15
  require File.dirname(__FILE__) + '/em-http/client'
@@ -82,9 +82,9 @@ module EventMachine
82
82
 
83
83
  # Escapes a URI.
84
84
  def escape(s)
85
- s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
85
+ s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/n) {
86
86
  '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
87
- }.tr(' ', '+')
87
+ }
88
88
  end
89
89
 
90
90
  # Unescapes a URI escaped string.
@@ -153,6 +153,29 @@ module EventMachine
153
153
  end
154
154
  end
155
155
 
156
+ def form_encode_body(obj)
157
+ pairs = []
158
+ recursive = Proc.new do |h, prefix|
159
+ h.each do |k,v|
160
+ key = prefix == '' ? escape(k) : "#{prefix}[#{escape(k)}]"
161
+
162
+ if v.is_a? Array
163
+ nh = Hash.new
164
+ v.size.times { |t| nh[t] = v[t] }
165
+ recursive.call(nh, key)
166
+
167
+ elsif v.is_a? Hash
168
+ recursive.call(v, key)
169
+ else
170
+ pairs << "#{key}=#{escape(v)}"
171
+ end
172
+ end
173
+ end
174
+
175
+ recursive.call(obj, '')
176
+ return pairs.join('&')
177
+ end
178
+
156
179
  # Encode a field in an HTTP header
157
180
  def encode_field(k, v)
158
181
  FIELD_ENCODING % [k, v]
@@ -236,8 +259,8 @@ module EventMachine
236
259
  @state = :connect_socks_proxy
237
260
  send_socks_handshake
238
261
 
239
- # if we need to negotiate the proxy connection first, then
240
- # issue a CONNECT query and wait for 200 response
262
+ # if we need to negotiate the proxy connection first, then
263
+ # issue a CONNECT query and wait for 200 response
241
264
  elsif connect_proxy? and @state == :response_header
242
265
  @state = :connect_http_proxy
243
266
  send_request_header
@@ -308,7 +331,7 @@ module EventMachine
308
331
  def normalize_body
309
332
  @normalized_body ||= begin
310
333
  if @options[:body].is_a? Hash
311
- @options[:body].to_params
334
+ form_encode_body(@options[:body])
312
335
  else
313
336
  @options[:body]
314
337
  end
@@ -845,4 +868,3 @@ module EventMachine
845
868
  end
846
869
 
847
870
  end
848
-
@@ -1,122 +1,122 @@
1
- require 'zlib'
2
- require 'stringio'
3
-
4
- ##
5
- # Provides a unified callback interface to decompression libraries.
6
- module EventMachine::HttpDecoders
7
-
8
- class DecoderError < StandardError
9
- end
10
-
11
- class << self
12
- def accepted_encodings
13
- DECODERS.inject([]) { |r,d| r + d.encoding_names }
14
- end
15
-
16
- def decoder_for_encoding(encoding)
17
- DECODERS.each { |d|
18
- return d if d.encoding_names.include? encoding
19
- }
20
- nil
21
- end
22
- end
23
-
24
- class Base
25
- def self.encoding_names
26
- name = to_s.split('::').last.downcase
27
- [name]
28
- end
29
-
30
- ##
31
- # chunk_callback:: [Block] To handle a decompressed chunk
32
- def initialize(&chunk_callback)
33
- @chunk_callback = chunk_callback
34
- end
35
-
36
- def <<(compressed)
37
- return unless compressed && compressed.size > 0
38
-
39
- decompressed = decompress(compressed)
40
- receive_decompressed decompressed
41
- end
42
-
43
- def finalize!
44
- decompressed = finalize
45
- receive_decompressed decompressed
46
- end
47
-
48
- private
49
-
50
- def receive_decompressed(decompressed)
51
- if decompressed && decompressed.size > 0
52
- @chunk_callback.call(decompressed)
53
- end
54
- end
55
-
56
- protected
57
-
58
- ##
59
- # Must return a part of decompressed
60
- def decompress(compressed)
61
- nil
62
- end
63
-
64
- ##
65
- # May return last part
66
- def finalize
67
- nil
68
- end
69
- end
70
-
71
- class Deflate < Base
72
- def decompress(compressed)
73
- begin
74
- @zstream ||= Zlib::Inflate.new(nil)
75
- @zstream.inflate(compressed)
76
- rescue Zlib::Error
77
- raise DecoderError
78
- end
79
- end
80
-
81
- def finalize
82
- return nil unless @zstream
83
-
84
- begin
85
- r = @zstream.inflate(nil)
86
- @zstream.close
87
- r
88
- rescue Zlib::Error
89
- raise DecoderError
90
- end
91
- end
92
- end
93
-
94
- ##
95
- # Oneshot decompressor, due to lack of a streaming Gzip reader
96
- # implementation. We may steal code from Zliby to improve this.
97
- #
98
- # For now, do not put `gzip' or `compressed' in your accept-encoding
99
- # header if you expect much data through the :on_response interface.
100
- class GZip < Base
101
- def self.encoding_names
102
- %w(gzip compressed)
103
- end
104
-
105
- def decompress(compressed)
106
- @buf ||= ''
107
- @buf += compressed
108
- nil
109
- end
110
-
111
- def finalize
112
- begin
113
- Zlib::GzipReader.new(StringIO.new(@buf.to_s)).read
114
- rescue Zlib::Error
115
- raise DecoderError
116
- end
117
- end
118
- end
119
-
120
- DECODERS = [Deflate, GZip]
121
-
122
- end
1
+ require 'zlib'
2
+ require 'stringio'
3
+
4
+ ##
5
+ # Provides a unified callback interface to decompression libraries.
6
+ module EventMachine::HttpDecoders
7
+
8
+ class DecoderError < StandardError
9
+ end
10
+
11
+ class << self
12
+ def accepted_encodings
13
+ DECODERS.inject([]) { |r,d| r + d.encoding_names }
14
+ end
15
+
16
+ def decoder_for_encoding(encoding)
17
+ DECODERS.each { |d|
18
+ return d if d.encoding_names.include? encoding
19
+ }
20
+ nil
21
+ end
22
+ end
23
+
24
+ class Base
25
+ def self.encoding_names
26
+ name = to_s.split('::').last.downcase
27
+ [name]
28
+ end
29
+
30
+ ##
31
+ # chunk_callback:: [Block] To handle a decompressed chunk
32
+ def initialize(&chunk_callback)
33
+ @chunk_callback = chunk_callback
34
+ end
35
+
36
+ def <<(compressed)
37
+ return unless compressed && compressed.size > 0
38
+
39
+ decompressed = decompress(compressed)
40
+ receive_decompressed decompressed
41
+ end
42
+
43
+ def finalize!
44
+ decompressed = finalize
45
+ receive_decompressed decompressed
46
+ end
47
+
48
+ private
49
+
50
+ def receive_decompressed(decompressed)
51
+ if decompressed && decompressed.size > 0
52
+ @chunk_callback.call(decompressed)
53
+ end
54
+ end
55
+
56
+ protected
57
+
58
+ ##
59
+ # Must return a part of decompressed
60
+ def decompress(compressed)
61
+ nil
62
+ end
63
+
64
+ ##
65
+ # May return last part
66
+ def finalize
67
+ nil
68
+ end
69
+ end
70
+
71
+ class Deflate < Base
72
+ def decompress(compressed)
73
+ begin
74
+ @zstream ||= Zlib::Inflate.new(nil)
75
+ @zstream.inflate(compressed)
76
+ rescue Zlib::Error
77
+ raise DecoderError
78
+ end
79
+ end
80
+
81
+ def finalize
82
+ return nil unless @zstream
83
+
84
+ begin
85
+ r = @zstream.inflate(nil)
86
+ @zstream.close
87
+ r
88
+ rescue Zlib::Error
89
+ raise DecoderError
90
+ end
91
+ end
92
+ end
93
+
94
+ ##
95
+ # Oneshot decompressor, due to lack of a streaming Gzip reader
96
+ # implementation. We may steal code from Zliby to improve this.
97
+ #
98
+ # For now, do not put `gzip' or `compressed' in your accept-encoding
99
+ # header if you expect much data through the :on_response interface.
100
+ class GZip < Base
101
+ def self.encoding_names
102
+ %w(gzip compressed)
103
+ end
104
+
105
+ def decompress(compressed)
106
+ @buf ||= ''
107
+ @buf += compressed
108
+ nil
109
+ end
110
+
111
+ def finalize
112
+ begin
113
+ Zlib::GzipReader.new(StringIO.new(@buf.to_s)).read
114
+ rescue Zlib::Error
115
+ raise DecoderError
116
+ end
117
+ end
118
+ end
119
+
120
+ DECODERS = [Deflate, GZip]
121
+
122
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec/helper'
2
+
3
+ describe EventMachine::HttpEncoding do
4
+ include EventMachine::HttpEncoding
5
+
6
+ it "should transform a basic hash into HTTP POST Params" do
7
+ form_encode_body({:a => "alpha", :b => "beta"}).should == "a=alpha&b=beta"
8
+ end
9
+
10
+ it "should transform a more complex hash into HTTP POST Params" do
11
+ form_encode_body({:a => "a", :b => ["c", "d", "e"]}).should == "a=a&b[0]=c&b[1]=d&b[2]=e"
12
+ end
13
+
14
+ it "should transform a very complex hash into HTTP POST Params" do
15
+ params = form_encode_body({:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]})
16
+ params.should == "a=a&b[0][c]=c&b[0][d]=d&b[1][e]=e&b[1][f]=f"
17
+ end
18
+
19
+ it "should escape values" do
20
+ params = form_encode_body({:stuff => 'string&string'})
21
+ params.should == "stuff=string%26string"
22
+ end
23
+
24
+ it "should escape keys" do
25
+ params = form_encode_body({'bad&str'=> {'key&key' => [:a, :b]}})
26
+ params.should == 'bad%26str[key%26key][0]=a&bad%26str[key%26key][1]=b'
27
+ end
28
+
29
+ it "should escape keys and values" do
30
+ params = form_encode_body({'bad&str'=> {'key&key' => ['bad+&stuff', '[test]']}})
31
+ params.should == "bad%26str[key%26key][0]=bad%2B%26stuff&bad%26str[key%26key][1]=%5Btest%5D"
32
+ end
33
+
34
+ end
data/spec/request_spec.rb CHANGED
@@ -247,6 +247,19 @@ describe EventMachine::HttpRequest do
247
247
  }
248
248
  end
249
249
 
250
+ it "should escape body on POST" do
251
+ EventMachine.run {
252
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post :body => {:stuff => 'string&string'}
253
+
254
+ http.errback { failed }
255
+ http.callback {
256
+ http.response_header.status.should == 200
257
+ http.response.should == "stuff=string%26string"
258
+ EventMachine.stop
259
+ }
260
+ }
261
+ end
262
+
250
263
  it "should perform successfull POST with Ruby Hash/Array as params" do
251
264
  EventMachine.run {
252
265
  http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post :body => {"key1" => 1, "key2" => [2,3]}
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 13
9
- version: 0.2.13
8
+ - 14
9
+ version: 0.2.14
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ilya Grigorik
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-09-25 00:00:00 -04:00
17
+ date: 2010-10-06 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -83,14 +83,13 @@ files:
83
83
  - lib/em-http.rb
84
84
  - lib/em-http/client.rb
85
85
  - lib/em-http/core_ext/bytesize.rb
86
- - lib/em-http/core_ext/hash.rb
87
86
  - lib/em-http/decoders.rb
88
87
  - lib/em-http/http_options.rb
89
88
  - lib/em-http/mock.rb
90
89
  - lib/em-http/multi.rb
91
90
  - lib/em-http/request.rb
91
+ - spec/encoding_spec.rb
92
92
  - spec/fixtures/google.ca
93
- - spec/hash_spec.rb
94
93
  - spec/helper.rb
95
94
  - spec/mock_spec.rb
96
95
  - spec/multi_spec.rb
@@ -133,7 +132,7 @@ signing_key:
133
132
  specification_version: 3
134
133
  summary: EventMachine based, async HTTP Request interface
135
134
  test_files:
136
- - spec/hash_spec.rb
135
+ - spec/encoding_spec.rb
137
136
  - spec/helper.rb
138
137
  - spec/mock_spec.rb
139
138
  - spec/multi_spec.rb
@@ -1,53 +0,0 @@
1
- class Hash
2
- # Stolen partially from Merb : http://noobkit.com/show/ruby/gems/development/merb/hash/to_params.html
3
- # Convert this hash to a query string:
4
- #
5
- # { :name => "Bob",
6
- # :address => {
7
- # :street => '111 Ruby Ave.',
8
- # :city => 'Ruby Central',
9
- # :phones => ['111-111-1111', '222-222-2222']
10
- # }
11
- # }.to_params
12
- # #=> "name=Bob&address[city]=Ruby Central&address[phones]=111-111-1111222-222-2222&address[street]=111 Ruby Ave."
13
- #
14
- def to_params
15
- params = ''
16
- stack = []
17
-
18
- each do |k, v|
19
- if v.is_a?(Hash)
20
- stack << [k,v]
21
- elsif v.is_a?(Array)
22
- stack << [k,Hash.from_array(v)]
23
- else
24
- params << "#{k}=#{v}&"
25
- end
26
- end
27
-
28
- stack.each do |parent, hash|
29
- hash.each do |k, v|
30
- if v.is_a?(Hash)
31
- stack << ["#{parent}[#{k}]", v]
32
- else
33
- params << "#{parent}[#{k}]=#{v}&"
34
- end
35
- end
36
- end
37
-
38
- params.chop! # trailing &
39
- params
40
- end
41
-
42
- ##
43
- # Builds a hash from an array with keys as array indices.
44
- def self.from_array(array = [])
45
- h = Hash.new
46
- array.size.times do |t|
47
- h[t] = array[t]
48
- end
49
- h
50
- end
51
-
52
- end
53
-
data/spec/hash_spec.rb DELETED
@@ -1,24 +0,0 @@
1
- require 'spec/helper'
2
-
3
- describe Hash do
4
-
5
- describe ".to_params" do
6
- it "should transform a basic hash into HTTP POST Params" do
7
- {:a => "alpha", :b => "beta"}.to_params.split("&").should include "a=alpha"
8
- {:a => "alpha", :b => "beta"}.to_params.split("&").should include "b=beta"
9
- end
10
-
11
- it "should transform a more complex hash into HTTP POST Params" do
12
- {:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "a=a"
13
- {:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[0]=c"
14
- {:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[1]=d"
15
- {:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[2]=e"
16
- end
17
-
18
- it "should transform a very complex hash into HTTP POST Params" do
19
- params = {:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]}.to_params.split("&")
20
- params.should include "a=a"
21
- params.should include "b[0][d]=d"
22
- end
23
- end
24
- end