Skip to content

Commit 28e3ee2

Browse files
author
Arjan Singh
committed
Allow post-fastboot middlewares to recover from fastboot failure
1 parent b975bb3 commit 28e3ee2

File tree

4 files changed

+120
-76
lines changed

4 files changed

+120
-76
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ provide to the middleware to specify which Ember app to load and render.
4949

5050
By default, errors during render will cause the middleware to send an
5151
HTTP 500 status code as the response. In order to swallow errors and
52-
return a `200 OK` with an empty HTML page, set the `resilient` flag to
52+
return a `200` status code with an empty HTML page, set the `resilient` flag to
5353
true:
5454

5555
```js
@@ -58,6 +58,13 @@ app.get('/*', fastbootMiddleware('/path/to/dist', {
5858
}));
5959
```
6060

61+
Resilient mode still calls `next(err)` to propagate your error to any subsequent
62+
middleware that you apply after this one.
63+
You can use this feature to track errors or log analytics.
64+
65+
However, because FastBoot is reslient still sends the response to the client.
66+
***You cannot alter the `response`*** with any of your post-fastboot middleware.
67+
6168
## Custom FastBoot Instance
6269

6370
For more control over the FastBoot instance that is created to render

src/index.js

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,24 +52,16 @@ function fastbootExpressMiddleware(distPath, options) {
5252
res.send(html);
5353
})
5454
.catch(error => {
55-
log(500, error.stack);
55+
res.status(500);
5656
next(error);
57-
res.sendStatus(500);
5857
});
5958
}
6059

6160
function failure(error) {
62-
if (error.name === "UnrecognizedURLError") {
63-
next();
64-
} else {
65-
next(error);
66-
log(500, "Unknown Error: " + error.stack);
67-
if (error.stack) {
68-
res.status(500).send(error.stack);
69-
} else {
70-
res.sendStatus(500);
71-
}
61+
if (error.name !== "UnrecognizedURLError") {
62+
res.status(500);
7263
}
64+
next(error);
7365
}
7466
};
7567
}

test/helpers/test-http-server.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,15 @@ class TestHTTPServer {
2424
if (options.errorHandling) {
2525
app.use((err, req, res, next) => {
2626
res.set('x-test-error', 'error handler called');
27-
next();
27+
next(err);
28+
});
29+
}
30+
31+
if (options.recoverErrors) {
32+
app.use((err, req, res, next) => {
33+
res.set('x-test-recovery', 'recovered response');
34+
res.status(200);
35+
res.send('hello world');
2836
});
2937
}
3038

test/middleware-test.js

Lines changed: 99 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -65,68 +65,6 @@ describe("FastBoot", function() {
6565
});
6666
});
6767

