rbitter 0.1.0 → 0.1.2
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.
- checksums.yaml +4 -4
- data/.gitignore +21 -21
- data/.rspec +2 -2
- data/.travis.yml +15 -15
- data/Gemfile +12 -12
- data/LICENSE.txt +22 -22
- data/Rakefile +8 -8
- data/XMLRPC.md +19 -19
- data/bin/rbitter +20 -20
- data/lib/rbitter/arcserver.rb +130 -126
- data/lib/rbitter/console.rb +93 -93
- data/lib/rbitter/default/config_json.rb +37 -38
- data/lib/rbitter/dlthread.rb +62 -65
- data/lib/rbitter/env.rb +62 -62
- data/lib/rbitter/libtwitter_connection_override.rb +45 -45
- data/lib/rbitter/records.rb +121 -121
- data/lib/rbitter/records_migrate/20150327_add_index.rb +11 -11
- data/lib/rbitter/records_migrate/20150504_add_replyto_column.rb +11 -11
- data/lib/rbitter/streaming.rb +104 -104
- data/lib/rbitter/version.rb +20 -20
- data/lib/rbitter/xmlrpc.rb +3 -3
- data/lib/rbitter/xmlrpcd/base.rb +24 -24
- data/lib/rbitter/xmlrpcd/rpchandles.rb +11 -11
- data/lib/rbitter/xmlrpcd/xmlrpc_auth_server.rb +82 -82
- data/lib/rbitter/xmlrpcd/xmlrpcd.rb +69 -69
- data/lib/rbitter.rb +86 -86
- data/rbitter.gemspec +46 -42
- data/spec/config/default.json +32 -32
- data/spec/rbitter/arcserver_spec.rb +30 -30
- data/spec/rbitter/console_spec.rb +9 -9
- data/spec/rbitter/default/config_json_spec.rb +3 -3
- data/spec/rbitter/dlthread_spec.rb +8 -13
- data/spec/rbitter/env_spec.rb +62 -62
- data/spec/rbitter/libtwitter_connection_override_spec.rb +8 -8
- data/spec/rbitter/records_spec.rb +13 -13
- data/spec/rbitter/streaming_spec.rb +9 -9
- data/spec/rbitter/version_spec.rb +8 -8
- data/spec/rbitter/xmlrpc_spec.rb +8 -8
- data/spec/rbitter/xmlrpcd/base_spec.rb +29 -29
- data/spec/rbitter/xmlrpcd/rpchandles_spec.rb +10 -10
- data/spec/rbitter/xmlrpcd/xmlrpc_auth_server_spec.rb +8 -8
- data/spec/rbitter/xmlrpcd/xmlrpcd_spec.rb +9 -9
- data/spec/rbitter_spec.rb +42 -42
- data/spec/spec_helper.rb +39 -39
- metadata +29 -56
data/lib/rbitter/console.rb
CHANGED
@@ -1,93 +1,93 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#
|
3
|
-
# Rbitter Archive Access console (irb)
|
4
|
-
|
5
|
-
require "xmlrpc/client"
|
6
|
-
require "rbitter/version"
|
7
|
-
require "ripl"
|
8
|
-
|
9
|
-
module Rbitter
|
10
|
-
class Console
|
11
|
-
def initialize
|
12
|
-
puts "Rbitter console #{Rbitter::VERSION}"
|
13
|
-
help
|
14
|
-
end
|
15
|
-
|
16
|
-
def help
|
17
|
-
puts "Predefined methods:"
|
18
|
-
puts "ar - shortcut to call Rbitter::Record"
|
19
|
-
puts "connect_ar - Prepare Rbitter::Record to be ready"
|
20
|
-
puts "csv_backup - export Rbitter::Record into comma-separated values"
|
21
|
-
puts "help - to show this message again"
|
22
|
-
puts "xmlrpc - send xmlrpc command to destination"
|
23
|
-
puts "xmlrpc_dest - set destination for xmlrpc command"
|
24
|
-
puts "^D, 'exit' to exit from here"
|
25
|
-
end
|
26
|
-
|
27
|
-
def connect_ar
|
28
|
-
ARSupport.connect_database
|
29
|
-
puts "Rbitter::Record is ready."
|
30
|
-
end
|
31
|
-
|
32
|
-
def csv_backup *args
|
33
|
-
if args.length < 0
|
34
|
-
puts "Usage: csv_backup('filename.csv')"
|
35
|
-
puts "Estimated running time depends on system environment"
|
36
|
-
else
|
37
|
-
ARSupport.export_to_csv(args[0])
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def ar
|
42
|
-
Rbitter::Record
|
43
|
-
end
|
44
|
-
|
45
|
-
def exit
|
46
|
-
Kernel.exit(0)
|
47
|
-
end
|
48
|
-
|
49
|
-
def xmlrpc_dest args={}
|
50
|
-
if args.empty?
|
51
|
-
puts "Usage: xmlrpc_dest({ :rpchost => '', :rpcpath => '', :rpcport => 1400,"
|
52
|
-
puts " :xmlrpc_auth_id => '', xmlrpc_auth_password => '' })"
|
53
|
-
end
|
54
|
-
|
55
|
-
@rpchost = args.fetch(:rpchost) { "127.0.0.1" }
|
56
|
-
@rpcpath = args.fetch(:rpcpath) { "/" }
|
57
|
-
@rpcport = args.fetch(:rpcport) { 1400 }
|
58
|
-
|
59
|
-
cl = XMLRPC::Client.new(@rpchost, @rpcpath, @rpcport)
|
60
|
-
@xmlrpc_cookie = "auth_key=" + cl.call('rbitter.auth',
|
61
|
-
args.fetch(:xmlrpc_auth_id) { Rbitter.env['xmlrpc']['auth'][0] },
|
62
|
-
args.fetch(:xmlrpc_auth_password) { Rbitter.env['xmlrpc']['auth'][1] } )
|
63
|
-
|
64
|
-
if @xmlrpc_cookie != "auth_key="
|
65
|
-
puts "Authentication completed"
|
66
|
-
else
|
67
|
-
puts "Authentication failed"
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def xmlrpc *args
|
72
|
-
if args.empty?
|
73
|
-
puts "Usage: xmlrpc(command, [params in Array])"
|
74
|
-
puts "Ex) xmlrpc(\'rbitter.echo\',' [\"Hello World!\"])"
|
75
|
-
puts "Please configure XMLRPC destination with xmlrpc_dest method"
|
76
|
-
return false
|
77
|
-
end
|
78
|
-
|
79
|
-
cl = XMLRPC::Client.new(@rpchost, @rpcpath, @rpcport, @xmlrpc_cookie)
|
80
|
-
|
81
|
-
if args.length <= 1 or args[1].nil?
|
82
|
-
cl.call(args[0])
|
83
|
-
else
|
84
|
-
cl.call(args[0], *args[1])
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def start
|
89
|
-
Ripl.start :binding => binding
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Rbitter Archive Access console (irb)
|
4
|
+
|
5
|
+
require "xmlrpc/client"
|
6
|
+
require "rbitter/version"
|
7
|
+
require "ripl"
|
8
|
+
|
9
|
+
module Rbitter
|
10
|
+
class Console
|
11
|
+
def initialize
|
12
|
+
puts "Rbitter console #{Rbitter::VERSION}"
|
13
|
+
help
|
14
|
+
end
|
15
|
+
|
16
|
+
def help
|
17
|
+
puts "Predefined methods:"
|
18
|
+
puts "ar - shortcut to call Rbitter::Record"
|
19
|
+
puts "connect_ar - Prepare Rbitter::Record to be ready"
|
20
|
+
puts "csv_backup - export Rbitter::Record into comma-separated values"
|
21
|
+
puts "help - to show this message again"
|
22
|
+
puts "xmlrpc - send xmlrpc command to destination"
|
23
|
+
puts "xmlrpc_dest - set destination for xmlrpc command"
|
24
|
+
puts "^D, 'exit' to exit from here"
|
25
|
+
end
|
26
|
+
|
27
|
+
def connect_ar
|
28
|
+
ARSupport.connect_database
|
29
|
+
puts "Rbitter::Record is ready."
|
30
|
+
end
|
31
|
+
|
32
|
+
def csv_backup *args
|
33
|
+
if args.length < 0
|
34
|
+
puts "Usage: csv_backup('filename.csv')"
|
35
|
+
puts "Estimated running time depends on system environment"
|
36
|
+
else
|
37
|
+
ARSupport.export_to_csv(args[0])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def ar
|
42
|
+
Rbitter::Record
|
43
|
+
end
|
44
|
+
|
45
|
+
def exit
|
46
|
+
Kernel.exit(0)
|
47
|
+
end
|
48
|
+
|
49
|
+
def xmlrpc_dest args={}
|
50
|
+
if args.empty?
|
51
|
+
puts "Usage: xmlrpc_dest({ :rpchost => '', :rpcpath => '', :rpcport => 1400,"
|
52
|
+
puts " :xmlrpc_auth_id => '', xmlrpc_auth_password => '' })"
|
53
|
+
end
|
54
|
+
|
55
|
+
@rpchost = args.fetch(:rpchost) { "127.0.0.1" }
|
56
|
+
@rpcpath = args.fetch(:rpcpath) { "/" }
|
57
|
+
@rpcport = args.fetch(:rpcport) { 1400 }
|
58
|
+
|
59
|
+
cl = XMLRPC::Client.new(@rpchost, @rpcpath, @rpcport)
|
60
|
+
@xmlrpc_cookie = "auth_key=" + cl.call('rbitter.auth',
|
61
|
+
args.fetch(:xmlrpc_auth_id) { Rbitter.env['xmlrpc']['auth'][0] },
|
62
|
+
args.fetch(:xmlrpc_auth_password) { Rbitter.env['xmlrpc']['auth'][1] } )
|
63
|
+
|
64
|
+
if @xmlrpc_cookie != "auth_key="
|
65
|
+
puts "Authentication completed"
|
66
|
+
else
|
67
|
+
puts "Authentication failed"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def xmlrpc *args
|
72
|
+
if args.empty?
|
73
|
+
puts "Usage: xmlrpc(command, [params in Array])"
|
74
|
+
puts "Ex) xmlrpc(\'rbitter.echo\',' [\"Hello World!\"])"
|
75
|
+
puts "Please configure XMLRPC destination with xmlrpc_dest method"
|
76
|
+
return false
|
77
|
+
end
|
78
|
+
|
79
|
+
cl = XMLRPC::Client.new(@rpchost, @rpcpath, @rpcport, @xmlrpc_cookie)
|
80
|
+
|
81
|
+
if args.length <= 1 or args[1].nil?
|
82
|
+
cl.call(args[0])
|
83
|
+
else
|
84
|
+
cl.call(args[0], *args[1])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def start
|
89
|
+
Ripl.start :binding => binding
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
@@ -1,38 +1,37 @@
|
|
1
|
-
module Rbitter
|
2
|
-
DEFAULT_CONFIG_JSON = <<-ENDOFJSON
|
3
|
-
{
|
4
|
-
"twitter": {
|
5
|
-
"consumer_key": "",
|
6
|
-
"consumer_secret": "",
|
7
|
-
"access_token": "",
|
8
|
-
"access_token_secret": ""
|
9
|
-
},
|
10
|
-
"activerecord": "sqlite3",
|
11
|
-
"sqlite3": {
|
12
|
-
"dbfile": "rbitter.sqlite"
|
13
|
-
},
|
14
|
-
"mysql2": {
|
15
|
-
"host": "localhost",
|
16
|
-
"port": 3306,
|
17
|
-
"dbname": "archive",
|
18
|
-
"username": "",
|
19
|
-
"password": ""
|
20
|
-
},
|
21
|
-
"media_downloader": {
|
22
|
-
"large_image": true,
|
23
|
-
"
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
"
|
28
|
-
"
|
29
|
-
"
|
30
|
-
|
31
|
-
"
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
1
|
+
module Rbitter
|
2
|
+
DEFAULT_CONFIG_JSON = <<-ENDOFJSON
|
3
|
+
{
|
4
|
+
"twitter": {
|
5
|
+
"consumer_key": "",
|
6
|
+
"consumer_secret": "",
|
7
|
+
"access_token": "",
|
8
|
+
"access_token_secret": ""
|
9
|
+
},
|
10
|
+
"activerecord": "sqlite3",
|
11
|
+
"sqlite3": {
|
12
|
+
"dbfile": "rbitter.sqlite"
|
13
|
+
},
|
14
|
+
"mysql2": {
|
15
|
+
"host": "localhost",
|
16
|
+
"port": 3306,
|
17
|
+
"dbname": "archive",
|
18
|
+
"username": "",
|
19
|
+
"password": ""
|
20
|
+
},
|
21
|
+
"media_downloader": {
|
22
|
+
"large_image": true,
|
23
|
+
"download_dir": "imgs/"
|
24
|
+
},
|
25
|
+
"xmlrpc": {
|
26
|
+
"enable": true,
|
27
|
+
"bind_host": "0.0.0.0",
|
28
|
+
"bind_port": 1400,
|
29
|
+
"auth": {
|
30
|
+
"username": "username",
|
31
|
+
"password": "password"
|
32
|
+
},
|
33
|
+
"handles": ["/path/to/handles"]
|
34
|
+
}
|
35
|
+
}
|
36
|
+
ENDOFJSON
|
37
|
+
end
|
data/lib/rbitter/dlthread.rb
CHANGED
@@ -1,66 +1,63 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require "net/http"
|
4
|
-
require "openssl"
|
5
|
-
|
6
|
-
module Rbitter
|
7
|
-
class DLThread
|
8
|
-
def initialize(dlfolder,
|
9
|
-
@dest = dlfolder
|
10
|
-
if not File.directory?(dlfolder)
|
11
|
-
warn "[dlthread] Given download location is not available for downloading."
|
12
|
-
warn "[dlthread] Fallback to current directory."
|
13
|
-
@dest = "./"
|
14
|
-
end
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "net/http"
|
4
|
+
require "openssl"
|
5
|
+
|
6
|
+
module Rbitter
|
7
|
+
class DLThread
|
8
|
+
def initialize(dlfolder, large_flag)
|
9
|
+
@dest = dlfolder
|
10
|
+
if not File.directory?(dlfolder)
|
11
|
+
warn "[dlthread] Given download location is not available for downloading."
|
12
|
+
warn "[dlthread] Fallback to current directory."
|
13
|
+
@dest = "./"
|
14
|
+
end
|
15
|
+
|
16
|
+
if large_flag.nil?
|
17
|
+
@large_image = false
|
18
|
+
else
|
19
|
+
@large_image = large_flag
|
20
|
+
end
|
21
|
+
|
22
|
+
@pool = Array.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def <<(url_array)
|
26
|
+
download_task = Thread.new {
|
27
|
+
url_array.each { |url|
|
28
|
+
uri = URI.parse(@large_image ? url + ":large" : url)
|
29
|
+
ssl = uri.scheme.downcase == 'https'
|
30
|
+
|
31
|
+
Net::HTTP.start(uri.host, uri.port, :use_ssl => ssl) { |h|
|
32
|
+
req = Net::HTTP::Get.new uri.request_uri
|
33
|
+
h.request(req) { |res|
|
34
|
+
case res
|
35
|
+
when Net::HTTPOK
|
36
|
+
fname = File.basename(url)
|
37
|
+
|
38
|
+
puts "[fetch] remote: #{uri.path} => local: #{fname}"
|
39
|
+
open(File.join(@dest, fname), "wb") { |file|
|
40
|
+
res.read_body { |chunk| file.write(chunk) }
|
41
|
+
}
|
42
|
+
end
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
@pool.push download_task
|
49
|
+
end
|
50
|
+
|
51
|
+
def job_cleanup
|
52
|
+
until @pool.empty?
|
53
|
+
dlthrd = @pool.shift
|
54
|
+
|
55
|
+
if dlthrd.alive?
|
56
|
+
puts "[dlthread] Thread forceful cleaning up [remains: #{@pool.length}]"
|
57
|
+
dlthrd.terminate
|
58
|
+
dlthrd.join
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
66
63
|
end
|
data/lib/rbitter/env.rb
CHANGED
@@ -1,62 +1,62 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require "json"
|
4
|
-
|
5
|
-
module Rbitter
|
6
|
-
@@env = Hash.new
|
7
|
-
|
8
|
-
class ConfigFileError < StandardError; end
|
9
|
-
|
10
|
-
def self.[](k)
|
11
|
-
@@env[k]
|
12
|
-
end
|
13
|
-
|
14
|
-
module_function
|
15
|
-
def env
|
16
|
-
@@env
|
17
|
-
end
|
18
|
-
|
19
|
-
def env_reset
|
20
|
-
@@env.clear
|
21
|
-
end
|
22
|
-
|
23
|
-
def env_validate?
|
24
|
-
# TODO: Add validator
|
25
|
-
true
|
26
|
-
end
|
27
|
-
|
28
|
-
def config_initialize json_path=nil
|
29
|
-
env_reset
|
30
|
-
|
31
|
-
unless json_path.nil?
|
32
|
-
begin
|
33
|
-
open(json_path, 'r') { |file|
|
34
|
-
@@env = JSON.parse(file.read)
|
35
|
-
}
|
36
|
-
|
37
|
-
return @@env if env_validate?
|
38
|
-
fail StandardError, "Invalid configuration"
|
39
|
-
rescue => e
|
40
|
-
fail ConfigFileError, "Load Failure (#{json_path}): #{e.to_s}"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# Configuration default location
|
45
|
-
# 1. (current_dir)/config.json
|
46
|
-
# 2. (current_dir)/.rbitter/config.json
|
47
|
-
locations = ["config.json", ".rbitter/config.json"]
|
48
|
-
locations.collect! { |base| File.join(Dir.pwd, base) }
|
49
|
-
|
50
|
-
for location in locations
|
51
|
-
next unless File.file?(location)
|
52
|
-
open(location, 'r') { |file|
|
53
|
-
@@env = JSON.parse(file.read)
|
54
|
-
}
|
55
|
-
break if env_validate?
|
56
|
-
end
|
57
|
-
|
58
|
-
if @@env.empty?
|
59
|
-
fail ConfigFileError, "Can not load any configuration in [#{locations.join(', ')}]"
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module Rbitter
|
6
|
+
@@env = Hash.new
|
7
|
+
|
8
|
+
class ConfigFileError < StandardError; end
|
9
|
+
|
10
|
+
def self.[](k)
|
11
|
+
@@env[k]
|
12
|
+
end
|
13
|
+
|
14
|
+
module_function
|
15
|
+
def env
|
16
|
+
@@env
|
17
|
+
end
|
18
|
+
|
19
|
+
def env_reset
|
20
|
+
@@env.clear
|
21
|
+
end
|
22
|
+
|
23
|
+
def env_validate?
|
24
|
+
# TODO: Add validator
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def config_initialize json_path=nil
|
29
|
+
env_reset
|
30
|
+
|
31
|
+
unless json_path.nil?
|
32
|
+
begin
|
33
|
+
open(json_path, 'r') { |file|
|
34
|
+
@@env = JSON.parse(file.read)
|
35
|
+
}
|
36
|
+
|
37
|
+
return @@env if env_validate?
|
38
|
+
fail StandardError, "Invalid configuration"
|
39
|
+
rescue => e
|
40
|
+
fail ConfigFileError, "Load Failure (#{json_path}): #{e.to_s}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Configuration default location
|
45
|
+
# 1. (current_dir)/config.json
|
46
|
+
# 2. (current_dir)/.rbitter/config.json
|
47
|
+
locations = ["config.json", ".rbitter/config.json"]
|
48
|
+
locations.collect! { |base| File.join(Dir.pwd, base) }
|
49
|
+
|
50
|
+
for location in locations
|
51
|
+
next unless File.file?(location)
|
52
|
+
open(location, 'r') { |file|
|
53
|
+
@@env = JSON.parse(file.read)
|
54
|
+
}
|
55
|
+
break if env_validate?
|
56
|
+
end
|
57
|
+
|
58
|
+
if @@env.empty?
|
59
|
+
fail ConfigFileError, "Can not load any configuration in [#{locations.join(', ')}]"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -1,46 +1,46 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'http/parser'
|
4
|
-
require 'openssl'
|
5
|
-
require 'resolv'
|
6
|
-
|
7
|
-
module Twitter
|
8
|
-
module Streaming
|
9
|
-
class Connection
|
10
|
-
MODIFIED = true
|
11
|
-
attr_reader :tcp_socket_class, :ssl_socket_class
|
12
|
-
|
13
|
-
def initialize(options = {})
|
14
|
-
@tcp_socket_class = options.fetch(:tcp_socket_class) { TCPSocket }
|
15
|
-
@ssl_socket_class = options.fetch(:ssl_socket_class) { OpenSSL::SSL::SSLSocket }
|
16
|
-
end
|
17
|
-
|
18
|
-
def stream(request, response)
|
19
|
-
client_context = OpenSSL::SSL::SSLContext.new
|
20
|
-
client = @tcp_socket_class.new(Resolv.getaddress(request.uri.host), request.uri.port)
|
21
|
-
ssl_client = @ssl_socket_class.new(client, client_context)
|
22
|
-
ssl_client.connect
|
23
|
-
request.stream(ssl_client)
|
24
|
-
|
25
|
-
loop {
|
26
|
-
begin
|
27
|
-
body = ssl_client.read_nonblock(1024) # rubocop:disable AssignmentInCondition, WhileUntilModifier
|
28
|
-
response << body
|
29
|
-
rescue IO::WaitReadable
|
30
|
-
# The reason for setting 90 seconds as a timeout is documented on:
|
31
|
-
# https://dev.twitter.com/streaming/overview/connecting
|
32
|
-
r, w, e = IO.select([ssl_client], [], [], 90)
|
33
|
-
if r.nil?
|
34
|
-
# If timeout occurs
|
35
|
-
ssl_client.close
|
36
|
-
raise Twitter::Error::ServerError.new("Connection stalled")
|
37
|
-
else
|
38
|
-
# If socket is readable
|
39
|
-
retry
|
40
|
-
end
|
41
|
-
end
|
42
|
-
}
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'http/parser'
|
4
|
+
require 'openssl'
|
5
|
+
require 'resolv'
|
6
|
+
|
7
|
+
module Twitter
|
8
|
+
module Streaming
|
9
|
+
class Connection
|
10
|
+
MODIFIED = true
|
11
|
+
attr_reader :tcp_socket_class, :ssl_socket_class
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
@tcp_socket_class = options.fetch(:tcp_socket_class) { TCPSocket }
|
15
|
+
@ssl_socket_class = options.fetch(:ssl_socket_class) { OpenSSL::SSL::SSLSocket }
|
16
|
+
end
|
17
|
+
|
18
|
+
def stream(request, response)
|
19
|
+
client_context = OpenSSL::SSL::SSLContext.new
|
20
|
+
client = @tcp_socket_class.new(Resolv.getaddress(request.uri.host), request.uri.port)
|
21
|
+
ssl_client = @ssl_socket_class.new(client, client_context)
|
22
|
+
ssl_client.connect
|
23
|
+
request.stream(ssl_client)
|
24
|
+
|
25
|
+
loop {
|
26
|
+
begin
|
27
|
+
body = ssl_client.read_nonblock(1024) # rubocop:disable AssignmentInCondition, WhileUntilModifier
|
28
|
+
response << body
|
29
|
+
rescue IO::WaitReadable
|
30
|
+
# The reason for setting 90 seconds as a timeout is documented on:
|
31
|
+
# https://dev.twitter.com/streaming/overview/connecting
|
32
|
+
r, w, e = IO.select([ssl_client], [], [], 90)
|
33
|
+
if r.nil?
|
34
|
+
# If timeout occurs
|
35
|
+
ssl_client.close
|
36
|
+
raise Twitter::Error::ServerError.new("Connection stalled")
|
37
|
+
else
|
38
|
+
# If socket is readable
|
39
|
+
retry
|
40
|
+
end
|
41
|
+
end
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
46
|
end
|