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.
@@ -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
@@ -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,6 @@
1
+ class Reel::IO::WebSocket < Reel::IO::Connection
2
+ extend Reel::IO::Singletons::EventSource
3
+
4
+ #de TODO: Port Faye::EventSource
5
+
6
+ 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: []