UNPKG

hem

Version:

stitches CommonJS, and ties up other lose ends of web-app development.

154 lines (135 loc) 5.33 kB
connect = require('connect') mime = require('connect').static.mime httpProxy = require('http-proxy') http = require('http') fs = require('fs') utils = require('./utils') log = require('./log') # ------- Public Functions server = {} server.start = (hem) -> # create app to configure app = connect() app.use(server.middleware(hem)) # start server options = hem.options.hem if options.host is "*" http.createServer(app).listen(options.port) else http.createServer(app).listen(options.port, options.host) return app server.middleware = (hem) -> backend = connect() options = hem.options.hem statics = [] # create array of static routes for route, value of options?.static statics.push url : route path : value # determine if there is any dynamic or static routes to add for app in hem.apps # if verbose then print our mappings and apply the baseAppRoute if present log.info "> Apply route mappings for application: <green>#{app.name}</green>" for pkg in app.packages if options.baseAppRoute pkg.route = utils.cleanRoute(options.baseAppRoute, pkg.route) log.info " - Mapping route <yellow>#{pkg.route}</yellow> to <yellow>#{pkg.target}</yellow>" # loop over potential static routes and add them to main route array for route in app.static if options.baseAppRoute route.url = utils.cleanRoute(options.baseAppRoute, route.url) log.info " - Mapping static <yellow>#{route.url}</yellow> to <yellow>#{route.path}</yellow>" statics.push(route) # setup separate connect app for static routes and proxy middleware for route in statics # make sure path exists unless fs.existsSync(route.path) log.errorAndExit "The resource <yellow>#{route.path}</yellow> not found for static mapping <yellow>#{route.url}</yellow>" # test if file is directory or file.... if fs.lstatSync(route.path).isDirectory() backend.use(route.url, checkForRedirect()) backend.use(route.url, connect.static(route.path) ) else backend.use route.url, do (route) -> (req, res) -> fs.readFile route.path, (err, data) -> if err res.writeHead(404) res.end(JSON.stringify(err)) else res.writeHead(200) res.end(data) # setup proxy route for route, value of options.proxy display = "#{value.host}:#{value.port or 80}#{value.path}" log.info "> Proxy requests <yellow>#{route}</yellow> to <yellow>#{display}</yellow>" backend.use(route, createRoutingProxy(value, "#{options.host}:#{options.port}")) # return the custom middleware for connect to use return (req, res, next) -> # get url path url = require("url").parse(req.url)?.pathname.toLowerCase() or "" # loop over applications and call compile when there is a match if url.match(/(\.js|\.css)$/) for app in hem.apps if pkg = app.isMatchingRoute(url) str = pkg.build() res.charset = 'utf-8' res.setHeader('Content-Type', mime.lookup(pkg.target)) res.setHeader('Content-Length', Buffer.byteLength(str)) res.end((req.method is 'HEAD' and null) or str) return # pass request to static connect app to handle static/proxy requests backend.handle(req, res, next) # ------- Private Functions checkForRedirect = () -> return (req, res, next) -> pathname = require("url").parse(req.originalUrl).pathname if (req.url is "/" and not utils.endsWith(pathname,"/")) pathname += '/' res.statusCode = 301 res.setHeader('Location', pathname) res.end('Redirecting to ' + pathname) else next() createRoutingProxy = (options, returnHost) -> # handle https differently if options.https routingProxyOptions = {target: {https:true}} defaultPort = 443 else routingProxyOptions = {} defaultPort = 80 # create proxy proxy = new httpProxy.RoutingProxy(routingProxyOptions) # set options options.path or= "" options.port or= defaultPort options.patchRedirect or= true # handle redirects if options.patchRedirect proxy.once "start", (req, res) -> # get the requesting hostname and port patchServerResponseForRedirects(options.host, returnHost) # return function used by connect to access proxy return (req, res, next) -> req.url = "#{options.path}#{req.url}" req.headers.host = options.host proxy.proxyRequest(req, res, options) patchServerResponseForRedirects = (fromHost, returnHost) -> writeHead = http.ServerResponse.prototype.writeHead http.ServerResponse.prototype.writeHead = (status) -> headers = @_headers or {} if status in [301,302] oldLocation = new RegExp("s?:\/\/#{fromHost}:?[0-9]*") newLocation = "://#{returnHost}" headers.location = headers.location.replace(oldLocation,newLocation) if 'set-cookie' of headers newSetCookie = [] for cookie in headers['set-cookie'] # remove the secure setting, so we can get cookie setting to work newSetCookie.push cookie.replace /; Secure; HttpOnly/, '' headers['set-cookie'] = newSetCookie return writeHead.apply(@, arguments) # export the public functions module.exports = server