New HTTP/HTTPS server configuration, a lot more flexible.

This commit is contained in:
Julien Fontanet 2013-12-17 16:24:22 +01:00
parent 7fc9ca96b2
commit 8b3373a7cc
4 changed files with 163 additions and 120 deletions

View File

@ -14,6 +14,11 @@ indent_style = tab
insert_final_newline = true
trim_trailing_whitespaces = true
# YAML only allows spaces.
[*.yaml]
indent_size = 2
indent_style = space
# Special settings for NPM file.
[/package.json]
indent_size = 2

View File

@ -1,59 +1,56 @@
# Configuration of XO-Server's HTTP server.
# Note: Relative paths will be resolved from XO-Server's directory.
# Configuration of the embedded HTTP server.
http:
# Is HTTP enabled?
#
# Default: true
#enabled: false
# Address on which the server is listening on.
#
# Sets it to '127.0.0.1' to listen only on the local host.
#
# Default: 0.0.0.0 (all addresses)
#host: '127.0.0.1'
# Hosts & ports on which to listen.
listen:
# Basic HTTP.
-
# Address on which the server is listening on.
#
# Sets it to '127.0.0.1' to listen only on the local host.
#
# Default: '0.0.0.0' (all addresses)
#host: '127.0.0.1'
# Port on which the server is listening on.
#
# Default: 80
#port: 8080
# Port on which the server is listening on.
#
# Default: undefined
port: 80
# Configuration of XO-Server's HTTPS server.
#
# A certificate is required, see http://www.selfsignedcertificate.com/
# or https://devcenter.heroku.com/articles/ssl-certificate-self to
# easily create a (unsecure) self-signed certificate.
https:
# Is HTTPS enabled?
#
# Default: false
#enabled: true
# Instead of `host` and `port` a path to a UNIX socket may be
# specified (overrides `host` and `port`).
#
# Default: undefined
#socket: './http.sock'
# Address on which the server is listening on.
#
# Sets it to '127.0.0.1' to listen only on the local host.
#
# Default: 0.0.0.0 (all addresses)
#host: '127.0.0.1'
# Basic HTTPS.
# -
# # The only difference is the presence of the certificate and the
# # key.
# Port on which the server is listening on.
#
# Default: 443
#port: 8081
# #host: '127.0.0.1'
# port: 443
# File containing the certificate (PEM format).
#
# Default: './certificate.pem'
#certificate: '/path/to/the/certificate.pem'
# # File containing the certificate (PEM format).
# #
# # Default: undefined
# certificate: './certificate.pem'
# File containing the private key (PEM format).
#
# If the key is encrypted, the passphrase will be asked at server
# startup.
#
# Default: './key.pem'
#key: '/path/to/the/key.pem'
# # File containing the private key (PEM format).
# #
# # If the key is encrypted, the passphrase will be asked at
# # server startup.
# #
# # Default: undefined
# key: './key.pem'
# Configuration of the Redis server.
# List of files/directories which will be served.
mounts:
'/': './public/http/'
# Connection to the Redis server.
redis:
# Syntax: tcp://[db[:password]@]hostname[:port]
#

View File

