Flaska: HEAD request support across the board.
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
All checks were successful
continuous-integration/appveyor/branch AppVeyor build succeeded
Flaska: Set status to 204 if status is 200 and body is null Flaska: Fix implementation of how content-type is set FileResponse: Don't open file if running HEAD request
This commit is contained in:
parent
d4bac4940e
commit
b97e34c1eb
5 changed files with 637 additions and 216 deletions
53
flaska.mjs
53
flaska.mjs
|
@ -376,10 +376,13 @@ export class FileResponse {
|
|||
ctx.type = found[found.length - 1]
|
||||
}
|
||||
|
||||
let stream = useFs.createReadStream(this.filepath, readOptions)
|
||||
ctx.headers['Last-Modified'] = lastModified
|
||||
ctx.headers['Content-Length'] = size
|
||||
return stream
|
||||
if (ctx.method !== 'HEAD') {
|
||||
let stream = useFs.createReadStream(this.filepath, readOptions)
|
||||
return stream
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -912,7 +915,8 @@ ctx.state.nonce = nonce;
|
|||
|
||||
requestEnd(orgErr, ctx) {
|
||||
let err = orgErr
|
||||
if (ctx.body && ctx.body.handleRequest) {
|
||||
let handleUsed = Boolean(ctx.body && ctx.body.handleRequest)
|
||||
if (handleUsed) {
|
||||
try {
|
||||
ctx.body = ctx.body.handleRequest(ctx)
|
||||
} catch (newErr) {
|
||||
|
@ -931,13 +935,18 @@ ctx.state.nonce = nonce;
|
|||
return
|
||||
}
|
||||
|
||||
if (ctx.body === null && !handleUsed && ctx.status === 200) {
|
||||
ctx.status = 204
|
||||
}
|
||||
|
||||
if (statuses.empty[ctx.status]) {
|
||||
ctx.res.writeHead(ctx.status, ctx.headers)
|
||||
return ctx.res.end()
|
||||
}
|
||||
|
||||
let body = ctx.body
|
||||
let length = 0
|
||||
|
||||
// Special handling for files
|
||||
if (body && typeof(body.pipe) === 'function') {
|
||||
// Be smart when handling file handles, auto detect mime-type
|
||||
// based off of the extension of the file.
|
||||
|
@ -951,22 +960,42 @@ ctx.state.nonce = nonce;
|
|||
ctx.headers['Content-Type'] = ctx.type || 'application/octet-stream'
|
||||
|
||||
ctx.res.writeHead(ctx.status, ctx.headers)
|
||||
return this.stream.pipeline(body, ctx.res, function() { })
|
||||
|
||||
if (ctx.method !== 'HEAD') {
|
||||
return this.stream.pipeline(body, ctx.res, function() { })
|
||||
} else {
|
||||
try {
|
||||
body.destroy()
|
||||
} catch { }
|
||||
return ctx.res.end()
|
||||
}
|
||||
}
|
||||
if (typeof(body) === 'object') {
|
||||
|
||||
let length = 0
|
||||
|
||||
if (typeof(body) === 'object' && body) {
|
||||
body = JSON.stringify(body)
|
||||
length = Buffer.byteLength(body)
|
||||
ctx.headers['Content-Type'] = 'application/json; charset=utf-8'
|
||||
} else {
|
||||
ctx.type = 'application/json; charset=utf-8'
|
||||
} else if (body) {
|
||||
body = body.toString()
|
||||
length = Buffer.byteLength(body)
|
||||
ctx.headers['Content-Type'] = ctx.type || 'text/plain; charset=utf-8'
|
||||
ctx.type = ctx.type || 'text/plain; charset=utf-8'
|
||||
}
|
||||
|
||||
if (ctx.type) {
|
||||
ctx.headers['Content-Type'] = ctx.type
|
||||
}
|
||||
if (!ctx.headers['Content-Length']) {
|
||||
ctx.headers['Content-Length'] = length
|
||||
}
|
||||
|
||||
ctx.headers['Content-Length'] = length
|
||||
|
||||
ctx.res.writeHead(ctx.status, ctx.headers)
|
||||
ctx.res.end(body)
|
||||
if (body && ctx.method !== 'HEAD') {
|
||||
ctx.res.end(body)
|
||||
} else {
|
||||
ctx.res.end()
|
||||
}
|
||||
}
|
||||
|
||||
requestEnded(ctx) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "flaska",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.0",
|
||||
"description": "Flaska is a micro web-framework for node. It is designed to be fast, simple and lightweight, and is distributed as a single file module with no dependencies.",
|
||||
"main": "flaska.mjs",
|
||||
"scripts": {
|
||||
|
|
|
@ -13,84 +13,86 @@ t.test('should add path and stat', function() {
|
|||
})
|
||||
|
||||
t.describe('CreateReader()', function() {
|
||||
const assertPath = '/some/path/here.png'
|
||||
const assertBody = { a: 1 }
|
||||
const assertSize = 12498
|
||||
const assertmTime = new Date('2021-04-02T11:22:33.777')
|
||||
const roundedModified = new Date('2021-04-02T11:22:33')
|
||||
const assertIno = 3569723027
|
||||
const assertEtag = `"${assertIno}-${assertSize}-${assertmTime.getTime()}"`
|
||||
const stat = {
|
||||
size: assertSize,
|
||||
mtime: assertmTime,
|
||||
ino: assertIno,
|
||||
}
|
||||
t.describe('return fileReader', function() {
|
||||
const assertPath = '/some/path/here.png'
|
||||
const assertBody = { a: 1 }
|
||||
const assertSize = 12498
|
||||
const assertmTime = new Date('2021-04-02T11:22:33.777')
|
||||
const roundedModified = new Date('2021-04-02T11:22:33')
|
||||
const assertIno = 3569723027
|
||||
const assertEtag = `"${assertIno}-${assertSize}-${assertmTime.getTime()}"`
|
||||
const stat = {
|
||||
size: assertSize,
|
||||
mtime: assertmTime,
|
||||
ino: assertIno,
|
||||
}
|
||||
|
||||
let tests = [
|
||||
[
|
||||
'if no range',
|
||||
function() {},
|
||||
],
|
||||
[
|
||||
'if range start is higher than end',
|
||||
function(headers) { headers['range'] = 'bytes=2000-1000' },
|
||||
],
|
||||
[
|
||||
'if pre-condition if-match passes',
|
||||
function(headers) { headers['if-match'] = assertEtag },
|
||||
],
|
||||
[
|
||||
'if pre-condition if-unmodified-since passes',
|
||||
function(headers) { headers['if-unmodified-since'] = roundedModified.toUTCString(); },
|
||||
],
|
||||
[
|
||||
'if both pre-condition pass',
|
||||
function(headers) { headers['if-unmodified-since'] = new Date(roundedModified.getTime() + 1000).toUTCString(); headers['if-match'] = assertEtag },
|
||||
],
|
||||
[
|
||||
'if range is specified but if-range etag does not match',
|
||||
function(headers) {
|
||||
headers['if-range'] = `"${assertIno}-${assertSize}-${assertmTime.getTime() + 1}"`
|
||||
headers['range'] = 'bytes=1000-2000'
|
||||
}
|
||||
],
|
||||
[
|
||||
'if range is specified but if-range modified by is older',
|
||||
function(headers) {
|
||||
headers['if-range'] = `${new Date(roundedModified.getTime() - 1000).toUTCString()}`
|
||||
headers['range'] = 'bytes=1000-2000'
|
||||
}
|
||||
],
|
||||
[
|
||||
'if range is specified but if-range is garbage',
|
||||
function(headers) {
|
||||
headers['if-range'] = `asdf`
|
||||
headers['range'] = 'bytes=1000-2000'
|
||||
}
|
||||
],
|
||||
]
|
||||
|
||||
let tests = [
|
||||
[
|
||||
'if no range',
|
||||
function() {},
|
||||
],
|
||||
[
|
||||
'if range start is higher than end',
|
||||
function(headers) { headers['range'] = 'bytes=2000-1000' },
|
||||
],
|
||||
[
|
||||
'if pre-condition if-match passes',
|
||||
function(headers) { headers['if-match'] = assertEtag },
|
||||
],
|
||||
[
|
||||
'if pre-condition if-unmodified-since passes',
|
||||
function(headers) { headers['if-unmodified-since'] = roundedModified.toUTCString(); },
|
||||
],
|
||||
[
|
||||
'if both pre-condition pass',
|
||||
function(headers) { headers['if-unmodified-since'] = new Date(roundedModified.getTime() + 1000).toUTCString(); headers['if-match'] = assertEtag },
|
||||
],
|
||||
[
|
||||
'if range is specified but if-range etag does not match',
|
||||
function(headers) {
|
||||
headers['if-range'] = `"${assertIno}-${assertSize}-${assertmTime.getTime() + 1}"`
|
||||
headers['range'] = 'bytes=1000-2000'
|
||||
}
|
||||
],
|
||||
[
|
||||
'if range is specified but if-range modified by is older',
|
||||
function(headers) {
|
||||
headers['if-range'] = `${new Date(roundedModified.getTime() - 1000).toUTCString()}`
|
||||
headers['range'] = 'bytes=1000-2000'
|
||||
}
|
||||
],
|
||||
[
|
||||
'if range is specified but if-range is garbage',
|
||||
function(headers) {
|
||||
headers['if-range'] = `asdf`
|
||||
headers['range'] = 'bytes=1000-2000'
|
||||
}
|
||||
],
|
||||
]
|
||||
|
||||
tests.forEach(function(test){
|
||||
t.test('return fileReader ' + test[0], function() {
|
||||
|
||||
const stubCreateReadStream = stub().returns(assertBody)
|
||||
let ctx = createCtx()
|
||||
test[1](ctx.req.headers)
|
||||
ctx.body = new FileResponse(assertPath, stat)
|
||||
let checkBody = ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
||||
|
||||
assert.strictEqual(checkBody, assertBody)
|
||||
assert.strictEqual(stubCreateReadStream.firstCall[0], assertPath)
|
||||
assert.deepStrictEqual(stubCreateReadStream.firstCall[1], {})
|
||||
assert.strictEqual(ctx.headers['Etag'], assertEtag)
|
||||
assert.strictEqual(ctx.headers['Last-Modified'], assertmTime.toUTCString())
|
||||
assert.strictEqual(ctx.headers['Content-Length'], assertSize)
|
||||
assert.strictEqual(ctx.type, 'image/png')
|
||||
assert.notOk(ctx.headers['Content-Range'])
|
||||
assert.strictEqual(ctx.status, 200)
|
||||
tests.forEach(function(test){
|
||||
t.test(test[0], function() {
|
||||
|
||||
const stubCreateReadStream = stub().returns(assertBody)
|
||||
let ctx = createCtx()
|
||||
test[1](ctx.req.headers)
|
||||
ctx.body = new FileResponse(assertPath, stat)
|
||||
let checkBody = ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
||||
|
||||
assert.strictEqual(checkBody, assertBody)
|
||||
assert.strictEqual(stubCreateReadStream.firstCall[0], assertPath)
|
||||
assert.deepStrictEqual(stubCreateReadStream.firstCall[1], {})
|
||||
assert.strictEqual(ctx.headers['Etag'], assertEtag)
|
||||
assert.strictEqual(ctx.headers['Last-Modified'], assertmTime.toUTCString())
|
||||
assert.strictEqual(ctx.headers['Content-Length'], assertSize)
|
||||
assert.strictEqual(ctx.type, 'image/png')
|
||||
assert.notOk(ctx.headers['Content-Range'])
|
||||
assert.strictEqual(ctx.status, 200)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
t.test('return fileReader with proper length with range', function() {
|
||||
const assertPath = '/some/path/here.jpg'
|
||||
const assertBody = { a: 1 }
|
||||
|
@ -120,6 +122,101 @@ t.describe('CreateReader()', function() {
|
|||
assert.strictEqual(ctx.status, 206)
|
||||
})
|
||||
|
||||
t.test('should not call createReadStream if HEAD request', function() {
|
||||
const assertPath = '/some/path/here.png'
|
||||
const assertNotBody = { a: 1 }
|
||||
const assertSize = 12498
|
||||
const assertmTime = new Date('2021-04-02T11:22:33.777')
|
||||
const roundedModified = new Date('2021-04-02T11:22:33')
|
||||
const assertIno = 3569723027
|
||||
const assertEtag = `"${assertIno}-${assertSize}-${assertmTime.getTime()}"`
|
||||
const stat = {
|
||||
size: assertSize,
|
||||
mtime: assertmTime,
|
||||
ino: assertIno,
|
||||
}
|
||||
|
||||
let tests = [
|
||||
[
|
||||
'if no range',
|
||||
function() {},
|
||||
],
|
||||
[
|
||||
'if range start is higher than end',
|
||||
function(headers) { headers['range'] = 'bytes=2000-1000' },
|
||||
],
|
||||
[
|
||||
'if pre-condition if-match passes',
|
||||
function(headers) { headers['if-match'] = assertEtag },
|
||||
],
|
||||
[
|
||||
'if pre-condition if-unmodified-since passes',
|
||||
function(headers) { headers['if-unmodified-since'] = roundedModified.toUTCString(); },
|
||||
],
|
||||
[
|
||||
'if both pre-condition pass',
|
||||
function(headers) { headers['if-unmodified-since'] = new Date(roundedModified.getTime() + 1000).toUTCString(); headers['if-match'] = assertEtag },
|
||||
],
|
||||
[
|
||||
'if range is specified but if-range etag does not match',
|
||||
function(headers) {
|
||||
headers['if-range'] = `"${assertIno}-${assertSize}-${assertmTime.getTime() + 1}"`
|
||||
headers['range'] = 'bytes=1000-2000'
|
||||
}
|
||||
],
|
||||
[
|
||||
'if range is specified but if-range modified by is older',
|
||||
function(headers) {
|
||||
headers['if-range'] = `${new Date(roundedModified.getTime() - 1000).toUTCString()}`
|
||||
headers['range'] = 'bytes=1000-2000'
|
||||
}
|
||||
],
|
||||
[
|
||||
'if range is specified but if-range is garbage',
|
||||
function(headers) {
|
||||
headers['if-range'] = `asdf`
|
||||
headers['range'] = 'bytes=1000-2000'
|
||||
}
|
||||
],
|
||||
]
|
||||
|
||||
tests.forEach(function(test){
|
||||
const stubCreateReadStream = stub().returns(assertNotBody)
|
||||
let ctx = createCtx({
|
||||
method: 'HEAD',
|
||||
})
|
||||
test[1](ctx.req.headers)
|
||||
ctx.body = new FileResponse(assertPath, stat)
|
||||
let checkBody = ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
||||
|
||||
assert.strictEqual(checkBody, null)
|
||||
assert.notOk(stubCreateReadStream.called)
|
||||
assert.strictEqual(ctx.headers['Etag'], assertEtag)
|
||||
assert.strictEqual(ctx.headers['Last-Modified'], assertmTime.toUTCString())
|
||||
assert.strictEqual(ctx.headers['Content-Length'], assertSize)
|
||||
assert.strictEqual(ctx.type, 'image/png')
|
||||
assert.notOk(ctx.headers['Content-Range'])
|
||||
assert.strictEqual(ctx.status, 200)
|
||||
})
|
||||
|
||||
const testStart = [0, 1000, 1999]
|
||||
testStart.forEach(function(start) {
|
||||
const stubCreateReadStream = stub()
|
||||
let ctx = createCtx({
|
||||
method: 'HEAD',
|
||||
})
|
||||
ctx.req.headers['range'] = 'bytes=' + start + '-'
|
||||
ctx.body = new FileResponse('file.png', stat)
|
||||
let checkBody = ctx.body.handleRequest(ctx, { createReadStream: stubCreateReadStream })
|
||||
|
||||
assert.strictEqual(checkBody, null)
|
||||
assert.notOk(stubCreateReadStream.called)
|
||||
assert.strictEqual(ctx.headers['Content-Length'], assertSize - start)
|
||||
assert.strictEqual(ctx.headers['Content-Range'], `${start}-${assertSize - 1}/${assertSize}`)
|
||||
assert.strictEqual(ctx.status, 206)
|
||||
})
|
||||
})
|
||||
|
||||
t.test('return fileReader with proper length with range and if-range passes', function() {
|
||||
const assertPath = '/some/path/here.jpg'
|
||||
const assertBody = { a: 1 }
|
||||
|
|
|
@ -61,110 +61,320 @@ t.describe('#requestEnd()', function() {
|
|||
flaska.requestEnd(assertErrorNotSeen, ctx)
|
||||
})
|
||||
|
||||
t.test('call res and end correctly when dealing with objects', function(cb) {
|
||||
const assertStatus = 202
|
||||
// Calculated manually just in case
|
||||
const assertBodyLength = 7
|
||||
const assertBody = { a: 1 }
|
||||
let onFinish = function(body) {
|
||||
try {
|
||||
assert.strictEqual(ctx.status, assertStatus)
|
||||
assert.strictEqual(ctx.body, assertBody)
|
||||
assert.strictEqual(ctx.headers['Content-Type'], 'application/json; charset=utf-8')
|
||||
assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength)
|
||||
assert.ok(body)
|
||||
assert.strictEqual(body, '{"a":1}')
|
||||
assert.ok(ctx.res.writeHead.called)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
||||
cb()
|
||||
} catch (err) { cb(err) }
|
||||
}
|
||||
const ctx = createCtx({
|
||||
status: assertStatus,
|
||||
}, onFinish)
|
||||
ctx.body = assertBody
|
||||
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
flaska.requestEnd(null, ctx)
|
||||
})
|
||||
let testMethods = ['GET', 'HEAD']
|
||||
|
||||
t.test('call res and end correctly when dealing with strings', function(cb) {
|
||||
const assertStatus = 206
|
||||
// Calculated manually just in case
|
||||
const assertBodyLength = 4
|
||||
const assertBody = 'eða'
|
||||
let onFinish = function(body) {
|
||||
try {
|
||||
assert.strictEqual(ctx.status, assertStatus)
|
||||
assert.strictEqual(ctx.body, assertBody)
|
||||
assert.strictEqual(ctx.headers['Content-Type'], 'text/plain; charset=utf-8')
|
||||
assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength)
|
||||
assert.ok(body)
|
||||
assert.strictEqual(body, assertBody)
|
||||
assert.ok(ctx.res.writeHead.called)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
||||
cb()
|
||||
} catch (err) { cb(err) }
|
||||
}
|
||||
const ctx = createCtx({
|
||||
status: assertStatus,
|
||||
}, onFinish)
|
||||
ctx.body = assertBody
|
||||
testMethods.forEach(function(method) {
|
||||
t.describe(method, function() {
|
||||
t.test('call res and end correctly when dealing with objects', function(cb) {
|
||||
const assertStatus = 202
|
||||
// Calculated manually just in case
|
||||
const assertBodyLength = 7
|
||||
const assertBody = { a: 1 }
|
||||
let onFinish = function(body) {
|
||||
try {
|
||||
assert.strictEqual(ctx.status, assertStatus)
|
||||
assert.strictEqual(ctx.body, assertBody)
|
||||
assert.strictEqual(ctx.headers['Content-Type'], 'application/json; charset=utf-8')
|
||||
assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength)
|
||||
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
flaska.requestEnd(null, ctx)
|
||||
})
|
||||
|
||||
t.test('call res and end correctly when dealing with numbers', function(cb) {
|
||||
const assertStatus = 211
|
||||
// Calculated manually just in case
|
||||
const assertBodyLength = 7
|
||||
const assertBody = 4214124
|
||||
let onFinish = function(body) {
|
||||
try {
|
||||
assert.strictEqual(ctx.status, assertStatus)
|
||||
assert.strictEqual(ctx.body, assertBody)
|
||||
assert.strictEqual(ctx.headers['Content-Type'], 'text/plain; charset=utf-8')
|
||||
assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength)
|
||||
assert.ok(body)
|
||||
assert.strictEqual(body, assertBody.toString())
|
||||
assert.ok(ctx.res.writeHead.called)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
||||
cb()
|
||||
} catch (err) { cb(err) }
|
||||
}
|
||||
const ctx = createCtx({
|
||||
status: assertStatus,
|
||||
}, onFinish)
|
||||
ctx.body = assertBody
|
||||
if (method === 'GET') {
|
||||
assert.ok(body)
|
||||
assert.strictEqual(body, '{"a":1}')
|
||||
} else {
|
||||
assert.notOk(body)
|
||||
}
|
||||
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
flaska.requestEnd(null, ctx)
|
||||
})
|
||||
assert.ok(ctx.res.writeHead.called)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
||||
cb()
|
||||
} catch (err) { cb(err) }
|
||||
}
|
||||
const ctx = createCtx({
|
||||
method: method,
|
||||
status: assertStatus,
|
||||
}, onFinish)
|
||||
ctx.body = assertBody
|
||||
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
flaska.requestEnd(null, ctx)
|
||||
})
|
||||
|
||||
t.test('call handleRequest if body contains it', function() {
|
||||
let body = new FileResponse()
|
||||
let assertBody = { a : 1 }
|
||||
const ctx = createCtx({
|
||||
status: 204,
|
||||
t.test('call res and end correctly when dealing with null and no status override', function(cb) {
|
||||
// Calculated manually just in case
|
||||
const assertBodyLength = 0
|
||||
const assertBody = null
|
||||
let onFinish = function(body) {
|
||||
try {
|
||||
assert.strictEqual(ctx.status, 204)
|
||||
assert.strictEqual(ctx.body, assertBody)
|
||||
assert.notOk(ctx.headers['Content-Type'])
|
||||
assert.notOk(ctx.headers['Content-Length'])
|
||||
|
||||
assert.strictEqual(body, undefined)
|
||||
|
||||
assert.ok(ctx.res.writeHead.called)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
||||
cb()
|
||||
} catch (err) { cb(err) }
|
||||
}
|
||||
const ctx = createCtx({
|
||||
method: method,
|
||||
status: 200,
|
||||
}, onFinish)
|
||||
ctx.body = assertBody
|
||||
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
flaska.requestEnd(null, ctx)
|
||||
})
|
||||
|
||||
t.test('call res and end correctly when dealing with null and status override', function(cb) {
|
||||
const assertStatus = 202
|
||||
// Calculated manually just in case
|
||||
const assertBody = null
|
||||
let onFinish = function(body) {
|
||||
try {
|
||||
assert.strictEqual(ctx.status, assertStatus)
|
||||
assert.strictEqual(ctx.body, assertBody)
|
||||
assert.notOk(ctx.headers['Content-Type'])
|
||||
assert.strictEqual(ctx.headers['Content-Length'], 0)
|
||||
|
||||
assert.strictEqual(body, undefined)
|
||||
|
||||
assert.ok(ctx.res.writeHead.called)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
||||
cb()
|
||||
} catch (err) { cb(err) }
|
||||
}
|
||||
const ctx = createCtx({
|
||||
method: method,
|
||||
status: assertStatus,
|
||||
}, onFinish)
|
||||
ctx.body = assertBody
|
||||
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
flaska.requestEnd(null, ctx)
|
||||
})
|
||||
|
||||
t.test('call res and end correctly when dealing with strings', function(cb) {
|
||||
const assertStatus = 206
|
||||
// Calculated manually just in case
|
||||
const assertBodyLength = 4
|
||||
const assertBody = 'eða'
|
||||
let onFinish = function(body) {
|
||||
try {
|
||||
assert.strictEqual(ctx.status, assertStatus)
|
||||
assert.strictEqual(ctx.body, assertBody)
|
||||
assert.strictEqual(ctx.headers['Content-Type'], 'text/plain; charset=utf-8')
|
||||
assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength)
|
||||
|
||||
if (method === 'GET') {
|
||||
assert.ok(body)
|
||||
assert.strictEqual(body, assertBody)
|
||||
} else {
|
||||
assert.notOk(body)
|
||||
}
|
||||
|
||||
assert.ok(ctx.res.writeHead.called)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
||||
cb()
|
||||
} catch (err) { cb(err) }
|
||||
}
|
||||
const ctx = createCtx({
|
||||
method: method,
|
||||
status: assertStatus,
|
||||
}, onFinish)
|
||||
ctx.body = assertBody
|
||||
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
flaska.requestEnd(null, ctx)
|
||||
})
|
||||
|
||||
t.test('call res and end correctly when dealing with numbers', function(cb) {
|
||||
const assertStatus = 211
|
||||
// Calculated manually just in case
|
||||
const assertBodyLength = 7
|
||||
const assertBody = 4214124
|
||||
let onFinish = function(body) {
|
||||
try {
|
||||
assert.strictEqual(ctx.status, assertStatus)
|
||||
assert.strictEqual(ctx.body, assertBody)
|
||||
assert.strictEqual(ctx.headers['Content-Type'], 'text/plain; charset=utf-8')
|
||||
assert.strictEqual(ctx.headers['Content-Length'], assertBodyLength)
|
||||
|
||||
if (method === 'GET') {
|
||||
assert.ok(body)
|
||||
assert.strictEqual(body, assertBody.toString())
|
||||
} else {
|
||||
assert.notOk(body)
|
||||
}
|
||||
assert.ok(ctx.res.writeHead.called)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
||||
cb()
|
||||
} catch (err) { cb(err) }
|
||||
}
|
||||
const ctx = createCtx({
|
||||
method: method,
|
||||
status: assertStatus,
|
||||
}, onFinish)
|
||||
ctx.body = assertBody
|
||||
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
flaska.requestEnd(null, ctx)
|
||||
})
|
||||
|
||||
if (method === 'GET') {
|
||||
t.test('call handleRequest if body contains it', function(cb) {
|
||||
const assertContentType = 'test/test'
|
||||
const assertContentLength = 1241241
|
||||
const assertHandle = stub().returnWith(function(checkCtx, secondary) {
|
||||
assert.notOk(secondary)
|
||||
assert.strictEqual(checkCtx, ctx)
|
||||
assert.notOk(ctx.res.writeHead.called)
|
||||
ctx.type = assertContentType
|
||||
ctx.headers['Content-Length'] = assertContentLength
|
||||
return assertBody
|
||||
})
|
||||
|
||||
let body = new FileResponse()
|
||||
let assertBody = { pipe: function() {} }
|
||||
let onFinish = function(source, target, callback) {
|
||||
try {
|
||||
assert.ok(assertHandle.called)
|
||||
assert.ok(ctx.res.writeHead)
|
||||
assert.strictEqual(ctx.body, assertBody)
|
||||
assert.strictEqual(source, assertBody)
|
||||
assert.strictEqual(target, ctx.res)
|
||||
assert.strictEqual(typeof(callback), 'function')
|
||||
assert.ok(ctx.res.writeHead.called)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
||||
assert.strictEqual(ctx.headers['Content-Type'], assertContentType)
|
||||
assert.strictEqual(ctx.headers['Content-Length'], assertContentLength)
|
||||
cb()
|
||||
} catch (err) { cb(err) }
|
||||
}
|
||||
const ctx = createCtx({
|
||||
})
|
||||
ctx.body = body
|
||||
fakeStream.pipeline = onFinish
|
||||
body.handleRequest = assertHandle
|
||||
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
|
||||
try {
|
||||
flaska.requestEnd(null, ctx)
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.test('call pipeline correctly when dealing with pipe', function(cb) {
|
||||
const assertStatus = 211
|
||||
const assertType = 'herp/derp'
|
||||
const assertBody = { pipe: function() {} }
|
||||
|
||||
let onFinish = function(source, target, callback) {
|
||||
try {
|
||||
assert.strictEqual(ctx.status, assertStatus)
|
||||
assert.strictEqual(ctx.headers['Content-Type'], assertType)
|
||||
assert.strictEqual(source, assertBody)
|
||||
assert.strictEqual(target, ctx.res)
|
||||
assert.strictEqual(typeof(callback), 'function')
|
||||
assert.ok(ctx.res.writeHead.called)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
||||
cb()
|
||||
} catch (err) { cb(err) }
|
||||
}
|
||||
const ctx = createCtx({
|
||||
status: assertStatus,
|
||||
})
|
||||
fakeStream.pipeline = onFinish
|
||||
|
||||
ctx.body = assertBody
|
||||
ctx.type = assertType
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
flaska.requestEnd(null, ctx)
|
||||
})
|
||||
} else {
|
||||
t.test('call handleRequest if body contains it', function(cb) {
|
||||
const assertContentType = 'test/test'
|
||||
const assertContentLength = 1241241
|
||||
const assertHandle = stub().returnWith(function(checkCtx, secondary) {
|
||||
assert.notOk(secondary)
|
||||
assert.strictEqual(checkCtx, ctx)
|
||||
assert.notOk(ctx.res.writeHead.called)
|
||||
ctx.type = assertContentType
|
||||
ctx.headers['Content-Length'] = assertContentLength
|
||||
return null
|
||||
})
|
||||
|
||||
let body = new FileResponse()
|
||||
let onFinish = function(body) {
|
||||
try {
|
||||
assert.ok(assertHandle.called)
|
||||
assert.ok(ctx.res.writeHead)
|
||||
assert.strictEqual(ctx.body, null)
|
||||
assert.notOk(body)
|
||||
assert.ok(ctx.res.writeHead.called)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
||||
assert.strictEqual(ctx.headers['Content-Type'], assertContentType)
|
||||
assert.strictEqual(ctx.headers['Content-Length'], assertContentLength)
|
||||
cb()
|
||||
} catch (err) { cb(err) }
|
||||
}
|
||||
const ctx = createCtx({
|
||||
method: method,
|
||||
}, onFinish)
|
||||
ctx.body = body
|
||||
body.handleRequest = assertHandle
|
||||
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
|
||||
try {
|
||||
flaska.requestEnd(null, ctx)
|
||||
} catch (err) {
|
||||
cb(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.test('call pipeline correctly when dealing with pipe', function(cb) {
|
||||
const assertStatus = 211
|
||||
const assertType = 'herp/derp'
|
||||
const assertBody = { pipe: function() {}, destroy: stub() }
|
||||
|
||||
let onFinish = function(body) {
|
||||
try {
|
||||
assert.strictEqual(ctx.status, assertStatus)
|
||||
assert.strictEqual(ctx.headers['Content-Type'], assertType)
|
||||
assert.notOk(ctx.headers['Content-Length'])
|
||||
assert.notOk(body)
|
||||
|
||||
assert.ok(assertBody.destroy.called)
|
||||
assert.ok(ctx.res.writeHead.called)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
||||
cb()
|
||||
} catch (err) { cb(err) }
|
||||
}
|
||||
const ctx = createCtx({
|
||||
method: method,
|
||||
status: assertStatus,
|
||||
}, onFinish)
|
||||
|
||||
ctx.body = assertBody
|
||||
ctx.type = assertType
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
flaska.requestEnd(null, ctx)
|
||||
})
|
||||
}
|
||||
})
|
||||
ctx.body = body
|
||||
const assertHandle = stub().returnWith(function(checkCtx, secondary) {
|
||||
assert.notOk(secondary)
|
||||
assert.strictEqual(checkCtx, ctx)
|
||||
assert.notOk(ctx.res.writeHead.called)
|
||||
return assertBody
|
||||
})
|
||||
body.handleRequest = assertHandle
|
||||
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
flaska.requestEnd(null, ctx)
|
||||
assert.ok(assertHandle.called)
|
||||
assert.ok(ctx.res.writeHead)
|
||||
assert.strictEqual(ctx.body, assertBody)
|
||||
})
|
||||
|
||||
t.test('call _onerror with error if handleRequest throws error', function() {
|
||||
|
@ -216,35 +426,6 @@ t.describe('#requestEnd()', function() {
|
|||
flaska.requestEnd(null, ctx)
|
||||
})
|
||||
|
||||
t.test('call pipeline correctly when dealing with pipe', function(cb) {
|
||||
const assertStatus = 211
|
||||
const assertType = 'herp/derp'
|
||||
const assertBody = { pipe: function() {} }
|
||||
|
||||
let onFinish = function(source, target, callback) {
|
||||
try {
|
||||
assert.strictEqual(ctx.status, assertStatus)
|
||||
assert.strictEqual(ctx.headers['Content-Type'], assertType)
|
||||
assert.strictEqual(source, assertBody)
|
||||
assert.strictEqual(target, ctx.res)
|
||||
assert.strictEqual(typeof(callback), 'function')
|
||||
assert.ok(ctx.res.writeHead.called)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[0], ctx.status)
|
||||
assert.strictEqual(ctx.res.writeHead.firstCall[1], ctx.headers)
|
||||
cb()
|
||||
} catch (err) { cb(err) }
|
||||
}
|
||||
const ctx = createCtx({
|
||||
status: assertStatus,
|
||||
})
|
||||
fakeStream.pipeline = onFinish
|
||||
|
||||
ctx.body = assertBody
|
||||
ctx.type = assertType
|
||||
let flaska = new Flaska({}, fakerHttp, fakeStream)
|
||||
flaska.requestEnd(null, ctx)
|
||||
})
|
||||
|
||||
t.test('call pipe should have default type', function(cb) {
|
||||
let onFinish = function(source, target) {
|
||||
try {
|
||||
|
|
|
@ -285,6 +285,120 @@ t.describe('/file/upload', function() {
|
|||
})
|
||||
})
|
||||
|
||||
t.describe('HEAD', function() {
|
||||
const agent = new http.Agent({
|
||||
keepAlive: true,
|
||||
maxSockets: 1,
|
||||
keepAliveMsecs: 3000,
|
||||
})
|
||||
|
||||
t.describe('/file', function() {
|
||||
t.test('server return HEAD for pipes', async function() {
|
||||
log.error.reset()
|
||||
|
||||
let res = await client.customRequest('HEAD', '/file', null, { getRaw: true, agent: agent })
|
||||
|
||||
while (!file.closed) {
|
||||
await setTimeout(10)
|
||||
}
|
||||
|
||||
assert.ok(file.closed)
|
||||
|
||||
let statSource = await fs.stat('./test/test.png')
|
||||
|
||||
assert.strictEqual(res.data, '')
|
||||
assert.strictEqual(res.headers['content-type'], 'image/png')
|
||||
assert.notOk(res.headers['content-length'])
|
||||
})
|
||||
t.test('server should autoclose body file handles on errors', async function() {
|
||||
reset()
|
||||
|
||||
file = null
|
||||
|
||||
let req = await client.customRequest('HEAD', '/file/leak', null, { returnRequest: true })
|
||||
|
||||
req.end()
|
||||
|
||||
while (!file) {
|
||||
await setTimeout(10)
|
||||
}
|
||||
|
||||
assert.ok(file)
|
||||
assert.notOk(file.closed)
|
||||
|
||||
req.destroy()
|
||||
|
||||
while (!file.closed) {
|
||||
await setTimeout(10)
|
||||
}
|
||||
|
||||
assert.strictEqual(log.error.callCount, 0)
|
||||
assert.strictEqual(log.info.callCount, 1)
|
||||
assert.strictEqual(log.info.firstCall[0].message, 'aborted')
|
||||
|
||||
assert.ok(file.closed)
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('/filehandle', function() {
|
||||
t.test('server should send correctly', async function() {
|
||||
log.error.reset()
|
||||
|
||||
let res = await client.customRequest('HEAD', '/filehandle', null, { getRaw: true, agent: agent })
|
||||
assert.strictEqual(res.status, 200)
|
||||
assert.strictEqual(res.headers['content-length'], '11')
|
||||
assert.strictEqual(res.headers['content-type'], 'text/plain')
|
||||
assert.match(res.headers['etag'], /\"\d+-11-\d+"/)
|
||||
assert.ok(res.headers['last-modified'])
|
||||
let d = new Date(Date.parse(res.headers['last-modified']))
|
||||
let etag = res.headers['etag']
|
||||
assert.ok(d.getTime())
|
||||
assert.strictEqual(res.data, '')
|
||||
|
||||
res = await client.customRequest('HEAD', '/filehandle', null, { getRaw: true, agent: agent,
|
||||
headers: {
|
||||
'If-Modified-Since': d.toUTCString()
|
||||
},
|
||||
})
|
||||
assert.strictEqual(res.status, 304)
|
||||
assert.strictEqual(res.data, '')
|
||||
assert.strictEqual(res.headers['etag'], etag)
|
||||
|
||||
res = await client.customRequest('HEAD', '/filehandle', null, { getRaw: true, agent: agent,
|
||||
headers: {
|
||||
'If-None-Match': etag
|
||||
},
|
||||
})
|
||||
assert.strictEqual(res.status, 304)
|
||||
assert.strictEqual(res.data, '')
|
||||
assert.strictEqual(res.headers['etag'], etag)
|
||||
|
||||
res = await client.customRequest('HEAD', '/filehandle', null, { getRaw: true, agent: agent,
|
||||
headers: {
|
||||
'Range': 'bytes=2-5'
|
||||
},
|
||||
})
|
||||
assert.strictEqual(res.status, 206)
|
||||
assert.strictEqual(res.data, '')
|
||||
assert.strictEqual(res.headers['content-length'], '4')
|
||||
|
||||
|
||||
res = await client.customRequest('HEAD', '/filehandle', null, { getRaw: true, agent: agent,
|
||||
headers: {
|
||||
'Range': 'bytes=0-0'
|
||||
},
|
||||
})
|
||||
assert.strictEqual(res.status, 206)
|
||||
assert.strictEqual(res.data, '')
|
||||
assert.strictEqual(res.headers['content-length'], '1')
|
||||
})
|
||||
|
||||
t.after(function() {
|
||||
agent.destroy()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.after(function() {
|
||||
return Promise.all([
|
||||
fs.rm('./test_tmp.png', { force: true }),
|
||||
|
|
Loading…
Reference in a new issue