em-http-request 0.2.11 → 0.2.12

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ ## 0.2.12 / 2010-09-12
4
+
5
+ - added headers callback (http.headers {|h| p h})
6
+ - added .close method on client obj to terminate session (accepts message)
7
+
8
+ - bugfix: report 0 for response status on 1.9 on timeouts
9
+ - bugfix: handle bad Location host redirects
10
+ - bugfix: reset host override on connect
11
+
12
+ ## 0.2.11 / 2010-08-16
13
+
14
+ - all URIs are now normalized prior to dispatch (and on redirect)
15
+ - default to direct proxy (instead of CONNECT handshake) - better performance
16
+ - specify :proxy => {:tunnel => true} if you need to force CONNECT route
17
+ - MultiRequest accepts block syntax for dispatching parallel requests (see specs)
18
+ - MockHttpRequest accepts block syntax (see Mock wiki page)
19
+
20
+
21
+ - bugfix: nullbyte frame for websockets
22
+ - bugfix: set @uri on DNS resolve failure
23
+ - bugfix: handle bad hosts in absolute redirects
24
+ - bugfix: reset seen content on redirects (doh!)
25
+ - bugfix: invalid multibyte escape in websocket regex (1.9.2+)
26
+ - bugfix: report etag and last_modified headers correctly
27
+
data/README.md CHANGED
@@ -1,157 +1,157 @@
1
- EM-HTTP-Request
2
- ===============
3
-
4
- Asynchronous HTTP client for Ruby, based on EventMachine runtime.
5
-
6
- - Ragel HTTP parser for speed & performance
7
- - Simple interface for single & parallel requests via deferred callbacks
8
- - Automatic gzip & deflate decoding
9
- - Basic-Auth & OAuth support
10
- - Custom timeout support
11
- - Stream response processing
12
- - Proxy support (with SSL Tunneling)
13
- - Auto-follow 3xx redirects with custom max depth
14
- - Bi-directional communication with web-socket services
15
- - [Native mocking support](http://wiki.github.com/igrigorik/em-http-request/mocking-httprequest) and through [Webmock](http://github.com/bblimke/webmock)
16
-
17
- Getting started
18
- ---------------
19
-
20
- gem install em-http-request
21
- irb:0> require 'em-http'
22
-
23
- Or checkout [screencast / demo](http://everburning.com/news/eventmachine-screencast-em-http-request/) of using EM-HTTP-Request.
24
-
25
- Libraries & Applications using em-http
26
- --------------------------------------
27
-
28
- - [chirpstream](http://github.com/joshbuddy/chirpstream) - EM client for Twitters Chirpstream API
29
- - [RDaneel](http://github.com/hasmanydevelopers/RDaneel) - Ruby crawler which respects robots.txt
30
- - [rsolr-async](http://github.com/mwmitchell/rsolr-async) - An asynchronus connection adapter for RSolr
31
- - [PubSubHubbub](http://github.com/igrigorik/PubSubHubbub) - Asynchronous PubSubHubbub ruby client
32
- - and many others.. drop me a link if you want yours included!
33
-
34
- Simple client example
35
- ---------------------
36
-
37
- EventMachine.run {
38
- http = EventMachine::HttpRequest.new('http://127.0.0.1/').get :query => {'keyname' => 'value'}, :timeout => 10
39
-
40
- http.callback {
41
- p http.response_header.status
42
- p http.response_header
43
- p http.response
44
-
45
- EventMachine.stop
46
- }
47
- }
48
-
49
- Multi-request example
50
- ---------------------
51
-
52
- Fire and wait for multiple requests to complete via the MultiRequest interface.
53
-
54
- EventMachine.run {
55
- multi = EventMachine::MultiRequest.new
56
-
57
- # add multiple requests to the multi-handler
58
- multi.add(EventMachine::HttpRequest.new('http://www.google.com/').get)
59
- multi.add(EventMachine::HttpRequest.new('http://www.yahoo.com/').get)
60
-
61
- multi.callback {
62
- p multi.responses[:succeeded]
63
- p multi.responses[:failed]
64
-
65
- EventMachine.stop
66
- }
67
- }
68
-
69
- Basic-Auth example
70
- ------------------
71
-
72
- Full basic author support. For OAuth, check examples/oauth-tweet.rb file.
73
-
74
- EventMachine.run {
75
- http = EventMachine::HttpRequest.new('http://www.website.com/').get :head => {'authorization' => ['user', 'pass']}
76
-
77
- http.errback { failed }
78
- http.callback {
79
- p http.response_header
80
- EventMachine.stop
81
- }
82
- }
83
-
84
-
85
- POSTing data example
86
- --------------------
87
-
88
- EventMachine.run {
89
- http1 = EventMachine::HttpRequest.new('http://www.website.com/').post :body => {"key1" => 1, "key2" => [2,3]}
90
- http2 = EventMachine::HttpRequest.new('http://www.website.com/').post :body => "some data"
91
-
92
- # ...
93
- }
94
-
95
- Streaming body processing
96
- -------------------------
97
-
98
- Allows you to consume an HTTP stream of content in real-time. Each time a new piece of content is pushed
99
- to the client, it is passed to the stream callback for you to operate on.
100
-
101
- EventMachine.run {
102
- http = EventMachine::HttpRequest.new('http://www.website.com/').get
103
- http.stream { |chunk| print chunk }
104
- }
105
-
106
- Streaming files from disk
107
- -------------------------
108
- Allows you to efficiently stream a (large) file from disk via EventMachine's FileStream interface.
109
-
110
- EventMachine.run {
111
- http = EventMachine::HttpRequest.new('http://www.website.com/').post :file => 'largefile.txt'
112
- http.callback { |chunk| puts "Upload finished!" }
113
- }
114
-
115
- Proxy example
116
- -------------
117
-
118
- Full transparent proxy support with support for SSL tunneling.
119
-
120
- EventMachine.run {
121
- http = EventMachine::HttpRequest.new('http://www.website.com/').get :proxy => {
122
- :host => 'www.myproxy.com',
123
- :port => 8080,
124
- :authorization => ['username', 'password'] # authorization is optional
125
- }
126
-
127
- Auto-follow 3xx redirects
128
- -------------------------
129
-
130
- Specify the max depth of redirects to follow, default is 0.
131
-
132
- EventMachine.run {
133
- http = EventMachine::HttpRequest.new('http://www.google.com/').get :redirects => 1
134
- http.callback { p http.last_effective_url }
135
- }
136
-
137
- WebSocket example
138
- -----------------
139
-
140
- [Bi-directional communication with WebSockets](http://www.igvita.com/2009/12/22/ruby-websockets-tcp-for-the-browser/): simply pass in a ws:// resource and the client will negotiate the connection upgrade for you. On successful handshake the callback is invoked, and any incoming messages will be passed to the stream callback. The client can also send data to the server at will by calling the "send" method!
141
-
142
- EventMachine.run {
143
- http = EventMachine::HttpRequest.new("ws://yourservice.com/websocket").get :timeout => 0
144
-
145
- http.errback { puts "oops" }
146
- http.callback {
147
- puts "WebSocket connected!"
148
- http.send("Hello client")
149
- }
150
-
151
- http.stream { |msg|
152
- puts "Recieved: #{msg}"
153
- http.send "Pong: #{msg}"
154
- }
155
-
156
- http.disconnect { puts "oops, dropped connection?" }
157
- }
1
+ EM-HTTP-Request
2
+ ===============
3
+
4
+ Asynchronous HTTP client for Ruby, based on EventMachine runtime.
5
+
6
+ - Ragel HTTP parser for speed & performance
7
+ - Simple interface for single & parallel requests via deferred callbacks
8
+ - Automatic gzip & deflate decoding
9
+ - Basic-Auth & OAuth support
10
+ - Custom timeout support
11
+ - Stream response processing
12
+ - Proxy support (with SSL Tunneling)
13
+ - Auto-follow 3xx redirects with custom max depth
14
+ - Bi-directional communication with web-socket services
15
+ - [Native mocking support](http://wiki.github.com/igrigorik/em-http-request/mocking-httprequest) and through [Webmock](http://github.com/bblimke/webmock)
16
+
17
+ Getting started
18
+ ---------------
19
+
20
+ gem install em-http-request
21
+ irb:0> require 'em-http'
22
+
23
+ Or checkout [screencast / demo](http://everburning.com/news/eventmachine-screencast-em-http-request/) of using EM-HTTP-Request.
24
+
25
+ Libraries & Applications using em-http
26
+ --------------------------------------
27
+
28
+ - [chirpstream](http://github.com/joshbuddy/chirpstream) - EM client for Twitters Chirpstream API
29
+ - [RDaneel](http://github.com/hasmanydevelopers/RDaneel) - Ruby crawler which respects robots.txt
30
+ - [rsolr-async](http://github.com/mwmitchell/rsolr-async) - An asynchronus connection adapter for RSolr
31
+ - [PubSubHubbub](http://github.com/igrigorik/PubSubHubbub) - Asynchronous PubSubHubbub ruby client
32
+ - and many others.. drop me a link if you want yours included!
33
+
34
+ Simple client example
35
+ ---------------------
36
+
37
+ EventMachine.run {
38
+ http = EventMachine::HttpRequest.new('http://127.0.0.1/').get :query => {'keyname' => 'value'}, :timeout => 10
39
+
40
+ http.callback {
41
+ p http.response_header.status
42
+ p http.response_header
43
+ p http.response
44
+
45
+ EventMachine.stop
46
+ }
47
+ }
48
+
49
+ Multi-request example
50
+ ---------------------
51
+
52
+ Fire and wait for multiple requests to complete via the MultiRequest interface.
53
+
54
+ EventMachine.run {
55
+ multi = EventMachine::MultiRequest.new
56
+
57
+ # add multiple requests to the multi-handler
58
+ multi.add(EventMachine::HttpRequest.new('http://www.google.com/').get)
59
+ multi.add(EventMachine::HttpRequest.new('http://www.yahoo.com/').get)
60
+
61
+ multi.callback {
62
+ p multi.responses[:succeeded]
63
+ p multi.responses[:failed]
64
+
65
+ EventMachine.stop
66
+ }
67
+ }
68
+
69
+ Basic-Auth example
70
+ ------------------
71
+
72
+ Full basic author support. For OAuth, check examples/oauth-tweet.rb file.
73
+
74
+ EventMachine.run {
75
+ http = EventMachine::HttpRequest.new('http://www.website.com/').get :head => {'authorization' => ['user', 'pass']}
76
+
77
+ http.errback { failed }
78
+ http.callback {
79
+ p http.response_header
80
+ EventMachine.stop
81
+ }
82
+ }
83
+
84
+
85
+ POSTing data example
86
+ --------------------
87
+
88
+ EventMachine.run {
89
+ http1 = EventMachine::HttpRequest.new('http://www.website.com/').post :body => {"key1" => 1, "key2" => [2,3]}
90
+ http2 = EventMachine::HttpRequest.new('http://www.website.com/').post :body => "some data"
91
+
92
+ # ...
93
+ }
94
+
95
+ Streaming body processing
96
+ -------------------------
97
+
98
+ Allows you to consume an HTTP stream of content in real-time. Each time a new piece of content is pushed
99
+ to the client, it is passed to the stream callback for you to operate on.
100
+
101
+ EventMachine.run {
102
+ http = EventMachine::HttpRequest.new('http://www.website.com/').get
103
+ http.stream { |chunk| print chunk }
104
+ }
105
+
106
+ Streaming files from disk
107
+ -------------------------
108
+ Allows you to efficiently stream a (large) file from disk via EventMachine's FileStream interface.
109
+
110
+ EventMachine.run {
111
+ http = EventMachine::HttpRequest.new('http://www.website.com/').post :file => 'largefile.txt'
112
+ http.callback { |chunk| puts "Upload finished!" }
113
+ }
114
+
115
+ Proxy example
116
+ -------------
117
+
118
+ Full transparent proxy support with support for SSL tunneling.
119
+
120
+ EventMachine.run {
121
+ http = EventMachine::HttpRequest.new('http://www.website.com/').get :proxy => {
122
+ :host => 'www.myproxy.com',
123
+ :port => 8080,
124
+ :authorization => ['username', 'password'] # authorization is optional
125
+ }
126
+
127
+ Auto-follow 3xx redirects
128
+ -------------------------
129
+
130
+ Specify the max depth of redirects to follow, default is 0.
131
+
132
+ EventMachine.run {
133
+ http = EventMachine::HttpRequest.new('http://www.google.com/').get :redirects => 1
134
+ http.callback { p http.last_effective_url }
135
+ }
136
+
137
+ WebSocket example
138
+ -----------------
139
+
140
+ [Bi-directional communication with WebSockets](http://www.igvita.com/2009/12/22/ruby-websockets-tcp-for-the-browser/): simply pass in a ws:// resource and the client will negotiate the connection upgrade for you. On successful handshake the callback is invoked, and any incoming messages will be passed to the stream callback. The client can also send data to the server at will by calling the "send" method!
141
+
142
+ EventMachine.run {
143
+ http = EventMachine::HttpRequest.new("ws://yourservice.com/websocket").get :timeout => 0
144
+
145
+ http.errback { puts "oops" }
146
+ http.callback {
147
+ puts "WebSocket connected!"
148
+ http.send("Hello client")
149
+ }
150
+
151
+ http.stream { |msg|
152
+ puts "Recieved: #{msg}"
153
+ http.send "Pong: #{msg}"
154
+ }
155
+
156
+ http.disconnect { puts "oops, dropped connection?" }
157
+ }
data/Rakefile CHANGED
@@ -1,110 +1,110 @@
1
- require 'rake'
2
- require 'rake/clean'
3
- require 'rake/rdoctask'
4
- require 'rake/gempackagetask'
5
- require 'fileutils'
6
- include FileUtils
7
-
8
- # copied from EventMachine.
9
- MAKE = ENV['MAKE'] || if RUBY_PLATFORM =~ /mswin/ # mingw uses make.
10
- 'nmake'
11
- else
12
- 'make'
13
- end
14
-
15
- # Default Rake task is compile
16
- task :default => :compile
17
-
18
- # RDoc
19
- Rake::RDocTask.new(:rdoc) do |task|
20
- task.rdoc_dir = 'doc'
21
- task.title = 'EventMachine::HttpRequest'
22
- task.options = %w(--title HttpRequest --main README --line-numbers)
23
- task.rdoc_files.include(['lib/**/*.rb'])
24
- task.rdoc_files.include(['README', 'LICENSE'])
25
- end
26
-
27
- # Rebuild parser Ragel
28
- task :ragel do
29
- Dir.chdir "ext/http11_client" do
30
- target = "http11_parser.c"
31
- File.unlink target if File.exist? target
32
- sh "ragel http11_parser.rl | rlgen-cd -G2 -o #{target}"
33
- raise "Failed to build C source" unless File.exist? target
34
- end
35
- end
36
-
37
- require 'spec'
38
- require 'spec/rake/spectask'
39
- Spec::Rake::SpecTask.new(:spec) do |t|
40
- t.spec_opts ||= []
41
- t.spec_opts << "--options" << "spec/spec.opts"
42
- t.spec_files = FileList['spec/**/*_spec.rb']
43
- end
44
-
45
- def make(makedir)
46
- Dir.chdir(makedir) { sh MAKE }
47
- end
48
-
49
- def extconf(dir)
50
- Dir.chdir(dir) { ruby "extconf.rb" }
51
- end
52
-
53
- def setup_extension(dir, extension)
54
- ext = "ext/#{dir}"
55
- ext_so = "#{ext}/#{extension}.#{Config::CONFIG['DLEXT']}"
56
- ext_files = FileList[
57
- "#{ext}/*.c",
58
- "#{ext}/*.h",
59
- "#{ext}/extconf.rb",
60
- "#{ext}/Makefile",
61
- "lib"
62
- ]
63
-
64
- task "lib" do
65
- directory "lib"
66
- end
67
-
68
- desc "Builds just the #{extension} extension"
69
-
70
- mf = (extension + '_makefile').to_sym
71
-
72
- task mf do |t|
73
- extconf "#{ext}"
74
- end
75
-
76
- task extension.to_sym => [mf] do
77
- make "#{ext}"
78
- cp ext_so, "lib"
79
- end
80
- end
81
-
82
- setup_extension("buffer", "em_buffer")
83
- setup_extension("http11_client", "http11_client")
84
-
85
- task :compile => [:em_buffer, :http11_client]
86
-
87
- CLEAN.include ['build/*', '**/*.o', '**/*.so', '**/*.a', '**/*.log', 'pkg']
88
- CLEAN.include ['ext/buffer/Makefile', 'lib/em_buffer.*', 'lib/http11_client.*']
89
-
90
- begin
91
- require 'jeweler'
92
- Jeweler::Tasks.new do |gemspec|
93
- gemspec.name = "em-http-request"
94
- gemspec.summary = "EventMachine based, async HTTP Request interface"
95
- gemspec.description = gemspec.summary
96
- gemspec.email = "ilya@igvita.com"
97
- gemspec.homepage = "http://github.com/igrigorik/em-http-request"
98
- gemspec.authors = ["Ilya Grigorik"]
99
- gemspec.required_ruby_version = ">= 1.8.6"
100
- gemspec.extensions = ["ext/buffer/extconf.rb" , "ext/http11_client/extconf.rb"]
101
- gemspec.add_dependency('eventmachine', '>= 0.12.9')
102
- gemspec.add_dependency('addressable', '>= 2.0.0')
103
- gemspec.rubyforge_project = "em-http-request"
104
- gemspec.files = FileList[`git ls-files`.split]
105
- end
106
-
107
- Jeweler::GemcutterTasks.new
108
- rescue LoadError
109
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
110
- end
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+ require 'fileutils'
6
+ include FileUtils
7
+
8
+ # copied from EventMachine.
9
+ MAKE = ENV['MAKE'] || if RUBY_PLATFORM =~ /mswin/ # mingw uses make.
10
+ 'nmake'
11
+ else
12
+ 'make'
13
+ end
14
+
15
+ # Default Rake task is compile
16
+ task :default => :compile
17
+
18
+ # RDoc
19
+ Rake::RDocTask.new(:rdoc) do |task|
20
+ task.rdoc_dir = 'doc'
21
+ task.title = 'EventMachine::HttpRequest'
22
+ task.options = %w(--title HttpRequest --main README --line-numbers)
23
+ task.rdoc_files.include(['lib/**/*.rb'])
24
+ task.rdoc_files.include(['README', 'LICENSE'])
25
+ end
26
+
27
+ # Rebuild parser Ragel
28
+ task :ragel do
29
+ Dir.chdir "ext/http11_client" do
30
+ target = "http11_parser.c"
31
+ File.unlink target if File.exist? target
32
+ sh "ragel http11_parser.rl | rlgen-cd -G2 -o #{target}"
33
+ raise "Failed to build C source" unless File.exist? target
34
+ end
35
+ end
36
+
37
+ require 'spec'
38
+ require 'spec/rake/spectask'
39
+ Spec::Rake::SpecTask.new(:spec) do |t|
40
+ t.spec_opts ||= []
41
+ t.spec_opts << "--options" << "spec/spec.opts"
42
+ t.spec_files = FileList['spec/**/*_spec.rb']
43
+ end
44
+
45
+ def make(makedir)
46
+ Dir.chdir(makedir) { sh MAKE }
47
+ end
48
+
49
+ def extconf(dir)
50
+ Dir.chdir(dir) { ruby "extconf.rb" }
51
+ end
52
+
53
+ def setup_extension(dir, extension)
54
+ ext = "ext/#{dir}"
55
+ ext_so = "#{ext}/#{extension}.#{Config::MAKEFILE_CONFIG['DLEXT']}"
56
+ ext_files = FileList[
57
+ "#{ext}/*.c",
58
+ "#{ext}/*.h",
59
+ "#{ext}/extconf.rb",
60
+ "#{ext}/Makefile",
61
+ "lib"
62
+ ]
63
+
64
+ task "lib" do
65
+ directory "lib"
66
+ end
67
+
68
+ desc "Builds just the #{extension} extension"
69
+
70
+ mf = (extension + '_makefile').to_sym
71
+
72
+ task mf do |t|
73
+ extconf "#{ext}"
74
+ end
75
+
76
+ task extension.to_sym => [mf] do
77
+ make "#{ext}"
78
+ cp ext_so, "lib"
79
+ end
80
+ end
81
+
82
+ setup_extension("buffer", "em_buffer")
83
+ setup_extension("http11_client", "http11_client")
84
+
85
+ task :compile => [:em_buffer, :http11_client]
86
+
87
+ CLEAN.include ['build/*', '**/*.o', '**/*.so', '**/*.a', '**/*.log', 'pkg']
88
+ CLEAN.include ['ext/buffer/Makefile', 'lib/em_buffer.*', 'lib/http11_client.*']
89
+
90
+ begin
91
+ require 'jeweler'
92
+ Jeweler::Tasks.new do |gemspec|
93
+ gemspec.name = "em-http-request"
94
+ gemspec.summary = "EventMachine based, async HTTP Request interface"
95
+ gemspec.description = gemspec.summary
96
+ gemspec.email = "ilya@igvita.com"
97
+ gemspec.homepage = "http://github.com/igrigorik/em-http-request"
98
+ gemspec.authors = ["Ilya Grigorik"]
99
+ gemspec.required_ruby_version = ">= 1.8.6"
100
+ gemspec.extensions = ["ext/buffer/extconf.rb" , "ext/http11_client/extconf.rb"]
101
+ gemspec.add_dependency('eventmachine', '>= 0.12.9')
102
+ gemspec.add_dependency('addressable', '>= 2.0.0')
103
+ gemspec.rubyforge_project = "em-http-request"
104
+ gemspec.files = FileList[`git ls-files`.split]
105
+ end
106
+
107
+ Jeweler::GemcutterTasks.new
108
+ rescue LoadError
109
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
110
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.11
1
+ 0.2.12
@@ -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.11"
8
+ s.version = "0.2.12"
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-08-16}
12
+ s.date = %q{2010-09-12}
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"]
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  ]
20
20
  s.files = [
21
21
  ".gitignore",
22
+ "Changelog.md",
22
23
  "LICENSE",
23
24
  "README.md",
24
25
  "Rakefile",
@@ -62,7 +63,7 @@ Gem::Specification.new do |s|
62
63
  s.require_paths = ["lib"]
63
64
  s.required_ruby_version = Gem::Requirement.new(">= 1.8.6")
64
65
  s.rubyforge_project = %q{em-http-request}
65
- s.rubygems_version = %q{1.3.6}
66
+ s.rubygems_version = %q{1.3.7}
66
67
  s.summary = %q{EventMachine based, async HTTP Request interface}
67
68
  s.test_files = [
68
69
  "spec/hash_spec.rb",
@@ -83,7 +84,7 @@ Gem::Specification.new do |s|
83
84
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
84
85
  s.specification_version = 3
85
86
 
86
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
87
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
87
88
  s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.9"])
88
89
  s.add_runtime_dependency(%q<addressable>, [">= 2.0.0"])
89
90
  else
@@ -32,7 +32,7 @@ module EventMachine
32
32
 
33
33
  # HTTP response status as an integer
34
34
  def status
35
- Integer(http_status) rescue nil
35
+ Integer(http_status) rescue 0
36
36
  end
37
37
 
38
38
  # Length of content as an integer, or nil if chunked/unspecified
@@ -265,6 +265,7 @@ module EventMachine
265
265
  # fail the connection directly
266
266
  dns_error == true ? fail(self) : unbind
267
267
  end
268
+ alias :close :on_error
268
269
 
269
270
  # assign a stream processing block
270
271
  def stream(&blk)
@@ -276,6 +277,11 @@ module EventMachine
276
277
  @disconnect = blk
277
278
  end
278
279
 
280
+ # assign a headers parse callback
281
+ def headers(&blk)
282
+ @headers = blk
283
+ end
284
+
279
285
  # raw data push from the client (WebSocket) should
280
286
  # only be invoked after handshake, otherwise it will
281
287
  # inject data into the header exchange
@@ -403,20 +409,24 @@ module EventMachine
403
409
 
404
410
  def unbind
405
411
  if (@state == :finished) && (@last_effective_url != @uri) && (@redirects < @options[:redirects])
406
- # update uri to redirect location if we're allowed to traverse deeper
407
- @uri = @last_effective_url
412
+ begin
413
+ # update uri to redirect location if we're allowed to traverse deeper
414
+ @uri = @last_effective_url
408
415
 
409
- # keep track of the depth of requests we made in this session
410
- @redirects += 1
416
+ # keep track of the depth of requests we made in this session
417
+ @redirects += 1
411
418
 
412
- # swap current connection and reassign current handler
413
- req = HttpOptions.new(@method, @uri, @options)
414
- reconnect(req.host, req.port)
419
+ # swap current connection and reassign current handler
420
+ req = HttpOptions.new(@method, @uri, @options)
421
+ reconnect(req.host, req.port)
415
422
 
416
- @response_header = HttpResponseHeader.new
417
- @state = :response_header
418
- @response = ''
419
- @data.clear
423
+ @response_header = HttpResponseHeader.new
424
+ @state = :response_header
425
+ @response = ''
426
+ @data.clear
427
+ rescue EventMachine::ConnectionError => e
428
+ on_error(e.message, true)
429
+ end
420
430
  else
421
431
  if @state == :finished || (@state == :body && @bytes_remaining.nil?)
422
432
  succeed(self)
@@ -479,6 +489,10 @@ module EventMachine
479
489
  def parse_response_header
480
490
  return false unless parse_header(@response_header)
481
491
 
492
+ # invoke headers callback after full parse if one
493
+ # is specified by the user
494
+ @headers.call(@response_header) if @headers
495
+
482
496
  unless @response_header.http_status and @response_header.http_reason
483
497
  @state = :invalid
484
498
  on_error "no HTTP response"
@@ -674,3 +688,4 @@ module EventMachine
674
688
  end
675
689
 
676
690
  end
691
+
@@ -1,34 +1,34 @@
1
- class HttpOptions
2
- attr_reader :uri, :method, :host, :port, :options
3
-
4
- def initialize(method, uri, options)
5
- uri.normalize!
6
-
7
- @options = options
8
- @method = method.to_s.upcase
9
- @uri = uri
10
-
11
- if proxy = options[:proxy]
12
- @host = proxy[:host]
13
- @port = proxy[:port]
14
- else
15
- # optional host for cases where you may have
16
- # pre-resolved the host, or you need an override
17
- @host = options[:host] || uri.host
18
- @port = uri.port
19
- end
20
-
21
- @options[:timeout] ||= 10 # default connect & inactivity timeouts
22
- @options[:redirects] ||= 0 # default number of redirects to follow
23
-
24
- # Make sure the ports are set as Addressable::URI doesn't
25
- # set the port if it isn't there
26
- if uri.scheme == "https"
27
- @uri.port ||= 443
28
- @port ||= 443
29
- else
30
- @uri.port ||= 80
31
- @port ||= 80
32
- end
33
- end
1
+ class HttpOptions
2
+ attr_reader :uri, :method, :host, :port, :options
3
+
4
+ def initialize(method, uri, options)
5
+ uri.normalize!
6
+
7
+ @options = options
8
+ @method = method.to_s.upcase
9
+ @uri = uri
10
+
11
+ if proxy = options[:proxy]
12
+ @host = proxy[:host]
13
+ @port = proxy[:port]
14
+ else
15
+ # optional host for cases where you may have
16
+ # pre-resolved the host, or you need an override
17
+ @host = options.delete(:host) || uri.host
18
+ @port = uri.port
19
+ end
20
+
21
+ @options[:timeout] ||= 10 # default connect & inactivity timeouts
22
+ @options[:redirects] ||= 0 # default number of redirects to follow
23
+
24
+ # Make sure the ports are set as Addressable::URI doesn't
25
+ # set the port if it isn't there
26
+ if uri.scheme == "https"
27
+ @uri.port ||= 443
28
+ @port ||= 443
29
+ else
30
+ @uri.port ||= 80
31
+ @port ||= 80
32
+ end
33
+ end
34
34
  end
@@ -68,17 +68,37 @@ describe EventMachine::HttpRequest do
68
68
  }
69
69
  end
70
70
 
71
- it "should accept optional host override" do
72
- EventMachine.run {
73
- http = EventMachine::HttpRequest.new('http://google.com:8080/').get :host => '127.0.0.1'
71
+ context "host override" do
74
72
 
75
- http.errback { failed }
76
- http.callback {
77
- http.response_header.status.should == 200
78
- http.response.should match(/Hello/)
79
- EventMachine.stop
73
+ it "should accept optional host" do
74
+ EventMachine.run {
75
+ http = EventMachine::HttpRequest.new('http://google.com:8080/').get :host => '127.0.0.1'
76
+
77
+ http.errback { failed }
78
+ http.callback {
79
+ http.response_header.status.should == 200
80
+ http.response.should match(/Hello/)
81
+ EventMachine.stop
82
+ }
80
83
  }
81
- }
84
+ end
85
+
86
+ it "should reset host on redirect" do
87
+ EventMachine.run {
88
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect').get :redirects => 1, :host => '127.0.0.1'
89
+
90
+ http.errback { failed }
91
+ http.callback {
92
+ http.response_header.status.should == 200
93
+ http.response_header["CONTENT_ENCODING"].should == "gzip"
94
+ http.response.should == "compressed"
95
+ http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/gzip'
96
+ http.redirects.should == 1
97
+
98
+ EM.stop
99
+ }
100
+ }
101
+ end
82
102
  end
83
103
 
84
104
  it "should perform successfull GET with a URI passed as argument" do
@@ -246,7 +266,7 @@ describe EventMachine::HttpRequest do
246
266
  EventMachine.run {
247
267
 
248
268
  # digg.com uses chunked encoding
249
- http = EventMachine::HttpRequest.new('http://digg.com/').get
269
+ http = EventMachine::HttpRequest.new('http://digg.com/news').get
250
270
 
251
271
  http.errback { failed }
252
272
  http.callback {
@@ -435,12 +455,11 @@ describe EventMachine::HttpRequest do
435
455
  end
436
456
 
437
457
  it "should fail gracefully on an invalid host in Location header" do
438
- pending "validate tld's?"
439
458
  EventMachine.run {
440
459
  http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect/badhost').get :redirects => 1
441
460
  http.callback { failed }
442
461
  http.errback {
443
- http.error.should == 'Location header format error'
462
+ http.error.should == 'unable to resolve server address'
444
463
  EM.stop
445
464
  }
446
465
  }
@@ -464,6 +483,49 @@ describe EventMachine::HttpRequest do
464
483
  }
465
484
  end
466
485
 
486
+ context "optional header callback" do
487
+ it "should optionally pass the response headers" do
488
+ EventMachine.run {
489
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
490
+
491
+ http.errback { failed }
492
+ http.headers { |hash|
493
+ hash.should be_an_kind_of Hash
494
+ hash.should include 'CONNECTION'
495
+ hash.should include 'CONTENT_LENGTH'
496
+ }
497
+
498
+ http.callback {
499
+ http.response_header.status.should == 200
500
+ http.response.should match(/Hello/)
501
+ EventMachine.stop
502
+ }
503
+ }
504
+ end
505
+
506
+ it "should allow to terminate current connection from header callback" do
507
+ EventMachine.run {
508
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
509
+
510
+ http.callback { failed }
511
+ http.headers { |hash|
512
+ hash.should be_an_kind_of Hash
513
+ hash.should include 'CONNECTION'
514
+ hash.should include 'CONTENT_LENGTH'
515
+
516
+ http.close('header callback terminated connection')
517
+ }
518
+
519
+ http.errback { |e|
520
+ http.response_header.status.should == 200
521
+ http.error.should == 'header callback terminated connection'
522
+ http.response.should == ''
523
+ EventMachine.stop
524
+ }
525
+ }
526
+ end
527
+ end
528
+
467
529
  it "should optionally pass the deflate-encoded response body progressively" do
468
530
  EventMachine.run {
469
531
  body = ''
@@ -657,8 +719,8 @@ describe EventMachine::HttpRequest do
657
719
  http = EventMachine::MockHttpRequest.new('http://www.google.ca/').get
658
720
  http.errback { fail }
659
721
  http.callback {
660
- c1 = "PREF=ID=9454187d21c4a6a6:TM=1258403955:LM=1258403955:S=2-mf1n5oV5yAeT9-; expires=Wed, 16-Nov-2011 20:39:15 GMT; path=/; domain=.google.ca"
661
- c2 = "NID=28=lvxxVdiBQkCetu_WFaUxLyB7qPlHXS5OdAGYTqge_laVlCKVN8VYYeVBh4bNZiK_Oan2gm8oP9GA-FrZfMPC3ZMHeNq37MG2JH8AIW9LYucU8brOeuggMEbLNNXuiWg4; expires=Tue, 18-May-2010 20:39:15 GMT; path=/; domain=.google.ca; HttpOnly"
722
+ c1 = "PREF=ID=11955ae9690fd292:TM=1281823106:LM=1281823106:S=wHdloFqGQ_OLSE92; expires=Mon, 13-Aug-2012 21:58:26 GMT; path=/; domain=.google.ca"
723
+ c2 = "NID=37=USTdOsxOSMbLjphkJ3S5Ueua3Yc23COXuK_pbztcHx7JoyhomwQySrvebCf3_u8eyrBiLWssVzaZcEOiKGEJbNdy8lRhnq_mfrdz693LaMjNPh__ccW4sgn1ZO6nQltE; expires=Sun, 13-Feb-2011 21:58:26 GMT; path=/; domain=.google.ca; HttpOnly"
662
724
  http.response_header.cookie.should == [c1, c2]
663
725
 
664
726
  EventMachine.stop
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 11
9
- version: 0.2.11
8
+ - 12
9
+ version: 0.2.12
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ilya Grigorik
@@ -14,13 +14,14 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-16 00:00:00 -04:00
17
+ date: 2010-09-12 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: eventmachine
22
22
  prerelease: false
23
23
  requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
24
25
  requirements:
25
26
  - - ">="
26
27
  - !ruby/object:Gem::Version
@@ -35,6 +36,7 @@ dependencies:
35
36
  name: addressable
36
37
  prerelease: false
37
38
  requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
38
40
  requirements:
39
41
  - - ">="
40
42
  - !ruby/object:Gem::Version
@@ -57,6 +59,7 @@ extra_rdoc_files:
57
59
  - README.md
58
60
  files:
59
61
  - .gitignore
62
+ - Changelog.md
60
63
  - LICENSE
61
64
  - README.md
62
65
  - Rakefile
@@ -104,6 +107,7 @@ rdoc_options:
104
107
  require_paths:
105
108
  - lib
106
109
  required_ruby_version: !ruby/object:Gem::Requirement
110
+ none: false
107
111
  requirements:
108
112
  - - ">="
109
113
  - !ruby/object:Gem::Version
@@ -113,6 +117,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
113
117
  - 6
114
118
  version: 1.8.6
115
119
  required_rubygems_version: !ruby/object:Gem::Requirement
120
+ none: false
116
121
  requirements:
117
122
  - - ">="
118
123
  - !ruby/object:Gem::Version
@@ -122,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
127
  requirements: []
123
128
 
124
129
  rubyforge_project: em-http-request
125
- rubygems_version: 1.3.6
130
+ rubygems_version: 1.3.7
126
131
  signing_key:
127
132
  specification_version: 3
128
133
  summary: EventMachine based, async HTTP Request interface