You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ti...@apache.org on 2015/10/05 22:57:52 UTC

cordova-lib git commit: Refactor cordova-serve to use Express.

Repository: cordova-lib
Updated Branches:
  refs/heads/master d12be7c3e -> a633f6020


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-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/a633f602
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/a633f602
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/a633f602

Branch: refs/heads/master
Commit: a633f60201dc3633daee6e9ecd5d2fde31f9ee9a
Parents: d12be7c
Author: Tim Barham <ti...@microsoft.com>
Authored: Sat Oct 3 16:34:36 2015 -0700
Committer: Tim Barham <ti...@microsoft.com>
Committed: Mon Oct 5 13:46:32 2015 -0700

----------------------------------------------------------------------
 cordova-serve/README.md       |  86 ++-----------------------
 cordova-serve/package.json    |   8 +--
 cordova-serve/serve.js        |  42 +++++++++++--
 cordova-serve/src/platform.js |  14 ++---
 cordova-serve/src/server.js   | 124 ++++++++-----------------------------
 cordova-serve/src/stream.js   |  75 ----------------------
 6 files changed, 76 insertions(+), 273 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/a633f602/cordova-serve/README.md
----------------------------------------------------------------------
diff --git a/cordova-serve/README.md b/cordova-serve/README.md
index 0cd0065..4d45447 100644
--- a/cordova-serve/README.md
+++ b/cordova-serve/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-lib/blob/a633f602/cordova-serve/package.json
----------------------------------------------------------------------
diff --git a/cordova-serve/package.json b/cordova-serve/package.json
index 783378e..423ba37 100644
--- a/cordova-serve/package.json
+++ b/cordova-serve/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-lib/blob/a633f602/cordova-serve/serve.js
----------------------------------------------------------------------
diff --git a/cordova-serve/serve.js b/cordova-serve/serve.js
index b477a7a..10d000a 100644
--- a/cordova-serve/serve.js
+++ b/cordova-serve/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-lib/blob/a633f602/cordova-serve/src/platform.js
----------------------------------------------------------------------
diff --git a/cordova-serve/src/platform.js b/cordova-serve/src/platform.js
index 940b71b..7abbb81 100644
--- a/cordova-serve/src/platform.js
+++ b/cordova-serve/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-lib/blob/a633f602/cordova-serve/src/server.js
----------------------------------------------------------------------
diff --git a/cordova-serve/src/server.js b/cordova-serve/src/server.js
index ea31831..bb10a21 100644
--- a/cordova-serve/src/server.js
+++ b/cordova-serve/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('<html><head><title>Directory listing of ' + urlPath + '</title></head>');
-                    response.write('<h3>Items in this directory</h3>');
-                    response.write('<ul>');
-                    fs.readdirSync(filePath).forEach(function (file) {
-                        response.write('<li><a href="' + file + '">' + file + '</a></li>\n');
-                    });
+    if (opts.root) {
+        this.root = opts.root;
+        app.use(express.static(opts.root));
+    }
 
-                    response.write('</ul>');
-                    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-lib/blob/a633f602/cordova-serve/src/stream.js
----------------------------------------------------------------------
diff --git a/cordova-serve/src/stream.js b/cordova-serve/src/stream.js
deleted file mode 100644
index dd6ebaf..0000000
--- a/cordova-serve/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