1
0
Fork 0

add digest header for uploads

pull/1063/head
Rob Herley 2022-04-04 17:54:15 -04:00
parent f8a69bc473
commit c5d1911357
No known key found for this signature in database
GPG Key ID: D1602042C3543B06
2 changed files with 27 additions and 4 deletions

View File

@ -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> => {

View File

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