reel-io 0.0.0.pre
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 +7 -0
- data/lib/reel/io.rb +27 -0
- data/lib/reel/io/accessors.rb +55 -0
- data/lib/reel/io/connection.rb +95 -0
- data/lib/reel/io/eventsource.rb +6 -0
- data/lib/reel/io/singletons.rb +73 -0
- data/lib/reel/io/streamers.rb +65 -0
- data/lib/reel/io/websocket.rb +39 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b46155d8f78297c924151729c44bf1cbb654d797
|
4
|
+
data.tar.gz: fcdf2057bd1980f8c6be37dd418dc510ce92c12a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 10efb065e937a78f27c9af9b3fb7785d7d6ae663eae5c54df6105fa26006b6b16f9d869b2a606b7a9c96f767757de9a570e4769adc5426f086df7ef3b62dd2c0
|
7
|
+
data.tar.gz: 77e7c5a5c4f309e31ce4af0433fa19dd33b2e713b1582e985a18ba9b95dc6c3d108985bb1c21c936f3f149a92c50ef6f75de018e0f9f970b56e7c18da41beb3d
|
data/lib/reel/io.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'celluloid/current'
|
3
|
+
require 'websocket/driver'
|
4
|
+
|
5
|
+
module Reel::IO
|
6
|
+
require 'reel/io/singletons'
|
7
|
+
require 'reel/io/accessors'
|
8
|
+
require 'reel/io/connection'
|
9
|
+
require 'reel/io/websocket'
|
10
|
+
require 'reel/io/eventsource'
|
11
|
+
class << self
|
12
|
+
extend Forwardable
|
13
|
+
def_delegators WebSocket, websocket?, websocket!
|
14
|
+
def_delegators EventSource, eventsource?, eventsource!
|
15
|
+
def_delegators Connection, persistent?, persistent!, :[], :[]=
|
16
|
+
end
|
17
|
+
|
18
|
+
class Server
|
19
|
+
#de TODO: Replace Reel internals with this module
|
20
|
+
end
|
21
|
+
|
22
|
+
class Client
|
23
|
+
#de TODO: Replace celluloid-websocket-client.
|
24
|
+
#de Same API goes either direction.
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Reel::IO::Accessors
|
2
|
+
HEADERS = {
|
3
|
+
'HTTP_ORIGIN' => 'Origin',
|
4
|
+
'HTTP_SEC_WEBSOCKET_KEY' => 'Sec-WebSocket-Key',
|
5
|
+
'HTTP_SEC_WEBSOCKET_KEY1' => 'Sec-WebSocket-Key1',
|
6
|
+
'HTTP_SEC_WEBSOCKET_KEY2' => 'Sec-WebSocket-Key2',
|
7
|
+
'HTTP_SEC_WEBSOCKET_EXTENSIONS' => 'Sec-WebSocket-Extensions',
|
8
|
+
'HTTP_SEC_WEBSOCKET_PROTOCOL' => 'Sec-WebSocket-Protocol',
|
9
|
+
'HTTP_SEC_WEBSOCKET_VERSION' => 'Sec-WebSocket-Version'
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
PORTABLE_HASH = Hash.new do |hash, key|
|
13
|
+
if key
|
14
|
+
result = hash.keys.find {|k| "#{k}" =~ /#{key}/i}
|
15
|
+
result = hash[HEADERS[key]] unless result
|
16
|
+
hash[result]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def context(env={})
|
21
|
+
@options = env.delete(:options) || {} unless @context
|
22
|
+
@context ||= PORTABLE_HASH.merge(env)
|
23
|
+
end
|
24
|
+
|
25
|
+
def url
|
26
|
+
@url ||= "#{(secure?) ? 'wss' : 'ws'}://#{context[:HTTP_HOST]}#{context[:REQUEST_URI]}"
|
27
|
+
end
|
28
|
+
|
29
|
+
HEADERS = {
|
30
|
+
'HTTP_ORIGIN' => 'Origin',
|
31
|
+
'HTTP_SEC_WEBSOCKET_KEY' => 'Sec-WebSocket-Key',
|
32
|
+
'HTTP_SEC_WEBSOCKET_KEY1' => 'Sec-WebSocket-Key1',
|
33
|
+
'HTTP_SEC_WEBSOCKET_KEY2' => 'Sec-WebSocket-Key2',
|
34
|
+
'HTTP_SEC_WEBSOCKET_EXTENSIONS' => 'Sec-WebSocket-Extensions',
|
35
|
+
'HTTP_SEC_WEBSOCKET_PROTOCOL' => 'Sec-WebSocket-Protocol',
|
36
|
+
'HTTP_SEC_WEBSOCKET_VERSION' => 'Sec-WebSocket-Version'
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
PORTABLE_HASH = Hash.new do |hash, key|
|
40
|
+
if key
|
41
|
+
result = hash.keys.find {|k| "#{k}" =~ /#{key}/i}
|
42
|
+
result = hash[HEADERS[key]] unless result
|
43
|
+
hash[result]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def context(env={})
|
48
|
+
@options = env.delete(:options) || {} unless @context
|
49
|
+
@context ||= PORTABLE_HASH.merge(env)
|
50
|
+
end
|
51
|
+
|
52
|
+
def url
|
53
|
+
@url ||= "#{(secure?) ? 'wss' : 'ws'}://#{context[:HTTP_HOST]}#{context[:REQUEST_URI]}"
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
class Reel::IO::Connection
|
2
|
+
|
3
|
+
def websocket?
|
4
|
+
self.class == Reel::IO::WebSocket
|
5
|
+
end
|
6
|
+
|
7
|
+
def eventsource?
|
8
|
+
self.class == Reel::IO::EventSource
|
9
|
+
end
|
10
|
+
|
11
|
+
extend Reel::IO::Singletons::Connection
|
12
|
+
|
13
|
+
include Celluloid
|
14
|
+
include Reel::IO::Accessors
|
15
|
+
include Reel::IO::Streamers
|
16
|
+
extend Forwardable
|
17
|
+
|
18
|
+
attr_reader :io, :url
|
19
|
+
alias :env :context
|
20
|
+
alias :socket :io
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@polling = @options.fetch(:polling, :perpetual)
|
24
|
+
@autostart = @options.fetch(:autostart, true) #de Only if there are emitters:
|
25
|
+
@emitters = @options.fetch(:emitters, {})
|
26
|
+
@message_buffer = []
|
27
|
+
@driver.on(:message) do |message|
|
28
|
+
@message_buffer.push(message.data)
|
29
|
+
end
|
30
|
+
@driver.on(:close) do
|
31
|
+
@io.close
|
32
|
+
end
|
33
|
+
@driver.start
|
34
|
+
|
35
|
+
@io ||= begin
|
36
|
+
context_suffixed(:hijack).call
|
37
|
+
context_suffixed(:hijack_io)
|
38
|
+
end
|
39
|
+
|
40
|
+
@io = Celluloid::IO::TCPSocket.new(@io)
|
41
|
+
|
42
|
+
if @emitters.any?
|
43
|
+
@emitters.each { |type, proc|
|
44
|
+
@driver.on(type, proc)
|
45
|
+
}
|
46
|
+
run if @autostart
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_reader :socket
|
51
|
+
def_delegators :@io, :addr, :peeraddr
|
52
|
+
def_delegators :@driver, :ping, :on
|
53
|
+
|
54
|
+
def secure?
|
55
|
+
@secure ||= (@url && @url.start_with?('wss://')) ||
|
56
|
+
@context['HTTP_PORT'] == 443 ||
|
57
|
+
@context['HTTPS'] == true ||
|
58
|
+
@contex.select { |k,v| k.downcase.end_with?("scheme") && v.downcase == 'https' } > 0 ||
|
59
|
+
end
|
60
|
+
|
61
|
+
def closed?
|
62
|
+
@io.closed?
|
63
|
+
end
|
64
|
+
|
65
|
+
def close
|
66
|
+
@driver.close
|
67
|
+
@io.close
|
68
|
+
rescue
|
69
|
+
end
|
70
|
+
|
71
|
+
def context_suffixed(*key)
|
72
|
+
return key.find { |k| context_suffixed(k) } unless key.one?
|
73
|
+
key = "#{key.first}".downcase
|
74
|
+
context.find { |k, v| "#{k}".end_with?(key) && v }
|
75
|
+
end
|
76
|
+
|
77
|
+
def cancel_timer!
|
78
|
+
@timer && @timer.cancel
|
79
|
+
end
|
80
|
+
|
81
|
+
def server?
|
82
|
+
@mode == :server
|
83
|
+
end
|
84
|
+
|
85
|
+
def client?
|
86
|
+
@mode == :client
|
87
|
+
end
|
88
|
+
|
89
|
+
[:message, :error, :close, :ping, :pong].each do |meth|
|
90
|
+
define_method "on_#{meth}" do |&proc|
|
91
|
+
@driver.send(:on, meth, &proc)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Reel::IO::Singletons
|
2
|
+
|
3
|
+
module WebSocket
|
4
|
+
def websocket!(env, io=nil)
|
5
|
+
Reel::IO::WebSocket.new(env, io)
|
6
|
+
end
|
7
|
+
alias :[]= :websocket!
|
8
|
+
alias :[] :websocket!
|
9
|
+
|
10
|
+
def websocket?(env)
|
11
|
+
return false unless env['REQUEST_METHOD'] == 'GET'
|
12
|
+
connection, upgrade = Reel::IO::Singletons::Connection.header(env, :connection, :upgrade)
|
13
|
+
connection.downcase.split(/ *, */).include?('upgrade') and
|
14
|
+
upgrade.downcase == 'websocket'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module EventSource
|
19
|
+
def eventsource!(env, io=nil)
|
20
|
+
Reel::IO::EventSource.new(env, io)
|
21
|
+
end
|
22
|
+
alias :[]= :eventsource!
|
23
|
+
alias :[] :eventsource!
|
24
|
+
def eventsource?(env)
|
25
|
+
return false unless env['REQUEST_METHOD'] == 'GET'
|
26
|
+
Reel::IO::Singletons::Connection.header(env, :accept)
|
27
|
+
.downcase.split(/\s*,\s*/)
|
28
|
+
.include?('text/event-stream')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module Connection
|
33
|
+
|
34
|
+
extend Forwardable
|
35
|
+
def_delegators Reel::IO::WebSocket,
|
36
|
+
:websocket!,
|
37
|
+
:websocket?
|
38
|
+
|
39
|
+
def_delegators Reel::IO::EventSource,
|
40
|
+
:eventsource!,
|
41
|
+
:eventsource?
|
42
|
+
|
43
|
+
def []=(env)
|
44
|
+
persistent!(env)
|
45
|
+
end
|
46
|
+
|
47
|
+
def []=(env, io)
|
48
|
+
persistent!(env, io)
|
49
|
+
end
|
50
|
+
|
51
|
+
def persistent!(env, io=nil)
|
52
|
+
return unless persistent?(env)
|
53
|
+
return websocket!(env, io) if websocket?(env)
|
54
|
+
eventsource!(env, io)
|
55
|
+
end
|
56
|
+
def :connection! :persistent!
|
57
|
+
def :channel! :persistent!
|
58
|
+
|
59
|
+
def persistent?(env)
|
60
|
+
websocket?(env) || eventsource?(env)
|
61
|
+
end
|
62
|
+
|
63
|
+
def header(env, *key)
|
64
|
+
if key.length > 1
|
65
|
+
return key.map { |k| header(env, k) }
|
66
|
+
else
|
67
|
+
key = key.first
|
68
|
+
end
|
69
|
+
[ "HTTP_#{key}".upcase, "#{key}".capitalize ].find { |k| !env[k].nil? } || ''
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Reel::IO::Streamers
|
2
|
+
|
3
|
+
def run
|
4
|
+
(polling?) ? async.reading! || async.read && read_every(@polling)
|
5
|
+
end
|
6
|
+
|
7
|
+
alias :run! :run
|
8
|
+
alias :poll :run
|
9
|
+
alias :poll! :run
|
10
|
+
alias :start :run
|
11
|
+
alias :start! :run
|
12
|
+
alias :go :run
|
13
|
+
alias :go! :run
|
14
|
+
|
15
|
+
def reading!
|
16
|
+
loop {
|
17
|
+
read
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def perpetual?
|
22
|
+
@polling == :perpetual
|
23
|
+
end
|
24
|
+
|
25
|
+
def write(msg)
|
26
|
+
if msg.is_a? String
|
27
|
+
@driver.text(msg)
|
28
|
+
elsif msg.is_a? Array
|
29
|
+
@driver.binary(msg)
|
30
|
+
else
|
31
|
+
raise "Can only send byte array or string over driver."
|
32
|
+
end
|
33
|
+
rescue IOError, Errno::ECONNRESET, Errno::EPIPE
|
34
|
+
cancel_timer!
|
35
|
+
raise SocketError, "error writing to socket"
|
36
|
+
rescue
|
37
|
+
cancel_timer!
|
38
|
+
raise
|
39
|
+
end
|
40
|
+
alias_method :<<, :write
|
41
|
+
|
42
|
+
def read
|
43
|
+
while @message_buffer.empty?
|
44
|
+
buffer = @io.readpartial(Connection::BUFFER_SIZE)
|
45
|
+
@driver.parse(buffer)
|
46
|
+
end
|
47
|
+
@message_buffer.shift
|
48
|
+
end
|
49
|
+
|
50
|
+
def read_every(n, unit = :s)
|
51
|
+
cancel_timer! # only one timer allowed per stream
|
52
|
+
seconds = case unit.to_s
|
53
|
+
when /\Am/
|
54
|
+
n * 60
|
55
|
+
when /\Ah/
|
56
|
+
n * 3600
|
57
|
+
else
|
58
|
+
n
|
59
|
+
end
|
60
|
+
@timer = every(seconds) { read }
|
61
|
+
end
|
62
|
+
alias read_interval read_every
|
63
|
+
alias read_frequency read_every
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Reel::IO::WebSocket < Reel::IO::Connection
|
2
|
+
|
3
|
+
extend Reel::IO::Singletons::WebSocket
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
if args.last.respond_to? :to_io
|
7
|
+
@mode = :server
|
8
|
+
@io = args.pop
|
9
|
+
end
|
10
|
+
|
11
|
+
if args.first.is_a? Hash
|
12
|
+
env = args.shift
|
13
|
+
@url = env.delete(:connect)
|
14
|
+
elsif args.first.is_a? String
|
15
|
+
@mode = :client
|
16
|
+
@url = args.shift
|
17
|
+
end
|
18
|
+
|
19
|
+
raise ArgumentError unless args.empty? && (env.is_a?(Hash) || @url.is_a?(String) && !@url.empty?)
|
20
|
+
@mode ||= (@url) ? :client : :server
|
21
|
+
context(env)
|
22
|
+
|
23
|
+
@driver = if server?
|
24
|
+
if context[:HTTP_SEC_WEBSOCKET_VERSION]
|
25
|
+
::WebSocket::Hybi.new(Celluloid::Actor.curent, @options.merge(require_masking: true))
|
26
|
+
elsif context[:HTTP_SEC_WEBSOCKET_KEY1]
|
27
|
+
::WebSocket::Draft76.new(Celluloid::Actor.curent, @options)
|
28
|
+
else
|
29
|
+
::WebSocket::Draft75.new(Celluloid::Actor.curent, @options)
|
30
|
+
end
|
31
|
+
else
|
32
|
+
::WebSocket::Client.new(Celluloid::Actor.current, @options.merge(masking: true))
|
33
|
+
end
|
34
|
+
|
35
|
+
super()
|
36
|
+
rescue EOFError
|
37
|
+
close
|
38
|
+
end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: reel-io
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0.pre
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- digitalextremist //
|
8
|
+
- Tony Arcieri
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-07-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.5.4
|
20
|
+
name: websocket-driver
|
21
|
+
prerelease: false
|
22
|
+
type: :runtime
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 0.5.4
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.17.0
|
34
|
+
name: celluloid
|
35
|
+
prerelease: false
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 0.17.0
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.16.5.pre0
|
48
|
+
name: celluloid-io
|
49
|
+
prerelease: false
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 0.16.5.pre0
|
56
|
+
description: Evented WebSockets and Server Side Events for Ruby, using Celluloid::IO and WebSocket::Driver.
|
57
|
+
email:
|
58
|
+
- code@extremist.digital
|
59
|
+
- tony.arcieri@gmail.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- lib/reel/io.rb
|
65
|
+
- lib/reel/io/accessors.rb
|
66
|
+
- lib/reel/io/connection.rb
|
67
|
+
- lib/reel/io/eventsource.rb
|
68
|
+
- lib/reel/io/singletons.rb
|
69
|
+
- lib/reel/io/streamers.rb
|
70
|
+
- lib/reel/io/websocket.rb
|
71
|
+
homepage: https://github.com/celluloid/reel-io
|
72
|
+
licenses:
|
73
|
+
- MIT
|
74
|
+
metadata: {}
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - '>'
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 1.3.1
|
89
|
+
requirements: []
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 2.4.8
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: Simple, non-blocking persistent I/O for HTTP/S servers.
|
95
|
+
test_files: []
|