Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id E67FA200CAD for ; Tue, 6 Jun 2017 07:43:13 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id E5E03160BD4; Tue, 6 Jun 2017 05:43:13 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id D65BF160BE7 for ; Tue, 6 Jun 2017 07:43:11 +0200 (CEST) Received: (qmail 27498 invoked by uid 500); 6 Jun 2017 05:43:11 -0000 Mailing-List: contact commits-help@cordova.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Delivered-To: mailing list commits@cordova.apache.org Received: (qmail 26260 invoked by uid 99); 6 Jun 2017 05:43:08 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 06 Jun 2017 05:43:08 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id F0FBEE9809; Tue, 6 Jun 2017 05:43:07 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: steven@apache.org To: commits@cordova.apache.org Date: Tue, 06 Jun 2017 05:43:20 -0000 Message-Id: <935a10196a914fa387a3f925bfa414d9@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [14/24] cordova-serve git commit: Refactor cordova-serve to use Express. archived-at: Tue, 06 Jun 2017 05:43:14 -0000 Refactor cordova-serve to use Express. This simplifies a lot of code, and also provides a more standardized and modular way to customize the server (via Express middleware). Also changes cordova-serve to support multiple instances (i.e. multiple servers). Note that this is a breaking change, so for next release we will need to update the version to 0.2.0 (and I have updates to cordova-browser and cordova-lib ('cordova serve' command) ready to go to make use of this change. Project: http://git-wip-us.apache.org/repos/asf/cordova-serve/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-serve/commit/6973f653 Tree: http://git-wip-us.apache.org/repos/asf/cordova-serve/tree/6973f653 Diff: http://git-wip-us.apache.org/repos/asf/cordova-serve/diff/6973f653 Branch: refs/heads/master Commit: 6973f653781b5ea5a4f510d213b22342d18c1ff6 Parents: b0c613f Author: Tim Barham Authored: Sat Oct 3 16:34:36 2015 -0700 Committer: Tim Barham Committed: Mon Oct 5 13:46:32 2015 -0700 ---------------------------------------------------------------------- README.md | 86 +++-------------------------------- package.json | 8 ++-- serve.js | 42 ++++++++++++++--- src/platform.js | 14 +++--- src/server.js | 124 +++++++++++---------------------------------------- src/stream.js | 75 ------------------------------- 6 files changed, 76 insertions(+), 273 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-serve/blob/6973f653/README.md ---------------------------------------------------------------------- diff --git a/README.md b/README.md index 0cd0065..4d45447 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,6 @@ var serve = require('cordova-serve'); serve.launchServer(opts); serve.servePlatform(platform, opts); serve.launchBrowser(ops); -serve.sendStream(filePath, request, response[, readStream][, noCache]); ``` ## launchServer() @@ -67,22 +66,6 @@ following values (both optional): * **target**: The name of the browser to launch. Can be any of the following: `chrome`, `chromium`, `firefox`, `ie`, `opera`, `safari`. If no browser is specified, -## sendStream() - -``` js -sendStream(filePath, request, response[, readStream][, noCache]); -``` - -The server uses this method to stream files, and it is provided as a convenience method you can use if you are -customizing the stream by specifying `opts.streamHandler`. Parameters: - -* **filePath**: The absolute path to the file to be served (which will have been passed to your `streamHandler`). -* **request**: The request object (which will have been passed to your `streamHandler`). -* **response**: The response object (which will have been passed to your `streamHandler`). -* **readStream**: (optional) A custom read stream, if required. -* **noCache**: (optional) If true, browser caching will be disabled for this file (by setting response header - Cache-Control will be set to 'no-cache') - ## The *opts* Options Object The opts object passed to `launchServer()` and `servePlatform()` supports the following values (all optional): @@ -90,67 +73,8 @@ The opts object passed to `launchServer()` and `servePlatform()` supports the fo path to local file system path. * **port**: The port for the server. Note that if this port is already in use, it will be incremented until a free port is found. -* **urlPathHandler**: An optional method to provide custom handling for processing URLs and serving up the resulting data. - Can serve up the data itself using `response.write()`, or determine a custom local file path and call `serveFile()` to - serve it up, or do no processing and call `serveFile` with no params to treat `urlPath` as relative to the root. -* **streamHandler**: An optional custom stream handler - `cordova-serve` will by default stream files using - `sendStream()`, described above, which just streams files, but will first call this method, if provided, to - support custom streaming. This method is described in more detail below. -* **serverExtender**: This method is called as soon as the server is created, so that the caller can do - additional things with the server (like attach to certain events, for example). This method is described in more - detail below. - -## urlPathHandler() -Provide this method if you need to do custom processing of URL paths (that is, custom mapping of URL path to local file -path) and potentially directly handle serving up the resulting data. The signature of this method is as follows: - -``` js -urlPathHandler(urlPath, request, response, do302, do404, serveFile) -``` - -Parameters: - -* **urlPath**: The URL path to process. It is the value of `url.parse(request.url).pathname`. -* **request**: The server request object. -* **response**: The server response object. -* **do302**: A helper method to do a 302 HTTP response (redirection). It takes a single parameter - the URL to redirect to. -* **do404**: A helper method to do a 404 HTTP response (not found). -* **serveFile**: A helper method to serve up the resulting file. If `urlPathHandler()` doesn't write the response itself, - it should call this method either passing it the local file path to be served, or passing it nothing. In the latter case, - it will treat `urlPath` as relative to the root. - -## streamHandler() -Provide this method if you wish to perform custom stream handling. The signature of this method is as follows: - -``` js -streamHandler(filePath, request, response) -``` - -Parameters: - -* **filePath**: This is the path to the local file that will be streamed. It might be the value you returned from - urlPathProcessor(), in which case it doesn't necessarily have to reference an actual file: it might just be an - identifier string that your custom stream handler will recognize. If you are going to end up calling `sendStream()`, - it is useful if even a fake file name has a file extension, as that is used for mime type lookup. -* **request**: The server request object. -* **response**: The serve response object. - -Return value: - -Return `true` if you have handled the stream request, otherwise `false`. - -## serverExtender() - -If you provide this method, it will be called as soon as the server is created. It allows you to attach additional -functionality to the server, such has event handlers, web sockets etc. The signature of this method is as follows: - -``` js -serverExtender(server, root) -``` - -Parameters: - -* **server**: A reference to the server (the result of calling `http.createServer()`). -* **root**: The file path on the local file system that is used as the root for the server (if it was provided), for - default mapping of URL path to local file system path. - +* **router**: An `ExpressJS` router. If provided, this will be attached *before* default static handling. +* **noLogOutput**: If `true`, turns off all log output. +* **noServerInfo**: If `true`, cordova-serve won't output `Static file server running on...` message. +* **events**: An `EventEmitter` to use for logging. If provided, logging will be output using `events.emit('log', msg)`. + If not provided, `console.log()` will be used. Note that nothing will be output in either case if `noLogOutput` is `true`. http://git-wip-us.apache.org/repos/asf/cordova-serve/blob/6973f653/package.json ---------------------------------------------------------------------- diff --git a/package.json b/package.json index 783378e..423ba37 100644 --- a/package.json +++ b/package.json @@ -20,11 +20,9 @@ }, "dependencies": { "chalk": "^1.1.1", - "combined-stream": "^1.0.3", - "d8": "^0.4.4", - "mime": "^1.2.11", - "q": "^1.4.1", - "shelljs": "^0.5.3" + "compression": "^1.6.0", + "express": "^4.13.3", + "q": "^1.4.1" }, "devDependencies": { "jshint": "^2.8.0" http://git-wip-us.apache.org/repos/asf/cordova-serve/blob/6973f653/serve.js ---------------------------------------------------------------------- diff --git a/serve.js b/serve.js index b477a7a..10d000a 100644 --- a/serve.js +++ b/serve.js @@ -17,9 +17,41 @@ under the License. */ -module.exports = { - sendStream: require('./src/stream'), - servePlatform: require('./src/platform'), - launchServer: require('./src/server'), - launchBrowser: require('./src/browser') +var chalk = require('chalk'), + compression = require('compression'), + express = require('express'), + server = require('./src/server'); + +module.exports = function () { + return new CordovaServe(); }; + +function CordovaServe() { + this.app = express(); + + // Attach this before anything else to provide status output + this.app.use(function (req, res, next) { + res.on('finish', function () { + var color = this.statusCode == '404' ? chalk.red : chalk.green; + var msg = color(this.statusCode) + ' ' + this.req.originalUrl; + var encoding = this._headers && this._headers['content-encoding']; + if (encoding) { + msg += chalk.gray(' (' + encoding + ')'); + } + server.log(msg); + }); + next(); + }); + + // Turn on compression + this.app.use(compression()); + + this.servePlatform = require('./src/platform'); + this.launchServer = server; +} + +module.exports.launchBrowser = require('./src/browser'); + +// Expose some useful express statics +module.exports.Router = express.Router; +module.exports.static = express.static; http://git-wip-us.apache.org/repos/asf/cordova-serve/blob/6973f653/src/platform.js ---------------------------------------------------------------------- diff --git a/src/platform.js b/src/platform.js index 940b71b..7abbb81 100644 --- a/src/platform.js +++ b/src/platform.js @@ -17,8 +17,7 @@ under the License. */ -var server = require('./server'), - fs = require('fs'), +var fs = require('fs'), Q = require('q'), util = require('./util'); @@ -31,6 +30,7 @@ var server = require('./server'), * @returns {*|promise} */ module.exports = function (platform, opts) { + var that = this; return Q().then(function () { if (!platform) { throw new Error('A platform must be specified'); @@ -38,16 +38,14 @@ module.exports = function (platform, opts) { opts = opts || {}; var projectRoot = findProjectRoot(opts.root); - var platformRoot = opts.root = util.getPlatformWwwRoot(projectRoot, platform); + that.projectRoot = projectRoot; + + opts.root = util.getPlatformWwwRoot(projectRoot, platform); if (!fs.existsSync(opts.root)) { throw new Error('Project does not include the specified platform: ' + platform); } - return server(opts).then(function (serverInfo) { - serverInfo.projectRoot = projectRoot; - serverInfo.platformRoot = platformRoot; - return serverInfo; - }); + return that.launchServer(opts); }); }; http://git-wip-us.apache.org/repos/asf/cordova-serve/blob/6973f653/src/server.js ---------------------------------------------------------------------- diff --git a/src/server.js b/src/server.js index ea31831..bb10a21 100644 --- a/src/server.js +++ b/src/server.js @@ -17,121 +17,51 @@ under the License. */ -var chalk = require('chalk'), - fs = require('fs'), - http = require('http'), - url = require('url'), - path = require('path'), - Q = require('q'); +var chalk = require('chalk'), + express = require('express'), + Q = require('q'); /** * @desc Launches a server with the specified options and optional custom handlers. - * @param {{root: ?string, port: ?number, noLogOutput: ?bool, noServerInfo: ?bool, urlPathHandler: ?function, streamHandler: ?function, serverExtender: ?function}} opts - * urlPathHandler(urlPath, request, response, do302, do404, serveFile) - an optional method to provide custom handling for - * processing URLs and serving up the resulting data. Can serve up the data itself using response.write(), or determine - * a custom local file path and call serveFile to serve it up, or do no processing and call serveFile with no params to - * treat urlPath as relative to the root. - * streamHandler(filePath, request, response) - an optional custom stream handler, to stream whatever you want. If not - * provided, the file referenced by filePath will be streamed. Return true if the file is handled. - * serverExtender(server, root) - if provided, is called as soon as server is created so caller can attached to events etc. + * @param {{root: ?string, port: ?number, noLogOutput: ?bool, noServerInfo: ?bool, router: ?express.Router, events: EventEmitter}} opts * @returns {*|promise} */ module.exports = function (opts) { var deferred = Q.defer(); opts = opts || {}; - var root = opts.root; var port = opts.port || 8000; - var log = module.exports.log = function () { + var log = module.exports.log = function (msg) { if (!opts.noLogOutput) { - console.log.apply(console, arguments); + if (opts.events) { + opts.events.emit('log', msg); + } else { + console.log(msg); + } } }; - var server = http.createServer(function (request, response) { - function do404() { - log(chalk.red('404 ') + request.url); - response.writeHead(404, {'Content-Type': 'text/plain'}); - response.write('404 Not Found\n'); - response.end(); - } - - function do302(where) { - log(chalk.green('302 ') + request.url); - response.setHeader('Location', where); - response.writeHead(302, {'Content-Type': 'text/plain'}); - response.end(); - } - - function do304() { - log(chalk.green('304 ') + request.url); - response.writeHead(304, {'Content-Type': 'text/plain'}); - response.end(); - } - - function isFileChanged(path) { - var mtime = fs.statSync(path).mtime, - itime = request.headers['if-modified-since']; - return !itime || new Date(mtime) > new Date(itime); - } - - var urlPath = url.parse(request.url).pathname; + var app = this.app; + var server = require('http').Server(app); + this.server = server; - if (opts.urlPathHandler) { - opts.urlPathHandler(urlPath, request, response, do302, do404, serveFile); - } else { - serveFile(); - } - - function serveFile(filePath) { - if (!filePath) { - if (!root) { - throw new Error('No server root directory HAS BEEN specified!'); - } - filePath = path.join(root, urlPath); - } + if (opts.router) { + app.use(opts.router); + } - fs.exists(filePath, function (exists) { - if (!exists) { - do404(); - return; - } - if (fs.statSync(filePath).isDirectory()) { - var index = path.join(filePath, 'index.html'); - if (fs.existsSync(index)) { - filePath = index; - } - } - if (fs.statSync(filePath).isDirectory()) { - if (!/\/$/.test(urlPath)) { - do302(request.url + '/'); - return; - } - log(chalk.green('200 ') + request.url); - response.writeHead(200, {'Content-Type': 'text/html'}); - response.write('Directory listing of ' + urlPath + ''); - response.write('

Items in this directory

'); - response.write('
    '); - fs.readdirSync(filePath).forEach(function (file) { - response.write('
  • ' + file + '
  • \n'); - }); + if (opts.root) { + this.root = opts.root; + app.use(express.static(opts.root)); + } - response.write('
'); - response.end(); - } else if (!isFileChanged(filePath)) { - do304(); - } else { - var streamHandler = opts.streamHandler || require('./stream'); - streamHandler(filePath, request, response); - } - }); - } - }).on('listening', function () { + var that = this; + server.listen(port).on('listening', function () { + that.port = port; if (!opts.noServerInfo) { log('Static file server running on: ' + chalk.green('http://localhost:' + port) + ' (CTRL + C to shut down)'); } - deferred.resolve({server: server, port: port}); + deferred.resolve(); }).on('error', function (e) { if (e && e.toString().indexOf('EADDRINUSE') !== -1) { port++; @@ -139,11 +69,7 @@ module.exports = function (opts) { } else { deferred.reject(e); } - }).listen(port); - - if (opts.serverExtender) { - opts.serverExtender(server, root); - } + }); return deferred.promise; }; http://git-wip-us.apache.org/repos/asf/cordova-serve/blob/6973f653/src/stream.js ---------------------------------------------------------------------- diff --git a/src/stream.js b/src/stream.js deleted file mode 100644 index dd6ebaf..0000000 --- a/src/stream.js +++ /dev/null @@ -1,75 +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. - */ - -var chalk = require('chalk'), - fs = require('fs'), - mime = require('mime'), - zlib = require('zlib'), - server = require('./server'); - -// d8 is a date parsing and formatting micro-framework -// Used only for RFC 2822 formatting -require('d8'); -require('d8/locale/en-US'); - -/** - * Streams a file - * @param {string} filePath - the file to stream (if a readStream is provided, this can be a dummy file name to provide mime type) - * @param {http.IncomingMessage} request - request object provided by request event. - * @param {http.ServerResponse} response - response object provided by request event. - * @param {ReadStream} [readStream] - an optional read stream (for custom handling). - * @param {boolean} [noCache] - if true, response header Cache-Control will be set to 'no-cache'. - * @returns {ReadStream} - the provided ReadStream, otherwise one created for the specified file. - */ -module.exports = function (filePath, request, response, readStream, noCache) { - if ((typeof readStream) === 'boolean') { - noCache = readStream; - readStream = null; - } - - var mimeType = mime.lookup(filePath); - var respHeaders = { - 'Content-Type': mimeType - }; - - if (!readStream) { - readStream = fs.createReadStream(filePath); - } - - var acceptEncoding = request.headers['accept-encoding'] || ''; - var encoding = ''; - if (acceptEncoding.match(/\bgzip\b/)) { - encoding ='(gzip)'; - respHeaders['content-encoding'] = 'gzip'; - readStream = readStream.pipe(zlib.createGzip()); - } else if (acceptEncoding.match(/\bdeflate\b/)) { - encoding ='(deflate)'; - respHeaders['content-encoding'] = 'deflate'; - readStream = readStream.pipe(zlib.createDeflate()); - } - - respHeaders['Last-Modified'] = new Date(fs.statSync(filePath).mtime).format('r'); - if (noCache) { - respHeaders['Cache-Control'] = 'no-store'; - } - server.log(chalk.green('200 ') + request.url + ' ' + encoding); - response.writeHead(200, respHeaders); - readStream.pipe(response); - return readStream; -}; --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org For additional commands, e-mail: commits-help@cordova.apache.org