   }
 
   # Loads config files.
-  opts = $wait $appConf.load 'xo-server',
-    defaults: opts
-    ignoreUnknownFormats: true
+  opts = $wait $appConf.load('xo-server', {
+    defaults,
+    ignoreUnknownFormats: true,
+  })
 
   $debug 'Configuration loaded.'
 

  # Creates the main object which will connects to Xen servers and
  # manages all the models.
  xo = new $XO()

  # Starts it.
  xo.start {
    redis: {
      uri: opts.redis?.uri
    }
  }

  $debug 'Initializing connection to Xen servers…'

  # Static file serving (e.g. for XO-Web).
  connect = $connect()
  for urlPath, filePaths of opts.http.mounts
    filePaths = [filePaths] unless $isArray filePaths
    for filePath in filePaths
      $debug 'Setting up %s → %s', urlPath, filePath
      connect.use urlPath, $serveStatic filePath
  webServer.on 'request', connect

  # Creates the API.
  api = new $API xo

  connect.use $bind xo.handleProxyRequest, xo

  # WebSocket server for consoles.
  setUpConsoleProxy(webServer, xo)

  # Create the WebSocket server.
  wsServer = new $WSServer {
    server: webServer
    path: '/api/'
  }
  wsServer.on 'error', (error) ->
    console.error '[WARN] WebSocket server', error
    wsServer.close()
    return

  # Handle a WebSocket connection
  wsServer.on 'connection', (socket) ->
    $debug '+ WebSocket connection'

    # Forward declaration due to cyclic dependency connection <-> jsonRpc.
    connection = null

    # Create a JSON-RPC interface for this connection.
    jsonRpc = $jsonRpc.createServer (message) ->
      return api.exec connection, message if message.type is 'request'

    # Create a XO user connection.
    connection = xo.createUserConnection {
      close: $bind socket.close, socket
      notify: $bind jsonRpc.notify, jsonRpc
    }

    # Close the connection with the socket.
    socket.on 'close', ->
      $debug '- WebSocket connection'
      connection.close()
      return

    # Connect the WebSocket to the JSON-RPC server
    socket.on 'message', (message) ->
      jsonRpc.write message
      return
    jsonRpc.on 'data', (data) ->
      if socket.readyState is socket.OPEN
        socket.send (JSON.stringify data), (error) ->
          if error
            console.error '[WARN] WebSocket send', error
            connection.close()
          return
      return

    socket.on 'error', (error) ->
      console.error '[WARN] WebSocket connection', error
      connection.close()
      return

    return

  # Creates a default user if there is none.
  unless $wait xo.users.exists()

    if entry of opts
      console.warn "[Warn] `#{entry}` configuration is deprecated."

  return opts

#---------------------------------------------------------------------

$readFile = $Promise.promisify $fs.readFile

$httpListenSuccess = ->
  $debug "Web server listening on #{@niceAddress()}"
  return

$httpListenFailure = (error) ->
  console.warn "[WARN] Web server could not listen on #{@niceAddress()}"
  switch error.code
    when 'EACCES'
      console.warn '       Access denied.'
      console.warn '       Ports < 1024 are often reserved to privileges users.'
    when 'EADDRINUSE'
      console.warn '       Address already in use.'
  return

# Returns a promise to a new HTTP server plus when it is listening on
# all port.
createWebServer = (opts) ->
  webServer = new $WebServer()

  return $Promise.map(opts, (opts) ->
    return $Promise.try(() ->
      # Reads certificate and key if necessary.
      if opts.certificate and opts.key
        return $Promise.join(
          $readFile(opts.certificate), $readFile(opts.key),
          (certificate, key) ->
            opts.certificate = certificate
            opts.key = key
            return
        )
      return
    ).then(() ->
      return webServer.listen(opts).then($httpListenSuccess, $httpListenFailure)
    )
  ).return(webServer)

#---------------------------------------------------------------------

setUpStaticFiles = (connect, opts) ->
  for urlPath, filePaths of opts
    filePaths = [filePaths] unless $isArray filePaths
    for filePath in filePaths
      $debug 'Setting up %s → %s', urlPath, filePath
      connect.use urlPath, $serveStatic filePath

  return

#---------------------------------------------------------------------

setUpApi = (webServer, xo) ->
  api = new $API(xo)

  webSocketServer = new $WSServer({
    server: webServer,
    path: '/api/',
  })

  webSocketServer.on('connection', (connection) ->
    $debug '+ WebSocket connection'

    # Forward declaration.
    xoConnection = null

    # Create the JSON-RPC server for this connection.
    jsonRpc = $jsonRpc.createServer((message) ->
      if message.type is 'request'
        return api.exec(xoConnection, message)

      return
    )

    # Create the abstract XO object for this connection.
    xoConnection = xo.createUserConnection({
      close: $bind(connection.close, connection),
      notify: $bind(jsonRpc.notify, jsonRpc),
    })

    # Close the XO connection with this WebSocket.
    connection.once('close', () ->
      $debug '- WebSocket connection'
      xoConnection.close()

      return
    )

    # Connect the WebSocket to the JSON-RPC server.
    connection.on('message', (message) ->
      jsonRpc.write(message)
      return
    )
    webSocketSendError = (error) ->
      if error
        console.error('[WARN] WebSocket send', error)
        connection.close()
      return
    jsonRpc.on('data', (data) ->
      connection.send(JSON.stringify(data), webSocketSendError)
      return
    )

    return
  )

  return

#---------------------------------------------------------------------

getVmConsoleUrl = (xo, id) ->
  vm = xo.getObject(id, ['VM', 'VM-controller'])
  return unless vm?.power_state is 'Running'

  {sessionId} = xo.getXAPI(vm)

  for console in vm.consoles
    if console.protocol is 'rfb'
      return "#{console.location}&session_id=#{sessionId}"

  return

CONSOLE_PROXY_PATH_RE = /^\/consoles\/(.*)$/
setUpConsoleProxy = (webServer, xo) ->
  webSocketServer = new $WSServer({
    noServer: true,
  })

  webServer.on('upgrade', (req, res, head) ->
    matches = CONSOLE_PROXY_PATH_RE.exec(req.url)
    return unless matches

    url = getVmConsoleUrl(xo, matches[1])
    return unless url

    webSocketServer.handleUpgrade(req, res, head, (connection) ->
      $wsProxy(connection, url)
      return
    )
    return
  )

  return

#=====================================================================

# Main.
exports = module.exports = $coroutine (args) ->
  return exports.help() unless (
    (args.indexOf '--help') is -1 and
    (args.indexOf '-h') is -1
  )

  opts = $wait loadConfiguration()

  webServer = $wait createWebServer(opts.http.listen)

  # Now the web server is listening, drop privileges.
  try
