mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-17 19:11:08 +00:00
Check if file exists (not a 404) and that document is accessible and not using file:// URLs in Application
This commit is contained in:
parent
d4839bac32
commit
5524d2b0fb
5 changed files with 181 additions and 30 deletions
|
|
@ -8,10 +8,8 @@ var DEFAULT_PORT = 8000;
|
||||||
|
|
||||||
function main(argv) {
|
function main(argv) {
|
||||||
new HttpServer({
|
new HttpServer({
|
||||||
'GET': (function() {
|
'GET': createServlet(StaticServlet),
|
||||||
var servlet = new StaticServlet();
|
'HEAD': createServlet(StaticServlet)
|
||||||
return servlet.handleRequest.bind(servlet)
|
|
||||||
})()
|
|
||||||
}).start(Number(argv[2]) || DEFAULT_PORT);
|
}).start(Number(argv[2]) || DEFAULT_PORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,6 +20,11 @@ function escapeHtml(value) {
|
||||||
replace('"', '"');
|
replace('"', '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createServlet(Class) {
|
||||||
|
var servlet = new Class();
|
||||||
|
return servlet.handleRequest.bind(servlet);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An Http server implementation that uses a map of methods to decide
|
* An Http server implementation that uses a map of methods to decide
|
||||||
* action routing.
|
* action routing.
|
||||||
|
|
@ -61,11 +64,10 @@ HttpServer.prototype.handleRequest_ = function(req, res) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles static content.
|
* Handles static content.
|
||||||
*/
|
*/
|
||||||
function StaticServlet() {}
|
function StaticServlet() {}
|
||||||
|
|
||||||
StaticServlet.MimeMap = {
|
StaticServlet.MimeMap = {
|
||||||
'txt': 'text/plain',
|
'txt': 'text/plain',
|
||||||
|
|
@ -164,13 +166,17 @@ StaticServlet.prototype.sendFile_ = function(req, res, path) {
|
||||||
'Content-Type': StaticServlet.
|
'Content-Type': StaticServlet.
|
||||||
MimeMap[path.split('.').pop()] || 'text/plain'
|
MimeMap[path.split('.').pop()] || 'text/plain'
|
||||||
});
|
});
|
||||||
file.on('data', res.write.bind(res));
|
if (req.method === 'HEAD') {
|
||||||
file.on('close', function() {
|
|
||||||
res.end();
|
res.end();
|
||||||
});
|
} else {
|
||||||
file.on('error', function(error) {
|
file.on('data', res.write.bind(res));
|
||||||
self.sendError_(req, res, error);
|
file.on('close', function() {
|
||||||
});
|
res.end();
|
||||||
|
});
|
||||||
|
file.on('error', function(error) {
|
||||||
|
self.sendError_(req, res, error);
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
|
StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
|
||||||
|
|
@ -207,6 +213,10 @@ StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
'Content-Type': 'text/html'
|
'Content-Type': 'text/html'
|
||||||
});
|
});
|
||||||
|
if (req.method === 'HEAD') {
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
res.write('<!doctype html>\n');
|
res.write('<!doctype html>\n');
|
||||||
res.write('<title>' + escapeHtml(path) + '</title>\n');
|
res.write('<title>' + escapeHtml(path) + '</title>\n');
|
||||||
res.write('<style>\n');
|
res.write('<style>\n');
|
||||||
|
|
|
||||||
|
|
@ -37,28 +37,71 @@ angular.scenario.Application.prototype.getWindow_ = function() {
|
||||||
return contentWindow;
|
return contentWindow;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that a URL would return a 2xx success status code. Callback is called
|
||||||
|
* with no arguments on success, or with an error on failure.
|
||||||
|
*
|
||||||
|
* Warning: This requires the server to be able to respond to HEAD requests
|
||||||
|
* and not modify the state of your application.
|
||||||
|
*
|
||||||
|
* @param {string} url Url to check
|
||||||
|
* @param {Function} callback function(error) that is called with result.
|
||||||
|
*/
|
||||||
|
angular.scenario.Application.prototype.checkUrlStatus_ = function(url, callback) {
|
||||||
|
var self = this;
|
||||||
|
_jQuery.ajax({
|
||||||
|
url: url,
|
||||||
|
type: 'HEAD',
|
||||||
|
complete: function(request) {
|
||||||
|
if (request.status < 200 || request.status >= 300) {
|
||||||
|
if (!request.status) {
|
||||||
|
callback.call(self, 'Sandbox Error: Cannot access ' + url);
|
||||||
|
} else {
|
||||||
|
callback.call(self, request.status + ' ' + request.statusText);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback.call(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the location of the frame.
|
* Changes the location of the frame.
|
||||||
*
|
*
|
||||||
* @param {string} url The URL. If it begins with a # then only the
|
* @param {string} url The URL. If it begins with a # then only the
|
||||||
* hash of the page is changed.
|
* hash of the page is changed.
|
||||||
* @param {Function} onloadFn function($window, $document)
|
* @param {Function} loadFn function($window, $document) Called when frame loads.
|
||||||
|
* @param {Function} errorFn function(error) Called if any error when loading.
|
||||||
*/
|
*/
|
||||||
angular.scenario.Application.prototype.navigateTo = function(url, onloadFn) {
|
angular.scenario.Application.prototype.navigateTo = function(url, loadFn, errorFn) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var frame = this.getFrame_();
|
var frame = this.getFrame_();
|
||||||
if (url.charAt(0) === '#') {
|
//TODO(esprehn): Refactor to use rethrow()
|
||||||
|
errorFn = errorFn || function(e) { throw e; };
|
||||||
|
if (/^file:\/\//.test(url)) {
|
||||||
|
errorFn('Sandbox Error: Cannot load file:// URL.');
|
||||||
|
} else if (url.charAt(0) === '#') {
|
||||||
url = frame.attr('src').split('#')[0] + url;
|
url = frame.attr('src').split('#')[0] + url;
|
||||||
frame.attr('src', url);
|
frame.attr('src', url);
|
||||||
this.executeAction(onloadFn);
|
this.executeAction(loadFn);
|
||||||
} else {
|
} else {
|
||||||
frame.css('display', 'none').attr('src', 'about:blank');
|
frame.css('display', 'none').attr('src', 'about:blank');
|
||||||
this.context.find('#test-frames').append('<iframe>');
|
this.checkUrlStatus_(url, function(error) {
|
||||||
frame = this.getFrame_();
|
if (error) {
|
||||||
frame.load(function() {
|
return errorFn(error);
|
||||||
self.executeAction(onloadFn);
|
}
|
||||||
frame.unbind();
|
self.context.find('#test-frames').append('<iframe>');
|
||||||
}).attr('src', url);
|
frame = this.getFrame_();
|
||||||
|
frame.load(function() {
|
||||||
|
frame.unbind();
|
||||||
|
try {
|
||||||
|
self.executeAction(loadFn);
|
||||||
|
} catch (e) {
|
||||||
|
errorFn(e);
|
||||||
|
}
|
||||||
|
}).attr('src', url);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.context.find('> h2 a').attr('href', url).text(url);
|
this.context.find('> h2 a').attr('href', url).text(url);
|
||||||
};
|
};
|
||||||
|
|
@ -73,6 +116,9 @@ angular.scenario.Application.prototype.navigateTo = function(url, onloadFn) {
|
||||||
angular.scenario.Application.prototype.executeAction = function(action) {
|
angular.scenario.Application.prototype.executeAction = function(action) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var $window = this.getWindow_();
|
var $window = this.getWindow_();
|
||||||
|
if (!$window.document) {
|
||||||
|
throw 'Sandbox Error: Application document not accessible.';
|
||||||
|
}
|
||||||
if (!$window.angular) {
|
if (!$window.angular) {
|
||||||
return action.call(this, $window, _jQuery($window.document));
|
return action.call(this, $window, _jQuery($window.document));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup file for the Scenario.
|
* Setup file for the Scenario.
|
||||||
* Must be first in the compilation/bootstrap list.
|
* Must be first in the compilation/bootstrap list.
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ angular.scenario.dsl('navigateTo', function() {
|
||||||
}
|
}
|
||||||
application.navigateTo(url, function() {
|
application.navigateTo(url, function() {
|
||||||
done(null, url);
|
done(null, url);
|
||||||
});
|
}, done);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -271,7 +271,7 @@ angular.scenario.dsl('element', function() {
|
||||||
if (href && elements[0].nodeName.toUpperCase() === 'A') {
|
if (href && elements[0].nodeName.toUpperCase() === 'A') {
|
||||||
this.application.navigateTo(href, function() {
|
this.application.navigateTo(href, function() {
|
||||||
done();
|
done();
|
||||||
});
|
}, done);
|
||||||
} else {
|
} else {
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,24 @@
|
||||||
describe('angular.scenario.Application', function() {
|
describe('angular.scenario.Application', function() {
|
||||||
var app, frames;
|
var app, frames;
|
||||||
|
|
||||||
|
function callLoadHandlers(app) {
|
||||||
|
var handlers = app.getFrame_().data('events').load;
|
||||||
|
expect(handlers).toBeDefined();
|
||||||
|
expect(handlers.length).toEqual(1);
|
||||||
|
handlers[0].handler();
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
frames = _jQuery("<div></div>");
|
frames = _jQuery("<div></div>");
|
||||||
app = new angular.scenario.Application(frames);
|
app = new angular.scenario.Application(frames);
|
||||||
|
app.checkUrlStatus_ = function(url, callback) {
|
||||||
|
callback.call(this);
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return new $window and $document after navigate', function() {
|
it('should return new $window and $document after navigate', function() {
|
||||||
var called;
|
var called;
|
||||||
var testWindow, testDocument, counter = 0;
|
var testWindow, testDocument, counter = 0;
|
||||||
app.navigateTo = noop;
|
|
||||||
app.getWindow_ = function() {
|
app.getWindow_ = function() {
|
||||||
return {x:counter++, document:{x:counter++}};
|
return {x:counter++, document:{x:counter++}};
|
||||||
};
|
};
|
||||||
|
|
@ -50,6 +59,31 @@ describe('angular.scenario.Application', function() {
|
||||||
expect(app.getFrame_().attr('test')).toBeFalsy();
|
expect(app.getFrame_().attr('test')).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should call error handler if document not accessible', function() {
|
||||||
|
app.getWindow_ = function() {
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
app.navigateTo('about:blank', angular.noop, function(error) {
|
||||||
|
expect(error).toMatch(/Sandbox Error/);
|
||||||
|
});
|
||||||
|
callLoadHandlers(app);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call error handler if using file:// URL', function() {
|
||||||
|
app.navigateTo('file://foo/bar.txt', angular.noop, function(error) {
|
||||||
|
expect(error).toMatch(/Sandbox Error/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call error handler if status check fails', function() {
|
||||||
|
app.checkUrlStatus_ = function(url, callback) {
|
||||||
|
callback.call(this, 'Example Error');
|
||||||
|
};
|
||||||
|
app.navigateTo('about:blank', angular.noop, function(error) {
|
||||||
|
expect(error).toEqual('Example Error');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should hide old iframes and navigate to about:blank', function() {
|
it('should hide old iframes and navigate to about:blank', function() {
|
||||||
app.navigateTo('about:blank#foo');
|
app.navigateTo('about:blank#foo');
|
||||||
app.navigateTo('about:blank#bar');
|
app.navigateTo('about:blank#bar');
|
||||||
|
|
@ -70,15 +104,12 @@ describe('angular.scenario.Application', function() {
|
||||||
it('should call onload handler when frame loads', function() {
|
it('should call onload handler when frame loads', function() {
|
||||||
var called;
|
var called;
|
||||||
app.getWindow_ = function() {
|
app.getWindow_ = function() {
|
||||||
return {};
|
return {document: {}};
|
||||||
};
|
};
|
||||||
app.navigateTo('about:blank', function($window, $document) {
|
app.navigateTo('about:blank', function($window, $document) {
|
||||||
called = true;
|
called = true;
|
||||||
});
|
});
|
||||||
var handlers = app.getFrame_().data('events').load;
|
callLoadHandlers(app);
|
||||||
expect(handlers).toBeDefined();
|
|
||||||
expect(handlers.length).toEqual(1);
|
|
||||||
handlers[0].handler();
|
|
||||||
expect(called).toBeTruthy();
|
expect(called).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -113,4 +144,67 @@ describe('angular.scenario.Application', function() {
|
||||||
expect(handlers.length).toEqual(1);
|
expect(handlers.length).toEqual(1);
|
||||||
handlers[0]();
|
handlers[0]();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('jQuery ajax', function() {
|
||||||
|
var options;
|
||||||
|
var response;
|
||||||
|
var jQueryAjax;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
response = {
|
||||||
|
status: 200,
|
||||||
|
statusText: 'OK'
|
||||||
|
};
|
||||||
|
jQueryAjax = _jQuery.ajax;
|
||||||
|
_jQuery.ajax = function(opts) {
|
||||||
|
options = opts;
|
||||||
|
opts.complete.call(this, response);
|
||||||
|
};
|
||||||
|
app.checkUrlStatus_ = angular.scenario.Application.
|
||||||
|
prototype.checkUrlStatus_;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
_jQuery.ajax = jQueryAjax;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should perform a HEAD request to verify file existence', function() {
|
||||||
|
app.navigateTo('http://www.google.com/', angular.noop, angular.noop);
|
||||||
|
expect(options.type).toEqual('HEAD');
|
||||||
|
expect(options.url).toEqual('http://www.google.com/');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call error handler if status code is less than 200', function() {
|
||||||
|
var finished;
|
||||||
|
response.status = 199;
|
||||||
|
response.statusText = 'Error Message';
|
||||||
|
app.navigateTo('about:blank', angular.noop, function(error) {
|
||||||
|
expect(error).toEqual('199 Error Message');
|
||||||
|
finished = true;
|
||||||
|
});
|
||||||
|
expect(finished).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call error handler if status code is greater than 299', function() {
|
||||||
|
var finished;
|
||||||
|
response.status = 300;
|
||||||
|
response.statusText = 'Error';
|
||||||
|
app.navigateTo('about:blank', angular.noop, function(error) {
|
||||||
|
expect(error).toEqual('300 Error');
|
||||||
|
finished = true;
|
||||||
|
});
|
||||||
|
expect(finished).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call error handler if status code is 0 for sandbox error', function() {
|
||||||
|
var finished;
|
||||||
|
response.status = 0;
|
||||||
|
response.statusText = '';
|
||||||
|
app.navigateTo('about:blank', angular.noop, function(error) {
|
||||||
|
expect(error).toEqual('Sandbox Error: Cannot access about:blank');
|
||||||
|
finished = true;
|
||||||
|
});
|
||||||
|
expect(finished).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue