Skip to content
This repository was archived by the owner on Oct 20, 2023. It is now read-only.

Commit ef26157

Browse files
committed
feat: Add possibility to define custom response header
Closes #10 This can be configured globally via the new setting in `.env`.
1 parent 443b018 commit ef26157

File tree

8 files changed

+203
-15
lines changed

8 files changed

+203
-15
lines changed

.env

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ TEST_PORT=9090
1111
# URL Prefix for the endpoints
1212
# eg. http://localhost:8081/api/foo
1313
API_PREFIX=/api
14+
15+
# Custom response header
16+
#CUSTOM_HEADER_NAME=Authorization
17+
#CUSTOM_HEADER_VALUE=Bearer eyJhbGciOiJIUzUxMiJ9

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ npm start
7070

7171
Just starts the server via node.
7272

73-
## Configuring endpoints
73+
## Configure endpoints
7474

7575
Each endpoint needs a configuration file in `/server/api/` to define routes, http method and the response.
7676

@@ -236,7 +236,7 @@ The configuration object in Detail:
236236
* Optional
237237
* Every subroute of this endpoint will return a HTTP error with the given status code provided by [boom](https://github.com/hapijs/boom).
238238

239-
## Configuration
239+
## Configure server
240240

241241
The main config is handled via a file named `.env` with the following content:
242242

@@ -255,6 +255,10 @@ TEST_PORT=9090
255255
# eg. http://localhost:8081/api/foo
256256
API_PREFIX=/api
257257

258+
# Custom response header
259+
#CUSTOM_HEADER_NAME=Authorization
260+
#CUSTOM_HEADER_VALUE=Bearer eyJhbGciOiJIUzUxMiJ9
261+
258262
```
259263

260264

manifest.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const Confidence = require('confidence');
44
const Config = require('./config');
55
const Fs = require('fs');
66
const Path = require('path');
7+
const GetCustomResponseHeader = require('./server/api/setup/lib/getCustomResponseHeader');
78

89
const criteria = {
910
env: process.env.NODE_ENV
@@ -22,7 +23,8 @@ const manifest = {
2223
security: true,
2324
cors: {
2425
origin: ['*'],
25-
credentials: true
26+
credentials: true,
27+
additionalExposedHeaders: [GetCustomResponseHeader(process.env).name]
2628
}
2729
},
2830
router: {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'use strict';
2+
3+
module.exports = function (env) {
4+
5+
return {
6+
name: env.CUSTOM_HEADER_NAME || 'X-Powered-By',
7+
value: env.CUSTOM_HEADER_VALUE || 'https://hapijs.com'
8+
};
9+
};

server/api/setup/supportedMethod.js

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ const Boom = require('boom');
44
const Fs = require('fs');
55
const Path = require('path');
66

7-
const GetContentDisposition = require('./lib/getContentDisposition.js');
7+
const GetContentDisposition = require('./lib/getContentDisposition');
8+
const CustomResponseHeader = require('./lib/getCustomResponseHeader')(process.env);
89

910
module.exports = function (server, proposedRequest, settings, params, path) {
1011

@@ -39,23 +40,18 @@ module.exports = function (server, proposedRequest, settings, params, path) {
3940

4041
}
4142

42-
if (proposedRequest.statusCode && proposedRequest.response) {
43-
44-
if (sendFile && isFile) {
45-
return reply(response).code(proposedRequest.statusCode).type(mimeType).header('Content-Disposition', GetContentDisposition(proposedRequest.response));
46-
}
47-
return reply(response).code(proposedRequest.statusCode).type(mimeType);
48-
}
49-
5043
if (response.isBoom === true) {
44+
response.output.headers[CustomResponseHeader.name] = CustomResponseHeader.value;
5145
return reply(response);
5246
}
5347

5448
if (sendFile && isFile) {
55-
return reply(response).type(mimeType).header('Content-Disposition', GetContentDisposition(proposedRequest.response));
49+
return reply(response).code(proposedRequest.statusCode || 200).type(mimeType)
50+
.header('Content-Disposition', GetContentDisposition(proposedRequest.response))
51+
.header(CustomResponseHeader.name, CustomResponseHeader.value);
5652
}
57-
58-
return reply(response).type(mimeType);
53+
return reply(response).code(proposedRequest.statusCode || 200).type(mimeType)
54+
.header(CustomResponseHeader.name, CustomResponseHeader.value);
5955
}
6056
};
6157
};

server/api/setup/unsupportedMethods.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const Boom = require('boom');
4+
const CustomResponseHeader = require('./lib/getCustomResponseHeader')(process.env);
45

56
module.exports = function (settings, params, path) {
67

@@ -18,6 +19,7 @@ module.exports = function (settings, params, path) {
1819
response = Boom.methodNotAllowed();
1920
}
2021

22+
response.output.headers[CustomResponseHeader.name] = CustomResponseHeader.value;
2123
return reply(response);
2224
}
2325
};

test/manifest.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,10 @@ lab.experiment('Manifest', () => {
2222
Code.expect(Manifest.meta('/')).to.match(/hapi server config used by glue to compose the server/i);
2323
done();
2424
});
25+
26+
lab.test('it gets the correct custom response header', (done) => {
27+
28+
Code.expect(Manifest.get('/').server.connections.routes.cors.additionalExposedHeaders).to.equal(['X-Powered-By']);
29+
done();
30+
});
2531
});
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
'use strict';
2+
3+
const Lab = require('lab');
4+
const Code = require('code');
5+
const Config = require('../../../config');
6+
const Hapi = require('hapi');
7+
const SetupEndpoint = require('../../../server/api/setup/');
8+
const GetCustomResponseHeader = require('../../../server/api/setup/lib/getCustomResponseHeader');
9+
10+
const apiUrlPrefix = Config.get('/apiUrlPrefix');
11+
12+
const Endpoint = SetupEndpoint({
13+
name: 'customResponseHeader',
14+
urls: [
15+
{
16+
params: '/regularResponse',
17+
requests: [
18+
{ response: '/test/server/api/fixtures/response.json' }
19+
]
20+
},
21+
{
22+
params: '/fileResponse',
23+
requests: [{
24+
response: '/test/server/api/fixtures/example.pdf',
25+
sendFile: true,
26+
mimeType: 'application/pdf'
27+
}]
28+
},
29+
{
30+
params: '/boomError',
31+
requests: [{ statusCode: 402 }]
32+
}
33+
]
34+
});
35+
36+
const lab = exports.lab = Lab.script();
37+
let request;
38+
let server;
39+
40+
lab.beforeEach((done) => {
41+
42+
const plugins = [Endpoint];
43+
server = new Hapi.Server();
44+
server.connection({ port: Config.get('/port/web') });
45+
server.register(plugins, (err) => {
46+
47+
if (err) {
48+
return done(err);
49+
}
50+
51+
done();
52+
});
53+
});
54+
55+
56+
lab.experiment('Custom response header', () => {
57+
58+
lab.beforeEach((done) => {
59+
60+
61+
done();
62+
});
63+
64+
lab.test('should be read from .env file', (done) => {
65+
66+
const env = Object.assign({}, process.env);
67+
68+
env.CUSTOM_HEADER_NAME = 'Authorization';
69+
env.CUSTOM_HEADER_VALUE = 'Bearer eyJhbGciOiJIUzUxMiJ9';
70+
71+
Code.expect(GetCustomResponseHeader(env)).to.equal({
72+
name: 'Authorization',
73+
value: 'Bearer eyJhbGciOiJIUzUxMiJ9'
74+
});
75+
76+
done();
77+
});
78+
79+
lab.test('should have a fallback if not defined in .env file', (done) => {
80+
81+
82+
Code.expect(GetCustomResponseHeader(process.env)).to.equal({
83+
name: 'X-Powered-By',
84+
value: 'https://hapijs.com'
85+
});
86+
87+
done();
88+
});
89+
90+
lab.test('regular responses should have the defined response header', (done) => {
91+
92+
request = {
93+
method: 'GET',
94+
url: apiUrlPrefix + '/customResponseHeader/regularResponse'
95+
};
96+
97+
server.inject(request, (response) => {
98+
99+
Code.expect(response.headers['x-powered-by']).to.equal('https://hapijs.com');
100+
101+
done();
102+
});
103+
});
104+
105+
lab.test('file responses should have the defined response header', (done) => {
106+
107+
request = {
108+
method: 'GET',
109+
url: apiUrlPrefix + '/customResponseHeader/fileResponse'
110+
};
111+
112+
server.inject(request, (response) => {
113+
114+
Code.expect(response.headers['x-powered-by']).to.equal('https://hapijs.com');
115+
116+
done();
117+
});
118+
});
119+
120+
lab.test('boom errors should have the defined response header', (done) => {
121+
122+
request = {
123+
method: 'GET',
124+
url: apiUrlPrefix + '/customResponseHeader/boomError'
125+
};
126+
127+
server.inject(request, (response) => {
128+
129+
Code.expect(response.headers['x-powered-by']).to.equal('https://hapijs.com');
130+
131+
done();
132+
});
133+
});
134+
135+
lab.test('unallowed methods of regular responses should have the defined response header', (done) => {
136+
137+
request = {
138+
method: 'POST',
139+
url: apiUrlPrefix + '/customResponseHeader/regularResponse'
140+
};
141+
142+
server.inject(request, (response) => {
143+
144+
Code.expect(response.headers['x-powered-by']).to.equal('https://hapijs.com');
145+
146+
done();
147+
});
148+
});
149+
150+
lab.test('unallowed methods of boom errors should have the defined response header', (done) => {
151+
152+
request = {
153+
method: 'POST',
154+
url: apiUrlPrefix + '/customResponseHeader/boomError'
155+
};
156+
157+
server.inject(request, (response) => {
158+
159+
Code.expect(response.headers['x-powered-by']).to.equal('https://hapijs.com');
160+
161+
done();
162+
});
163+
});
164+
165+
});

0 commit comments

Comments
 (0)