Skip to content

CB-9127 Implements cordova-serve module. #238

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 10, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cordova-serve/.jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"node": true,
"bitwise": true,
"undef": true,
"trailing": true,
"quotmark": true,
"indent": 4,
"unused": "vars",
"latedef": "nofunc",
"-W030": false
}
142 changes: 142 additions & 0 deletions cordova-serve/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
This module can be used to serve up a Cordova application in the browser. It has no command-line, but rather is intended
to be called using the following API:

``` js
var serve = require('cordova-serve');
serve.launchServer(opts);
serve.servePlatform(platform, opts);
serve.launchBrowser(ops);
serve.sendStream(filePath, request, response[, readStream][, noCache]);
```

## launchServer()

``` js
launchServer(opts);
```

Launches a server with the specified options. Parameters:

* **opts**: Options, as described below.

## servePlatform()

``` js
servePlatform(platform, opts);
```

Launches a server that serves up any Cordova platform (e.g. `browser`, `android` etc) from the current project.
Parameters:

* **opts**: Options, as described below. Note that for `servePlatform()`, the `root` value should be a Cordova project's
root folder, or any folder within it - `servePlatform()` will replace it with the platform's `www_dir` folder. If this
value is not specified, the *cwd* will be used.

## launchBrowser()

``` js
launchBrowser(opts);
```

Launches a browser window pointing to the specified URL. The single parameter is an options object that supports the
following values (both optional):

* **url**: The URL to open in the browser.
* **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):

* **root**: The file path on the local file system that is used as the root for the server, for default mapping of URL
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.
* **urlPathProcessor**: An optional method to handle special case URLs - `cordova-serve` will by default
treat the URL as relative to the platform's `www_dir`, but will first call this method, if provided, to support
custom handling.
* **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.

## urlPathProcessor()
Provide this method if you need to do custom processing of URL paths. That is, custom mapping of URL path to local file path.
The signature of this method is as follows:

``` js
urlPathProcessor(urlPath, request, response, do302, do404)
```

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).

Return value:

Broadly, there are three possible actions you can take in your `urlPathProcessor` handler:

1. You completely handle the request (presumably by doing some sort of response and ultimately calling `response.end()`.
In this scenario, you should return `null`.
2. You have mapped the URL path to a custom local file path. In this scenario, you should return `{filePath: <value>}`,
where `<value>` is the local file path.
3. You have determined you don't need to do any custom processing and will let cordova-serve to its default mapping. In
this scenario, you should return `{filePath: null}`.

## 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.

22 changes: 22 additions & 0 deletions cordova-serve/RELEASENOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!--
#
# 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.
#
-->
# Cordova-serve Release Notes

38 changes: 38 additions & 0 deletions cordova-serve/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "cordova-serve",
"version": "0.1.2-dev",
"description": "Apache Cordova server support for cordova-lib and cordova-browser.",
"main": "serve.js",
"repository": {
"type": "git",
"url": "git://git-wip-us.apache.org/repos/asf/cordova-lib.git"
},
"keywords": [
"cordova",
"server",
"apache"
],
"author": "Apache Software Foundation",
"license": "Apache version 2.0",
"bugs": {
"url": "https://issues.apache.org/jira/browse/CB",
"email": "dev@cordova.apache.org"
},
"dependencies": {
"combined-stream": "^1.0.3",
"d8": "^0.4.4",
"mime": "^1.2.11",
"q": "^1.4.1",
"shelljs": "^0.4.0"
},
"devDependencies": {
"jshint": "^2.8.0"
},
"scripts": {
"jshint": "node node_modules/jshint/bin/jshint src"
},
"engines": {
"node": ">= 0.12.0",
"npm": ">= 2.5.1"
}
}
25 changes: 25 additions & 0 deletions cordova-serve/serve.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
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.
*/

module.exports = {
sendStream: require('./src/stream'),
servePlatform: require('./src/platform'),
launchServer: require('./src/server'),
launchBrowser: require('./src/browser')
};
100 changes: 100 additions & 0 deletions cordova-serve/src/browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
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 exec = require('./exec'),
Q = require('q');

/**
* Launches the specified browser with the given URL.
* Based on https://github.com/domenic/opener
* @param {{target: ?string, url: ?string, dataDir: ?string}} opts - parameters:
* target - the target browser - ie, chrome, safari, opera, firefox or chromium
* url - the url to open in the browser
* dataDir - a data dir to provide to Chrome (can be used to force it to open in a new window)
* @return {Q} Promise to launch the specified browser
*/
module.exports = function (opts) {
//target, url, dataDir
var target = opts.target || 'chrome';
var url = opts.url || '';

return getBrowser(target, opts.dataDir).then(function (browser) {
var args;
switch (process.platform) {
case 'darwin':
args = ['open'];
if (target == 'chrome') {
// Chrome needs to be launched in a new window. Other browsers, particularly, opera does not work with this.
args.push('-n');
}
args.push('-a', browser);
break;
case 'win32':
// On Windows, we really want to use the "start" command. But, the rules regarding arguments with spaces, and
// escaping them with quotes, can get really arcane. So the easiest way to deal with this is to pass off the
// responsibility to "cmd /c", which has that logic built in.
//
// Furthermore, if "cmd /c" double-quoted the first parameter, then "start" will interpret it as a window title,
// so we need to add a dummy empty-string window title: http://stackoverflow.com/a/154090/3191
args = ['cmd /c start ""', browser];
break;
case 'linux':
// if a browser is specified, launch it with the url as argument
// otherwise, use xdg-open.
args = [browser];
break;
}
args.push(url);
var command = args.join(' ');
console.log('Executing command: ' + command);
return exec(command);
});
};

function getBrowser(target, dataDir) {
dataDir = dataDir || 'temp_chrome_user_data_dir_for_cordova';

var chromeArgs = ' --user-data-dir=/tmp/' + dataDir;
var browsers = {
'win32': {
'ie': 'iexplore',
'chrome': 'chrome --user-data-dir=%TEMP%\\' + dataDir,
'safari': 'safari',
'opera': 'opera',
'firefox': 'firefox'
},
'darwin': {
'chrome': '"Google Chrome" --args' + chromeArgs,
'safari': 'safari',
'firefox': 'firefox',
'opera': 'opera'
},
'linux' : {
'chrome': 'google-chrome' + chromeArgs ,
'chromium': 'chromium-browser' + chromeArgs,
'firefox': 'firefox',
'opera': 'opera'
}
};
target = target.toLowerCase();
if (target in browsers[process.platform]) {
return Q(browsers[process.platform][target]);
}
return Q.reject('Browser target not supported: ' + target);
}
46 changes: 46 additions & 0 deletions cordova-serve/src/exec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
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 child_process = require('child_process'),
Q = require('q');

/**
* Executes the command specified.
* @param {string} cmd Command to execute
* @param {[string]} opt_cwd Current working directory
* @return {Q} promise a promise that either resolves with the stdout, or rejects with an error message and the stderr.
*/
module.exports = function (cmd, opt_cwd) {
var d = Q.defer();
try {
child_process.exec(cmd, {cwd: opt_cwd, maxBuffer: 1024000}, function (err, stdout, stderr) {
if (err) {
d.reject(new Error('Error executing "' + cmd + '": ' + stderr));
}
else {
d.resolve(stdout);
}
});
} catch (e) {
console.error('error caught: ' + e);
d.reject(e);
}
return d.promise;
};

Loading