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