class Midori::APIEngine

Merge and manage all APIs. @attr [Hash] routes A hash of all routes merged

Attributes

routes[RW]

Public Class Methods

new(root_api, type = :sinatra) click to toggle source

Init an API Engine @param [Class] root_api API inherited from [Midori::API] @param [Symbol] type type mustermann support

# File lib/midori/api_engine.rb, line 10
def initialize(root_api, type = :sinatra)
  @routes = {}
  Midori::Const::ROUTE_METHODS.map {|method| @routes[method] = []}  
  @root_api = root_api
  @type = type
  @routes = merge('', root_api, [])
  @routes.delete :MOUNT
  @routes.each do |method|
    method[1].each do |route|
      route.path = Mustermann.new(route.path, type: type)
    end
  end
end
websocket_header(key) click to toggle source

Return websocket header with given key @param [String] key 'Sec-WebSocket-Key' in request header @return [Hash] header

# File lib/midori/api_engine.rb, line 83
def self.websocket_header(key)
  header = Midori::Const::WEBSOCKET_HEADER.clone
  header['Sec-WebSocket-Accept'] = Digest::SHA1.base64digest(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
  header
end

Public Instance Methods

receive(request, connection = nil) click to toggle source

Process after receive data from client @param request [Midori::Request] Http Raw Request @param connection [EM::Connection] A connection created by EventMachine @return [Midori::Response] Http Response @raise [Midori::Error::NotFound] If no route matched

# File lib/midori/api_engine.rb, line 46
def receive(request, connection = nil)
  @routes[request.method].each do |route|
    params = route.path.params(request.path)
    next unless params # Skip if not matched
    request.params = params
    clean_room = Midori::CleanRoom.new(request)
    if request.websocket?
      # Send 101 Switching Protocol
      connection.send_data Midori::Response.new(
        status: 101,
        header: Midori::APIEngine.websocket_header(request.header['Sec-WebSocket-Key']),
        body: '')
      connection.websocket.request = request
      Midori::Sandbox.run(clean_room, route.function, connection.websocket)
      return Midori::Response.new
    elsif request.eventsource?
      connection.send_data Midori::Response.new(
        status: 200,
        header: Midori::Const::EVENTSOURCE_HEADER)
      Midori::Sandbox.run(clean_room, route.function, connection.eventsource)
      return Midori::Response.new
    else
      request = middleware_exec(route.middlewares, clean_room, request)
      return request if request.is_a? Midori::Response # Early stop
      result = Midori::Sandbox.run(clean_room, route.function)
      clean_room.body = result
      response = result.is_a?(Midori::Response) ? result : clean_room.raw_response
      response = middleware_exec(route.middlewares, clean_room, request, response)
      return response
    end
  end
  raise Midori::Exception::NotFound
end

Private Instance Methods

merge(prefix, root_api, middlewares) click to toggle source

Merge all routes with a Depth-first search

# File lib/midori/api_engine.rb, line 25
        def merge(prefix, root_api, middlewares)
  root_api.routes[:MOUNT].each do |mount|
    root_api.routes.merge!(merge(mount[0], mount[1], root_api.scope_middlewares)) do |_key, old_val, new_val|
      old_val + new_val
    end
  end
  root_api.routes.delete :MOUNT
  root_api.routes.each do |method|
    method[1].each do |route|
      route.path = prefix + route.path
      route.middlewares = middlewares + route.middlewares
    end
  end
  root_api.routes
end
middleware_exec(middlewares, clean_room, request, response=nil) click to toggle source

Exec middlewares

# File lib/midori/api_engine.rb, line 90
        def middleware_exec(middlewares, clean_room, request, response=nil)
  result = response.nil? ? request : response
  middlewares.each do |middleware|
    if response.nil?
      result = Midori::Sandbox.run(
        clean_room,
        proc { |req| middleware.before(req) },
        result)
    else
      result = Midori::Sandbox.run(
        clean_room,
        proc { |req, resp| middleware.after(req, resp) },
        request,
        result)
    end
    return result if response.nil? && result.is_a?(Midori::Response) # Early stop
  end
  result
end