cocaine-framework-ruby 0.11.2

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