Merge pull request #1354 from sharelatex/ew-async-zip-download

use async project history zip download api

GitOrigin-RevId: 847e22eb3bf89e2e9e3c86bcc9ae92f8c90b25b1
This commit is contained in:
Ersun Warncke 2019-01-07 13:19:50 -04:00 committed by sharelatex
parent a521a86fb7
commit c303af5d4d
2 changed files with 57 additions and 19 deletions

View file

@ -1,3 +1,5 @@
_ = require "lodash"
async = require "async"
logger = require "logger-sharelatex" logger = require "logger-sharelatex"
request = require "request" request = require "request"
settings = require "settings-sharelatex" settings = require "settings-sharelatex"
@ -156,26 +158,52 @@ module.exports = HistoryController =
HistoryController._pipeHistoryZipToResponse v1_id, version, "#{project.name} (Version #{version})", res, next HistoryController._pipeHistoryZipToResponse v1_id, version, "#{project.name} (Version #{version})", res, next
_pipeHistoryZipToResponse: (v1_project_id, version, name, res, next) -> _pipeHistoryZipToResponse: (v1_project_id, version, name, res, next) ->
# increase timeout to 6 minutes
res.setTimeout(6 * 60 * 1000)
url = "#{settings.apis.v1_history.url}/projects/#{v1_project_id}/version/#{version}/zip" url = "#{settings.apis.v1_history.url}/projects/#{v1_project_id}/version/#{version}/zip"
logger.log {v1_project_id, version, url}, "proxying to history api" logger.log {v1_project_id, version, url}, "getting s3 url from history api"
getReq = request( options =
url: url
auth: auth:
user: settings.apis.v1_history.user user: settings.apis.v1_history.user
pass: settings.apis.v1_history.pass pass: settings.apis.v1_history.pass
sendImmediately: true json: true
) method: 'post'
getReq.on 'response', (response) -> url: url
# pipe also proxies the headers, but we want to customize these ones request options, (err, response, body) ->
delete response.headers['content-disposition'] if err
delete response.headers['content-type'] logger.error {err, v1_project_id, version}, "history API error"
res.status response.statusCode return next(err)
res.setContentDisposition( retryAttempt = 0
'attachment', retryDelay = 2000
{filename: "#{name}.zip"} # retry for about 6 minutes starting with short delay
) async.retry 40,
res.contentType('application/zip') (callback) ->
getReq.pipe(res) setTimeout(() ->
getReq.on "error", (err) -> # increase delay by 1 second up to 10
logger.error {err, v1_project_id, version}, "history API error" retryDelay += 1000 if retryDelay < 10000
next(error) retryAttempt++
getReq = request(
url: body.zipUrl
sendImmediately: true
)
getReq.on 'response', (response) ->
return callback new Error "invalid response" unless response.statusCode == 200
# pipe also proxies the headers, but we want to customize these ones
delete response.headers['content-disposition']
delete response.headers['content-type']
res.status response.statusCode
res.setContentDisposition(
'attachment',
{filename: "#{name}.zip"}
)
res.contentType('application/zip')
getReq.pipe(res)
callback()
getReq.on "error", (err) ->
logger.error {err, v1_project_id, version, retryAttempt}, "history s3 download error"
callback(err)
, retryDelay)
(err) ->
if err
logger.error {err, v1_project_id, version, retryAttempt}, "history s3 download failed"
next(err)

View file

@ -5,12 +5,22 @@ app = express()
{ObjectId} = require 'mongojs' {ObjectId} = require 'mongojs'
module.exports = MockV1HistoryApi = module.exports = MockV1HistoryApi =
fakeZipCall: 0
run: () -> run: () ->
app.get "/api/projects/:project_id/version/:version/zip", (req, res, next) => app.get "/api/projects/:project_id/version/:version/zip", (req, res, next) =>
res.header('content-disposition', 'attachment; name=project.zip') res.header('content-disposition', 'attachment; name=project.zip')
res.header('content-type', 'application/octet-stream') res.header('content-type', 'application/octet-stream')
res.send "Mock zip for #{req.params.project_id} at version #{req.params.version}" res.send "Mock zip for #{req.params.project_id} at version #{req.params.version}"
app.get "/fake-zip-download/:project_id/version/:version", (req, res, next) =>
return res.sendStatus 404 unless @fakeZipCall++ > 0
res.header('content-disposition', 'attachment; name=project.zip')
res.header('content-type', 'application/octet-stream')
res.send "Mock zip for #{req.params.project_id} at version #{req.params.version}"
app.post "/api/projects/:project_id/version/:version/zip", (req, res, next) =>
res.json zipUrl: "http://localhost:3100/fake-zip-download/#{req.params.project_id}/version/#{req.params.version}"
app.listen 3100, (error) -> app.listen 3100, (error) ->
throw error if error? throw error if error?
.on "error", (error) -> .on "error", (error) ->