reel-io 0.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []