Class: BERTREM::Server

Inherits:
EventMachine::Connection
  • Object
show all
Includes:
BERTRPC::Encodes
Defined in:
lib/bertrem/server.rb

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.current_modObject

Returns the value of attribute current_mod.



12
13
14
# File 'lib/bertrem/server.rb', line 12

def current_mod
  @current_mod
end

.logObject

Returns the value of attribute log.



12
13
14
# File 'lib/bertrem/server.rb', line 12

def log
  @log
end

.modsObject

Returns the value of attribute mods.



12
13
14
# File 'lib/bertrem/server.rb', line 12

def mods
  @mods
end

Class Method Details

.dispatch(mod, fun, args) ⇒ Object

Dispatch the request to the proper mod:fun.

+mod+ is the module Symbol
+fun+ is the function Symbol
+args+ is the Array of arguments

Returns the Ruby object response



83
84
85
86
87
# File 'lib/bertrem/server.rb', line 83

def self.dispatch(mod, fun, args)
  mods[mod] || raise(ServerError.new("No such module '#{mod}'"))
  mods[mod].funs[fun] || raise(ServerError.new("No such function '#{mod}:#{fun}'"))
  mods[mod].funs[fun].call(*args)
end

.expose(name, mixin) ⇒ Object

Expose all public methods in a Ruby module:

+name+ is the ernie module Symbol
+mixin+ is the ruby module whose public methods are exposed

Returns nothing



50
51
52
53
54
55
56
57
58
59
# File 'lib/bertrem/server.rb', line 50

def self.expose(name, mixin)
  context = Object.new
  context.extend mixin
  self.mod(name, lambda {
    mixin.public_instance_methods.each do |meth|
      self.fun(meth.to_sym, context.method(meth))
    end
  })
  context
end

.fun(name, block) ⇒ Object

Record a function.

+name+ is the function Symbol
+block+ is the Block to associate

Returns nothing



41
42
43
# File 'lib/bertrem/server.rb', line 41

def self.fun(name, block)
  self.current_mod.fun(name, block)
end

.logfile(file) ⇒ Object

Set the logfile to given path.

+file+ is the String path to the logfile

Returns nothing



65
66
67
# File 'lib/bertrem/server.rb', line 65

def self.logfile(file)
  self.log = Logger.new(file)
end

.loglevel(level) ⇒ Object

Set the log level.

+level+ is the Logger level (Logger::WARN, etc)

Returns nothing



73
74
75
# File 'lib/bertrem/server.rb', line 73

def self.loglevel(level)
  self.log.level = level
end

.mod(name, block) ⇒ Object

Record a module.

+name+ is the module Symbol
+block+ is the Block containing function definitions

Returns nothing



29
30
31
32
33
34
# File 'lib/bertrem/server.rb', line 29

def self.mod(name, block)
  m = Mod.new(name)
  self.current_mod = m
  self.mods[name] = m
  block.call
end

.start(host, port) ⇒ Object



20
21
22
# File 'lib/bertrem/server.rb', line 20

def self.start(host, port)
  EM.start_server(host, port, self)
end

Instance Method Details

#post_initObject



100
101
102
103
104
# File 'lib/bertrem/server.rb', line 100

def post_init
  @receive_buf = ""; @receive_len = 0; @more = false
  Server.log.info("(#{Process.pid}) Starting")
  Server.log.debug(Server.mods.inspect)
end

#receive_data(bert_request) ⇒ Object

Receive data on the connection.



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/bertrem/server.rb', line 108

def receive_data(bert_request)
  @receive_buf << bert_request

  while @receive_buf.length > 0 do
    unless @more
      begin
        if @receive_buf.length > 4
          @receive_len = @receive_buf.slice!(0..3).unpack('N').first if @receive_len == 0
          raise BERTRPC::ProtocolError.new(BERTRPC::ProtocolError::NO_DATA) unless @receive_buf.length > 0
        else
          raise BERTRPC::ProtocolError.new(BERTRPC::ProtocolError::NO_HEADER)
        end
      rescue Exception => e
        log "Bad BERT message: #{e.message}"
        return       
      end
    end

    if @receive_buf.length >= @receive_len
      bert = @receive_buf.slice!(0..(@receive_len - 1))     
      @receive_len = 0; @more = false   
      iruby = BERT.decode(bert)

      unless iruby
        Server.log.info("(#{Process.pid}) No Ruby in this here packet.  On to the next one...")
        next
      end

      if iruby.size == 4 && iruby[0] == :call
        mod, fun, args = iruby[1..3]
        Server.log.info("-> " + iruby.inspect)
        begin
          res = Server.dispatch(mod, fun, args)
          oruby = t[:reply, res]
          Server.log.debug("<- " + oruby.inspect)
          write_berp(oruby)
        rescue ServerError => e
          oruby = t[:error, t[:server, 0, e.class.to_s, e.message, e.backtrace]]
          Server.log.error("<- " + oruby.inspect)
          Server.log.error(e.backtrace.join("\n"))
          write_berp(oruby)
        rescue Object => e
          oruby = t[:error, t[:user, 0, e.class.to_s, e.message, e.backtrace]]
          Server.log.error("<- " + oruby.inspect)
          Server.log.error(e.backtrace.join("\n"))
          write_berp(oruby)
        end
      elsif iruby.size == 4 && iruby[0] == :cast
        mod, fun, args = iruby[1..3]
        Server.log.info("-> " + [:cast, mod, fun, args].inspect)
        begin
          Server.dispatch(mod, fun, args)
        rescue Object => e
          # ignore
        end
        write_berp(t[:noreply])
      else
        Server.log.error("-> " + iruby.inspect)
        oruby = t[:error, t[:server, 0, "Invalid request: #{iruby.inspect}"]]
        Server.log.error("<- " + oruby.inspect)
        write_berp(oruby)
      end
    else
      @more = true
      break
    end
  end
end

#write_berp(ruby) ⇒ Object

Write the given Ruby object to the wire as a BERP.

+output+ is the IO on which to write
+ruby+ is the Ruby object to encode

Returns nothing



94
95
96
97
98
# File 'lib/bertrem/server.rb', line 94

def write_berp(ruby)
  data = BERT.encode(ruby)
  send_data([data.length].pack("N"))
  send_data(data)
end