68-
it("renders no FastBoot markup if the resilient flag is set", function() {
69-
let middleware = fastbootMiddleware({
70-
distPath: fixture('rejected-promise'),
71-
resilient: true
72-
});
73-
server = new TestHTTPServer(middleware);
74-
75-
return server.start()
76-
.then(() => server.request('/'))
77-
.then(html => {
78-
expect(html).to.not.match(/error/);
79-
});
80-
});
81-
82-
it("propagates to error handling middleware if the resilient flag is set", function() {
83-
let middleware = fastbootMiddleware({
84-
distPath: fixture('rejected-promise'),
85-
resilient: true
86-
});
87-
server = new TestHTTPServer(middleware, { errorHandling: true });
88-
89-
return server.start()
90-
.then(() => server.request('/', { resolveWithFullResponse: true }))
91-
.then(({ body, statusCode, headers }) => {
92-
expect(statusCode).to.equal(200);
93-
expect(headers['x-test-error']).to.match(/error handler called/);
94-
expect(body).to.match(/hello world/);
95-
});
96-
});
97-
98-
it("propagates to error handling middleware if the resilient flag is not set", function() {
99-
let middleware = fastbootMiddleware({
100-
distPath: fixture('rejected-promise'),
101-
resilient: false,
102-
});
103-
server = new TestHTTPServer(middleware, { errorHandling: true });
104-
105-
return server.start()
106-
.then(() => server.request('/', { resolveWithFullResponse: true }))
107-
.catch(({ statusCode, response: { headers } }) => {
108-
expect(statusCode).to.equal(500);
109-
expect(headers['x-test-error']).to.match(/error handler called/);
110-
});
111-
});
112-
113-
it("is does not propagate errors when the reslient flag is set and there is no error handling middleware", function() {
114-
let middleware = fastbootMiddleware({
115-
distPath: fixture('rejected-promise'),
116-
resilient: true,
117-
});
118-
server = new TestHTTPServer(middleware, { errorHandling: false });
119-
120-
return server.start()
121-
.then(() => server.request('/', { resolveWithFullResponse: true }))
122-
.then(({ body, statusCode, headers }) => {
123-
expect(statusCode).to.equal(200);
124-
expect(headers['x-test-error']).to.not.match(/error handler called/);
125-
expect(body).to.not.match(/error/);
126-
expect(body).to.match(/hello world/);
127-
});
128-
});
129-
13068
it("can be provided with a custom FastBoot instance", function() {
13169
let fastboot = new FastBoot({
13270
distPath: fixture('basic-app')
@@ -181,4 +119,103 @@ describe("FastBoot", function() {
181119
});
182120
}
183121
});
122+
123+
describe('when reslient mode is enabled', function () {
124+
it("renders no FastBoot markup", function() {
125+
let middleware = fastbootMiddleware({
126+
distPath: fixture('rejected-promise'),
127+
resilient: true
128+
});
129+
server = new TestHTTPServer(middleware);
130+
131+
return server.start()
132+
.then(() => server.request('/'))
133+
.then(html => {
134+
expect(html).to.not.match(/error/);
135+
});
136+
});
137+
138+
it("propagates to error handling middleware", function() {
139+
let middleware = fastbootMiddleware({
140+
distPath: fixture('rejected-promise'),
141+
resilient: true
142+
});
143+
server = new TestHTTPServer(middleware, { errorHandling: true });
144+
145+
return server.start()
146+
.then(() => server.request('/', { resolveWithFullResponse: true }))
147+
.then(({ body, statusCode, headers }) => {
148+
expect(statusCode).to.equal(200);
149+
expect(headers['x-test-error']).to.match(/error handler called/);
150+
expect(body).to.match(/hello world/);
151+
});
152+
});
153+
154+
it("is does not propagate errors when and there is no error handling middleware", function() {
155+
let middleware = fastbootMiddleware({
156+
distPath: fixture('rejected-promise'),
157+
resilient: true,
158+
});
159+
server = new TestHTTPServer(middleware, { errorHandling: false });
160+
161+
return server.start()
162+
.then(() => server.request('/', { resolveWithFullResponse: true }))
163+
.then(({ body, statusCode, headers }) => {
164+
expect(statusCode).to.equal(200);
165+
expect(headers['x-test-error']).to.not.match(/error handler called/);
166+
expect(body).to.not.match(/error/);
167+
expect(body).to.match(/hello world/);
168+
});
169+
});
170+
171+
it("allows post-fastboot middleware to recover the response when it fails", function() {
172+
let middleware = fastbootMiddleware({
173+
distPath: fixture('rejected-promise'),
174+
resilient: true
175+
});
176+
server = new TestHTTPServer(middleware, { recoverErrors: true });
177+
178+
return server.start()
179+
.then(() => server.request('/', { resolveWithFullResponse: true }))
180+
.then(({ body, statusCode, headers }) => {
181+
expect(statusCode).to.equal(200);
182+
expect(headers['x-test-recovery']).to.match(/recovered response/);
183+
expect(body).to.match(/hello world/);
184+
});
185+
});
186+
});
187+
188+
describe('when reslient mode is disabled', function () {
189+
it("propagates to error handling middleware", function() {
190+
let middleware = fastbootMiddleware({
191+
distPath: fixture('rejected-promise'),
192+
resilient: false,
193+
});
194+
server = new TestHTTPServer(middleware, { errorHandling: true });
195+
196+
return server.start()
197+
.then(() => server.request('/', { resolveWithFullResponse: true }))
198+
.catch(({ statusCode, response: { headers } }) => {
199+
expect(statusCode).to.equal(500);
200+
expect(headers['x-test-error']).to.match(/error handler called/);
201+
});
202+
});
203+
204+
it("allows post-fastboot middleware to recover the response when it fails", function() {
205+
let middleware = fastbootMiddleware({
206+
distPath: fixture('rejected-promise'),
207+
resilient: false
208+
});
209+
server = new TestHTTPServer(middleware, { recoverErrors: true });
210+
211+
return server.start()
212+
.then(() => server.request('/', { resolveWithFullResponse: true }))
213+
.then(({ body, statusCode, headers }) => {
214+
expect(statusCode).to.equal(200);
215+
expect(headers['x-test-recovery']).to.match(/recovered response/);
216+
expect(body).to.match(/hello world/);
217+
});
218+
});
219+
});
220+
184221
});

0 commit comments

Comments
 (0)