mirror of
				https://github.com/actions/cache.git
				synced 2025-10-31 18:34:19 +08:00 
			
		
		
		
	Merge pull request #269 from actions/socket-timeout
Adds socket timeout and validate file size
This commit is contained in:
		
						commit
						54626c4a4f
					
				
							
								
								
									
										28
									
								
								dist/restore/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								dist/restore/index.js
									
									
									
									
										vendored
									
									
								
							| @ -2285,7 +2285,24 @@ function downloadCache(archiveLocation, archivePath) { | |||||||
|         const stream = fs.createWriteStream(archivePath); |         const stream = fs.createWriteStream(archivePath); | ||||||
|         const httpClient = new http_client_1.HttpClient("actions/cache"); |         const httpClient = new http_client_1.HttpClient("actions/cache"); | ||||||
|         const downloadResponse = yield httpClient.get(archiveLocation); |         const downloadResponse = yield httpClient.get(archiveLocation); | ||||||
|  |         // Abort download if no traffic received over the socket.
 | ||||||
|  |         downloadResponse.message.socket.setTimeout(constants_1.SocketTimeout, () => { | ||||||
|  |             downloadResponse.message.destroy(); | ||||||
|  |             core.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); | ||||||
|  |         }); | ||||||
|         yield pipeResponseToStream(downloadResponse, stream); |         yield pipeResponseToStream(downloadResponse, stream); | ||||||
|  |         // Validate download size.
 | ||||||
