Add support for redirecting to signed URLs

This commit is contained in:
Simon Detheridge
2020-04-15 12:22:54 +01:00
parent 2b9b165d72
commit 3637cd70ae
10 changed files with 217 additions and 21 deletions
+7
View File
@@ -74,6 +74,11 @@ async function getFileStream(location, name, opts) {
return fs.createReadStream(null, opts)
}
async function getRedirectUrl() {
// not implemented
return null
}
async function getFileSize(location, filename) {
const fullPath = path.join(location, filterName(filename))
@@ -211,6 +216,7 @@ module.exports = {
sendFile: callbackify(sendFile),
sendStream: callbackify(sendStream),
getFileStream: callbackify(getFileStream),
getRedirectUrl: callbackify(getRedirectUrl),
getFileSize: callbackify(getFileSize),
getFileMd5Hash: callbackify(getFileMd5Hash),
copyFile: callbackify(copyFile),
@@ -222,6 +228,7 @@ module.exports = {
sendFile,
sendStream,
getFileStream,
getRedirectUrl,
getFileSize,
getFileMd5Hash,
copyFile,
+30 -19
View File
@@ -46,31 +46,42 @@ function getFile(req, res, next) {
}
}
FileHandler.getFile(bucket, key, options, function(err, fileStream) {
FileHandler.getRedirectUrl(bucket, key, options, function(err, redirectUrl) {
if (err) {
if (err instanceof Errors.NotFoundError) {
res.sendStatus(404)
} else {
next(err)
}
return
metrics.inc('file_redirect_error')
}
if (req.query.cacheWarm) {
return res.sendStatus(200).end()
if (redirectUrl) {
metrics.inc('file_redirect')
return res.redirect(redirectUrl)
}
pipeline(fileStream, res, err => {
if (err && err.code === 'ERR_STREAM_PREMATURE_CLOSE') {
res.end()
} else if (err) {
next(
new Errors.ReadError({
message: 'error transferring stream',
info: { bucket, key, format, style }
}).withCause(err)
)
FileHandler.getFile(bucket, key, options, function(err, fileStream) {
if (err) {
if (err instanceof Errors.NotFoundError) {
res.sendStatus(404)
} else {
next(err)
}
return
}
if (req.query.cacheWarm) {
return res.sendStatus(200).end()
}
pipeline(fileStream, res, err => {
if (err && err.code === 'ERR_STREAM_PREMATURE_CLOSE') {
res.end()
} else if (err) {
next(
new Errors.ReadError({
message: 'error transferring stream',
info: { bucket, key, format, style }
}).withCause(err)
)
}
})
})
})
}
+20
View File
@@ -13,10 +13,12 @@ module.exports = {
deleteFile: callbackify(deleteFile),
deleteProject: callbackify(deleteProject),
getFile: callbackify(getFile),
getRedirectUrl: callbackify(getRedirectUrl),
getFileSize: callbackify(getFileSize),
getDirectorySize: callbackify(getDirectorySize),
promises: {
getFile,
getRedirectUrl,
insertFile,
deleteFile,
deleteProject,
@@ -73,6 +75,24 @@ async function getFile(bucket, key, opts) {
}
}
async function getRedirectUrl(bucket, key, opts) {
// if we're doing anything unusual with options, or the request isn't for
// one of the default buckets, return null so that we proxy the file
opts = opts || {}
if (
!opts.start &&
!opts.end &&
!opts.format &&
!opts.style &&
Object.values(Settings.filestore.stores).includes(bucket) &&
Settings.filestore.allowRedirects
) {
return PersistorManager.promises.getRedirectUrl(bucket, key)
}
return null
}
async function getFileSize(bucket, key) {
return PersistorManager.promises.getFileSize(bucket, key)
}
+22
View File
@@ -36,6 +36,7 @@ const GcsPersistor = {
sendFile: callbackify(sendFile),
sendStream: callbackify(sendStream),
getFileStream: callbackify(getFileStream),
getRedirectUrl: callbackify(getRedirectUrl),
getFileMd5Hash: callbackify(getFileMd5Hash),
deleteDirectory: callbackify(deleteDirectory),
getFileSize: callbackify(getFileSize),
@@ -47,6 +48,7 @@ const GcsPersistor = {
sendFile,
sendStream,
getFileStream,
getRedirectUrl,
getFileMd5Hash,
deleteDirectory,
getFileSize,
@@ -141,6 +143,26 @@ async function getFileStream(bucketName, key, _opts = {}) {
}
}
async function getRedirectUrl(bucketName, key) {
try {
const [url] = await storage
.bucket(bucketName)
.file(key)
.getSignedUrl({
action: 'read',
expires: new Date().getTime() + settings.filestore.signedUrlExpiryInMs
})
return url
} catch (err) {
throw PersistorHelper.wrapError(
err,
'error generating signed url for GCS file',
{ bucketName, key },
ReadError
)
}
}
async function getFileSize(bucketName, key) {
try {
const [metadata] = await storage
@@ -203,6 +203,7 @@ module.exports = function(primary, fallback) {
sendFile: primary.sendFile,
sendStream: primary.sendStream,
getFileStream: callbackify(getFileStreamWithFallback),
getRedirectUrl: primary.getRedirectUrl,
getFileMd5Hash: callbackify(_wrapFallbackMethod('getFileMd5Hash')),
deleteDirectory: callbackify(
_wrapMethodOnBothPersistors('deleteDirectory')
@@ -216,6 +217,7 @@ module.exports = function(primary, fallback) {
sendFile: primary.promises.sendFile,
sendStream: primary.promises.sendStream,
getFileStream: getFileStreamWithFallback,
getRedirectUrl: primary.promises.getRedirectUrl,
getFileMd5Hash: _wrapFallbackMethod('getFileMd5Hash'),
deleteDirectory: _wrapMethodOnBothPersistors('deleteDirectory'),
getFileSize: _wrapFallbackMethod('getFileSize'),
+7
View File
@@ -24,6 +24,7 @@ const S3Persistor = {
sendFile: callbackify(sendFile),
sendStream: callbackify(sendStream),
getFileStream: callbackify(getFileStream),
getRedirectUrl: callbackify(getRedirectUrl),
getFileMd5Hash: callbackify(getFileMd5Hash),
deleteDirectory: callbackify(deleteDirectory),
getFileSize: callbackify(getFileSize),
@@ -35,6 +36,7 @@ const S3Persistor = {
sendFile,
sendStream,
getFileStream,
getRedirectUrl,
getFileMd5Hash,
deleteDirectory,
getFileSize,
@@ -146,6 +148,11 @@ async function getFileStream(bucketName, key, opts) {
}
}
async function getRedirectUrl() {
// not implemented
return null
}
async function deleteDirectory(bucketName, key) {
let response