Skip to content

Commit c0fade7

Browse files
dylancarlonegr2m
authored andcommitted
fix: handle requests with body already parsed (#23)
1 parent 896e943 commit c0fade7

File tree

4 files changed

+102
-44
lines changed

4 files changed

+102
-44
lines changed

middleware/get-payload.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
module.exports = getPayload
2+
3+
function getPayload (request) {
4+
// If request.body already exists we can stop here
5+
// See https://github.com/octokit/webhooks.js/pull/23
6+
if (request.body) {
7+
return Promise.resolve(request.body)
8+
}
9+
10+
return new Promise((resolve, reject) => {
11+
const dataChunks = []
12+
13+
request.on('error', reject)
14+
request.on('data', (chunk) => dataChunks.push(chunk))
15+
request.on('end', () => {
16+
const data = Buffer.concat(dataChunks).toString()
17+
try {
18+
resolve(JSON.parse(data))
19+
} catch (error) {
20+
error.message = 'Invalid JSON'
21+
error.status = 400
22+
reject(error)
23+
}
24+
})
25+
})
26+
}

middleware/middleware.js

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module.exports = middleware
22

33
const isntWebhook = require('./isnt-webhook')
44
const getMissingHeaders = require('./get-missing-headers')
5+
const getPayload = require('./get-payload')
56
const verifyAndReceive = require('./verify-and-receive')
67

78
const debug = require('debug')('webhooks:receiver')
@@ -39,42 +40,23 @@ function middleware (state, request, response, next) {
3940

4041
debug(`${eventName} event received (id: ${id})`)
4142

42-
const dataChunks = []
43-
request.on('error', (error) => {
44-
response.statusCode = 500
45-
response.end(error.toString())
46-
})
43+
return getPayload(request)
4744

48-
request.on('data', (chunk) => {
49-
dataChunks.push(chunk)
50-
})
51-
52-
request.on('end', () => {
53-
const data = Buffer.concat(dataChunks).toString()
54-
let payload
55-
56-
try {
57-
payload = JSON.parse(data)
58-
} catch (error) {
59-
response.statusCode = 400
60-
response.end('Invalid JSON')
61-
return
62-
}
63-
64-
verifyAndReceive(state, {
65-
id: id,
66-
name: eventName,
67-
payload,
68-
signature
45+
.then((payload) => {
46+
return verifyAndReceive(state, {
47+
id: id,
48+
name: eventName,
49+
payload,
50+
signature
51+
})
6952
})
7053

71-
.then(() => {
72-
response.end('ok\n')
73-
})
54+
.then(() => {
55+
response.end('ok\n')
56+
})
7457

75-
.catch(error => {
76-
response.statusCode = error.status || 500
77-
response.end(error.toString())
78-
})
79-
})
58+
.catch(error => {
59+
response.statusCode = error.status || 500
60+
response.end(error.toString())
61+
})
8062
}

test/integration/middleware-test.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ test('Invalid payload', t => {
2525
const middleware = createMiddleware({ secret: 'mysecret' })
2626
middleware(requestMock, responseMock)
2727

28+
.then(() => {
29+
t.is(responseMock.statusCode, 400)
30+
t.is(responseMock.end.lastCall.arg, 'SyntaxError: Invalid JSON')
31+
t.end()
32+
})
33+
2834
requestMock.emit('data', Buffer.from('foo'))
2935
requestMock.emit('end')
30-
31-
t.is(responseMock.statusCode, 400)
32-
t.is(responseMock.end.lastCall.arg, 'Invalid JSON')
33-
34-
t.end()
3536
})
3637

3738
test('request error', t => {
@@ -47,11 +48,13 @@ test('request error', t => {
4748
const middleware = createMiddleware({ secret: 'mysecret' })
4849
middleware(requestMock, responseMock)
4950

50-
const error = new Error('oops')
51-
requestMock.emit('error', error)
51+
.then(() => {
52+
t.is(responseMock.statusCode, 500)
53+
t.is(responseMock.end.lastCall.arg, 'Error: oops')
5254

53-
t.is(responseMock.statusCode, 500)
54-
t.is(responseMock.end.lastCall.arg, 'Error: oops')
55+
t.end()
56+
})
5557

56-
t.end()
58+
const error = new Error('oops')
59+
requestMock.emit('error', error)
5760
})

test/integration/server-test.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,53 @@ test('POST / with push event payload', (t) => {
9090
.catch(t.error)
9191
})
9292

93+
test('POST / with push event payload (request.body already parsed)', (t) => {
94+
t.plan(2)
95+
96+
const api = new Webhooks({ secret: 'mysecret' })
97+
const dataChunks = []
98+
const server = http.createServer((req, res) => {
99+
req.once('data', chunk => dataChunks.push(chunk))
100+
req.once('end', () => {
101+
req.body = JSON.parse(Buffer.concat(dataChunks).toString())
102+
api.middleware(req, res)
103+
104+
setTimeout(() => {
105+
res.statusCode = 500
106+
res.end('Middleware timeout')
107+
}, 3000)
108+
})
109+
})
110+
111+
api.on('push', (event) => {
112+
t.is(event.id, '123e4567-e89b-12d3-a456-426655440000')
113+
})
114+
115+
promisify(server.listen.bind(server))(this.port)
116+
117+
.then(() => {
118+
return axios.post(`http://localhost:${this.port}`, pushEventPayload, {
119+
headers: {
120+
'X-GitHub-Delivery': '123e4567-e89b-12d3-a456-426655440000',
121+
'X-GitHub-Event': 'push',
122+
'X-Hub-Signature': 'sha1=f4d795e69b5d03c139cc6ea991ad3e5762d13e2f'
123+
}
124+
})
125+
})
126+
127+
.catch(t.error)
128+
129+
.then(result => {
130+
t.is(result.status, 200)
131+
})
132+
133+
.then(() => {
134+
server.close()
135+
})
136+
137+
.catch(t.error)
138+
})
139+
93140
test('POST / with push event payload (no signature)', (t) => {
94141
const api = new Webhooks({ secret: 'mysecret' })
95142
const server = http.createServer(api.middleware)

0 commit comments

Comments
 (0)