initial commit
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
const toObject = require('to-object-reducer');
|
||||
const decodeMetadata = require('./tus-metadata').decode;
|
||||
|
||||
// TODO: Buffer.from silently ignores invalid bytes :(
|
||||
// https://github.com/nodejs/node/issues/8569
|
||||
const base64decode = str => Buffer.from(str, 'base64')
|
||||
|
||||
const nonNegativeInteger = (v) => {
|
||||
// need this because parseInt parses floats too
|
||||
if (v !== '0' && !v.match(/^[1-9][0-9]*$/)) return
|
||||
const num = parseInt(v, 10)
|
||||
if (isNaN(num)) return
|
||||
if (num >= 0) return num
|
||||
}
|
||||
|
||||
/*
|
||||
const enum = (possibleVals = []) => (v) => {
|
||||
if (!possibleVals.includes(v)) return
|
||||
return v
|
||||
}
|
||||
*/
|
||||
|
||||
const id = v => v
|
||||
const parsers = {
|
||||
'tus-resumable': id,
|
||||
// The Upload-Offset request and response header indicates a byte offset
|
||||
// within a resource. The value MUST be a non-negative integer.
|
||||
'upload-offset': nonNegativeInteger,
|
||||
// The Upload-Defer-Length request and response header indicates that the
|
||||
// size of the upload is not known currently and will be transferred later.
|
||||
// Its value MUST be 1. If the length of an upload is not deferred, this
|
||||
// header MUST be omitted.
|
||||
'upload-defer-length': v => {
|
||||
const num = nonNegativeInteger(v)
|
||||
if (num === 1) return 1
|
||||
},
|
||||
// The Upload-Length request and response header indicates the size of the
|
||||
// entire upload in bytes. The value MUST be a non-negative integer.
|
||||
'upload-length': nonNegativeInteger,
|
||||
'upload-metadata': decodeMetadata,
|
||||
// The Upload-Checksum request header contains information about the checksum
|
||||
// of the current body payload. The header MUST consist of the name of the
|
||||
// used checksum algorithm and the Base64 encoded checksum separated by a
|
||||
// space.
|
||||
'upload-checksum': v => {
|
||||
const parts = v.split(' ')
|
||||
if (parts.length !== 2) return
|
||||
const algorithm = parts[0]
|
||||
const checksum = base64decode(parts[1])
|
||||
if (!checksum.length) return
|
||||
return { algorithm, checksum }
|
||||
},
|
||||
// The Upload-Concat request and response header MUST be set in both partial
|
||||
// and final upload creation requests. It indicates whether the upload is
|
||||
// either a partial or final upload. If the upload is a partial one, the
|
||||
// header value MUST be partial. In the case of a final upload, its value
|
||||
// MUST be final followed by a semicolon and a space-separated list of
|
||||
// partial upload URLs that will be concatenated. The partial uploads URLs
|
||||
// MAY be absolute or relative and MUST NOT contain spaces as defined in RFC
|
||||
// 3986.
|
||||
'upload-concat': v => {
|
||||
if (v === 'partial') return { partial: true }
|
||||
const parts = v.split(';')
|
||||
if (!parts.length) return
|
||||
const firstPart = parts.shift()
|
||||
if (firstPart !== 'final') return
|
||||
const urls = parts.join(';').split(' ')
|
||||
if (urls.some(url => url === '')) return
|
||||
// validate urls? nah...
|
||||
return { final: true, urls }
|
||||
},
|
||||
}
|
||||
|
||||
const knownHeaders = Object.keys(parsers)
|
||||
|
||||
const parseHeader = (tmpHeaderName, str) => {
|
||||
const headerName = tmpHeaderName.toLowerCase()
|
||||
if (!knownHeaders.includes(headerName)) return
|
||||
return parsers[headerName](str)
|
||||
}
|
||||
|
||||
// Pre-computed for speed
|
||||
const camelCase = {
|
||||
'tus-resumable': 'tusResumable',
|
||||
'upload-offset': 'uploadOffset',
|
||||
'upload-defer-length': 'uploadDeferLength',
|
||||
'upload-length': 'uploadLength',
|
||||
'upload-metadata': 'uploadMetadata',
|
||||
'upload-checksum': 'uploadChecksum',
|
||||
'upload-concat': 'uploadConcat',
|
||||
}
|
||||
|
||||
const parseHeaders = (headers) => (
|
||||
Object
|
||||
.keys(headers)
|
||||
.filter(headerName => knownHeaders.includes(headerName))
|
||||
.map(headerName => ([
|
||||
camelCase[headerName],
|
||||
parsers[headerName](headers[headerName]),
|
||||
]))
|
||||
.reduce(toObject, {})
|
||||
)
|
||||
|
||||
module.exports = () => (req, res, next) => {
|
||||
req.tus = parseHeaders(req.headers)
|
||||
next()
|
||||
};
|
||||
|
||||
module.exports.knownHeaders = knownHeaders;
|
||||
module.exports.parseHeader = parseHeader;
|
||||
module.exports.parseHeaders = parseHeaders;
|
||||
Reference in New Issue
Block a user