|  |         const contentLengthHeader = downloadResponse.message.headers["content-length"]; | ||||||
|  |         if (contentLengthHeader) { | ||||||
|  |             const expectedLength = parseInt(contentLengthHeader); | ||||||
|  |             const actualLength = utils.getArchiveFileSize(archivePath); | ||||||
|  |             if (actualLength != expectedLength) { | ||||||
|  |                 throw new Error(`Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}`); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             core.debug("Unable to validate download, no Content-Length header"); | ||||||
|  |         } | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| exports.downloadCache = downloadCache; | exports.downloadCache = downloadCache; | ||||||
| @ -3583,6 +3600,12 @@ class HttpClientResponse { | |||||||
|             this.message.on('data', (chunk) => { |             this.message.on('data', (chunk) => { | ||||||
|                 output = Buffer.concat([output, chunk]); |                 output = Buffer.concat([output, chunk]); | ||||||
|             }); |             }); | ||||||
|  |             this.message.on('aborted', () => { | ||||||
|  |                 reject("Request was aborted or closed prematurely"); | ||||||
|  |             }); | ||||||
|  |             this.message.on('timeout', (socket) => { | ||||||
|  |                 reject("Request timed out"); | ||||||
|  |             }); | ||||||
|             this.message.on('end', () => { |             this.message.on('end', () => { | ||||||
|                 resolve(output.toString()); |                 resolve(output.toString()); | ||||||
|             }); |             }); | ||||||
| @ -3704,6 +3727,7 @@ class HttpClient { | |||||||
|         let response; |         let response; | ||||||
|         while (numTries < maxTries) { |         while (numTries < maxTries) { | ||||||
|             response = await this.requestRaw(info, data); |             response = await this.requestRaw(info, data); | ||||||
|  | 
 | ||||||
|             // Check if it's an authentication challenge
 |             // Check if it's an authentication challenge
 | ||||||
|             if (response && response.message && response.message.statusCode === HttpCodes.Unauthorized) { |             if (response && response.message && response.message.statusCode === HttpCodes.Unauthorized) { | ||||||
|                 let authenticationHandler; |                 let authenticationHandler; | ||||||
| @ -4468,6 +4492,10 @@ var Events; | |||||||
|     Events["PullRequest"] = "pull_request"; |     Events["PullRequest"] = "pull_request"; | ||||||
| })(Events = exports.Events || (exports.Events = {})); | })(Events = exports.Events || (exports.Events = {})); | ||||||
| exports.CacheFilename = "cache.tgz"; | exports.CacheFilename = "cache.tgz"; | ||||||
|  | // Socket timeout in milliseconds during download.  If no traffic is received
 | ||||||
|  | // over the socket during this period, the socket is destroyed and the download
 | ||||||
|  | // is aborted.
 | ||||||
|  | exports.SocketTimeout = 5000; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /***/ }), | /***/ }), | ||||||
|  | |||||||
							
								
								
									
										28
									
								
								dist/save/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								dist/save/index.js
									
									
									
									
										vendored
									
									
								
							| @ -2285,7 +2285,24 @@ function downloadCache(archiveLocation, archivePath) { | |||||||
|         const stream = fs.createWriteStream(archivePath); |         const stream = fs.createWriteStream(archivePath); | ||||||
|         const httpClient = new http_client_1.HttpClient("actions/cache"); |         const httpClient = new http_client_1.HttpClient("actions/cache"); | ||||||
|         const downloadResponse = yield httpClient.get(archiveLocation); |         const downloadResponse = yield httpClient.get(archiveLocation); | ||||||
|  |         // Abort download if no traffic received over the socket.
 | ||||||
|  |         downloadResponse.message.socket.setTimeout(constants_1.SocketTimeout, () => { | ||||||
|  |             downloadResponse.message.destroy(); | ||||||
|  |             core.debug(`Aborting download, socket timed out after ${constants_1.SocketTimeout} ms`); | ||||||
|  |         }); | ||||||
|         yield pipeResponseToStream(downloadResponse, stream); |         yield pipeResponseToStream(downloadResponse, stream); | ||||||
|  |         // Validate download size.
 | ||||||
|  |         const contentLengthHeader = downloadResponse.message.headers["content-length"]; | ||||||
|  |         if (contentLengthHeader) { | ||||||
|  |             const expectedLength = parseInt(contentLengthHeader); | ||||||
|  |             const actualLength = utils.getArchiveFileSize(archivePath); | ||||||
|  |             if (actualLength != expectedLength) { | ||||||
|  |                 throw new Error(`Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}`); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |             core.debug("Unable to validate download, no Content-Length header"); | ||||||
|  |         } | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| exports.downloadCache = downloadCache; | exports.downloadCache = downloadCache; | ||||||
| @ -3583,6 +3600,12 @@ class HttpClientResponse { | |||||||
|             this.message.on('data', (chunk) => { |             this.message.on('data', (chunk) => { | ||||||
|                 output = Buffer.concat([output, chunk]); |                 output = Buffer.concat([output, chunk]); | ||||||
|             }); |             }); | ||||||
|  |             this.message.on('aborted', () => { | ||||||
|  |                 reject("Request was aborted or closed prematurely"); | ||||||
|  |             }); | ||||||
|  |             this.message.on('timeout', (socket) => { | ||||||
|  |                 reject("Request timed out"); | ||||||
|  |             }); | ||||||
|             this.message.on('end', () => { |             this.message.on('end', () => { | ||||||
|                 resolve(output.toString()); |                 resolve(output.toString()); | ||||||
|             }); |             }); | ||||||
| @ -3704,6 +3727,7 @@ class HttpClient { | |||||||
|         let response; |         let response; | ||||||
|         while (numTries < maxTries) { |         while (numTries < maxTries) { | ||||||
|             response = await this.requestRaw(info, data); |             response = await this.requestRaw(info, data); | ||||||
|  | 
 | ||||||
|             // Check if it's an authentication challenge
 |             // Check if it's an authentication challenge
 | ||||||
|             if (response && response.message && response.message.statusCode === HttpCodes.Unauthorized) { |             if (response && response.message && response.message.statusCode === HttpCodes.Unauthorized) { | ||||||
|                 let authenticationHandler; |                 let authenticationHandler; | ||||||
| @ -4554,6 +4578,10 @@ var Events; | |||||||
|     Events["PullRequest"] = "pull_request"; |     Events["PullRequest"] = "pull_request"; | ||||||
| })(Events = exports.Events || (exports.Events = {})); | })(Events = exports.Events || (exports.Events = {})); | ||||||
| exports.CacheFilename = "cache.tgz"; | exports.CacheFilename = "cache.tgz"; | ||||||
|  | // Socket timeout in milliseconds during download.  If no traffic is received
 | ||||||
|  | // over the socket during this period, the socket is destroyed and the download
 | ||||||
|  | // is aborted.
 | ||||||
|  | exports.SocketTimeout = 5000; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /***/ }), | /***/ }), | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ import { | |||||||
| import * as crypto from "crypto"; | import * as crypto from "crypto"; | ||||||
| import * as fs from "fs"; | import * as fs from "fs"; | ||||||
| 
 | 
 | ||||||
| import { Inputs } from "./constants"; | import { Inputs, SocketTimeout } from "./constants"; | ||||||
| import { | import { | ||||||
|     ArtifactCacheEntry, |     ArtifactCacheEntry, | ||||||
|     CommitCacheRequest, |     CommitCacheRequest, | ||||||
| @ -144,7 +144,33 @@ export async function downloadCache( | |||||||
|     const stream = fs.createWriteStream(archivePath); |     const stream = fs.createWriteStream(archivePath); | ||||||
|     const httpClient = new HttpClient("actions/cache"); |     const httpClient = new HttpClient("actions/cache"); | ||||||
|     const downloadResponse = await httpClient.get(archiveLocation); |     const downloadResponse = await httpClient.get(archiveLocation); | ||||||
|  | 
 | ||||||
|  |     // Abort download if no traffic received over the socket.
 | ||||||
|  |     downloadResponse.message.socket.setTimeout(SocketTimeout, () => { | ||||||
|  |         downloadResponse.message.destroy(); | ||||||
|  |         core.debug( | ||||||
|  |             `Aborting download, socket timed out after ${SocketTimeout} ms` | ||||||
|  |         ); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     await pipeResponseToStream(downloadResponse, stream); |     await pipeResponseToStream(downloadResponse, stream); | ||||||
|  | 
 | ||||||
|  |     // Validate download size.
 | ||||||
|  |     const contentLengthHeader = | ||||||
|  |         downloadResponse.message.headers["content-length"]; | ||||||
|  | 
 | ||||||
|  |     if (contentLengthHeader) { | ||||||
|  |         const expectedLength = parseInt(contentLengthHeader); | ||||||
|  |         const actualLength = utils.getArchiveFileSize(archivePath); | ||||||
|  | 
 | ||||||
|  |         if (actualLength != expectedLength) { | ||||||
|  |             throw new Error( | ||||||
|  |                 `Incomplete download. Expected file size: ${expectedLength}, actual file size: ${actualLength}` | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         core.debug("Unable to validate download, no Content-Length header"); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Reserve Cache
 | // Reserve Cache
 | ||||||
|  | |||||||
| @ -20,3 +20,8 @@ export enum Events { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const CacheFilename = "cache.tgz"; | export const CacheFilename = "cache.tgz"; | ||||||
|  | 
 | ||||||
|  | // Socket timeout in milliseconds during download.  If no traffic is received
 | ||||||
|  | // over the socket during this period, the socket is destroyed and the download
 | ||||||
|  | // is aborted.
 | ||||||
|  | export const SocketTimeout = 5000; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 David Hadka
						David Hadka