mirror of https://github.com/actions/toolkit
artifact header cleanup (#441)
* Update NPM packages for @actions/artifact * Clarifications around headers * Revert NPM updates * Apply suggestions from code review Co-authored-by: Josh Gross <joshmgross@github.com> Co-authored-by: Josh Gross <joshmgross@github.com>pull/454/head
parent
d1b52e7168
commit
0471ed4ad7
|
@ -120,7 +120,7 @@ describe('Utils', () => {
|
||||||
const size = 24
|
const size = 24
|
||||||
const uncompressedLength = 100
|
const uncompressedLength = 100
|
||||||
const range = 'bytes 0-199/200'
|
const range = 'bytes 0-199/200'
|
||||||
const options = utils.getUploadRequestOptions(
|
const headers = utils.getUploadHeaders(
|
||||||
contentType,
|
contentType,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
@ -128,47 +128,47 @@ describe('Utils', () => {
|
||||||
size,
|
size,
|
||||||
range
|
range
|
||||||
)
|
)
|
||||||
expect(Object.keys(options).length).toEqual(8)
|
expect(Object.keys(headers).length).toEqual(8)
|
||||||
expect(options['Accept']).toEqual(
|
expect(headers['Accept']).toEqual(
|
||||||
`application/json;api-version=${utils.getApiVersion()}`
|
`application/json;api-version=${utils.getApiVersion()}`
|
||||||
)
|
)
|
||||||
expect(options['Content-Type']).toEqual(contentType)
|
expect(headers['Content-Type']).toEqual(contentType)
|
||||||
expect(options['Connection']).toEqual('Keep-Alive')
|
expect(headers['Connection']).toEqual('Keep-Alive')
|
||||||
expect(options['Keep-Alive']).toEqual('10')
|
expect(headers['Keep-Alive']).toEqual('10')
|
||||||
expect(options['Content-Encoding']).toEqual('gzip')
|
expect(headers['Content-Encoding']).toEqual('gzip')
|
||||||
expect(options['x-tfs-filelength']).toEqual(uncompressedLength)
|
expect(headers['x-tfs-filelength']).toEqual(uncompressedLength)
|
||||||
expect(options['Content-Length']).toEqual(size)
|
expect(headers['Content-Length']).toEqual(size)
|
||||||
expect(options['Content-Range']).toEqual(range)
|
expect(headers['Content-Range']).toEqual(range)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Test constructing upload headers with only required parameter', () => {
|
it('Test constructing upload headers with only required parameter', () => {
|
||||||
const options = utils.getUploadRequestOptions('application/octet-stream')
|
const headers = utils.getUploadHeaders('application/octet-stream')
|
||||||
expect(Object.keys(options).length).toEqual(2)
|
expect(Object.keys(headers).length).toEqual(2)
|
||||||
expect(options['Accept']).toEqual(
|
expect(headers['Accept']).toEqual(
|
||||||
`application/json;api-version=${utils.getApiVersion()}`
|
`application/json;api-version=${utils.getApiVersion()}`
|
||||||
)
|
)
|
||||||
expect(options['Content-Type']).toEqual('application/octet-stream')
|
expect(headers['Content-Type']).toEqual('application/octet-stream')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Test constructing download headers with all optional parameters', () => {
|
it('Test constructing download headers with all optional parameters', () => {
|
||||||
const contentType = 'application/json'
|
const contentType = 'application/json'
|
||||||
const options = utils.getDownloadRequestOptions(contentType, true, true)
|
const headers = utils.getDownloadHeaders(contentType, true, true)
|
||||||
expect(Object.keys(options).length).toEqual(5)
|
expect(Object.keys(headers).length).toEqual(5)
|
||||||
expect(options['Content-Type']).toEqual(contentType)
|
expect(headers['Content-Type']).toEqual(contentType)
|
||||||
expect(options['Connection']).toEqual('Keep-Alive')
|
expect(headers['Connection']).toEqual('Keep-Alive')
|
||||||
expect(options['Keep-Alive']).toEqual('10')
|
expect(headers['Keep-Alive']).toEqual('10')
|
||||||
expect(options['Accept-Encoding']).toEqual('gzip')
|
expect(headers['Accept-Encoding']).toEqual('gzip')
|
||||||
expect(options['Accept']).toEqual(
|
expect(headers['Accept']).toEqual(
|
||||||
`application/octet-stream;api-version=${utils.getApiVersion()}`
|
`application/octet-stream;api-version=${utils.getApiVersion()}`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Test constructing download headers with only required parameter', () => {
|
it('Test constructing download headers with only required parameter', () => {
|
||||||
const options = utils.getDownloadRequestOptions('application/octet-stream')
|
const headers = utils.getDownloadHeaders('application/octet-stream')
|
||||||
expect(Object.keys(options).length).toEqual(2)
|
expect(Object.keys(headers).length).toEqual(2)
|
||||||
expect(options['Content-Type']).toEqual('application/octet-stream')
|
expect(headers['Content-Type']).toEqual('application/octet-stream')
|
||||||
// check for default accept type
|
// check for default accept type
|
||||||
expect(options['Accept']).toEqual(
|
expect(headers['Accept']).toEqual(
|
||||||
`application/json;api-version=${utils.getApiVersion()}`
|
`application/json;api-version=${utils.getApiVersion()}`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,7 +3,7 @@ import * as core from '@actions/core'
|
||||||
import * as zlib from 'zlib'
|
import * as zlib from 'zlib'
|
||||||
import {
|
import {
|
||||||
getArtifactUrl,
|
getArtifactUrl,
|
||||||
getDownloadRequestOptions,
|
getDownloadHeaders,
|
||||||
isSuccessStatusCode,
|
isSuccessStatusCode,
|
||||||
isRetryableStatusCode,
|
isRetryableStatusCode,
|
||||||
isThrottledStatusCode,
|
isThrottledStatusCode,
|
||||||
|
@ -40,8 +40,8 @@ export class DownloadHttpClient {
|
||||||
|
|
||||||
// use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately
|
// use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately
|
||||||
const client = this.downloadHttpManager.getClient(0)
|
const client = this.downloadHttpManager.getClient(0)
|
||||||
const requestOptions = getDownloadRequestOptions('application/json')
|
const headers = getDownloadHeaders('application/json')
|
||||||
const response = await client.get(artifactUrl, requestOptions)
|
const response = await client.get(artifactUrl, headers)
|
||||||
const body: string = await response.readBody()
|
const body: string = await response.readBody()
|
||||||
|
|
||||||
if (isSuccessStatusCode(response.message.statusCode) && body) {
|
if (isSuccessStatusCode(response.message.statusCode) && body) {
|
||||||
|
@ -68,8 +68,8 @@ export class DownloadHttpClient {
|
||||||
|
|
||||||
// use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately
|
// use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately
|
||||||
const client = this.downloadHttpManager.getClient(0)
|
const client = this.downloadHttpManager.getClient(0)
|
||||||
const requestOptions = getDownloadRequestOptions('application/json')
|
const headers = getDownloadHeaders('application/json')
|
||||||
const response = await client.get(resourceUrl.toString(), requestOptions)
|
const response = await client.get(resourceUrl.toString(), headers)
|
||||||
const body: string = await response.readBody()
|
const body: string = await response.readBody()
|
||||||
|
|
||||||
if (isSuccessStatusCode(response.message.statusCode) && body) {
|
if (isSuccessStatusCode(response.message.statusCode) && body) {
|
||||||
|
@ -149,22 +149,19 @@ export class DownloadHttpClient {
|
||||||
let retryCount = 0
|
let retryCount = 0
|
||||||
const retryLimit = getRetryLimit()
|
const retryLimit = getRetryLimit()
|
||||||
const destinationStream = fs.createWriteStream(downloadPath)
|
const destinationStream = fs.createWriteStream(downloadPath)
|
||||||
const requestOptions = getDownloadRequestOptions(
|
const headers = getDownloadHeaders('application/json', true, true)
|
||||||
'application/json',
|
|
||||||
true,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
// a single GET request is used to download a file
|
// a single GET request is used to download a file
|
||||||
const makeDownloadRequest = async (): Promise<IHttpClientResponse> => {
|
const makeDownloadRequest = async (): Promise<IHttpClientResponse> => {
|
||||||
const client = this.downloadHttpManager.getClient(httpClientIndex)
|
const client = this.downloadHttpManager.getClient(httpClientIndex)
|
||||||
return await client.get(artifactLocation, requestOptions)
|
return await client.get(artifactLocation, headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the response headers to determine if the file was compressed using gzip
|
// check the response headers to determine if the file was compressed using gzip
|
||||||
const isGzip = (headers: IncomingHttpHeaders): boolean => {
|
const isGzip = (incomingHeaders: IncomingHttpHeaders): boolean => {
|
||||||
return (
|
return (
|
||||||
'content-encoding' in headers && headers['content-encoding'] === 'gzip'
|
'content-encoding' in incomingHeaders &&
|
||||||
|
incomingHeaders['content-encoding'] === 'gzip'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
import {
|
import {
|
||||||
getArtifactUrl,
|
getArtifactUrl,
|
||||||
getContentRange,
|
getContentRange,
|
||||||
getUploadRequestOptions,
|
getUploadHeaders,
|
||||||
isRetryableStatusCode,
|
isRetryableStatusCode,
|
||||||
isSuccessStatusCode,
|
isSuccessStatusCode,
|
||||||
isThrottledStatusCode,
|
isThrottledStatusCode,
|
||||||
|
@ -63,8 +63,8 @@ export class UploadHttpClient {
|
||||||
|
|
||||||
// use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately
|
// use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately
|
||||||
const client = this.uploadHttpManager.getClient(0)
|
const client = this.uploadHttpManager.getClient(0)
|
||||||
const requestOptions = getUploadRequestOptions('application/json', false)
|
const headers = getUploadHeaders('application/json', false)
|
||||||
const rawResponse = await client.post(artifactUrl, data, requestOptions)
|
const rawResponse = await client.post(artifactUrl, data, headers)
|
||||||
const body: string = await rawResponse.readBody()
|
const body: string = await rawResponse.readBody()
|
||||||
|
|
||||||
if (isSuccessStatusCode(rawResponse.message.statusCode) && body) {
|
if (isSuccessStatusCode(rawResponse.message.statusCode) && body) {
|
||||||
|
@ -354,7 +354,7 @@ export class UploadHttpClient {
|
||||||
totalFileSize: number
|
totalFileSize: number
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
// prepare all the necessary headers before making any http call
|
// prepare all the necessary headers before making any http call
|
||||||
const requestOptions = getUploadRequestOptions(
|
const headers = getUploadHeaders(
|
||||||
'application/octet-stream',
|
'application/octet-stream',
|
||||||
true,
|
true,
|
||||||
isGzip,
|
isGzip,
|
||||||
|
@ -365,7 +365,7 @@ export class UploadHttpClient {
|
||||||
|
|
||||||
const uploadChunkRequest = async (): Promise<IHttpClientResponse> => {
|
const uploadChunkRequest = async (): Promise<IHttpClientResponse> => {
|
||||||
const client = this.uploadHttpManager.getClient(httpClientIndex)
|
const client = this.uploadHttpManager.getClient(httpClientIndex)
|
||||||
return await client.sendStream('PUT', resourceUrl, data, requestOptions)
|
return await client.sendStream('PUT', resourceUrl, data, headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
let retryCount = 0
|
let retryCount = 0
|
||||||
|
@ -464,7 +464,7 @@ export class UploadHttpClient {
|
||||||
* Updating the size indicates that we are done uploading all the contents of the artifact
|
* Updating the size indicates that we are done uploading all the contents of the artifact
|
||||||
*/
|
*/
|
||||||
async patchArtifactSize(size: number, artifactName: string): Promise<void> {
|
async patchArtifactSize(size: number, artifactName: string): Promise<void> {
|
||||||
const requestOptions = getUploadRequestOptions('application/json', false)
|
const headers = getUploadHeaders('application/json', false)
|
||||||
const resourceUrl = new URL(getArtifactUrl())
|
const resourceUrl = new URL(getArtifactUrl())
|
||||||
resourceUrl.searchParams.append('artifactName', artifactName)
|
resourceUrl.searchParams.append('artifactName', artifactName)
|
||||||
|
|
||||||
|
@ -477,7 +477,7 @@ export class UploadHttpClient {
|
||||||
const response: HttpClientResponse = await client.patch(
|
const response: HttpClientResponse = await client.patch(
|
||||||
resourceUrl.toString(),
|
resourceUrl.toString(),
|
||||||
data,
|
data,
|
||||||
requestOptions
|
headers
|
||||||
)
|
)
|
||||||
const body: string = await response.readBody()
|
const body: string = await response.readBody()
|
||||||
if (isSuccessStatusCode(response.message.statusCode)) {
|
if (isSuccessStatusCode(response.message.statusCode)) {
|
||||||
|
|
|
@ -62,7 +62,6 @@ export function isForbiddenStatusCode(statusCode?: number): boolean {
|
||||||
if (!statusCode) {
|
if (!statusCode) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return statusCode === HttpCodes.Forbidden
|
return statusCode === HttpCodes.Forbidden
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +83,6 @@ export function isThrottledStatusCode(statusCode?: number): boolean {
|
||||||
if (!statusCode) {
|
if (!statusCode) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return statusCode === HttpCodes.TooManyRequests
|
return statusCode === HttpCodes.TooManyRequests
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,9 +131,9 @@ export function getContentRange(
|
||||||
* @param {boolean} isKeepAlive is the same connection being used to make multiple calls
|
* @param {boolean} isKeepAlive is the same connection being used to make multiple calls
|
||||||
* @param {boolean} acceptGzip can we accept a gzip encoded response
|
* @param {boolean} acceptGzip can we accept a gzip encoded response
|
||||||
* @param {string} acceptType the type of content that we can accept
|
* @param {string} acceptType the type of content that we can accept
|
||||||
* @returns appropriate request options to make a specific http call during artifact download
|
* @returns appropriate headers to make a specific http call during artifact download
|
||||||
*/
|
*/
|
||||||
export function getDownloadRequestOptions(
|
export function getDownloadHeaders(
|
||||||
contentType: string,
|
contentType: string,
|
||||||
isKeepAlive?: boolean,
|
isKeepAlive?: boolean,
|
||||||
acceptGzip?: boolean
|
acceptGzip?: boolean
|
||||||
|
@ -172,9 +170,9 @@ export function getDownloadRequestOptions(
|
||||||
* @param {number} uncompressedLength the original size of the content if something is being uploaded that has been compressed
|
* @param {number} uncompressedLength the original size of the content if something is being uploaded that has been compressed
|
||||||
* @param {number} contentLength the length of the content that is being uploaded
|
* @param {number} contentLength the length of the content that is being uploaded
|
||||||
* @param {string} contentRange the range of the content that is being uploaded
|
* @param {string} contentRange the range of the content that is being uploaded
|
||||||
* @returns appropriate request options to make a specific http call during artifact upload
|
* @returns appropriate headers to make a specific http call during artifact upload
|
||||||
*/
|
*/
|
||||||
export function getUploadRequestOptions(
|
export function getUploadHeaders(
|
||||||
contentType: string,
|
contentType: string,
|
||||||
isKeepAlive?: boolean,
|
isKeepAlive?: boolean,
|
||||||
isGzip?: boolean,
|
isGzip?: boolean,
|
||||||
|
@ -207,7 +205,7 @@ export function getUploadRequestOptions(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createHttpClient(): HttpClient {
|
export function createHttpClient(): HttpClient {
|
||||||
return new HttpClient('action/artifact', [
|
return new HttpClient('actions/artifact', [
|
||||||
new BearerCredentialHandler(getRuntimeToken())
|
new BearerCredentialHandler(getRuntimeToken())
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue