[Pkg-javascript-commits] [node-serve-static] 01/02: Imported Upstream version 1.1.0
Leo Iannacone
l3on-guest at moszumanska.debian.org
Mon Apr 28 10:34:32 UTC 2014
This is an automated email from the git hooks/post-receive script.
l3on-guest pushed a commit to branch master
in repository node-serve-static.
commit 3198d240d88e732899d18230d8d84c15e2d7078c
Author: Leo Iannacone <l3on at ubuntu.com>
Date: Mon Apr 28 12:11:51 2014 +0200
Imported Upstream version 1.1.0
---
.gitignore | 65 ++++++
.npmignore | 1 +
.travis.yml | 5 +
History.md | 31 +++
LICENSE | 25 +++
Readme.md | 41 ++++
index.js | 139 ++++++++++++
package.json | 30 +++
test/fixtures/.hidden | 1 +
test/fixtures/foo bar | 1 +
test/fixtures/nums | 1 +
test/fixtures/todo.txt | 1 +
test/fixtures/users/index.html | 1 +
test/fixtures/users/tobi.txt | 1 +
test/test.js | 473 +++++++++++++++++++++++++++++++++++++++++
15 files changed, 816 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c4cdf95
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,65 @@
+# Compiled source #
+###################
+*.com
+*.class
+*.dll
+*.exe
+*.o
+*.so
+
+# Packages #
+############
+# it's better to unpack these files and commit the raw source
+# git has its own built in compression methods
+*.7z
+*.dmg
+*.gz
+*.iso
+*.jar
+*.rar
+*.tar
+*.zip
+
+# Logs and databases #
+######################
+*.log
+*.sql
+*.sqlite
+
+# OS generated files #
+######################
+.DS_Store*
+ehthumbs.db
+Thumbs.db
+
+# Node.js #
+###########
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+
+pids
+logs
+results
+
+node_modules
+npm-debug.log
+
+# Git #
+#######
+*.orig
+*.BASE.*
+*.BACKUP.*
+*.LOCAL.*
+*.REMOTE.*
+
+# Components #
+##############
+
+/build
+/components
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..9daeafb
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1 @@
+test
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..99cdc74
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,5 @@
+language: node_js
+node_js:
+ - "0.8"
+ - "0.10"
+ - "0.11"
diff --git a/History.md b/History.md
new file mode 100644
index 0000000..442e1d3
--- /dev/null
+++ b/History.md
@@ -0,0 +1,31 @@
+1.1.0 / 2014-04-24
+==================
+
+ * Accept options directly to `send` module
+ * deps: send at 0.3.0
+
+1.0.4 / 2014-04-07
+==================
+
+ * Resolve relative paths at middleware setup
+ * Use parseurl to parse the URL from request
+
+1.0.3 / 2014-03-20
+==================
+
+ * Do not rely on connect-like environments
+
+1.0.2 / 2014-03-06
+==================
+
+ * deps: send at 0.2.0
+
+1.0.1 / 2014-03-05
+==================
+
+ * Add mime export for back-compat
+
+1.0.0 / 2014-03-05
+==================
+
+ * Genesis from `connect`
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b7bc085
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+(The MIT License)
+
+Copyright (c) 2010 Sencha Inc.
+Copyright (c) 2011 LearnBoost
+Copyright (c) 2011 TJ Holowaychuk
+Copyright (c) 2014 Douglas Christopher Wilson
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Readme.md b/Readme.md
new file mode 100644
index 0000000..af1caaa
--- /dev/null
+++ b/Readme.md
@@ -0,0 +1,41 @@
+# Serve Static
+
+Previously `connect.static()`.
+
+[![Build Status](https://travis-ci.org/expressjs/serve-static.svg?branch=master)](https://travis-ci.org/expressjs/serve-static)
+
+Usage:
+
+```js
+var connect = require('connect');
+var serveStatic = require('serve-static');
+
+var app = connect();
+
+app.use(serveStatic('public/ftp', {'index': ['default.html', 'default.htm']}));
+app.listen();
+```
+
+## License
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Douglas Christopher Wilson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..d0043fb
--- /dev/null
+++ b/index.js
@@ -0,0 +1,139 @@
+/*!
+ * Connect - static
+ * Copyright(c) 2010 Sencha Inc.
+ * Copyright(c) 2011 TJ Holowaychuk
+ * Copyright(c) 2014 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var parseurl = require('parseurl');
+var resolve = require('path').resolve;
+var send = require('send');
+var url = require('url');
+
+/**
+ * Static:
+ *
+ * Static file server with the given `root` path.
+ *
+ * Examples:
+ *
+ * var oneDay = 86400000;
+ * var serveStatic = require('serve-static');
+ *
+ * connect()
+ * .use(serveStatic(__dirname + '/public'))
+ *
+ * connect()
+ * .use(serveStatic(__dirname + '/public', { maxAge: oneDay }))
+ *
+ * Options:
+ *
+ * - `maxAge` Browser cache maxAge in milliseconds. defaults to 0
+ * - `hidden` Allow transfer of hidden files. defaults to false
+ * - `redirect` Redirect to trailing "/" when the pathname is a dir. defaults to true
+ * - `index` Default file name, defaults to 'index.html'
+ *
+ * Further options are forwarded on to `send`.
+ *
+ * @param {String} root
+ * @param {Object} options
+ * @return {Function}
+ * @api public
+ */
+
+exports = module.exports = function(root, options){
+ options = extend({}, options);
+
+ // root required
+ if (!root) throw new TypeError('root path required');
+
+ // resolve root to absolute
+ root = resolve(root);
+
+ // default redirect
+ var redirect = false !== options.redirect;
+
+ // setup options for send
+ options.maxage = options.maxage || options.maxAge || 0;
+ options.root = root;
+
+ return function staticMiddleware(req, res, next) {
+ if ('GET' != req.method && 'HEAD' != req.method) return next();
+ var opts = extend({}, options);
+ var originalUrl = url.parse(req.originalUrl || req.url);
+ var path = parseurl(req).pathname;
+
+ if (path == '/' && originalUrl.pathname[originalUrl.pathname.length - 1] != '/') {
+ return directory();
+ }
+
+ function directory() {
+ if (!redirect) return next();
+ var target;
+ originalUrl.pathname += '/';
+ target = url.format(originalUrl);
+ res.statusCode = 303;
+ res.setHeader('Location', target);
+ res.end('Redirecting to ' + escape(target));
+ }
+
+ function error(err) {
+ if (404 == err.status) return next();
+ next(err);
+ }
+
+ send(req, path, opts)
+ .on('error', error)
+ .on('directory', directory)
+ .pipe(res);
+ };
+};
+
+/**
+ * Expose mime module.
+ *
+ * If you wish to extend the mime table use this
+ * reference to the "mime" module in the npm registry.
+ */
+
+exports.mime = send.mime;
+
+/**
+ * Escape the given string of `html`.
+ *
+ * @param {String} html
+ * @return {String}
+ * @api private
+ */
+
+function escape(html) {
+ return String(html)
+ .replace(/&(?!\w+;)/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .replace(/"/g, '"');
+};
+
+/**
+ * Shallow clone a single object.
+ *
+ * @param {Object} obj
+ * @param {Object} source
+ * @return {Object}
+ * @api private
+ */
+
+function extend(obj, source) {
+ if (!source) return obj;
+
+ for (var prop in source) {
+ obj[prop] = source[prop];
+ }
+
+ return obj;
+};
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..1636a66
--- /dev/null
+++ b/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "serve-static",
+ "description": "Serve static files",
+ "version": "1.1.0",
+ "author": "Douglas Christopher Wilson <doug at somethingdoug.com>",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/expressjs/serve-static.git"
+ },
+ "bugs": {
+ "url": "https://github.com/expressjs/serve-static/issues"
+ },
+ "dependencies": {
+ "parseurl": "1.0.1",
+ "send": "0.3.0"
+ },
+ "devDependencies": {
+ "connect": "~2.14.1",
+ "mocha": "~1.18.2",
+ "should": "~3.3.0",
+ "supertest": "~0.11.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ },
+ "scripts": {
+ "test": "mocha --reporter spec --require should"
+ }
+}
diff --git a/test/fixtures/.hidden b/test/fixtures/.hidden
new file mode 100644
index 0000000..b885243
--- /dev/null
+++ b/test/fixtures/.hidden
@@ -0,0 +1 @@
+I am hidden
\ No newline at end of file
diff --git a/test/fixtures/foo bar b/test/fixtures/foo bar
new file mode 100644
index 0000000..3f95386
--- /dev/null
+++ b/test/fixtures/foo bar
@@ -0,0 +1 @@
+baz
\ No newline at end of file
diff --git a/test/fixtures/nums b/test/fixtures/nums
new file mode 100644
index 0000000..e2e107a
--- /dev/null
+++ b/test/fixtures/nums
@@ -0,0 +1 @@
+123456789
\ No newline at end of file
diff --git a/test/fixtures/todo.txt b/test/fixtures/todo.txt
new file mode 100644
index 0000000..8c3539d
--- /dev/null
+++ b/test/fixtures/todo.txt
@@ -0,0 +1 @@
+- groceries
\ No newline at end of file
diff --git a/test/fixtures/users/index.html b/test/fixtures/users/index.html
new file mode 100644
index 0000000..00a2db4
--- /dev/null
+++ b/test/fixtures/users/index.html
@@ -0,0 +1 @@
+<p>tobi, loki, jane</p>
\ No newline at end of file
diff --git a/test/fixtures/users/tobi.txt b/test/fixtures/users/tobi.txt
new file mode 100644
index 0000000..9d9529d
--- /dev/null
+++ b/test/fixtures/users/tobi.txt
@@ -0,0 +1 @@
+ferret
\ No newline at end of file
diff --git a/test/test.js b/test/test.js
new file mode 100644
index 0000000..d1a37f5
--- /dev/null
+++ b/test/test.js
@@ -0,0 +1,473 @@
+
+process.env.NODE_ENV = 'test';
+
+var connect = require('connect');
+var http = require('http');
+var path = require('path');
+var request = require('supertest');
+var serveStatic = require('..');
+
+var fixtures = __dirname + '/fixtures';
+var relative = path.relative(process.cwd(), fixtures);
+
+var skipRelative = ~relative.indexOf('..') || path.resolve(relative) === relative;
+
+describe('serveStatic()', function(){
+ describe('basic operations', function(){
+ var server;
+ before(function () {
+ server = createServer();
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should serve static files', function(done){
+ request(server)
+ .get('/todo.txt')
+ .expect(200, '- groceries', done);
+ });
+
+ it('should support nesting', function(done){
+ request(server)
+ .get('/users/tobi.txt')
+ .expect(200, 'ferret', done);
+ });
+
+ it('should set Content-Type', function(done){
+ request(server)
+ .get('/todo.txt')
+ .expect('Content-Type', 'text/plain; charset=UTF-8')
+ .expect(200, done);
+ });
+
+ it('should default max-age=0', function(done){
+ request(server)
+ .get('/todo.txt')
+ .expect('Cache-Control', 'public, max-age=0')
+ .expect(200, done);
+ });
+
+ it('should support urlencoded pathnames', function(done){
+ request(server)
+ .get('/foo%20bar')
+ .expect(200, 'baz', done);
+ });
+
+ it('should not choke on auth-looking URL', function(done){
+ request(server)
+ .get('//todo at txt')
+ .expect(404, done);
+ });
+
+ it('should redirect directories with query string', function (done) {
+ request(server)
+ .get('/users?name=john')
+ .expect('Location', '/users/?name=john', done);
+ });
+
+ it('should redirect directories', function(done){
+ request(server)
+ .get('/users')
+ .expect(303, done);
+ });
+
+ it('should not redirect incorrectly', function (done) {
+ request(server)
+ .get('/')
+ .expect(404, done);
+ });
+
+ it('should support index.html', function(done){
+ request(server)
+ .get('/users/')
+ .expect(200)
+ .expect('Content-Type', /html/)
+ .expect('<p>tobi, loki, jane</p>', done);
+ });
+
+ it('should support ../', function(done){
+ request(server)
+ .get('/users/../todo.txt')
+ .expect(200, '- groceries', done);
+ });
+
+ it('should support HEAD', function(done){
+ request(server)
+ .head('/todo.txt')
+ .expect(200, '', done);
+ });
+
+ it('should support conditional requests', function(done){
+ request(server)
+ .get('/todo.txt')
+ .end(function(err, res){
+ if (err) throw err;
+ request(server)
+ .get('/todo.txt')
+ .set('If-None-Match', res.headers.etag)
+ .expect(304, done);
+ });
+ });
+
+ it('should ignore hidden files', function(done){
+ request(server)
+ .get('/.hidden')
+ .expect(404, done);
+ });
+
+ it('should set max-age=0 by default', function(done){
+ request(server)
+ .get('/todo.txt')
+ .expect('cache-control', 'public, max-age=0')
+ .expect(200, done)
+ });
+ });
+
+ (skipRelative ? describe.skip : describe)('current dir', function(){
+ var server;
+ before(function () {
+ server = createServer('.');
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should be served with "."', function(done){
+ var dest = relative.split(path.sep).join('/');
+ request(server)
+ .get('/' + dest + '/todo.txt')
+ .expect(200, '- groceries', done);
+ })
+ })
+
+ describe('hidden files', function(){
+ var server;
+ before(function () {
+ server = createServer(fixtures, {'hidden': true});
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should be served when hidden: true is given', function(done){
+ request(server)
+ .get('/.hidden')
+ .expect(200, 'I am hidden', done);
+ })
+ })
+
+ describe('maxAge', function(){
+ var server;
+ before(function () {
+ server = createServer(fixtures, {'maxAge': Infinity});
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should be reasonable when infinite', function(done){
+ request(server)
+ .get('/todo.txt')
+ .expect('cache-control', 'public, max-age=' + 60*60*24*365)
+ .expect(200, done)
+ });
+ });
+
+ describe('when traversing passed root', function(){
+ var server;
+ before(function () {
+ server = createServer();
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should respond with 403 Forbidden', function(done){
+ request(server)
+ .get('/users/../../todo.txt')
+ .expect(403, done);
+ })
+
+ it('should catch urlencoded ../', function(done){
+ request(server)
+ .get('/users/%2e%2e/%2e%2e/todo.txt')
+ .expect(403, done);
+ });
+ });
+
+ describe('on ENOENT', function(){
+ var server;
+ before(function () {
+ server = createServer();
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should next()', function(done){
+ request(server)
+ .get('/does-not-exist')
+ .expect(404, 'sorry!', done);
+ });
+ });
+
+ describe('Range', function(){
+ var server;
+ before(function () {
+ server = createServer();
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should support byte ranges', function(done){
+ request(server)
+ .get('/nums')
+ .set('Range', 'bytes=0-4')
+ .expect('12345', done);
+ });
+
+ it('should be inclusive', function(done){
+ request(server)
+ .get('/nums')
+ .set('Range', 'bytes=0-0')
+ .expect('1', done);
+ });
+
+ it('should set Content-Range', function(done){
+ request(server)
+ .get('/nums')
+ .set('Range', 'bytes=2-5')
+ .expect('Content-Range', 'bytes 2-5/9', done);
+ });
+
+ it('should support -n', function(done){
+ request(server)
+ .get('/nums')
+ .set('Range', 'bytes=-3')
+ .expect('789', done);
+ });
+
+ it('should support n-', function(done){
+ request(server)
+ .get('/nums')
+ .set('Range', 'bytes=3-')
+ .expect('456789', done);
+ });
+
+ it('should respond with 206 "Partial Content"', function(done){
+ request(server)
+ .get('/nums')
+ .set('Range', 'bytes=0-4')
+ .expect(206, done);
+ });
+
+ it('should set Content-Length to the # of octets transferred', function(done){
+ request(server)
+ .get('/nums')
+ .set('Range', 'bytes=2-3')
+ .expect('Content-Length', '2')
+ .expect(206, '34', done);
+ });
+
+ describe('when last-byte-pos of the range is greater than current length', function(){
+ it('is taken to be equal to one less than the current length', function(done){
+ request(server)
+ .get('/nums')
+ .set('Range', 'bytes=2-50')
+ .expect('Content-Range', 'bytes 2-8/9', done)
+ });
+
+ it('should adapt the Content-Length accordingly', function(done){
+ request(server)
+ .get('/nums')
+ .set('Range', 'bytes=2-50')
+ .expect('Content-Length', '7')
+ .expect(206, done);
+ });
+ });
+
+ describe('when the first- byte-pos of the range is greater than the current length', function(){
+ it('should respond with 416', function(done){
+ request(server)
+ .get('/nums')
+ .set('Range', 'bytes=9-50')
+ .expect(416, done);
+ });
+
+ it('should include a Content-Range field with a byte-range- resp-spec of "*" and an instance-length specifying the current length', function(done){
+ request(server)
+ .get('/nums')
+ .set('Range', 'bytes=9-50')
+ .expect('Content-Range', 'bytes */9', done)
+ });
+ });
+
+ describe('when syntactically invalid', function(){
+ it('should respond with 200 and the entire contents', function(done){
+ request(server)
+ .get('/nums')
+ .set('Range', 'asdf')
+ .expect('123456789', done);
+ });
+ });
+ });
+
+ describe('with a malformed URL', function(){
+ var server;
+ before(function () {
+ server = createServer();
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should respond with 400', function(done){
+ request(server)
+ .get('/%')
+ .expect(400, done);
+ });
+ });
+
+ describe('on ENAMETOOLONG', function(){
+ var server;
+ before(function () {
+ server = createServer();
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should next()', function(done){
+ var path = Array(100).join('foobar');
+
+ request(server)
+ .get('/' + path)
+ .expect(404, done);
+ });
+ });
+
+ describe('on ENOTDIR', function(){
+ var server;
+ before(function () {
+ server = createServer();
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should next()', function(done) {
+ request(server)
+ .get('/todo.txt/a.php')
+ .expect(404, done);
+ });
+ });
+
+ describe('when index at mount point', function(){
+ var server;
+ before(function () {
+ var app = connect();
+ app.use('/users', serveStatic('test/fixtures/users'));
+ server = app.listen();
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should redirect correctly', function (done) {
+ request(server)
+ .get('/users')
+ .expect('Location', '/users/')
+ .expect(303, done);
+ });
+ });
+
+ describe('when mounted', function(){
+ var server;
+ before(function () {
+ var app = connect();
+ app.use('/static', serveStatic(fixtures));
+ server = app.listen();
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should redirect relative to the originalUrl', function(done){
+ request(server)
+ .get('/static/users')
+ .expect('Location', '/static/users/')
+ .expect(303, done);
+ });
+ });
+
+ describe('when responding non-2xx or 304', function(){
+ var server;
+ before(function () {
+ var app = connect();
+ var n = 0;
+
+ app.use(function(req, res, next){
+ switch (n++) {
+ case 0: return next();
+ case 1: res.statusCode = 500; return next();
+ }
+ });
+
+ app.use(serveStatic(fixtures));
+
+ server = app.listen();
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should respond as-is', function(done){
+ request(server)
+ .get('/todo.txt')
+ .expect(200)
+ .end(function(err, res){
+ if (err) throw err;
+ request(server)
+ .get('/todo.txt')
+ .set('If-None-Match', res.headers.etag)
+ .expect(500, '- groceries', done);
+ });
+ });
+ });
+
+ describe('raw http server', function(){
+ var server;
+ before(function () {
+ var middleware = serveStatic(fixtures);
+ server = http.createServer(function (req, res) {
+ middleware(req, res, function (err) {
+ res.statusCode = err ? 500 : 404;
+ res.end(err ? err.stack : '');
+ });
+ });
+ server.listen();
+ });
+ after(function (done) {
+ server.close(done);
+ });
+
+ it('should work on raw node.js http servers', function(done){
+ request(server)
+ .get('/todo.txt')
+ .expect(200, '- groceries', done);
+ });
+ });
+});
+
+function createServer(dir, opts) {
+ var app = connect();
+ dir = dir || fixtures;
+ app.use(serveStatic(dir, opts));
+ app.use(function(req, res){
+ res.statusCode = 404;
+ res.end('sorry!');
+ });
+ return app.listen();
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-serve-static.git
More information about the Pkg-javascript-commits
mailing list