weinre.coffee 6.91 KB
#-------------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#-------------------------------------------------------------------------------

fs   = require 'fs'
net  = require 'net'
dns  = require 'dns'
path = require 'path'

_       = require 'underscore'
express = require 'express'

utils              = require './utils'
jsonBodyParser     = require './jsonBodyParser'
HttpChannelHandler = require './HttpChannelHandler'
dumpingHandler     = require './dumpingHandler'
channelManager     = require './channelManager'
serviceManager     = require './serviceManager'

#-------------------------------------------------------------------------------
exports.run = (options) ->
    processOptions(options, run2)

#-------------------------------------------------------------------------------
run2 = ->
    options = utils.options
    
    serviceManager.registerProxyClass 'WeinreClientEvents'
    serviceManager.registerProxyClass 'WeinreTargetEvents'
    serviceManager.registerLocalClass 'WeinreClientCommands'
    serviceManager.registerLocalClass 'WeinreTargetCommands'
    
    startDeathWatcher options.deathTimeout
    
    startServer()    

#-------------------------------------------------------------------------------
processOptions = (options, cb) ->
    options.httpPort     = utils.ensureInteger( options.httpPort,     'the value of the option httpPort is not a number')
    options.boundHost    = utils.ensureString(  options.boundHost,    'the value of the option boundHost is not a string')
    options.verbose      = utils.ensureBoolean( options.verbose,      'the value of the option verbose is not a boolean')
    options.debug        = utils.ensureBoolean( options.debug,        'the value of the option debug is not a boolean')
    options.readTimeout  = utils.ensureInteger( options.readTimeout,  'the value of the option readTimeout is not a number')
    options.deathTimeout = utils.ensureInteger( options.deathTimeout, 'the value of the option deathTimeout is not a number')

    options.verbose = true if options.debug
    
    options.staticWebDir = getStaticWebDir()
    
    utils.logVerbose "pid:                 #{process.pid}"
    utils.logVerbose "version:             #{getVersion()}"
    utils.logVerbose "node versions:"
    
    names   = _.keys(process.versions)
    reducer = (memo, name) -> Math.max(memo, name.length)
    nameLen = _.reduce(names, reducer, 0)
    
    for name in names
        utils.logVerbose "   #{utils.alignLeft(name, nameLen)}: #{process.versions[name]}"
    
    utils.logVerbose "options:"
    utils.logVerbose "   httpPort:     #{options.httpPort}"
    utils.logVerbose "   boundHost:    #{options.boundHost}"
    utils.logVerbose "   verbose:      #{options.verbose}"
    utils.logVerbose "   debug:        #{options.debug}"
    utils.logVerbose "   readTimeout:  #{options.readTimeout}"
    utils.logVerbose "   deathTimeout: #{options.deathTimeout}"

    utils.setOptions options

    checkHost options.boundHost, (err) ->
        if err
            utils.exit "unable to resolve boundHost address: #{options.boundHost}"

        cb()

#-------------------------------------------------------------------------------
checkHost = (hostName, cb) ->
    return cb() if hostName == '-all-'
    return cb() if hostName == 'localhost'
    
    return cb() if net.isIP(hostName)
    
    dns.lookup hostName, cb

#-------------------------------------------------------------------------------
deathTimeout = null

#-------------------------------------------------------------------------------
startDeathWatcher = (timeout) ->
    deathTimeout = utils.options.deathTimeout * 1000
    
    setInterval checkForDeath, 1000

#-------------------------------------------------------------------------------
checkForDeath = ->
    now = (new Date).valueOf()
    for channel in channelManager.getChannels()
        if now - channel.lastRead > deathTimeout
            channel.close()

#-------------------------------------------------------------------------------
startServer = () ->
    options = utils.options

    clientHandler = new HttpChannelHandler('/ws/client')
    targetHandler = new HttpChannelHandler('/ws/target')
    
    channelManager.initialize()
    
    favIcon = "#{options.staticWebDir}/images/weinre-icon-32x32.png"

    staticCacheOptions =
        maxObjects: 500
        maxLength:  32 * 1024 * 1024
        
    app = express.createServer()
    
    app.on 'error', (error) ->
        utils.exit "error running server: #{error}"

    app.use express.favicon(favIcon)

    app.use jsonBodyParser()

    app.all /^\/ws\/client(.*)/, (request, response, next) ->
        uri = request.params[0]
        uri = '/' if uri == ''
        
        dumpingHandler(request, response, uri) if options.debug
        clientHandler.handle(request, response, uri)

    app.all /^\/ws\/target(.*)/, (request, response, next) ->
        uri = request.params[0]
        uri = '/' if uri == ''

        dumpingHandler(request, response, uri) if options.debug
        targetHandler.handle(request, response, uri)

    app.use express.errorHandler(dumpExceptions: true)

    app.use express.staticCache(staticCacheOptions)
    app.use express.static(options.staticWebDir)
    
    if options.boundHost == '-all-'
        utils.log "starting server at http://localhost:#{options.httpPort}"
        app.listen options.httpPort
        
    else
        utils.log "starting server at http://#{options.boundHost}:#{options.httpPort}"
        app.listen options.httpPort, options.boundHost

#-------------------------------------------------------------------------------
getStaticWebDir = () ->
    webDir = path.normalize path.join(__dirname,'../web')
    return webDir if utils.fileExistsSync webDir
    
    utils.exit 'unable to find static files to serve in #{webDir}; did you do a build?'
    
#-------------------------------------------------------------------------------
Version = null
getVersion = exports.getVersion = () ->
    return Version if Version 

    packageJsonName  = path.join(path.dirname(fs.realpathSync(__filename)), '../package.json')

    json = fs.readFileSync(packageJsonName, 'utf8')
    values = JSON.parse(json)

    Version = values.version
    return Version