Просмотр исходного кода

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

use async project history zip download api

GitOrigin-RevId: 847e22eb3bf89e2e9e3c86bcc9ae92f8c90b25b1
Ersun Warncke 7 лет назад
Родитель
Сommit
c303af5d4d

+ 47 - 19
services/web/app/coffee/Features/History/HistoryController.coffee

@@ -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"
-		getReq = request(
-			url: url
+		logger.log {v1_project_id, version, url}, "getting s3 url from history api"
+		options =
 			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
-		)
-		getReq.on 'response', (response) ->
-			# 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)
-		getReq.on "error", (err) ->
-			logger.error {err, v1_project_id, version}, "history API error"
-			next(error)
+			json: true
+			method: 'post'
+			url: url
+		request options, (err, response, body) ->
+			if err
+				logger.error {err, v1_project_id, version}, "history API error"
+				return next(err)
+			retryAttempt = 0
+			retryDelay = 2000
+			# retry for about 6 minutes starting with short delay
+			async.retry 40,
+				(callback) ->
+					setTimeout(() ->
+						# increase delay by 1 second up to 10
+						retryDelay += 1000 if retryDelay < 10000
+						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)

+ 10 - 0
services/web/test/acceptance/coffee/helpers/MockV1HistoryApi.coffee

@@ -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) ->