twitter-stream 0.1.4 → 0.1.6
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 twitter-stream might be problematic. Click here for more details.
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/lib/twitter/json_stream.rb +50 -5
- data/spec/spec_helper.rb +11 -0
- data/spec/twitter/json_stream.rb +59 -86
- data/twitter-stream.gemspec +7 -3
- metadata +28 -13
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.6
|
data/lib/twitter/json_stream.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'eventmachine'
|
2
2
|
require 'em/buftok'
|
3
|
+
require 'uri'
|
3
4
|
|
4
5
|
module Twitter
|
5
6
|
class JSONStream < EventMachine::Connection
|
6
|
-
MAX_LINE_LENGTH =
|
7
|
+
MAX_LINE_LENGTH = 1024*1024
|
7
8
|
|
8
9
|
# network failure reconnections
|
9
10
|
NF_RECONNECT_START = 0.25
|
@@ -25,8 +26,11 @@ module Twitter
|
|
25
26
|
:path => '/1/statuses/filter.json',
|
26
27
|
:host => 'stream.twitter.com',
|
27
28
|
:port => 80,
|
29
|
+
:ssl => false,
|
28
30
|
:auth => 'test:test',
|
29
31
|
:user_agent => 'TwitterStream',
|
32
|
+
:timeout => 0,
|
33
|
+
:proxy => ENV['HTTP_PROXY']
|
30
34
|
}
|
31
35
|
|
32
36
|
attr_accessor :code
|
@@ -34,10 +38,24 @@ module Twitter
|
|
34
38
|
attr_accessor :nf_last_reconnect
|
35
39
|
attr_accessor :af_last_reconnect
|
36
40
|
attr_accessor :reconnect_retries
|
41
|
+
attr_accessor :proxy
|
37
42
|
|
38
43
|
def self.connect options = {}
|
44
|
+
options[:port] = 443 if options[:ssl] && !options.has_key?(:port)
|
39
45
|
options = DEFAULT_OPTIONS.merge(options)
|
40
|
-
|
46
|
+
|
47
|
+
host = options[:host]
|
48
|
+
port = options[:port]
|
49
|
+
|
50
|
+
if options[:proxy]
|
51
|
+
proxy_uri = URI.parse(options[:proxy])
|
52
|
+
host = proxy_uri.host
|
53
|
+
port = proxy_uri.port
|
54
|
+
end
|
55
|
+
|
56
|
+
connection = EventMachine.connect host, port, self, options
|
57
|
+
connection.start_tls if options[:ssl]
|
58
|
+
connection
|
41
59
|
end
|
42
60
|
|
43
61
|
def initialize options = {}
|
@@ -46,6 +64,8 @@ module Twitter
|
|
46
64
|
@nf_last_reconnect = nil
|
47
65
|
@af_last_reconnect = nil
|
48
66
|
@reconnect_retries = 0
|
67
|
+
@immediate_reconnect = false
|
68
|
+
@proxy = URI.parse(options[:proxy]) if options[:proxy]
|
49
69
|
end
|
50
70
|
|
51
71
|
def each_item &block
|
@@ -68,6 +88,12 @@ module Twitter
|
|
68
88
|
@gracefully_closed = true
|
69
89
|
close_connection
|
70
90
|
end
|
91
|
+
|
92
|
+
def immediate_reconnect
|
93
|
+
@immediate_reconnect = true
|
94
|
+
@gracefully_closed = false
|
95
|
+
close_connection
|
96
|
+
end
|
71
97
|
|
72
98
|
def unbind
|
73
99
|
receive_line(@buffer.flush) unless @buffer.empty?
|
@@ -80,7 +106,7 @@ module Twitter
|
|
80
106
|
receive_line(line)
|
81
107
|
end
|
82
108
|
rescue Exception => e
|
83
|
-
receive_error(e.message)
|
109
|
+
receive_error("#{e.class}: " + [e.message, e.backtrace].flatten.join("\n\t"))
|
84
110
|
close_connection
|
85
111
|
return
|
86
112
|
end
|
@@ -104,12 +130,22 @@ module Twitter
|
|
104
130
|
|
105
131
|
def reconnect_after timeout
|
106
132
|
@reconnect_callback.call(timeout, @reconnect_retries) if @reconnect_callback
|
107
|
-
|
133
|
+
|
134
|
+
if timeout == 0
|
108
135
|
reconnect @options[:host], @options[:port]
|
136
|
+
else
|
137
|
+
EventMachine.add_timer(timeout) do
|
138
|
+
reconnect @options[:host], @options[:port]
|
139
|
+
end
|
109
140
|
end
|
110
141
|
end
|
111
142
|
|
112
143
|
def reconnect_timeout
|
144
|
+
if @immediate_reconnect
|
145
|
+
@immediate_reconnect = false
|
146
|
+
return 0
|
147
|
+
end
|
148
|
+
|
113
149
|
if (@code == 0) # network failure
|
114
150
|
if @nf_last_reconnect
|
115
151
|
@nf_last_reconnect += NF_RECONNECT_ADD
|
@@ -128,6 +164,7 @@ module Twitter
|
|
128
164
|
end
|
129
165
|
|
130
166
|
def reset_state
|
167
|
+
set_comm_inactivity_timeout @options[:timeout] if @options[:timeout] > 0
|
131
168
|
@code = 0
|
132
169
|
@headers = []
|
133
170
|
@state = :init
|
@@ -136,10 +173,18 @@ module Twitter
|
|
136
173
|
|
137
174
|
def send_request
|
138
175
|
data = []
|
139
|
-
|
176
|
+
request_uri = @options[:path]
|
177
|
+
if @proxy
|
178
|
+
# proxies need the request to be for the full url
|
179
|
+
request_uri = "http#{'s' if @options[:ssl]}://#{@options[:host]}:#{@options[:port]}#{request_uri}"
|
180
|
+
end
|
181
|
+
data << "#{@options[:method]} #{request_uri} HTTP/1.1"
|
140
182
|
data << "Host: #{@options[:host]}"
|
141
183
|
data << "User-agent: #{@options[:user_agent]}" if @options[:user_agent]
|
142
184
|
data << "Authorization: Basic " + [@options[:auth]].pack('m').delete("\r\n")
|
185
|
+
if @proxy && @proxy.user
|
186
|
+
data << "Proxy-Authorization: Basic " + ["#{@proxy.user}:#{@proxy.password}"].pack('m').delete("\r\n")
|
187
|
+
end
|
143
188
|
if @options[:method] == 'POST'
|
144
189
|
data << "Content-type: #{@options[:content_type]}"
|
145
190
|
data << "Content-length: #{@options[:content].length}"
|
data/spec/spec_helper.rb
CHANGED
@@ -12,4 +12,15 @@ end
|
|
12
12
|
|
13
13
|
def read_fixture(path)
|
14
14
|
File.read(fixture_path(path))
|
15
|
+
end
|
16
|
+
|
17
|
+
def connect_stream(opts={}, &blk)
|
18
|
+
EM.run {
|
19
|
+
opts.merge!(:host => Host, :port => Port)
|
20
|
+
stop_in = opts.delete(:stop_in) || 0.5
|
21
|
+
EM.start_server Host, Port, JSONServer
|
22
|
+
@stream = JSONStream.connect(opts)
|
23
|
+
blk.call if blk
|
24
|
+
EM.add_timer(stop_in){ EM.stop }
|
25
|
+
}
|
15
26
|
end
|
data/spec/twitter/json_stream.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
$:.unshift "."
|
1
2
|
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
3
|
require 'twitter/json_stream'
|
3
4
|
|
@@ -23,9 +24,18 @@ describe JSONStream do
|
|
23
24
|
stream = JSONStream.connect {}
|
24
25
|
end
|
25
26
|
|
27
|
+
it "should connect to the proxy if provided" do
|
28
|
+
EM.should_receive(:connect).with do |host, port, handler, opts|
|
29
|
+
host.should == 'my-proxy'
|
30
|
+
port.should == 8080
|
31
|
+
opts[:host].should == 'stream.twitter.com'
|
32
|
+
opts[:port].should == 80
|
33
|
+
opts[:proxy].should == 'http://my-proxy:8080'
|
34
|
+
end
|
35
|
+
stream = JSONStream.connect(:proxy => "http://my-proxy:8080") {}
|
36
|
+
end
|
26
37
|
end
|
27
38
|
|
28
|
-
|
29
39
|
Host = "127.0.0.1"
|
30
40
|
Port = 9550
|
31
41
|
|
@@ -41,6 +51,7 @@ describe JSONStream do
|
|
41
51
|
end
|
42
52
|
|
43
53
|
context "on valid stream" do
|
54
|
+
attr_reader :stream
|
44
55
|
before :each do
|
45
56
|
$data_to_send = read_fixture('twitter/basic_http.txt')
|
46
57
|
$recieved_data = ''
|
@@ -48,142 +59,104 @@ describe JSONStream do
|
|
48
59
|
end
|
49
60
|
|
50
61
|
it "should parse headers" do
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
EM.add_timer(0.5){ EM.stop }
|
55
|
-
}
|
56
|
-
@stream.code.should == 200
|
57
|
-
@stream.headers[0].downcase.should include 'content-type'
|
62
|
+
connect_stream
|
63
|
+
stream.code.should == 200
|
64
|
+
stream.headers[0].downcase.should include 'content-type'
|
58
65
|
end
|
59
66
|
|
60
67
|
it "should parse headers even after connection close" do
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
@stream = JSONStream.connect :host => Host, :port => Port
|
65
|
-
EM.add_timer(0.5){ EM.stop }
|
66
|
-
}
|
67
|
-
@stream.code.should == 200
|
68
|
-
@stream.headers[0].downcase.should include 'content-type'
|
68
|
+
connect_stream
|
69
|
+
stream.code.should == 200
|
70
|
+
stream.headers[0].downcase.should include 'content-type'
|
69
71
|
end
|
70
72
|
|
71
73
|
it "should extract records" do
|
72
|
-
|
73
|
-
EM.start_server Host, Port, JSONServer
|
74
|
-
@stream = JSONStream.connect :host => Host, :port => Port, :user_agent => 'TEST_USER_AGENT'
|
75
|
-
EM.add_timer(0.5){ EM.stop }
|
76
|
-
}
|
74
|
+
connect_stream :user_agent => 'TEST_USER_AGENT'
|
77
75
|
$recieved_data.upcase.should include('USER-AGENT: TEST_USER_AGENT')
|
78
76
|
end
|
79
77
|
|
80
78
|
it "should send correct user agent" do
|
81
|
-
|
82
|
-
$close_connection = true
|
83
|
-
EM.start_server Host, Port, JSONServer
|
84
|
-
@stream = JSONStream.connect :host => Host, :port => Port
|
85
|
-
EM.add_timer(0.5){ EM.stop }
|
86
|
-
}
|
79
|
+
connect_stream
|
87
80
|
end
|
88
81
|
end
|
89
82
|
|
90
83
|
context "on network failure" do
|
84
|
+
attr_reader :stream
|
91
85
|
before :each do
|
92
86
|
$data_to_send = ''
|
93
87
|
$close_connection = true
|
94
88
|
end
|
95
89
|
|
90
|
+
it "should timeout on inactivity" do
|
91
|
+
connect_stream :stop_in => 1.5 do
|
92
|
+
stream.should_receive(:reconnect)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
96
|
it "should reconnect on network failure" do
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
@stream.should_receive(:reconnect)
|
101
|
-
EM.add_timer(0.5){ EM.stop }
|
102
|
-
}
|
97
|
+
connect_stream do
|
98
|
+
stream.should_receive(:reconnect)
|
99
|
+
end
|
103
100
|
end
|
104
101
|
|
105
102
|
it "should reconnect with 0.25 at base" do
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
@stream.should_receive(:reconnect_after).with(0.25)
|
110
|
-
EM.add_timer(0.5){ EM.stop }
|
111
|
-
}
|
103
|
+
connect_stream do
|
104
|
+
stream.should_receive(:reconnect_after).with(0.25)
|
105
|
+
end
|
112
106
|
end
|
113
107
|
|
114
108
|
it "should reconnect with linear timeout" do
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
@stream.should_receive(:reconnect_after).with(1.25)
|
120
|
-
EM.add_timer(0.5){ EM.stop }
|
121
|
-
}
|
109
|
+
connect_stream do
|
110
|
+
stream.nf_last_reconnect = 1
|
111
|
+
stream.should_receive(:reconnect_after).with(1.25)
|
112
|
+
end
|
122
113
|
end
|
123
114
|
|
124
115
|
it "should stop reconnecting after 100 times" do
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
@stream.should_not_receive(:reconnect_after)
|
130
|
-
EM.add_timer(0.5){ EM.stop }
|
131
|
-
}
|
116
|
+
connect_stream do
|
117
|
+
stream.reconnect_retries = 100
|
118
|
+
stream.should_not_receive(:reconnect_after)
|
119
|
+
end
|
132
120
|
end
|
133
121
|
|
134
122
|
it "should notify after reconnect limit is reached" do
|
135
123
|
timeout, retries = nil, nil
|
136
|
-
|
137
|
-
|
138
|
-
@stream = JSONStream.connect :host => Host, :port => Port
|
139
|
-
@stream.on_max_reconnects do |t, r|
|
124
|
+
connect_stream do
|
125
|
+
stream.on_max_reconnects do |t, r|
|
140
126
|
timeout, retries = t, r
|
141
127
|
end
|
142
|
-
|
143
|
-
|
144
|
-
}
|
128
|
+
stream.reconnect_retries = 100
|
129
|
+
end
|
145
130
|
timeout.should == 0.25
|
146
131
|
retries.should == 101
|
147
132
|
end
|
148
133
|
end
|
149
134
|
|
150
135
|
context "on application failure" do
|
136
|
+
attr_reader :stream
|
151
137
|
before :each do
|
152
138
|
$data_to_send = 'HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm="Firehose"\r\n\r\n1'
|
153
139
|
$close_connection = true
|
154
140
|
end
|
155
141
|
|
156
142
|
it "should reconnect on application failure 10 at base" do
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
@stream.should_receive(:reconnect_after).with(10)
|
161
|
-
EM.add_timer(0.5){ EM.stop }
|
162
|
-
}
|
143
|
+
connect_stream do
|
144
|
+
stream.should_receive(:reconnect_after).with(10)
|
145
|
+
end
|
163
146
|
end
|
164
147
|
|
165
148
|
it "should reconnect with exponential timeout" do
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
@stream.should_receive(:reconnect_after).with(320)
|
171
|
-
EM.add_timer(0.5){ EM.stop }
|
172
|
-
}
|
149
|
+
connect_stream do
|
150
|
+
stream.af_last_reconnect = 160
|
151
|
+
stream.should_receive(:reconnect_after).with(320)
|
152
|
+
end
|
173
153
|
end
|
174
154
|
|
175
155
|
it "should not try to reconnect after limit is reached" do
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
@stream.should_not_receive(:reconnect_after)
|
181
|
-
EM.add_timer(0.5){ EM.stop }
|
182
|
-
}
|
156
|
+
connect_stream do
|
157
|
+
stream.af_last_reconnect = 320
|
158
|
+
stream.should_not_receive(:reconnect_after)
|
159
|
+
end
|
183
160
|
end
|
184
|
-
end
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
end
|
189
|
-
|
161
|
+
end
|
162
|
+
end
|
data/twitter-stream.gemspec
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
1
4
|
# -*- encoding: utf-8 -*-
|
2
5
|
|
3
6
|
Gem::Specification.new do |s|
|
4
7
|
s.name = %q{twitter-stream}
|
5
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.6"
|
6
9
|
|
7
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
11
|
s.authors = ["Vladimir Kolesnikov"]
|
9
|
-
s.date = %q{
|
12
|
+
s.date = %q{2010-05-19}
|
10
13
|
s.description = %q{Simple Ruby client library for twitter streaming API. Uses EventMachine for connection handling. Adheres to twitter's reconnection guidline. JSON format only.}
|
11
14
|
s.email = %q{voloko@gmail.com}
|
12
15
|
s.extra_rdoc_files = [
|
@@ -27,7 +30,7 @@ Gem::Specification.new do |s|
|
|
27
30
|
s.homepage = %q{http://github.com/voloko/twitter-stream}
|
28
31
|
s.rdoc_options = ["--charset=UTF-8"]
|
29
32
|
s.require_paths = ["lib"]
|
30
|
-
s.rubygems_version = %q{1.3.
|
33
|
+
s.rubygems_version = %q{1.3.6}
|
31
34
|
s.summary = %q{Twitter realtime API client}
|
32
35
|
s.test_files = [
|
33
36
|
"spec/spec_helper.rb",
|
@@ -51,3 +54,4 @@ Gem::Specification.new do |s|
|
|
51
54
|
s.add_dependency(%q<rspec>, [">= 1.2.8"])
|
52
55
|
end
|
53
56
|
end
|
57
|
+
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twitter-stream
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 6
|
9
|
+
version: 0.1.6
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Vladimir Kolesnikov
|
@@ -9,29 +14,37 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date:
|
17
|
+
date: 2010-05-19 00:00:00 +04:00
|
13
18
|
default_executable:
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: eventmachine
|
17
|
-
|
18
|
-
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
24
|
requirements:
|
21
25
|
- - ">="
|
22
26
|
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
- 12
|
30
|
+
- 8
|
23
31
|
version: 0.12.8
|
24
|
-
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
25
34
|
- !ruby/object:Gem::Dependency
|
26
35
|
name: rspec
|
27
|
-
|
28
|
-
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
38
|
requirements:
|
31
39
|
- - ">="
|
32
40
|
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 1
|
43
|
+
- 2
|
44
|
+
- 8
|
33
45
|
version: 1.2.8
|
34
|
-
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
35
48
|
description: Simple Ruby client library for twitter streaming API. Uses EventMachine for connection handling. Adheres to twitter's reconnection guidline. JSON format only.
|
36
49
|
email: voloko@gmail.com
|
37
50
|
executables: []
|
@@ -64,18 +77,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
64
77
|
requirements:
|
65
78
|
- - ">="
|
66
79
|
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
67
82
|
version: "0"
|
68
|
-
version:
|
69
83
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
84
|
requirements:
|
71
85
|
- - ">="
|
72
86
|
- !ruby/object:Gem::Version
|
87
|
+
segments:
|
88
|
+
- 0
|
73
89
|
version: "0"
|
74
|
-
version:
|
75
90
|
requirements: []
|
76
91
|
|
77
92
|
rubyforge_project:
|
78
|
-
rubygems_version: 1.3.
|
93
|
+
rubygems_version: 1.3.6
|
79
94
|
signing_key:
|
80
95
|
specification_version: 3
|
81
96
|
summary: Twitter realtime API client
|