-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(web-server): a little refactoring, use connect ;-)
The architecture was actually pretty similar to connect, so this refactoring could have been a very simple change. Unfortunately I decided to clean things up a little bit… Closes #105
- Loading branch information
Showing
13 changed files
with
778 additions
and
642 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/** | ||
* This module contains some common helpers shared between middlewares | ||
*/ | ||
|
||
var mime = require('mime'); | ||
var log = require('../logger').create('web-server'); | ||
|
||
var PromiseContainer = function() { | ||
var promise; | ||
|
||
this.then = function(success, error) { | ||
return promise.then(success, error); | ||
}; | ||
|
||
this.set = function(newPromise) { | ||
promise = newPromise; | ||
}; | ||
}; | ||
|
||
|
||
var serve404 = function(response, path) { | ||
log.warn('404: ' + path); | ||
response.writeHead(404); | ||
return response.end('NOT FOUND'); | ||
}; | ||
|
||
|
||
var createServeFile = function(fs, directory) { | ||
return function(filepath, response, transform) { | ||
if (directory) { | ||
filepath = directory + filepath; | ||
} | ||
|
||
return fs.readFile(filepath, function(error, data) { | ||
if (error) { | ||
return serve404(response, filepath); | ||
} | ||
|
||
response.setHeader('Content-Type', mime.lookup(filepath, 'text/plain')); | ||
|
||
// call custom transform fn to transform the data | ||
var responseData = transform && transform(data.toString()) || data; | ||
|
||
response.writeHead(200); | ||
|
||
log.debug('serving: ' + filepath); | ||
return response.end(responseData); | ||
}); | ||
}; | ||
}; | ||
|
||
|
||
var setNoCacheHeaders = function(response) { | ||
response.setHeader('Cache-Control', 'no-cache'); | ||
response.setHeader('Pragma', 'no-cache'); | ||
response.setHeader('Expires', (new Date(0)).toString()); | ||
}; | ||
|
||
|
||
var setHeavyCacheHeaders = function(response) { | ||
response.setHeader('Cache-Control', ['public', 'max-age=31536000']); | ||
}; | ||
|
||
|
||
// PUBLIC API | ||
exports.PromiseContainer = PromiseContainer; | ||
exports.createServeFile = createServeFile; | ||
exports.setNoCacheHeaders = setNoCacheHeaders; | ||
exports.setHeavyCacheHeaders = setHeavyCacheHeaders; | ||
exports.serve404 = serve404; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
/** | ||
* Karma middleware is responsible for serving: | ||
* - client.html (the entrypoint for capturing a browser) | ||
* - debug.html | ||
* - context.html (the execution context, loaded within an iframe) | ||
* - karma.js | ||
* | ||
* The main part is generating context.html, as it contains: | ||
* - generating mappings | ||
* - including <script> and <link> tags | ||
* - setting propert caching headers | ||
*/ | ||
|
||
var path = require('path'); | ||
var util = require('util'); | ||
|
||
var common = require('./common'); | ||
|
||
var VERSION = require('../constants').VERSION; | ||
var SCRIPT_TAG = '<script type="%s" src="%s"></script>'; | ||
var LINK_TAG = '<link type="text/css" href="%s" rel="stylesheet">'; | ||
var SCRIPT_TYPE = { | ||
'.js': 'text/javascript', | ||
'.dart': 'application/dart' | ||
}; | ||
|
||
|
||
var filePathToUrlPath = function(filePath, basePath) { | ||
if (filePath.indexOf(basePath) === 0) { | ||
return '/base' + filePath.substr(basePath.length); | ||
} | ||
|
||
return '/absolute' + filePath; | ||
}; | ||
|
||
var createKarmaMiddleware = function(filesPromise, serveStaticFile, | ||
/* config.basePath */ basePath, /* config.urlRoot */ urlRoot) { | ||
|
||
return function(request, response, next) { | ||
var requestUrl = request.url.replace(/\?.*/, ''); | ||
|
||
// redirect /__karma__ to /__karma__ (trailing slash) | ||
if (requestUrl === urlRoot.substr(0, urlRoot.length - 1)) { | ||
response.setHeader('Location', urlRoot); | ||
response.writeHead(301); | ||
return response.end('MOVED PERMANENTLY'); | ||
} | ||
|
||
// ignore urls outside urlRoot | ||
if (requestUrl.indexOf(urlRoot) !== 0) { | ||
return next(); | ||
} | ||
|
||
// remove urlRoot prefix | ||
requestUrl = requestUrl.substr(urlRoot.length - 1); | ||
|
||
// serve client.html | ||
if (requestUrl === '/') { | ||
return serveStaticFile('/client.html', response); | ||
} | ||
|
||
// serve karma.js | ||
if (requestUrl === '/karma.js') { | ||
return serveStaticFile(requestUrl, response, function(data) { | ||
return data.replace('%KARMA_URL_ROOT%', urlRoot) | ||
.replace('%KARMA_VERSION%', VERSION); | ||
}); | ||
} | ||
|
||
// serve context.html - execution context within the iframe | ||
// or debug.html - execution context without channel to the server | ||
if (requestUrl === '/context.html' || requestUrl === '/debug.html') { | ||
return filesPromise.then(function(files) { | ||
serveStaticFile(requestUrl, response, function(data) { | ||
common.setNoCacheHeaders(response); | ||
|
||
var scriptTags = files.included.map(function(file) { | ||
var filePath = file.path; | ||
var fileExt = path.extname(filePath); | ||
|
||
if (!file.isUrl) { | ||
// TODO(vojta): serve these files from within urlRoot as well | ||
filePath = filePathToUrlPath(filePath, basePath); | ||
|
||
if (requestUrl === '/context.html') { | ||
filePath += '?' + file.mtime.getTime(); | ||
} | ||
} | ||
|
||
if (fileExt === '.css') { | ||
return util.format(LINK_TAG, filePath); | ||
} | ||
|
||
return util.format(SCRIPT_TAG, SCRIPT_TYPE[fileExt] || 'text/javascript', filePath); | ||
}); | ||
|
||
// TODO(vojta): don't compute if it's not in the template | ||
var mappings = files.served.map(function(file) { | ||
var filePath = filePathToUrlPath(file.path, basePath); | ||
|
||
return util.format(' \'%s\': \'%d\'', filePath, file.mtime.getTime()); | ||
}); | ||
|
||
mappings = 'window.__karma__.files = {\n' + mappings.join(',\n') + '\n};\n'; | ||
|
||
return data.replace('%SCRIPTS%', scriptTags.join('\n')).replace('%MAPPINGS%', mappings); | ||
}); | ||
}); | ||
} | ||
|
||
return next(); | ||
}; | ||
}; | ||
|
||
|
||
// PUBLIC API | ||
exports.create = createKarmaMiddleware; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/** | ||
* Source Files middleware is responsible for serving all the source files under the test. | ||
*/ | ||
|
||
var querystring = require('querystring'); | ||
var common = require('./common'); | ||
var pause = require('connect').utils.pause; | ||
|
||
|
||
var findByPath = function(files, path) { | ||
for (var i = 0; i < files.length; i++) { | ||
if (files[i].path === path) { | ||
return files[i]; | ||
} | ||
} | ||
|
||
return null; | ||
}; | ||
|
||
|
||
var createSourceFilesMiddleware = function(filesPromise, serveFile, | ||
/* config.basePath */ basePath) { | ||
|
||
return function(request, response, next) { | ||
var requestedFilePath = querystring.unescape(request.url.replace(/\?.*/, '')) | ||
.replace(/^\/absolute/, '') | ||
.replace(/^\/base/, basePath); | ||
|
||
// Need to pause the request because of proxying, see: | ||
// https://groups.google.com/forum/#!topic/q-continuum/xr8znxc_K5E/discussion | ||
// TODO(vojta): remove once we don't care about Node 0.8 | ||
var pausedRequest = pause(request); | ||
|
||
return filesPromise.then(function(files) { | ||
// TODO(vojta): change served to be a map rather then an array | ||
var file = findByPath(files.served, requestedFilePath); | ||
|
||
if (file) { | ||
serveFile(file.contentPath, response, function() { | ||
if (/\?\d+/.test(request.url)) { | ||
// files with timestamps - cache one year, rely on timestamps | ||
common.setHeavyCacheHeaders(response); | ||
} else { | ||
// without timestamps - no cache (debug) | ||
common.setNoCacheHeaders(response); | ||
} | ||
}); | ||
} else { | ||
next(); | ||
} | ||
|
||
pausedRequest.resume(); | ||
}); | ||
}; | ||
}; | ||
|
||
|
||
// PUBLIC API | ||
exports.create = createSourceFilesMiddleware; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.