@ -1,10 +1,6 @@
# File system handling.
$fs = require 'fs'
# HTTP(S) handling.
$http = require 'http'
$https = require 'https'
#---------------------------------------------------------------------
# Low level tools.
@ -31,34 +27,11 @@ $XO = require './xo'
# Helpers for dealing with fibers.
{$fiberize, $waitForPromise} = require './fibers-utils'
# HTTP/HTTPS server which can listen on multiple ports.
$WebServer = require './web-server'
#=====================================================================
$createWebServer = ({host, port, certificate, key}) ->
# Creates the web server.
if certificate? and key?
protocol = 'HTTPS'
server = $https.createServer {
cert: certificate
key: key
}
else
protocol = 'HTTP'
server = $http.createServer()
# Starts listening.
server.listen port, host
# Prints a message when it has started to listen.
server.once 'listening', ->
console.log "#{protocol} server is listening on #{host}:#{port}"
# Prints an error message if if failed to listen.
server.once 'error', ->
console.warn "#{protocol} server could not listen on #{host}:#{port}"
# Returns the server.
server
$handleJsonRpcCall = (api, session, encodedRequest) ->
request = {
id: null
@ -122,19 +95,11 @@ do $fiberize ->
# Defines defaults configuration.
$nconf.defaults {
http: {
enabled: true
host: '0.0.0.0'
port: 80
}
https: {
enabled: false
host: '0.0.0.0'
port: 443
certificate: './certificate.pem'
key: './key.pem'
listen: []
mounts: []
}
redis: {
uri: 'tcp://127.0.0.1:6379'
# Default values are handled by `redis`.
}
}
@ -154,44 +119,35 @@ do $fiberize ->
}
}
# Creates web servers according to the configuration.
webServers = []
if $nconf.get 'http:enabled'
webServers.push $createWebServer {
host: $nconf.get 'http:host'
port: $nconf.get 'http:port'
}
if $nconf.get 'https:enabled'
webServers.push $createWebServer {
host: $nconf.get 'https:host'
port: $nconf.get 'https:port'
certificate: $nconf.get 'https:certificate'
key: $nconf.get 'https:key'
}
# Creates the web server according to the configuration.
webServer = new $WebServer()
webServer.listen options for options in $nconf.get 'http:listen'
# Static file serving (e.g. for XO-Web).
connect = $connect()
.use $connect.static "#{__dirname}/../public/http"
webServer.on 'request', connect for webServer in webServers
for urlPath, filePaths of $nconf.get 'http:mounts'
filePaths = [filePaths] unless $_.isArray filePaths
for filePath in filePaths
connect.use urlPath, $connect.static filePath
webServer.on 'request', connect
# Creates the API.
api = new $API xo
# JSON-RPC over WebSocket.
for webServer in webServers
new $WSServer({
server: webServer
path: '/api/'
}).on 'connection', (socket) ->
# Binds a session to this connection.
session = new $Session xo
session.once 'close', -> socket.close()
socket.once 'close', -> session.close()
# # JSON-RPC over WebSocket.
new $WSServer({
server: webServer
path: '/api/'
}).on 'connection', (socket) ->
# Binds a session to this connection.
session = new $Session xo
session.once 'close', -> socket.close()
socket.once 'close', -> session.close()
# Handles each request in a separate fiber.
socket.on 'message', $fiberize (request) ->
response = $handleJsonRpcCall api, session, request
# Handles each request in a separate fiber.
socket.on 'message', $fiberize (request) ->
response = $handleJsonRpcCall api, session, request
# The socket may have closed beetween the request and the
# response.
socket.send response if socket.readyState is socket.OPEN
# The socket may have closed beetween the request and the
# response.
socket.send response if socket.readyState is socket.OPEN

85
src/web-server.coffee Normal file
View File

@ -0,0 +1,85 @@
# Events handling.
{EventEmitter: $EventEmitter} = require 'events'
# File handling.
$fs = require 'fs'
# HTTP(S) handling.
$http = require 'http'
$https = require 'https'
#---------------------------------------------------------------------
# Low level tools.
$_ = require 'underscore'
#---------------------------------------------------------------------
# Helpers for dealing with fibers.
{$sleep, $synchronize} = require './fibers-utils'
#=====================================================================
# Events which may be emitted by a `http(s).Server`
$events = [
'checkContinue'
'clientError'
'close'
'connect'
'connection'
'error'
'request'
'upgrade'
]
$readFile = $synchronize 'readFile', $fs
#=====================================================================
# HTTP/HTTPS server which can listen on multiple address/ports or
# sockets.
class $WebServer extends $EventEmitter
constructor: ->
@_servers = []
close: ->
server.close() for server in @_servers
# Does not return anything.
listen: ({host, port, socket, certificate, key}) ->
server = if certificate? and key?
$https.createServer {
cert: $readFile certificate
key: $readFile key
}
else
$http.createServer()
@_servers.push server
# Makes it start listening.
if socket?
server.listen socket
else
server.listen port, host
# Helpful message.
server.once 'listening', ->
address = server.address()
if $_.isObject address
{address, port} = address
address = "#{address}:#{port}"
console.log "WebServer listening on #{address}"
# Forwards events to this object.
$_.each $events, (event) =>
server.on event, (args...) => @emit event, args...
# Does not return anything.
undefined
#=====================================================================
module.exports = $WebServer