Skip to content

Commit 1111294

Browse files
committed
Always attach an error listener to fetched resources.
A datasource loaded with an invalid source now emits an error rather than crashing the server.
1 parent d79a1ae commit 1111294

File tree

2 files changed

+68
-3
lines changed

2 files changed

+68
-3
lines changed

lib/datasources/Datasource.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ Datasource.prototype._executeQuery = function (query, destination) {
128128

129129
// Retrieves a stream through HTTP or the local file system
130130
Datasource.prototype._fetch = function (options) {
131-
var stream, url = options.url, protocolMatch = /^(?:([a-z]+):\/\/)?/.exec(url);
131+
var self = this, stream,
132+
url = options.url, protocolMatch = /^(?:([a-z]+):)?/.exec(url);
132133
switch (protocolMatch[1] || 'file') {
133134
// Fetch a representation through HTTP(S)
134135
case 'http':
@@ -147,8 +148,18 @@ Datasource.prototype._fetch = function (options) {
147148
stream = fs.createReadStream(url.substr(protocolMatch[0].length), { encoding: 'utf8' });
148149
break;
149150
default:
150-
throw new Error('Unknown protocol: ' + url);
151+
stream = new EventEmitter();
152+
setImmediate(function () {
153+
stream.emit('error', new Error('Unknown protocol: ' + protocolMatch[1]));
154+
});
151155
}
156+
157+
// If the stream has no other error handlers attached (besides this one),
158+
// emit the stream error as a datasource error
159+
stream.on('error', function (error) {
160+
if (stream.listenerCount('error') === 1)
161+
self.emit('error', error);
162+
});
152163
return stream;
153164
};
154165

test/datasources/Datasource-test.js

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
/*! @license MIT ©2013-2016 Ruben Verborgh, Ghent University - imec */
22
var Datasource = require('../../lib/datasources/Datasource');
33

4-
var EventEmitter = require('events');
4+
var EventEmitter = require('events'),
5+
fs = require('fs'),
6+
path = require('path');
7+
8+
var exampleFile = path.join(__dirname, '../assets/test.ttl');
59

610
describe('Datasource', function () {
711
describe('The Datasource module', function () {
@@ -54,6 +58,56 @@ describe('Datasource', function () {
5458
.should.throw('_executeQuery has not been implemented');
5559
});
5660

61+
describe('fetching a resource', function () {
62+
it('fetches an existing resource', function (done) {
63+
var result = datasource._fetch({ url: 'file://' + exampleFile }), buffer = '';
64+
result.on('data', function (d) { buffer += d; });
65+
result.on('end', function () {
66+
buffer.should.equal(fs.readFileSync(exampleFile, 'utf8'));
67+
done();
68+
});
69+
result.on('error', done);
70+
});
71+
72+
it('assumes file:// as the default protocol', function (done) {
73+
var result = datasource._fetch({ url: exampleFile }), buffer = '';
74+
result.on('data', function (d) { buffer += d; });
75+
result.on('end', function () {
76+
buffer.should.equal(fs.readFileSync(exampleFile, 'utf8'));
77+
done();
78+
});
79+
result.on('error', done);
80+
});
81+
82+
it('emits an error when the protocol is unknown', function (done) {
83+
var result = datasource._fetch({ url: 'myprotocol:abc' });
84+
result.on('error', function (error) {
85+
error.message.should.contain('Unknown protocol: myprotocol');
86+
done();
87+
});
88+
});
89+
90+
it('emits an error on the datasource when no error listener is attached to the result', function (done) {
91+
var result = datasource._fetch({ url: exampleFile + 'notfound' });
92+
result.on('data', done);
93+
datasource.on('error', function (error) {
94+
error.message.should.contain('ENOENT: no such file or directory');
95+
done();
96+
});
97+
});
98+
99+
it('does not emit an error on the datasource when an error listener is attached to the result', function (done) {
100+
var result = datasource._fetch({ url: exampleFile + 'notfound' });
101+
result.on('error', function (error) {
102+
error.message.should.contain('ENOENT: no such file or directory');
103+
done();
104+
});
105+
datasource.on('error', function (error) {
106+
done(error);
107+
});
108+
});
109+
});
110+
57111
describe('when closed without a callback', function () {
58112
it('should do nothing', function () {
59113
datasource.close();

0 commit comments

Comments
 (0)