@@ -27,11 +27,20 @@ import {GoogleAuth, GoogleAuthOptions} from 'google-auth-library';
2727import { Readable , Writable } from 'stream' ;
2828import retry = require( 'async-retry' ) ;
2929import { RetryOptions , PreconditionOptions } from './storage' ;
30+ import * as uuid from 'uuid' ;
3031
3132const NOT_FOUND_STATUS_CODE = 404 ;
3233const TERMINATED_UPLOAD_STATUS_CODE = 410 ;
3334const RESUMABLE_INCOMPLETE_STATUS_CODE = 308 ;
3435const DEFAULT_API_ENDPOINT_REGEX = / .* \. g o o g l e a p i s \. c o m / ;
36+ let packageJson : ReturnType < JSON [ 'parse' ] > = { } ;
37+ try {
38+ // if requiring from 'build' (default)
39+ packageJson = require ( '../../package.json' ) ;
40+ } catch ( e ) {
41+ // if requiring directly from TypeScript context
42+ packageJson = require ( '../package.json' ) ;
43+ }
3544
3645export const PROTOCOL_REGEX = / ^ ( \w * ) : \/ \/ / ;
3746
@@ -262,6 +271,11 @@ export class Upload extends Writable {
262271 contentLength : number | '*' ;
263272 retryOptions : RetryOptions ;
264273 timeOfFirstRequest : number ;
274+ private currentInvocationId = {
275+ chunk : uuid . v4 ( ) ,
276+ uri : uuid . v4 ( ) ,
277+ offset : uuid . v4 ( ) ,
278+ } ;
265279 private upstreamChunkBuffer : Buffer = Buffer . alloc ( 0 ) ;
266280 private chunkBufferEncoding ?: BufferEncoding = undefined ;
267281 private numChunksReadInRequest = 0 ;
@@ -530,6 +544,7 @@ export class Upload extends Writable {
530544 protected async createURIAsync ( ) : Promise < string > {
531545 const metadata = this . metadata ;
532546
547+ // Check if headers already exist before creating new ones
533548 const reqOpts : GaxiosOptions = {
534549 method : 'POST' ,
535550 url : [ this . baseURI , this . bucket , 'o' ] . join ( '/' ) ,
@@ -541,7 +556,9 @@ export class Upload extends Writable {
541556 this . params
542557 ) ,
543558 data : metadata ,
544- headers : { } ,
559+ headers : {
560+ 'x-goog-api-client' : `gl-node/${ process . versions . node } gccl/${ packageJson . version } gccl-invocation-id/${ this . currentInvocationId . uri } ` ,
561+ } ,
545562 } ;
546563
547564 if ( metadata . contentLength ) {
@@ -572,6 +589,8 @@ export class Upload extends Writable {
572589 async ( bail : ( err : Error ) => void ) => {
573590 try {
574591 const res = await this . makeRequest ( reqOpts ) ;
592+ // We have successfully got a URI we can now create a new invocation id
593+ this . currentInvocationId . uri = uuid . v4 ( ) ;
575594 return res . headers . location ;
576595 } catch ( err ) {
577596 const e = err as GaxiosError ;
@@ -707,20 +726,20 @@ export class Upload extends Writable {
707726 } ,
708727 } ) ;
709728
710- let headers : GaxiosOptions [ 'headers' ] = { } ;
729+ const headers : GaxiosOptions [ 'headers' ] = {
730+ 'x-goog-api-client' : `gl-node/${ process . versions . node } gccl/${ packageJson . version } gccl-invocation-id/${ this . currentInvocationId . chunk } ` ,
731+ } ;
711732
712733 // If using multiple chunk upload, set appropriate header
713734 if ( multiChunkMode && expectedUploadSize ) {
714735 // The '-1' is because the ending byte is inclusive in the request.
715736 const endingByte = expectedUploadSize + this . numBytesWritten - 1 ;
716- headers = {
717- 'Content-Length' : expectedUploadSize ,
718- 'Content-Range' : `bytes ${ this . offset } - ${ endingByte } / ${ this . contentLength } ` ,
719- } ;
737+ headers [ 'Content-Length' ] = expectedUploadSize ;
738+ headers [
739+ 'Content-Range'
740+ ] = `bytes ${ this . offset } - ${ endingByte } / ${ this . contentLength } ` ;
720741 } else {
721- headers = {
722- 'Content-Range' : `bytes ${ this . offset } -*/${ this . contentLength } ` ,
723- } ;
742+ headers [ 'Content-Range' ] = `bytes ${ this . offset } -*/${ this . contentLength } ` ;
724743 }
725744
726745 const reqOpts : GaxiosOptions = {
@@ -750,6 +769,9 @@ export class Upload extends Writable {
750769 return ;
751770 }
752771
772+ // At this point we can safely create a new id for the chunk
773+ this . currentInvocationId . chunk = uuid . v4 ( ) ;
774+
753775 const shouldContinueWithNextMultiChunkRequest =
754776 this . chunkSize &&
755777 resp . status === RESUMABLE_INCOMPLETE_STATUS_CODE &&
@@ -849,10 +871,16 @@ export class Upload extends Writable {
849871 const opts : GaxiosOptions = {
850872 method : 'PUT' ,
851873 url : this . uri ! ,
852- headers : { 'Content-Length' : 0 , 'Content-Range' : 'bytes */*' } ,
874+ headers : {
875+ 'Content-Length' : 0 ,
876+ 'Content-Range' : 'bytes */*' ,
877+ 'x-goog-api-client' : `gl-node/${ process . versions . node } gccl/${ packageJson . version } gccl-invocation-id/${ this . currentInvocationId . offset } ` ,
878+ } ,
853879 } ;
854880 try {
855881 const resp = await this . makeRequest ( opts ) ;
882+ // Successfully got the offset we can now create a new offset invocation id
883+ this . currentInvocationId . offset = uuid . v4 ( ) ;
856884 if ( resp . status === RESUMABLE_INCOMPLETE_STATUS_CODE ) {
857885 if ( resp . headers . range ) {
858886 const range = resp . headers . range as string ;
0 commit comments