mirror of https://github.com/actions/toolkit
add digest header for uploads
parent
f8a69bc473
commit
c5d1911357
|
@ -9,6 +9,7 @@ import {
|
||||||
UploadResults
|
UploadResults
|
||||||
} from './contracts'
|
} from './contracts'
|
||||||
import {
|
import {
|
||||||
|
digestForStream,
|
||||||
getArtifactUrl,
|
getArtifactUrl,
|
||||||
getContentRange,
|
getContentRange,
|
||||||
getUploadHeaders,
|
getUploadHeaders,
|
||||||
|
@ -406,6 +407,9 @@ export class UploadHttpClient {
|
||||||
isGzip: boolean,
|
isGzip: boolean,
|
||||||
totalFileSize: number
|
totalFileSize: number
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
|
// open a new stream and read it to compute the digest
|
||||||
|
const digest = await digestForStream(openStream())
|
||||||
|
|
||||||
// prepare all the necessary headers before making any http call
|
// prepare all the necessary headers before making any http call
|
||||||
const headers = getUploadHeaders(
|
const headers = getUploadHeaders(
|
||||||
'application/octet-stream',
|
'application/octet-stream',
|
||||||
|
@ -413,7 +417,8 @@ export class UploadHttpClient {
|
||||||
isGzip,
|
isGzip,
|
||||||
totalFileSize,
|
totalFileSize,
|
||||||
end - start + 1,
|
end - start + 1,
|
||||||
getContentRange(start, end, uploadFileSize)
|
getContentRange(start, end, uploadFileSize),
|
||||||
|
digest
|
||||||
)
|
)
|
||||||
|
|
||||||
const uploadChunkRequest = async (): Promise<IHttpClientResponse> => {
|
const uploadChunkRequest = async (): Promise<IHttpClientResponse> => {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import {debug, info, warning} from '@actions/core'
|
import * as crypto from 'crypto'
|
||||||
import {promises as fs} from 'fs'
|
import {promises as fs} from 'fs'
|
||||||
|
import {IncomingHttpHeaders} from 'http'
|
||||||
|
import {debug, info, warning} from '@actions/core'
|
||||||
import {HttpCodes, HttpClient} from '@actions/http-client'
|
import {HttpCodes, HttpClient} from '@actions/http-client'
|
||||||
import {BearerCredentialHandler} from '@actions/http-client/auth'
|
import {BearerCredentialHandler} from '@actions/http-client/auth'
|
||||||
import {IHeaders, IHttpClientResponse} from '@actions/http-client/interfaces'
|
import {IHeaders, IHttpClientResponse} from '@actions/http-client/interfaces'
|
||||||
import {IncomingHttpHeaders} from 'http'
|
|
||||||
import {
|
import {
|
||||||
getRuntimeToken,
|
getRuntimeToken,
|
||||||
getRuntimeUrl,
|
getRuntimeUrl,
|
||||||
|
@ -180,7 +181,8 @@ export function getUploadHeaders(
|
||||||
isGzip?: boolean,
|
isGzip?: boolean,
|
||||||
uncompressedLength?: number,
|
uncompressedLength?: number,
|
||||||
contentLength?: number,
|
contentLength?: number,
|
||||||
contentRange?: string
|
contentRange?: string,
|
||||||
|
digest?: string
|
||||||
): IHeaders {
|
): IHeaders {
|
||||||
const requestOptions: IHeaders = {}
|
const requestOptions: IHeaders = {}
|
||||||
requestOptions['Accept'] = `application/json;api-version=${getApiVersion()}`
|
requestOptions['Accept'] = `application/json;api-version=${getApiVersion()}`
|
||||||
|
@ -202,6 +204,10 @@ export function getUploadHeaders(
|
||||||
if (contentRange) {
|
if (contentRange) {
|
||||||
requestOptions['Content-Range'] = contentRange
|
requestOptions['Content-Range'] = contentRange
|
||||||
}
|
}
|
||||||
|
if (digest) {
|
||||||
|
// TODO(robherley): should we use 'Digest' directly? https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Digest
|
||||||
|
requestOptions['X-Digest'] = `sha-256=${digest}`
|
||||||
|
}
|
||||||
|
|
||||||
return requestOptions
|
return requestOptions
|
||||||
}
|
}
|
||||||
|
@ -291,3 +297,15 @@ export function getProperRetention(
|
||||||
export async function sleep(milliseconds: number): Promise<void> {
|
export async function sleep(milliseconds: number): Promise<void> {
|
||||||
return new Promise(resolve => setTimeout(resolve, milliseconds))
|
return new Promise(resolve => setTimeout(resolve, milliseconds))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function digestForStream(
|
||||||
|
stream: NodeJS.ReadableStream
|
||||||
|
): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// TODO(robherley): switch to crc64 for production
|
||||||
|
const hasher = crypto.createHash('sha256')
|
||||||
|
stream.on('data', data => hasher.update(data))
|
||||||
|
stream.on('end', () => resolve(hasher.digest('hex')))
|
||||||
|
stream.on('error', reject)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue