Class: MIDB::API::Engine

Inherits:
Object
  • Object
show all
Defined in:
lib/midb/serverengine_controller.rb

Overview

This class handles runs the server engine using sockets and a loop.

Author:

  • unrar

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(db, stat, cnf, hooks = nil) ⇒ Engine

Constructor

Parameters:

  • db (String)

    Database to which the server will bind.

  • stat (Fixnum)

    HTTP Status

  • cnf (Hash)

    Config from the server controller.



42
43
44
45
46
47
48
49
50
51
# File 'lib/midb/serverengine_controller.rb', line 42

def initialize(db, stat, cnf, hooks=nil)
  @config = cnf
  @db = db
  @http_status = stat
  if hooks == nil
    @hooks = MIDB::API::Hooks.new
  else
    @hooks = hooks
  end
end

Instance Attribute Details

#configHash

Returns Contains the project’s configuration, saved in .midb.yaml.

Returns:

  • (Hash)

    Contains the project’s configuration, saved in .midb.yaml



28
29
30
# File 'lib/midb/serverengine_controller.rb', line 28

def config
  @config
end

#dbString

Returns Database name (if SQLite is the engine, file name without extension).

Returns:

  • (String)

    Database name (if SQLite is the engine, file name without extension)



28
# File 'lib/midb/serverengine_controller.rb', line 28

attr_accessor :config, :db, :http_status, :hooks

#hObject

Returns MIDB::API::Hooks instance.

Returns:

  • (Object)

    MIDB::API::Hooks instance



28
# File 'lib/midb/serverengine_controller.rb', line 28

attr_accessor :config, :db, :http_status, :hooks

#hooksObject

Attribute declaration here



28
29
30
# File 'lib/midb/serverengine_controller.rb', line 28

def hooks
  @hooks
end

#http_statusString

Returns HTTP status code and string representation for the header.

Returns:

  • (String)

    HTTP status code and string representation for the header



28
# File 'lib/midb/serverengine_controller.rb', line 28

attr_accessor :config, :db, :http_status, :hooks

Instance Method Details

#parse_request(req) ⇒ Object

Method: parse_request Parses an HTTP requests and returns an array [method, uri]



210
211
212
# File 'lib/midb/serverengine_controller.rb', line 210

def parse_request(req)
  [req.split(" ")[0], req.split(" ")[1]]
end

#start(port = 8081) ⇒ Object

Starts the server on a given port (default: 8081)

Parameters:

  • port (Fixnum) (defaults to: 8081)

    Port to which the server will listen.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/midb/serverengine_controller.rb', line 56

