Return-Path: X-Original-To: apmail-cordova-commits-archive@www.apache.org Delivered-To: apmail-cordova-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id EFC11113BD for ; Sat, 30 Aug 2014 18:42:35 +0000 (UTC) Received: (qmail 99986 invoked by uid 500); 30 Aug 2014 18:42:35 -0000 Delivered-To: apmail-cordova-commits-archive@cordova.apache.org Received: (qmail 99919 invoked by uid 500); 30 Aug 2014 18:42:35 -0000 Mailing-List: contact commits-help@cordova.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cordova.apache.org Delivered-To: mailing list commits@cordova.apache.org Received: (qmail 99592 invoked by uid 99); 30 Aug 2014 18:42:35 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 30 Aug 2014 18:42:35 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 60F4E9AB634; Sat, 30 Aug 2014 18:42:35 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: pmuellr@apache.org To: commits@cordova.apache.org Date: Sat, 30 Aug 2014 18:42:47 -0000 Message-Id: In-Reply-To: <9da7f6caddd8430a825c3a11bbfee077@git.apache.org> References: <9da7f6caddd8430a825c3a11bbfee077@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [14/14] weinre commit: pre-compile CoffeeScript files in server pre-compile CoffeeScript files in server Project: http://git-wip-us.apache.org/repos/asf/cordova-weinre/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-weinre/commit/9b06777d Tree: http://git-wip-us.apache.org/repos/asf/cordova-weinre/tree/9b06777d Diff: http://git-wip-us.apache.org/repos/asf/cordova-weinre/diff/9b06777d Branch: refs/heads/CB-7430 Commit: 9b06777d6b23d1d2785b66a0e3f6f6c754561a08 Parents: feb2f06 Author: Patrick Mueller Authored: Sat Aug 30 14:42:05 2014 -0400 Committer: Patrick Mueller Committed: Sat Aug 30 14:42:05 2014 -0400 ---------------------------------------------------------------------- .wr | 2 +- weinre.build/build.xml | 21 + weinre.build/package.json.template | 10 +- weinre.server/lib-src/Channel.coffee | 118 + weinre.server/lib-src/HttpChannelHandler.coffee | 154 ++ weinre.server/lib-src/MessageQueue.coffee | 87 + weinre.server/lib-src/channelManager.coffee | 126 ++ weinre.server/lib-src/cli.coffee | 130 ++ weinre.server/lib-src/dumpingHandler.coffee | 77 + weinre.server/lib-src/extensionManager.coffee | 30 + weinre.server/lib-src/jsonBodyParser.coffee | 47 + weinre.server/lib-src/messageHandler.coffee | 59 + .../lib-src/service/WeinreClientCommands.coffee | 126 ++ .../lib-src/service/WeinreTargetCommands.coffee | 90 + weinre.server/lib-src/serviceManager.coffee | 95 + weinre.server/lib-src/utils.coffee | 196 ++ weinre.server/lib-src/weinre.coffee | 186 ++ weinre.server/lib/Channel.coffee | 118 - weinre.server/lib/Channel.js | 130 ++ weinre.server/lib/HttpChannelHandler.coffee | 154 -- weinre.server/lib/HttpChannelHandler.js | 130 ++ weinre.server/lib/MessageQueue.coffee | 87 - weinre.server/lib/MessageQueue.js | 82 + weinre.server/lib/channelManager.coffee | 126 -- weinre.server/lib/channelManager.js | 122 + weinre.server/lib/cli.coffee | 130 -- weinre.server/lib/cli.js | 97 + weinre.server/lib/dumpingHandler.coffee | 77 - weinre.server/lib/dumpingHandler.js | 74 + weinre.server/lib/extensionManager.coffee | 30 - weinre.server/lib/extensionManager.js | 15 + weinre.server/lib/jsonBodyParser.coffee | 47 - weinre.server/lib/jsonBodyParser.js | 39 + weinre.server/lib/messageHandler.coffee | 59 - weinre.server/lib/messageHandler.js | 51 + .../lib/service/WeinreClientCommands.coffee | 126 -- .../lib/service/WeinreClientCommands.js | 140 ++ .../lib/service/WeinreTargetCommands.coffee | 90 - .../lib/service/WeinreTargetCommands.js | 78 + weinre.server/lib/serviceManager.coffee | 95 - weinre.server/lib/serviceManager.js | 90 + weinre.server/lib/utils.coffee | 196 -- weinre.server/lib/utils.js | 194 ++ weinre.server/lib/weinre.coffee | 186 -- weinre.server/lib/weinre.js | 193 ++ weinre.server/node_modules/.bin/cake | 2 +- weinre.server/node_modules/.bin/coffee | 2 +- weinre.server/node_modules/.bin/express | 2 +- weinre.server/node_modules/.bin/nopt | 2 +- .../node_modules/coffee-script/CONTRIBUTING.md | 9 + .../node_modules/coffee-script/LICENSE | 4 +- weinre.server/node_modules/coffee-script/README | 21 +- .../node_modules/coffee-script/README.md | 60 + .../node_modules/coffee-script/Rakefile | 78 - .../node_modules/coffee-script/extras/jsl.conf | 44 - .../coffee-script/lib/coffee-script/browser.js | 100 +- .../coffee-script/lib/coffee-script/cake.js | 11 +- .../lib/coffee-script/coffee-script.js | 296 ++- .../coffee-script/lib/coffee-script/command.js | 554 +++-- .../coffee-script/lib/coffee-script/grammar.js | 79 +- .../coffee-script/lib/coffee-script/helpers.js | 181 +- .../coffee-script/lib/coffee-script/index.js | 2 +- .../coffee-script/lib/coffee-script/lexer.js | 412 ++-- .../coffee-script/lib/coffee-script/nodes.js | 1578 +++++++------ .../coffee-script/lib/coffee-script/optparse.js | 9 +- .../coffee-script/lib/coffee-script/parser.js | 753 ++++--- .../coffee-script/lib/coffee-script/register.js | 66 + .../coffee-script/lib/coffee-script/repl.js | 381 ++-- .../coffee-script/lib/coffee-script/rewriter.js | 402 ++-- .../coffee-script/lib/coffee-script/scope.js | 6 +- .../lib/coffee-script/sourcemap.js | 161 ++ .../node_modules/mkdirp/.npmignore | 2 + .../node_modules/mkdirp/.travis.yml | 5 + .../coffee-script/node_modules/mkdirp/LICENSE | 21 + .../node_modules/mkdirp/examples/pow.js | 6 + .../coffee-script/node_modules/mkdirp/index.js | 82 + .../node_modules/mkdirp/package.json | 34 + .../node_modules/mkdirp/readme.markdown | 63 + .../node_modules/mkdirp/test/chmod.js | 38 + .../node_modules/mkdirp/test/clobber.js | 37 + .../node_modules/mkdirp/test/mkdirp.js | 28 + .../node_modules/mkdirp/test/perm.js | 32 + .../node_modules/mkdirp/test/perm_sync.js | 39 + .../node_modules/mkdirp/test/race.js | 41 + .../node_modules/mkdirp/test/rel.js | 32 + .../node_modules/mkdirp/test/return.js | 25 + .../node_modules/mkdirp/test/return_sync.js | 24 + .../node_modules/mkdirp/test/root.js | 18 + .../node_modules/mkdirp/test/sync.js | 32 + .../node_modules/mkdirp/test/umask.js | 28 + .../node_modules/mkdirp/test/umask_sync.js | 32 + .../node_modules/coffee-script/package.json | 38 +- .../node_modules/coffee-script/register.js | 1 + .../node_modules/coffee-script/repl.js | 1 + weinre.server/node_modules/express/History.md | 5 + .../node_modules/express/lib/express.js | 2 +- .../node_modules/express/lib/request.js | 23 +- .../express/node_modules/connect/lib/connect.js | 2 +- .../node_modules/connect/lib/middleware/csrf.js | 2 +- .../connect/node_modules/formidable/.npmignore | 5 +- .../connect/node_modules/formidable/.travis.yml | 5 +- .../connect/node_modules/formidable/LICENSE | 7 + .../connect/node_modules/formidable/Makefile | 14 - .../connect/node_modules/formidable/Readme.md | 468 ++-- .../connect/node_modules/formidable/TODO | 3 - .../benchmark/bench-multipart-parser.js | 70 - .../node_modules/formidable/example/post.js | 43 - .../node_modules/formidable/example/upload.js | 48 - .../connect/node_modules/formidable/index.js | 2 +- .../connect/node_modules/formidable/lib/file.js | 45 +- .../formidable/lib/incoming_form.js | 285 ++- .../node_modules/formidable/lib/json_parser.js | 35 + .../formidable/lib/multipart_parser.js | 50 +- .../node_modules/formidable/lib/octet_parser.js | 20 + .../formidable/lib/querystring_parser.js | 10 +- .../connect/node_modules/formidable/lib/util.js | 6 - .../formidable/node-gently/Makefile | 4 - .../formidable/node-gently/Readme.md | 167 -- .../formidable/node-gently/example/dog.js | 22 - .../node-gently/example/event_emitter.js | 11 - .../formidable/node-gently/index.js | 1 - .../formidable/node-gently/lib/gently/gently.js | 184 -- .../formidable/node-gently/lib/gently/index.js | 1 - .../formidable/node-gently/package.json | 14 - .../formidable/node-gently/test/common.js | 8 - .../node-gently/test/simple/test-gently.js | 348 --- .../node_modules/formidable/package.json | 27 +- .../node_modules/formidable/test/common.js | 19 - .../test/fixture/file/funkyfilename.txt | 1 - .../formidable/test/fixture/file/plain.txt | 1 - .../http/special-chars-in-filename/info.md | 3 - .../formidable/test/fixture/js/no-filename.js | 3 - .../fixture/js/special-chars-in-filename.js | 21 - .../formidable/test/fixture/multipart.js | 72 - .../test/integration/test-fixtures.js | 89 - .../formidable/test/legacy/common.js | 24 - .../legacy/integration/test-multipart-parser.js | 80 - .../formidable/test/legacy/simple/test-file.js | 104 - .../test/legacy/simple/test-incoming-form.js | 727 ------ .../test/legacy/simple/test-multipart-parser.js | 50 - .../legacy/simple/test-querystring-parser.js | 45 - .../legacy/system/test-multi-video-upload.js | 75 - .../connect/node_modules/formidable/test/run.js | 2 - .../formidable/test/unit/test-incoming-form.js | 63 - .../node_modules/formidable/tool/record.js | 47 - .../express/node_modules/connect/package.json | 9 +- .../express/node_modules/connect/test.js | 39 +- .../express/node_modules/mime/package.json | 10 +- .../express/node_modules/mkdirp/package.json | 8 +- .../express/node_modules/qs/package.json | 6 + weinre.server/node_modules/express/package.json | 10 +- weinre.server/node_modules/express/test.js | 3 + weinre.server/node_modules/nopt/.npmignore | 1 + weinre.server/node_modules/nopt/README.md | 15 +- weinre.server/node_modules/nopt/bin/nopt.js | 14 +- weinre.server/node_modules/nopt/lib/nopt.js | 282 +-- .../nopt/node_modules/abbrev/CONTRIBUTING.md | 3 + .../nopt/node_modules/abbrev/LICENSE | 23 + .../nopt/node_modules/abbrev/abbrev.js | 62 + .../nopt/node_modules/abbrev/lib/abbrev.js | 106 - .../nopt/node_modules/abbrev/package.json | 17 +- .../nopt/node_modules/abbrev/test.js | 47 + weinre.server/node_modules/nopt/package.json | 18 +- weinre.server/node_modules/nopt/test/basic.js | 251 +++ .../node_modules/underscore/.npmignore | 3 - weinre.server/node_modules/underscore/CNAME | 1 - weinre.server/node_modules/underscore/LICENSE | 5 +- weinre.server/node_modules/underscore/README.md | 25 +- .../node_modules/underscore/favicon.ico | Bin 1406 -> 0 bytes .../node_modules/underscore/index.html | 2109 ------------------ weinre.server/node_modules/underscore/index.js | 1 - .../node_modules/underscore/package.json | 38 +- .../node_modules/underscore/raw/underscore.psd | Bin 215540 -> 0 bytes .../node_modules/underscore/underscore-min.js | 38 +- .../node_modules/underscore/underscore.js | 1390 +++++++----- weinre.server/weinre | 11 +- 176 files changed, 8950 insertions(+), 9342 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/.wr ---------------------------------------------------------------------- diff --git a/.wr b/.wr index 265dd0a..caf5fe6 100644 --- a/.wr +++ b/.wr @@ -7,7 +7,7 @@ weinre.build/scripts weinre.doc weinre.server/interfaces -weinre.server/lib +weinre.server/lib-src weinre.server/package.json weinre.server/README.md http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.build/build.xml ---------------------------------------------------------------------- diff --git a/weinre.build/build.xml b/weinre.build/build.xml index 5324454..17f58d5 100644 --- a/weinre.build/build.xml +++ b/weinre.build/build.xml @@ -174,6 +174,7 @@ To update the weinre dependencies, use >git: ${git-log} + @@ -202,6 +203,26 @@ To update the weinre dependencies, use + + + + + + + + + + + + + + + + + + http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.build/package.json.template ---------------------------------------------------------------------- diff --git a/weinre.build/package.json.template b/weinre.build/package.json.template index 71f3762..1779836 100644 --- a/weinre.build/package.json.template +++ b/weinre.build/package.json.template @@ -21,10 +21,13 @@ }, "dependencies": { - "coffee-script": "1.3.x", "express": "2.5.x", - "nopt": "1.0.x", - "underscore": "1.3.x" + "nopt": "3.0.x", + "underscore": "1.7.x" + }, + "devDependencies": + { + "coffee-script": "1.8.x" }, "main" : "./lib/weinre", "bin": @@ -39,4 +42,3 @@ "url": "https://git-wip-us.apache.org/repos/asf/cordova-weinre.git" } } - http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/Channel.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/Channel.coffee b/weinre.server/lib-src/Channel.coffee new file mode 100644 index 0000000..70f5841 --- /dev/null +++ b/weinre.server/lib-src/Channel.coffee @@ -0,0 +1,118 @@ +#------------------------------------------------------------------------------- +# 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. +#------------------------------------------------------------------------------- + +_ = require 'underscore' + +utils = require './utils' +channelManager = require './channelManager' +messageHandler = require './messageHandler' +MessageQueue = require './MessageQueue' + +AnonymousId = 'anonymous' + +#------------------------------------------------------------------------------- +module.exports = utils.registerClass class Channel + + #--------------------------------------------------------------------------- + constructor: (@pathPrefix, @id, @remoteAddress, @isClient) -> + prefix = if @isClient then 'c-' else 't-' + @name = "#{prefix}#{utils.getNextSequenceNumber()}" + @messageQueue = new MessageQueue + @isClosed = false + @connections = [] + @isTarget = !@isClient + @readTimeout = utils.options.readTimeout * 1000 + + @id = AnonymousId if !@id + + @description = + channel: @name + id: @id + hostName: @remoteAddress + remoteAddress: @remoteAddress + + @updateLastRead() + + channelManager.created @ + + #--------------------------------------------------------------------------- + close: () -> + return if @isClosed + + channelManager.destroyed @ + + @isClosed = true + @messageQueue.shutdown() + + #--------------------------------------------------------------------------- + sendCallback: (intfName, callbackId, args...) -> + return if !callbackId + + args.unshift callbackId + + @sendMessage intfName, 'sendCallback', args... + + #--------------------------------------------------------------------------- + sendMessage: (intfName, method, args...) -> + + message = genJSON + interface: intfName + method: method + args: args + + @messageQueue.push message + + #--------------------------------------------------------------------------- + handleMessages: (messages) -> + + for message in messages + message = parseJSON(message) + continue if !message + + messageHandler.handleMessage @, message + + #--------------------------------------------------------------------------- + getMessages: (callback) -> + @updateLastRead() + return callback.call(null, null) if @isClosed + + @messageQueue.pullAll @readTimeout, callback + + #--------------------------------------------------------------------------- + updateLastRead: () -> + @lastRead = (new Date).valueOf() + + #--------------------------------------------------------------------------- + toString: () -> + connections = _.map(@connections, (val) -> val.name).join(',') + "Channel(#{@name}, closed:#{@isClosed}, connections:[#{connections}])" + +#------------------------------------------------------------------------------- +parseJSON = (message) -> + try + return JSON.parse(message) + catch e + return null + +#------------------------------------------------------------------------------- +genJSON = (message) -> + try + return JSON.stringify(message) + catch e + return null http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/HttpChannelHandler.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/HttpChannelHandler.coffee b/weinre.server/lib-src/HttpChannelHandler.coffee new file mode 100644 index 0000000..dd7f2f4 --- /dev/null +++ b/weinre.server/lib-src/HttpChannelHandler.coffee @@ -0,0 +1,154 @@ +#------------------------------------------------------------------------------- +# 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. +#------------------------------------------------------------------------------- + +_ = require 'underscore' + +utils = require './utils' +Channel = require './Channel' +channelManager = require './channelManager' + +#------------------------------------------------------------------------------- +module.exports = utils.registerClass class HttpChannelHandler + + #--------------------------------------------------------------------------- + constructor: (@pathPrefix) -> + + if @pathPrefix == '/ws/client' + @isClient = true + + else if @pathPrefix == '/ws/target' + @isClient = false + + else + utils.pitch "invalid pathPrefix: #{@pathPrefix}" + + @isTarget = !@isClient + + #--------------------------------------------------------------------------- + handle: (request, response, uri) -> + + setCORSHeaders request, response + setCacheHeaders request, response + + #----------------- + + # * #{pathPrefix}a + if uri[0] != '/' + return handleError(request, response, 404) + + #----------------- + + if uri == '/' + + # OPTIONS #{pathPrefix}/ + if request.method == 'OPTIONS' + return handleOptions(request, response) + + # POST #{pathPrefix}/ + if request.method == 'POST' + return handleCreate(@pathPrefix, @isClient, request, response) + + # * #{pathPrefix}/ + return handleError(request, response, 405) + + #----------------- + + parts = uri.split('/') + + # * #{pathPrefix}/x/y + if parts.length > 2 + return handleError(request, response, 404) + + #----------------- + + channelName = parts[1] + + # OPTIONS #{pathPrefix}/x + if request.method == 'OPTIONS' + return handleOptions(request, response) + + # GET #{pathPrefix}/x + if request.method == 'GET' + return handleGet(request, response, channelName) + + # POST #{pathPrefix}/x + if request.method == 'POST' + return handlePost(request, response, channelName) + + # anything else + return handleError(request, response, 405) + +#------------------------------------------------------------------------------- +handleCreate = (pathPrefix, isClient, request, response) -> + id = request.body?.id + + remoteAddress = request.connection?.remoteAddress || "" + + channel = new Channel(pathPrefix, id, remoteAddress, isClient) + + response.contentType 'application/json' + response.send JSON.stringify + channel: channel.name + id: channel.id + +#------------------------------------------------------------------------------- +handleGet = (request, response, channelName) -> + remoteAddress = request.connection?.remoteAddress || "" + channel = channelManager.getChannel(channelName, remoteAddress) + return handleError(request, response, 404) if !channel + + channel.getMessages (messages) => + return handleError(request, response, 404) if channel.isClosed + return handleError(request, response, 404) if !messages + + response.contentType 'application/json' + response.send JSON.stringify(messages) + +#------------------------------------------------------------------------------- +handlePost = (request, response, channelName) -> + remoteAddress = request.connection?.remoteAddress || "" + channel = channelManager.getChannel(channelName, remoteAddress) + return handleError(request, response, 404) if !channel + + channel.handleMessages(request.body) + response.send('') + +#------------------------------------------------------------------------------- +handleOptions = (request, response) -> + response.send('') + +#------------------------------------------------------------------------------- +handleError = (request, response, status) -> + response.send(status) + +#------------------------------------------------------------------------------- +setCORSHeaders = (request, response) -> + origin = request.header 'Origin' + return if !origin + + response.header 'Access-Control-Allow-Origin', origin + response.header 'Access-Control-Max-Age', '600' + response.header 'Access-Control-Allow-Methods', 'GET, POST' + +#------------------------------------------------------------------------------- +setCacheHeaders = (request, response) -> + response.header 'Pragma', 'no-cache' + response.header 'Expires', '0' + response.header 'Cache-Control', 'no-cache' + response.header 'Cache-Control', 'no-store' http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/MessageQueue.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/MessageQueue.coffee b/weinre.server/lib-src/MessageQueue.coffee new file mode 100644 index 0000000..52af9ea --- /dev/null +++ b/weinre.server/lib-src/MessageQueue.coffee @@ -0,0 +1,87 @@ +#------------------------------------------------------------------------------- +# 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. +#------------------------------------------------------------------------------- + +_ = require 'underscore' + +utils = require './utils' + +#------------------------------------------------------------------------------- +module.exports = utils.registerClass class MessageQueue + + #--------------------------------------------------------------------------- + constructor: () -> + @messages = [] + @closed = false + @callback = null + @timer = null + + _.bindAll @, '_timerExpired', '_updated' + + #--------------------------------------------------------------------------- + shutdown: () -> + return if @closed + + @closed = true + + clearTimeout @timer if @timer + @callback.call(null, @messages) if @callback + + @callback = null + @messages = null + @timer = null + + #--------------------------------------------------------------------------- + push: (message) -> + return if @closed + + @messages.push message + process.nextTick @_updated + + #--------------------------------------------------------------------------- + pullAll: (timeout, callback) -> + return callback.call(null, null) if @closed + return callback.call(null, []) if @callback + + if @messages.length + callback.call(null, @messages) + @messages = [] + return + + @callback = callback + @timer = setTimeout @_timerExpired, timeout + + #--------------------------------------------------------------------------- + _timerExpired: () -> + @_updated() + + #--------------------------------------------------------------------------- + _updated: () -> + return if @closed + return if !@callback + + callback = @callback + messages = @messages + clearTimeout @timer if @timer + + @callback = null + @messages = [] + @timer = null + + callback.call(null, messages) + http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/channelManager.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/channelManager.coffee b/weinre.server/lib-src/channelManager.coffee new file mode 100644 index 0000000..d55bb01 --- /dev/null +++ b/weinre.server/lib-src/channelManager.coffee @@ -0,0 +1,126 @@ +#------------------------------------------------------------------------------- +# 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. +#------------------------------------------------------------------------------- + +_ = require 'underscore' + +utils = require './utils' +serviceManager = require './serviceManager' + +WeinreClientEvents = null +WeinreTargetEvents = null + +channelManager = null + +#------------------------------------------------------------------------------- +utils.registerClass class ChannelManager + + #--------------------------------------------------------------------------- + constructor: () -> + @channels = {} + + #--------------------------------------------------------------------------- + initialize: -> + + WeinreClientEvents = serviceManager.get 'WeinreClientEvents' + WeinreTargetEvents = serviceManager.get 'WeinreTargetEvents' + + if !WeinreClientEvents + utils.exit 'WeinreClientEvents service not registered' + + if !WeinreTargetEvents + utils.exit 'WeinreTargetEvents service not registered' + + #--------------------------------------------------------------------------- + created: (channel) -> + @channels[channel.name] = channel + + #--------------------------------------------------------------------------- + destroyed: (channel) -> + if channel.isClient + for connection in channel.connections + @disconnectChannels(channel, connection) + else + for connection in channel.connections + @disconnectChannels(connection, channel) + + clients = @getClientChannels(channel.id) + + if channel.isClient + WeinreClientEvents.clientUnregistered(clients, channel.name) + else + WeinreClientEvents.targetUnregistered(clients, channel.name) + + delete @channels[channel.name] + + #--------------------------------------------------------------------------- + getChannel: (name, remoteAddress) -> + return null if !_.has(@channels, name) + + channel = @channels[name] + + return null if !channel + +# if remoteAddress +# return null if channel.remoteAddress != remoteAddress + + channel + + #--------------------------------------------------------------------------- + connectChannels: (client, target) -> + return if client.isClosed or target.isClosed + + if client.connections.length + @disconnectChannels(client, client.connections[0]) + + client.connections.push target + target.connections.push client + + clients = @getClientChannels(client.id) + + WeinreClientEvents.connectionCreated(clients, client.name, target.name) + WeinreTargetEvents.connectionCreated(target, client.name, target.name) + + #--------------------------------------------------------------------------- + disconnectChannels: (client, target) -> + + clients = @getClientChannels(client.id) + + WeinreClientEvents.connectionDestroyed(clients, client.name, target.name) + WeinreTargetEvents.connectionDestroyed(target, client.name, target.name) + + client.connections = _.without(client.connections, target) + target.connections = _.without(target.connections, client) + + #--------------------------------------------------------------------------- + getChannels: (id) -> + if id? + _.filter(@channels, (item) -> item.id == id) + else + _.values(@channels) + + #--------------------------------------------------------------------------- + getClientChannels: (id) -> + _.filter(@channels, (item) -> item.isClient && item.id == id) + + #--------------------------------------------------------------------------- + getTargetChannels: (id) -> + _.filter(@channels, (item) -> item.isTarget && item.id == id) + +#------------------------------------------------------------------------------- +module.exports = new ChannelManager http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/cli.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/cli.coffee b/weinre.server/lib-src/cli.coffee new file mode 100644 index 0000000..197e39c --- /dev/null +++ b/weinre.server/lib-src/cli.coffee @@ -0,0 +1,130 @@ +#------------------------------------------------------------------------------- +# 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' +path = require 'path' + +_ = require 'underscore' +nopt = require 'nopt' + +utils = require './utils' +weinre = require './weinre' + +optionDefaults = + httpPort: 8080 + boundHost: 'localhost' + verbose: false + debug: false + readTimeout: 5 + +#------------------------------------------------------------------------------- +exports.run = -> + + knownOpts = + httpPort: Number + boundHost: String + verbose: Boolean + debug: Boolean + readTimeout: Number + deathTimeout: Number + help: Boolean + + shortHands = + '?': ['--help'] + 'h': ['--help'] + + nopt.invalidHandler = printNoptError + parsedOpts = nopt(knownOpts, shortHands, process.argv, 2) + + #---- + + printHelp() if parsedOpts.help + + args = parsedOpts.argv.remain + + printHelp() if args.length != 0 + + #---- + + delete parsedOpts.argv + opts = _.extend {}, optionDefaults, getDotWeinreServerProperties(), parsedOpts + + if !opts.deathTimeout? + opts.deathTimeout = 3 * opts.readTimeout + + utils.setOptions opts + + weinre.run opts + +#------------------------------------------------------------------------------- +printNoptError = (key, val, types) -> + utils.exit "error with option '#{key}', value '#{val}'" + +#------------------------------------------------------------------------------- +printHelp = () -> + version = weinre.getVersion() + + console.error """ +usage: #{utils.Program} [options] +version: #{version} + +options: + --httpPort port to run the http server on default: #{optionDefaults.httpPort} + --boundHost ip address to bind the server to default: #{optionDefaults.boundHost} + --verbose print more diagnostics default: #{optionDefaults.verbose} + --debug print even more diagnostics default: #{optionDefaults.debug} + --readTimeout seconds to wait for a client message default: #{optionDefaults.readTimeout} + --deathTimeout seconds to wait to kill client default: 3*readTimeout + +--boundHost can be an ip address, hostname, or -all-, where -all- +means binding to all ip address on the current machine' + +for more info see: http://people.apache.org/~pmuellr/weinre/ +""" + process.exit() + +#------------------------------------------------------------------------------- +getDotWeinreServerProperties = () -> + properties = {} + + fileName = replaceTilde '~/.weinre/server.properties' + return properties if !utils.fileExistsSync(fileName) + + contents = fs.readFileSync(fileName, 'utf8') + lines = contents.split('\n') + + for line in lines + line = line.replace(/#.*/,'') + match = line.match /\s*(\w+)\s*:\s*(.+)\s*/ + continue if !match + + key = utils.trim match[1] + val = utils.trim match[2] + + properties[key] = val + + properties + +#------------------------------------------------------------------------------- +replaceTilde = (fileName) -> + fileName.replace('~', getTildeReplacement()) + +#------------------------------------------------------------------------------- +getTildeReplacement = () -> + process.env["HOME"] || process.env["USERPROFILE"] || '.' http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/dumpingHandler.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/dumpingHandler.coffee b/weinre.server/lib-src/dumpingHandler.coffee new file mode 100644 index 0000000..310852d --- /dev/null +++ b/weinre.server/lib-src/dumpingHandler.coffee @@ -0,0 +1,77 @@ +#------------------------------------------------------------------------------- +# 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. +#------------------------------------------------------------------------------- + +_ = require 'underscore' + +utils = require './utils' + +#------------------------------------------------------------------------------- +dumpingHandler = (request, response, uri) -> + originalSend = response.send + response.send = (body) -> + dumpResponse(originalSend, body, request, response, uri) + + return if request.method != 'POST' + + utils.logVerbose '--------------------------------------------------' + utils.logVerbose "#{request.method} #{uri} [request]" + + if _.isArray(request.body) + for element in request.body + utils.logVerbose " #{enhance(JSON.parse(element))}" + else + utils.logVerbose " #{enhance(request.body)}" + +#------------------------------------------------------------------------------- +dumpResponse = (originalSend, body, request, response, uri) -> + originalSend.call(response, body) + + return if request.method not in ['GET', 'POST'] + + try + body = JSON.parse(body) + catch e + return + + return if _.isArray(body) && (body.length == 0) + + utils.logVerbose '--------------------------------------------------' + utils.logVerbose "#{request.method} #{uri} #{response.statusCode} [response]" + + if _.isArray(body) + for element in body + utils.logVerbose " #{enhance(JSON.parse(element))}" + else + utils.logVerbose " #{enhance(body)}" + +#------------------------------------------------------------------------------- +enhance = (object) -> + if !object.interface || !object.method || !object.args + return JSON.stringify(object) + + signature = "#{object.interface}.#{object.method}" + + args = JSON.stringify(object.args) + if args.length > 500 + args = "#{args.substr(0,50)}..." + + return "#{signature}(#{args})" + +#------------------------------------------------------------------------------- +module.exports = dumpingHandler http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/extensionManager.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/extensionManager.coffee b/weinre.server/lib-src/extensionManager.coffee new file mode 100644 index 0000000..c367425 --- /dev/null +++ b/weinre.server/lib-src/extensionManager.coffee @@ -0,0 +1,30 @@ +#------------------------------------------------------------------------------- +# 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. +#------------------------------------------------------------------------------- + +utils = require './utils' + +#------------------------------------------------------------------------------- +utils.registerClass class ExtensionManager + + #--------------------------------------------------------------------------- + constructor: () -> + @extensions = [] + +#------------------------------------------------------------------------------- +module.exports = new ExtensionManager http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/jsonBodyParser.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/jsonBodyParser.coffee b/weinre.server/lib-src/jsonBodyParser.coffee new file mode 100644 index 0000000..58f755b --- /dev/null +++ b/weinre.server/lib-src/jsonBodyParser.coffee @@ -0,0 +1,47 @@ +#------------------------------------------------------------------------------- +# 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. +#------------------------------------------------------------------------------- + +#------------------------------------------------------------------------------- +jsonBodyParser = -> + return (request, response, next) -> parseBodyAsJSON(request, response, next) + +#------------------------------------------------------------------------------- +parseBodyAsJSON = (request, response, next) -> + + return next() if request.body + + request.body = {} + + return next() if request.method != 'POST' + + request.setEncoding 'utf8' + + buffer = '' + request.on 'data', (chunk) -> buffer += chunk + request.on 'end', -> + return next() if '' == buffer + + try + request.body = JSON.parse(buffer) + next() + catch e + next(e) + +#------------------------------------------------------------------------------- +module.exports = jsonBodyParser http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/messageHandler.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/messageHandler.coffee b/weinre.server/lib-src/messageHandler.coffee new file mode 100644 index 0000000..0c79430 --- /dev/null +++ b/weinre.server/lib-src/messageHandler.coffee @@ -0,0 +1,59 @@ +#------------------------------------------------------------------------------- +# 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. +#------------------------------------------------------------------------------- + +utils = require './utils' +channelManager = require './channelManager' +serviceManager = require './serviceManager' + +#------------------------------------------------------------------------------- +utils.registerClass class MessageHandler + + #--------------------------------------------------------------------------- + handleMessage: (channel, message) -> + @_serviceMethodInvoker(channel, message.interface, message.method, message.args) + + #--------------------------------------------------------------------------- + _serviceMethodInvoker: (channel, intfName, method, args) -> + methodSignature = "#{intfName}.#{method}()" + # utils.logVerbose "MessageHandler._serviceMethodInvoker(#{methodSignature})" + + service = serviceManager.get(intfName) + + if !service + return @_redirectToConnections(channel, intfName, method, args) + + args = args.slice() + args.unshift channel + + try + service[method].apply(service, args) + + catch e + utils.log "error running service method #{methodSignature}: #{e}" + utils.log "stack:\n#{e.stack}" + + #--------------------------------------------------------------------------- + _redirectToConnections: (channel, intfName, method, args) -> + # utils.logVerbose "MessageHandler._redirectToConnections(#{channel.name}, #{intfName}, #{method})" + + for connection in channel.connections + connection.sendMessage(intfName, method, args...) + +#------------------------------------------------------------------------------- +module.exports = new MessageHandler http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/service/WeinreClientCommands.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/service/WeinreClientCommands.coffee b/weinre.server/lib-src/service/WeinreClientCommands.coffee new file mode 100644 index 0000000..713a48b --- /dev/null +++ b/weinre.server/lib-src/service/WeinreClientCommands.coffee @@ -0,0 +1,126 @@ +#------------------------------------------------------------------------------- +# 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. +#------------------------------------------------------------------------------- + +_ = require('underscore') + +weinre = require '../weinre' +utils = require '../utils' +channelManager = require '../channelManager' +serviceManager = require '../serviceManager' +extensionManager = require '../extensionManager' + +WeinreClientEvents = serviceManager.get 'WeinreClientEvents' + +#------------------------------------------------------------------------------- +module.exports = utils.registerClass class WeinreClientCommands + + #--------------------------------------------------------------------------- + registerClient: (channel, callbackId) -> + if callbackId + WeinreClientEvents.sendCallback(channel, callbackId, channel.description) + + options = _.extend {}, utils.options + for own key, val of options + if typeof val in ['number', 'boolean'] + options[key] = "#{val}" + + options.version = weinre.getVersion() + + WeinreClientEvents.serverProperties(channel, options) + + clients = channelManager.getClientChannels(channel.id) + WeinreClientEvents.clientRegistered(clients, channel.description) + + #--------------------------------------------------------------------------- + getTargets: (channel, callbackId) -> + channels = channelManager.getTargetChannels(channel.id) + result = _.pluck(channels, 'description') + + if callbackId + WeinreClientEvents.sendCallback(channel, callbackId, [result]) + + #--------------------------------------------------------------------------- + getClients: (channel, callbackId) -> + channels = channelManager.getClientChannels(channel.id) + result = _.pluck(channels, 'description') + + if callbackId + WeinreClientEvents.sendCallback(channel, callbackId, [result]) + + #--------------------------------------------------------------------------- + getExtensions: (channel, callbackId) -> + result = for extension in extensionManager.extensions + { startPage: "extensions/#{extension}/extension.html" } + + if callbackId + WeinreClientEvents.sendCallback(channel, callbackId, [result]) + + #--------------------------------------------------------------------------- + connectTarget: (channel, clientName, targetName, callbackId) -> + client = channelManager.getChannel(clientName) + return if !client + + target = channelManager.getChannel(targetName) + return if !target + + channelManager.connectChannels(client, target) + + if callbackId + WeinreClientEvents.sendCallback(channel, callbackId) + + #--------------------------------------------------------------------------- + disconnectTarget: (channel, clientName, callbackId) -> + client = connectionManager.getClient(clientName) + return if !client + + target = client.getConnectedTarget() + return if !target + + connectionManager.disconnect(client, target) + + if callbackId + WeinreClientEvents.sendCallback(channel, callbackId) + + #--------------------------------------------------------------------------- + logDebug: (channel, message, callbackId) -> + utils.logVerbose "client #{channel.name}: #{message}" + + if callbackId + WeinreClientEvents.sendCallback(channel, callbackId) + + #--------------------------------------------------------------------------- + logInfo: (channel, message, callbackId) -> + utils.log "client #{channel.name}: #{message}" + + if callbackId + WeinreClientEvents.sendCallback(channel, callbackId) + + #--------------------------------------------------------------------------- + logWarning: (channel, message, callbackId) -> + utils.log "client #{channel.name}: #{message}" + + if callbackId + WeinreClientEvents.sendCallback(channel, callbackId) + + #--------------------------------------------------------------------------- + logError: (channel, message, callbackId) -> + utils.log "client #{channel.name}: #{message}" + + if callbackId + WeinreClientEvents.sendCallback(channel, callbackId) http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/service/WeinreTargetCommands.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/service/WeinreTargetCommands.coffee b/weinre.server/lib-src/service/WeinreTargetCommands.coffee new file mode 100644 index 0000000..4603e20 --- /dev/null +++ b/weinre.server/lib-src/service/WeinreTargetCommands.coffee @@ -0,0 +1,90 @@ +#------------------------------------------------------------------------------- +# 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. +#------------------------------------------------------------------------------- + +utils = require '../utils' +channelManager = require '../channelManager' +serviceManager = require '../serviceManager' + +WeinreClientEvents = serviceManager.get 'WeinreClientEvents' +WeinreTargetEvents = serviceManager.get 'WeinreTargetEvents' + +#------------------------------------------------------------------------------- +module.exports = utils.registerClass class WeinreTargetCommands + + #--------------------------------------------------------------------------- + registerTarget: (channel, url, callbackId) -> + channel.description.url = url + + clients = channelManager.getClientChannels(channel.id) + WeinreClientEvents.targetRegistered(clients, channel.description) + + if callbackId + WeinreTargetEvents.sendCallback(channel, callbackId, channel.description) + + #--------------------------------------------------------------------------- + sendClientCallback: (channel, clientCallbackId, args, callbackId) -> + + # the channel to send the callback to is embedded in the callbackId + callbackChannel = getCallbackChannel(clientCallbackId) + if !callbackChannel + return main.warn "#{@constructor.name}.sendClientCallback() sent with invalid callbackId: #{clientCallbackId}" + + callbackChannel = channelManager.getChannel(callbackChannel) + if !callbackChannel + # indication that channel was closed; this message may generate a lot of noise + return main.warn "#{@constructor.name}.sendClientCallback() unable to find channel : #{clientCallbackId}" + + WeinreClientEvents.sendCallback(callbackChannel, clientCallbackId, args) + + if callbackId + WeinreTargetEvents.sendCallback(channel, callbackId, description) + + #--------------------------------------------------------------------------- + logDebug: (channel, message, callbackId) -> + utils.logVerbose "target #{channel.name}: #{message}" + + if callbackId + WeinreTargetEvents.sendCallback(channel, callbackId, description) + + #--------------------------------------------------------------------------- + logInfo: (channel, message, callbackId) -> + utils.log "target #{channel.name}: #{message}" + + if callbackId + WeinreTargetEvents.sendCallback(channel, callbackId, description) + + #--------------------------------------------------------------------------- + logWarning: (channel, message, callbackId) -> + utils.log "target #{channel.name}: #{message}" + + if callbackId + WeinreTargetEvents.sendCallback(channel, callbackId, description) + + #--------------------------------------------------------------------------- + logError: (channel, message, callbackId) -> + utils.log "target #{channel.name}: #{message}" + + if callbackId + WeinreTargetEvents.sendCallback(channel, callbackId, description) + +#--------------------------------------------------------------------------- +getCallbackChannel = (callbackId) -> + callbackId = callbackId.toString() + callbackId.split('::')[0] + http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/serviceManager.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/serviceManager.coffee b/weinre.server/lib-src/serviceManager.coffee new file mode 100644 index 0000000..ade070f --- /dev/null +++ b/weinre.server/lib-src/serviceManager.coffee @@ -0,0 +1,95 @@ +#------------------------------------------------------------------------------- +# 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. +#------------------------------------------------------------------------------- + +path = require 'path' +fs = require 'fs' + +_ = require 'underscore' + +utils = require './utils' + +Services = {} + +#------------------------------------------------------------------------------- +utils.registerClass class ServiceManager + + #--------------------------------------------------------------------------- + constructor: -> + @services = {} + + #--------------------------------------------------------------------------- + get: (name) -> + return @services[name] if _.has(@services, name) + + null + + #--------------------------------------------------------------------------- + registerLocalClass: (name) -> + + serviceClass = null + try + serviceClass = require "./service/#{name}" + catch e + utils.log "local service class not found: #{name}" + throw e + + @services[name] = new serviceClass + + #--------------------------------------------------------------------------- + registerProxyClass: (name) -> + + intf = getServiceInterface(name) + + if !intf + utils.exit "proxy service class not found: #{name}" + + if intf.name != name + utils.exit "proxy interface '#{intf.name}' loaded when '#{name}' requested" + + service = {} + + for method in intf.methods + service[method.name] = getMethodProxy(name, method.name) + + @services[name] = service + + +#------------------------------------------------------------------------------- +getMethodProxy = (intfName, methodName) -> + (channels, args...) -> + channels = [channels] if !_.isArray(channels) + + for channel in channels + channel.sendMessage(intfName, methodName, args...) + +#------------------------------------------------------------------------------- +getServiceInterface = (name) -> + jsonName = "#{name}.json" + fileName = path.join utils.options.staticWebDir, 'interfaces', jsonName + + return null if !utils.fileExistsSync(fileName) + + contents = fs.readFileSync(fileName, 'utf8') + + serviceInterface = JSON.parse(contents) + + return serviceInterface.interfaces[0] + +#------------------------------------------------------------------------------- +module.exports = new ServiceManager \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/utils.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/utils.coffee b/weinre.server/lib-src/utils.coffee new file mode 100644 index 0000000..ee5a72b --- /dev/null +++ b/weinre.server/lib-src/utils.coffee @@ -0,0 +1,196 @@ +#------------------------------------------------------------------------------- +# 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' +path = require 'path' + +utils = exports + +utils.Program = Program = path.basename process.argv[1] + +SequenceNumberMax = 100 * 1024 * 1024 +SequenceNumber = 0 + +#------------------------------------------------------------------------------- +utils.getNextSequenceNumber = (g) -> + SequenceNumber++ + + if SequenceNumber > SequenceNumberMax + SequenceNumber = 0 + + SequenceNumber + +#------------------------------------------------------------------------------- +utils.trim = (string) -> + string.replace(/(^\s+)|(\s+$)/g,'') + +#------------------------------------------------------------------------------- +utils.log = log = (message) -> + date = new Date() + time = date.toISOString() + console.log "#{time} #{Program}: #{message}" + +#------------------------------------------------------------------------------- +utils.logVerbose = (message) -> + return if !utils?.options?.verbose + + log message + +#------------------------------------------------------------------------------- +utils.logDebug = (message) -> + return if !utils?.options?.debug + + log message + +#------------------------------------------------------------------------------- +utils.exit = (message) -> + log message + process.exit 1 + +#------------------------------------------------------------------------------- +utils.pitch = (message) -> + log message + throw message + +#------------------------------------------------------------------------------- +utils.setOptions = (options) -> + utils.options = options + +#------------------------------------------------------------------------------- +utils.ensureInteger = (value, message) -> + newValue = parseInt value + + if isNaN newValue + utils.exit "#{message}: '#{value}'" + + newValue + +#------------------------------------------------------------------------------- +utils.ensureString = (value, message) -> + + if typeof value != 'string' + utils.exit "#{message}: '#{value}'" + + value + +#------------------------------------------------------------------------------- +utils.ensureBoolean = (value, message) -> + uValue = value.toString().toUpperCase() + + newValue = null + switch uValue + when 'TRUE' then newValue = true + when 'FALSE' then newValue = false + + if typeof(newValue) != 'boolean' + utils.exit "#{message}: '#{value}'" + + newValue + +#------------------------------------------------------------------------------- +utils.setNamesForClass = (aClass) -> + + for own key, val of aClass + if typeof(val) is "function" + val.signature = "#{aClass.name}::#{key}" + val.displayName = val.signature + val.name = val.signature + + for own key, val of aClass.prototype + if typeof(val) is "function" + val.signature = "#{aClass.name}.#{key}" + val.displayName = val.signature + val.name = val.signature + +#------------------------------------------------------------------------------- +utils.registerClass = (aClass) -> + utils.setNamesForClass(aClass) + aClass + +#------------------------------------------------------------------------------- +utils.alignLeft = (string, length) -> + while string.length < length + string = "#{string} " + + string + +#------------------------------------------------------------------------------- +utils.alignRight = (string, length) -> + while string.length < length + string = " #{string}" + + string + +#------------------------------------------------------------------------------- +utils.fileExistsSync = (name) -> + + if fs.existsSync + return fs.existsSync name + + return path.existsSync(name) + +#------------------------------------------------------------------------------- +Error.prepareStackTrace = (error, structuredStackTrace) -> + result = [] + result.push "---------------------------------------------------------" + result.push "error: #{error}" + result.push "---------------------------------------------------------" + result.push "stack: " + + longestFile = 0 + longestLine = 0 + + for callSite in structuredStackTrace + file = callSite.getFileName() + line = callSite.getLineNumber() + + file = path.basename(file) + line = "#{line}" + + if file.length > longestFile + longestFile = file.length + + if line.length > longestLine + longestLine = line.length + + for callSite in structuredStackTrace + func = callSite.getFunction() + file = callSite.getFileName() + line = callSite.getLineNumber() + + file = path.basename(file) + line = "#{line}" + + file = utils.alignRight(file, longestFile) + line = utils.alignLeft( line, longestLine) + + funcName = func.displayName || + func.name || + callSite.getFunctionName() + callSite.getMethodName() + '???' + + if funcName == "Module._compile" + result.pop() + result.pop() + break + + result.push " #{file}:#{line} - #{funcName}()" + + result.join "\n" http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib-src/weinre.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib-src/weinre.coffee b/weinre.server/lib-src/weinre.coffee new file mode 100644 index 0000000..8b7945d --- /dev/null +++ b/weinre.server/lib-src/weinre.coffee @@ -0,0 +1,186 @@ +#------------------------------------------------------------------------------- +# 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 http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib/Channel.coffee ---------------------------------------------------------------------- diff --git a/weinre.server/lib/Channel.coffee b/weinre.server/lib/Channel.coffee deleted file mode 100644 index 70f5841..0000000 --- a/weinre.server/lib/Channel.coffee +++ /dev/null @@ -1,118 +0,0 @@ -#------------------------------------------------------------------------------- -# 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. -#------------------------------------------------------------------------------- - -_ = require 'underscore' - -utils = require './utils' -channelManager = require './channelManager' -messageHandler = require './messageHandler' -MessageQueue = require './MessageQueue' - -AnonymousId = 'anonymous' - -#------------------------------------------------------------------------------- -module.exports = utils.registerClass class Channel - - #--------------------------------------------------------------------------- - constructor: (@pathPrefix, @id, @remoteAddress, @isClient) -> - prefix = if @isClient then 'c-' else 't-' - @name = "#{prefix}#{utils.getNextSequenceNumber()}" - @messageQueue = new MessageQueue - @isClosed = false - @connections = [] - @isTarget = !@isClient - @readTimeout = utils.options.readTimeout * 1000 - - @id = AnonymousId if !@id - - @description = - channel: @name - id: @id - hostName: @remoteAddress - remoteAddress: @remoteAddress - - @updateLastRead() - - channelManager.created @ - - #--------------------------------------------------------------------------- - close: () -> - return if @isClosed - - channelManager.destroyed @ - - @isClosed = true - @messageQueue.shutdown() - - #--------------------------------------------------------------------------- - sendCallback: (intfName, callbackId, args...) -> - return if !callbackId - - args.unshift callbackId - - @sendMessage intfName, 'sendCallback', args... - - #--------------------------------------------------------------------------- - sendMessage: (intfName, method, args...) -> - - message = genJSON - interface: intfName - method: method - args: args - - @messageQueue.push message - - #--------------------------------------------------------------------------- - handleMessages: (messages) -> - - for message in messages - message = parseJSON(message) - continue if !message - - messageHandler.handleMessage @, message - - #--------------------------------------------------------------------------- - getMessages: (callback) -> - @updateLastRead() - return callback.call(null, null) if @isClosed - - @messageQueue.pullAll @readTimeout, callback - - #--------------------------------------------------------------------------- - updateLastRead: () -> - @lastRead = (new Date).valueOf() - - #--------------------------------------------------------------------------- - toString: () -> - connections = _.map(@connections, (val) -> val.name).join(',') - "Channel(#{@name}, closed:#{@isClosed}, connections:[#{connections}])" - -#------------------------------------------------------------------------------- -parseJSON = (message) -> - try - return JSON.parse(message) - catch e - return null - -#------------------------------------------------------------------------------- -genJSON = (message) -> - try - return JSON.stringify(message) - catch e - return null http://git-wip-us.apache.org/repos/asf/cordova-weinre/blob/9b06777d/weinre.server/lib/Channel.js ---------------------------------------------------------------------- diff --git a/weinre.server/lib/Channel.js b/weinre.server/lib/Channel.js new file mode 100644 index 0000000..040e4c3 --- /dev/null +++ b/weinre.server/lib/Channel.js @@ -0,0 +1,130 @@ +// Generated by CoffeeScript 1.8.0 +var AnonymousId, Channel, MessageQueue, channelManager, genJSON, messageHandler, parseJSON, utils, _, + __slice = [].slice; + +_ = require('underscore'); + +utils = require('./utils'); + +channelManager = require('./channelManager'); + +messageHandler = require('./messageHandler'); + +MessageQueue = require('./MessageQueue'); + +AnonymousId = 'anonymous'; + +module.exports = utils.registerClass(Channel = (function() { + function Channel(pathPrefix, id, remoteAddress, isClient) { + var prefix; + this.pathPrefix = pathPrefix; + this.id = id; + this.remoteAddress = remoteAddress; + this.isClient = isClient; + prefix = this.isClient ? 'c-' : 't-'; + this.name = "" + prefix + (utils.getNextSequenceNumber()); + this.messageQueue = new MessageQueue; + this.isClosed = false; + this.connections = []; + this.isTarget = !this.isClient; + this.readTimeout = utils.options.readTimeout * 1000; + if (!this.id) { + this.id = AnonymousId; + } + this.description = { + channel: this.name, + id: this.id, + hostName: this.remoteAddress, + remoteAddress: this.remoteAddress + }; + this.updateLastRead(); + channelManager.created(this); + } + + Channel.prototype.close = function() { + if (this.isClosed) { + return; + } + channelManager.destroyed(this); + this.isClosed = true; + return this.messageQueue.shutdown(); + }; + + Channel.prototype.sendCallback = function() { + var args, callbackId, intfName; + intfName = arguments[0], callbackId = arguments[1], args = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + if (!callbackId) { + return; + } + args.unshift(callbackId); + return this.sendMessage.apply(this, [intfName, 'sendCallback'].concat(__slice.call(args))); + }; + + Channel.prototype.sendMessage = function() { + var args, intfName, message, method; + intfName = arguments[0], method = arguments[1], args = 3 <= arguments.length ? __slice.call(arguments, 2) : []; + message = genJSON({ + "interface": intfName, + method: method, + args: args + }); + return this.messageQueue.push(message); + }; + + Channel.prototype.handleMessages = function(messages) { + var message, _i, _len, _results; + _results = []; + for (_i = 0, _len = messages.length; _i < _len; _i++) { + message = messages[_i]; + message = parseJSON(message); + if (!message) { + continue; + } + _results.push(messageHandler.handleMessage(this, message)); + } + return _results; + }; + + Channel.prototype.getMessages = function(callback) { + this.updateLastRead(); + if (this.isClosed) { + return callback.call(null, null); + } + return this.messageQueue.pullAll(this.readTimeout, callback); + }; + + Channel.prototype.updateLastRead = function() { + return this.lastRead = (new Date).valueOf(); + }; + + Channel.prototype.toString = function() { + var connections; + connections = _.map(this.connections, function(val) { + return val.name; + }).join(','); + return "Channel(" + this.name + ", closed:" + this.isClosed + ", connections:[" + connections + "])"; + }; + + return Channel; + +})()); + +parseJSON = function(message) { + var e; + try { + return JSON.parse(message); + } catch (_error) { + e = _error; + return null; + } +}; + +genJSON = function(message) { + var e; + try { + return JSON.stringify(message); + } catch (_error) { + e = _error; + return null; + } +};