cocaine-framework-ruby 0.11.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.
@@ -0,0 +1,46 @@
1
+ require 'cocaine/asio/channel/combiner'
2
+ require 'cocaine/asio/channel/manager'
3
+ require 'cocaine/client/error'
4
+ require 'cocaine/dispatcher'
5
+
6
+ $log = Logger.new(STDERR)
7
+ $log.level = Logger::DEBUG
8
+
9
+
10
+ class Cocaine::ClientDispatcher < Cocaine::Dispatcher
11
+ def initialize(conn)
12
+ super conn
13
+ @channels = Cocaine::ChannelManager.new
14
+ end
15
+
16
+ def invoke(method_id, *data)
17
+ session, channel = @channels.create
18
+ message = MessagePack::pack([method_id, session, data])
19
+ @conn.send_data message
20
+ Cocaine::ChannelCombiner.new channel
21
+ end
22
+
23
+ protected
24
+ def process(session, message)
25
+ channel = @channels[session]
26
+ case message.id
27
+ when RPC::CHUNK
28
+ channel.trigger unpack message.data
29
+ when RPC::ERROR
30
+ channel.error Cocaine::ServiceError.new "[#{message.errno}] #{message.reason}"
31
+ when RPC::CHOKE
32
+ channel.error ChokeEvent.new
33
+ channel.close
34
+ else
35
+ raise "unexpected message id: #{message.id}"
36
+ end
37
+ end
38
+
39
+ private
40
+ def unpack(data)
41
+ if data.kind_of?(Array)
42
+ data = data.join(',')
43
+ end
44
+ MessagePack.unpack(data)
45
+ end
46
+ end
@@ -0,0 +1,7 @@
1
+ module Cocaine
2
+ class ConnectionError < Exception
3
+ end
4
+
5
+ class ServiceError < Exception
6
+ end
7
+ end
@@ -0,0 +1,130 @@
1
+ require 'rubygems'
2
+ require 'logger'
3
+
4
+ require 'msgpack'
5
+ require 'eventmachine'
6
+
7
+ require 'cocaine/asio/channel'
8
+ require 'cocaine/asio/connection'
9
+ require 'cocaine/client/dispatcher'
10
+ require 'cocaine/client/error'
11
+ require 'cocaine/protocol'
12
+
13
+
14
+ class Object
15
+ def metaclass
16
+ class << self
17
+ self
18
+ end
19
+ end
20
+ end
21
+
22
+
23
+ $log = Logger.new(STDERR)
24
+ $log.level = Logger::DEBUG
25
+
26
+
27
+ class Cocaine::AbstractService
28
+ attr_reader :api
29
+
30
+ def initialize(name)
31
+ @name = name
32
+ @api = {}
33
+ end
34
+
35
+ def invoke(method_id, *args)
36
+ $log.debug "invoking '#{@name}' method #{method_id} with #{args}"
37
+ @dispatcher.invoke method_id, *args
38
+ end
39
+
40
+ private
41
+ def connect_to_endpoint(*endpoint)
42
+ df = EM::DefaultDeferrable.new
43
+ $log.debug "connecting to the service '#{@name}' at #{endpoint} ..."
44
+ EM.connect *endpoint, Cocaine::Connection do |conn|
45
+ conn.hooks.on :connected do
46
+ conn.hooks.clear
47
+ $log.debug "connection established with service '#{@name}' at #{endpoint}"
48
+ @dispatcher = Cocaine::ClientDispatcher.new conn
49
+ df.succeed
50
+ end
51
+
52
+ conn.hooks.on :disconnected do
53
+ conn.hooks.clear
54
+ $log.debug "failed to connect to the '#{@name}' service at #{endpoint}"
55
+ df.fail Cocaine::ConnectionError.new
56
+ end
57
+ end
58
+ df
59
+ end
60
+ end
61
+
62
+
63
+ class Cocaine::Locator < Cocaine::AbstractService
64
+ @default_host = 'localhost'
65
+ @default_port = 10053
66
+ class << self
67
+ attr_accessor :default_host, :default_port
68
+ end
69
+
70
+ def initialize(host=self.class.default_host, port=self.class.default_port)
71
+ @name = 'locator'
72
+ @host = host
73
+ @port = port
74
+ end
75
+
76
+ def resolve(name)
77
+ df = EventMachine::DefaultDeferrable.new
78
+ connect_df = connect_to_endpoint @host, @port
79
+ connect_df.callback { do_resolve name, df }
80
+ connect_df.errback { |err| df.fail err }
81
+ df
82
+ end
83
+
84
+ private
85
+ def do_resolve(name, df)
86
+ $log.debug "resolving service '#{name}'"
87
+ channel = invoke 0, name
88
+ channel.callback do |future|
89
+ begin
90
+ df.succeed future.get
91
+ rescue Exception => err
92
+ df.fail err
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+
99
+ class Cocaine::Service < Cocaine::AbstractService
100
+ def initialize(name, host='localhost', port=10053)
101
+ super name
102
+ @settings = {:host => host, :port => port}
103
+ end
104
+
105
+ def connect
106
+ df = EventMachine::DefaultDeferrable.new
107
+ locator = Cocaine::Locator.new @settings[:host], @settings[:port]
108
+ d = locator.resolve @name
109
+ d.callback { |result| on_connect result, df }
110
+ d.errback { |err| df.fail err }
111
+ df
112
+ end
113
+
114
+ private
115
+ def on_connect(result, df)
116
+ $log.debug "service '#{@name}' resolved: #{result}"
117
+
118
+ @endpoint, @version, @api = result
119
+ @api.each do |id, name|
120
+ self.metaclass.send(:define_method, name) do |*args|
121
+ invoke id, *args
122
+ end
123
+ end
124
+
125
+ #todo: No ipv6 support yet
126
+ connect_df = connect_to_endpoint *@endpoint
127
+ connect_df.callback { df.succeed }
128
+ connect_df.errback { |err| df.fail err }
129
+ end
130
+ end
@@ -0,0 +1,11 @@
1
+ require 'cocaine/namespace'
2
+
3
+
4
+ class Cocaine::Decoder
5
+ def feed(data, &block)
6
+ @decoder ||= MessagePack::Unpacker.new
7
+ @decoder.feed_each(data) do |decoded|
8
+ block.call decoded
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ require 'logger'
2
+
3
+ $log = Logger.new(STDERR)
4
+ $log.level = Logger::DEBUG
5
+
6
+
7
+ class Cocaine::Dispatcher
8
+ def initialize(conn)
9
+ @conn = conn
10
+ @conn.hooks.on :message do |session, message|
11
+ process session, message
12
+ end
13
+ end
14
+
15
+ protected
16
+ def process(session, message)
17
+ raise NotImplementedError
18
+ end
19
+ end
@@ -0,0 +1,2 @@
1
+ class IllegalStateError < StandardError
2
+ end
@@ -0,0 +1,26 @@
1
+ class Cocaine::Future
2
+ def set_value(value)
3
+ @value = value
4
+ end
5
+
6
+ def set_error(error)
7
+ @error = error
8
+ end
9
+
10
+ def get
11
+ raise @error if @error
12
+ @value
13
+ end
14
+
15
+ def self.value(value)
16
+ f = Cocaine::Future.new
17
+ f.set_value value
18
+ f
19
+ end
20
+
21
+ def self.error(error)
22
+ f = Cocaine::Future.new
23
+ f.set_error error
24
+ f
25
+ end
26
+ end
@@ -0,0 +1,4 @@
1
+ module Cocaine
2
+ module Synchrony
3
+ end
4
+ end
@@ -0,0 +1,155 @@
1
+ require 'msgpack'
2
+
3
+ require 'cocaine/namespace'
4
+
5
+ module RPC
6
+ HANDSHAKE = 0
7
+ HEARTBEAT = 1
8
+ TERMINATE = 2
9
+ INVOKE = 3
10
+ CHUNK = 4
11
+ ERROR = 5
12
+ CHOKE = 6
13
+ end
14
+
15
+
16
+ class Protocol
17
+ attr_reader :id
18
+
19
+ def pack(session)
20
+ [@id, session, content].to_msgpack
21
+ end
22
+
23
+ def to_s
24
+ "#{self.class.name}(#{content})"
25
+ end
26
+
27
+ protected
28
+ def initialize(id)
29
+ @id = id
30
+ end
31
+
32
+ protected
33
+ def content
34
+ []
35
+ end
36
+ end
37
+
38
+
39
+ class Handshake < Protocol
40
+ def initialize(uuid)
41
+ super RPC::HANDSHAKE
42
+ @uuid = uuid
43
+ end
44
+
45
+ protected
46
+ def content
47
+ [@uuid]
48
+ end
49
+ end
50
+
51
+
52
+ class Heartbeat < Protocol
53
+ def initialize
54
+ super RPC::HEARTBEAT
55
+ end
56
+ end
57
+
58
+
59
+ class Terminate < Protocol
60
+ attr_reader :errno
61
+ attr_reader :reason
62
+
63
+ def initialize(errno, reason)
64
+ super RPC::TERMINATE
65
+ @errno = errno
66
+ @reason = reason
67
+ end
68
+
69
+ protected
70
+ def content
71
+ [@errno, @reason]
72
+ end
73
+ end
74
+
75
+
76
+ class Invoke < Protocol
77
+ attr_reader :event
78
+
79
+ def initialize(event)
80
+ super RPC::INVOKE
81
+ @event = event
82
+ end
83
+
84
+ protected
85
+ def content
86
+ [@event]
87
+ end
88
+ end
89
+
90
+
91
+ class Chunk < Protocol
92
+ attr_reader :data
93
+
94
+ def initialize(data)
95
+ super RPC::CHUNK
96
+ @data = data
97
+ end
98
+
99
+ protected
100
+ def content
101
+ [@data]
102
+ end
103
+ end
104
+
105
+
106
+ class Error < Protocol
107
+ attr_reader :errno
108
+ attr_reader :reason
109
+
110
+ def initialize(errno, reason)
111
+ super RPC::ERROR
112
+ @errno = errno
113
+ @reason = reason
114
+ end
115
+
116
+ protected
117
+ def content
118
+ [@errno, @reason]
119
+ end
120
+ end
121
+
122
+
123
+ class Choke < Protocol
124
+ def initialize
125
+ super RPC::CHOKE
126
+ end
127
+ end
128
+
129
+
130
+ class Cocaine::ProtocolFactory
131
+ def self.create(id, data)
132
+ case id
133
+ when RPC::HANDSHAKE
134
+ Handshake.new *data
135
+ when RPC::HEARTBEAT
136
+ Heartbeat.new *data
137
+ when RPC::TERMINATE
138
+ Terminate.new *data
139
+ when RPC::INVOKE
140
+ Invoke.new *data
141
+ when RPC::CHUNK
142
+ Chunk.new *data
143
+ when RPC::ERROR
144
+ Error.new *data
145
+ when RPC::CHOKE
146
+ Choke.new *data
147
+ else
148
+ raise "unexpected message id: #{id}"
149
+ end
150
+ end
151
+ end
152
+
153
+
154
+ class ChokeEvent < Exception
155
+ end
@@ -0,0 +1,72 @@
1
+ require 'cocaine/asio/channel'
2
+ require 'cocaine/protocol'
3
+ require 'cocaine/server/health'
4
+ require 'cocaine/server/request'
5
+ require 'cocaine/server/response'
6
+
7
+
8
+ class Cocaine::WorkerDispatcher < Cocaine::Dispatcher
9
+ def initialize(worker, conn)
10
+ super conn
11
+ @worker = worker
12
+ @health = Cocaine::HealthManager.new self
13
+ @health.start
14
+ @channels = {}
15
+ end
16
+
17
+ def send_handshake(session, uuid)
18
+ send Handshake.new(uuid), session
19
+ end
20
+
21
+ def send_heartbeat(session)
22
+ send Heartbeat.new, session
23
+ end
24
+
25
+ def send_terminate(session, errno, reason)
26
+ send Terminate.new(errno, reason), session
27
+ end
28
+
29
+ def send_chunk(session, data)
30
+ send Chunk.new(data), session
31
+ end
32
+
33
+ def send_error(session, errno, reason)
34
+ send Error.new(errno, reason), session
35
+ end
36
+
37
+ def send_choke(session)
38
+ send Choke.new, session
39
+ end
40
+
41
+ protected
42
+ def process(session, message)
43
+ case message.id
44
+ when RPC::HEARTBEAT
45
+ @health.breath()
46
+ when RPC::TERMINATE
47
+ @worker.terminate message.errno, message.reason
48
+ when RPC::INVOKE
49
+ channel = Cocaine::Channel.new
50
+ request = Cocaine::Request.new channel
51
+ response = Cocaine::Response.new session, self
52
+ @channels[session] = channel
53
+ @worker.invoke(message.event, request, response)
54
+ when RPC::CHUNK
55
+ df = @channels[session]
56
+ df.trigger message.data
57
+ when RPC::ERROR
58
+ df = @channels[session]
59
+ df.error message.reason
60
+ when RPC::CHOKE
61
+ df = @channels.delete(session)
62
+ df.close
63
+ else
64
+ raise "unexpected message id: #{id}"
65
+ end
66
+ end
67
+
68
+ private
69
+ def send(message, session)
70
+ @conn.send_data message.pack(session)
71
+ end
72
+ end