def start(port=8081)
  serv = TCPServer.new("localhost", port)
  MIDB::Interface::Server.info(:start, port)

  # Manage the requests
  loop do
    socket = serv.accept
    MIDB::Interface::Server.info(:incoming_request, socket.addr[3])

    request = self.parse_request(socket.gets)

    # Get a hash with the headers
    headers = {}
    while line = socket.gets.split(' ', 2)
      break if line[0] == "" 
      headers[line[0].chop] = line[1].strip
    end
    data = socket.read(headers["Content-Length"].to_i)


    MIDB::Interface::Server.info(:request, request)
    response_json = Hash.new()

    # Endpoint syntax: ["", FILE, ID, (ACTION)]
    endpoint = request[1].split("/")
    if endpoint.length >= 2
      ep_file = endpoint[1].split("?")[0]
    else
      ep_file = ""
    end

    method = request[0]
    endpoints = [] # Valid endpoints

    # Load the JSON served files
    @config["serves"].each do |js|
      # The filename is a valid endpoint
      endpoints.push File.basename(js, ".*")
    end

    # Load the endpoints
    found = false
    endpoints.each do |ep|
      if ep_file == ep
        found = true
        MIDB::Interface::Server.info(:match_json, ep)
        # Create the model
        dbop = MIDB::API::Model.new(ep, @db, self)
        # Analyze the request and pass it to the model
        # Is the method accepted?
        accepted_methods = ["GET", "POST", "PUT", "DELETE"]
        unless accepted_methods.include? method
          @http_status = "405 Method Not Allowed"
          response_json = MIDB::Interface::Server.json_error(405, "Method Not Allowed").to_json
        else
          # Do we need authentication?
          auth_req = false
          unauthenticated = false
          if @config["privacy#{method.downcase}"] == true
            MIDB::Interface::Server.info(:auth_required)
            auth_req = true
            
            # For GET and DELETE requests, the object of the digest is the endpoint
            if (method == "GET") || (method == "DELETE")
              data = ep_file
            end

            # If it's a GET request and we have a different key for GET methods...
            if (@config["apigetkey"] != nil) && (method == "GET")
              unauthenticated = (not headers.has_key? "Authentication") ||
               (not MIDB::API::Security.check?(headers["Authentication"], data, @config["apigetkey"]))
            else
              unauthenticated = (not headers.has_key? "Authentication") ||
                 (not MIDB::API::Security.check?(headers["Authentication"], data, @config["apikey"]))
            end
          end
          # Proceed to handle the request
          if unauthenticated
            response_json = self.unauth_request
            puts ">> has header: #{headers.has_key? "Authentication"}"
          else
            MIDB::Interface::Server.info(:auth_success) if (not unauthenticated) && auth_req
            if method == "GET"
              case endpoint.length
              when 2
                # No ID has been specified. Return all the entries
                # Pass it to the model and get the JSON
                MIDB::Interface::Server.info(:fetch, "get_all_entries()")
                response_json = dbop.get_all_entries().to_json
              when 3
                # This regular expression checks if it contains an integer
                if /\A[-+]?\d+\z/ === endpoint[2]
                  # An ID has been specified. Should it exist, return all of its entries.
                  MIDB::Interface::Server.info(:fetch, "get_entries(#{endpoint[2]})")
                  response_json = dbop.get_entries(endpoint[2].to_i).to_json
                else
                  # A row has been specified, but no pattern
                  MIDB::Interface::Server.info(:fetch, "get_column_entries(#{endpoint[2]})")
                  response_json = dbop.get_column_entries(endpoint[2]).to_json
                end
              when 4
                if (endpoint[2].is_a? String) && (endpoint[3].is_a? String) then
                  # A row and a pattern have been specified
                  MIDB::Interface::Server.info(:fetch, "get_matching_rows(#{endpoint[2]}, #{endpoint[3]})")
                  response_json = dbop.get_matching_rows(endpoint[2], endpoint[3]).to_json
                end
              end
            elsif method == "POST"
              MIDB::Interface::Server.info(:fetch, "post(#{data})")
              response_json = dbop.post(data).to_json
            else
              if endpoint.length >= 3
                if method == "DELETE"
                  MIDB::Interface::Server.info(:fetch, "delete(#{endpoint[2]})")
                  response_json = dbop.delete(endpoint[2]).to_json 
                elsif method == "PUT"
                  MIDB::Interface::Server.info(:fetch, "put(#{endpoint[2]}, data)")
                  response_json = dbop.put(endpoint[2], data).to_json
                end
              else
                @http_status = "404 Not Found"
                response_json = MIDB::Interface::Server.json_error(404, "Must specify an ID.").to_json
              end
            end
          end
        end 
        MIDB::Interface::Server.info(:response, response_json)
        # Return the results via HTTP
        socket.print "HTTP/1.1 #{@http_status}\r\n" +
                    "Content-Type: text/json\r\n" +
                    "Content-Length: #{response_json.size}\r\n" +
                    "Connection: close\r\n"
        socket.print "\r\n"
        socket.print response_json
        socket.print "\r\n"
        MIDB::Interface::Server.info(:success)
      end
    end
    unless found
      MIDB::Interface::Server.info(:not_found)
      response = MIDB::Interface::Server.json_error(404, "Invalid API endpoint.").to_json

      socket.print "HTTP/1.1 404 Not Found\r\n" +
                   "Content-Type: text/json\r\n" +
                   "Content-Length: #{response.size}\r\n" +
                   "Connection: close\r\n"
      socket.print "\r\n"
      socket.print response
    end
  end
end

#unauth_requestObject

Handle an unauthorized request



31
32
33
34
35
# File 'lib/midb/serverengine_controller.rb', line 31

def unauth_request
  @http_status = "401 Unauthorized"
  MIDB::Interface::Server.info(:no_auth)
  MIDB::Interface::Server.json_error(401, "Unauthorized").to_json
end