import { ContentData , NetworkRequest } from './NetworkRequest' ; import { NetworkCookie } from './NetworkCookie' ; import { Content , Cookie , Entry , Param , PostData , Request , Response , Timings } from 'har-format' ; import Protocol from 'devtools-protocol' ; export class EntryBuilder { constructor ( private readonly request ) { } public async build ( ) { let serverIPAddress = this . request . remoteAddress ; const portPositionInString = serverIPAddress . lastIndexOf ( ':' ) ; if ( portPositionInString !== - 1 ) { serverIPAddress = serverIPAddress . substr ( 0 , portPositionInString ) ; } const timings = this . buildTimings ( ) ; const time = Object . values ( timings ) . reduce ( ( acc , t ) : number => ( acc += Math . max ( t , 0 ) ) , 0 ) ; const entry = { startedDateTime : new Date ( this . request . getWallTime ( this . request . issueTime ) * 1000 ) . toJSON ( ) , time , request : await this . buildRequest ( ) , response : await this . buildResponse ( ) , cache : { } , timings , serverIPAddress : serverIPAddress . replace ( /\[]/g , '' ) , _priority : this . request . priority , _resourceType : this . request . resourceType , _webSocketMessages : this . request . frames ?? [ ] } ; if ( this . request . connectionId !== '0' ) { entry . connection = this . request . connectionId ; } return entry ; } private getResponseBodySize ( ) { if ( this . request . statusCode === 304 ) { return 0 ; } if ( ! this . request . responseHeadersText ) { return - 1 ; } return this . request . transferSize - this . request . responseHeadersText . length ; } private getResponseCompression ( ) { if ( this . request . statusCode === 304 || this . request . statusCode === 206 ) { return ; } if ( ! this . request . responseHeadersText ) { return ; } return this . request . resourceSize - this . getResponseBodySize ( ) ; } private toMilliseconds ( time ) { return time === - 1 ? - 1 : time * 1000 ; } private async buildRequest ( ) { const res = { method : this . request . requestMethod , url : this . buildRequestURL ( this . request . url ) , httpVersion : this . request . requestHttpVersion , headers : this . request . requestHeaders , queryString : [ ... ( this . request . queryParameters ?? [ ] ) ] , cookies : this . buildCookies ( this . request . requestCookies ?? [ ] ) , headersSize : this . request . requestHeadersText ?. length ?? - 1 , bodySize : await this . requestBodySize ( ) } ; const postData = await this . buildPostData ( ) ; if ( postData ) { res . postData = postData ; } return res ; } private async buildResponse ( ) { return { status : this . request . statusCode , statusText : this . request . statusText , httpVersion : this . request . responseHttpVersion ( ) , headers : this . request . responseHeaders , cookies : this . buildCookies ( this . request . responseCookies || [ ] ) , content : await this . buildContent ( ) , redirectURL : this . request . responseHeaderValue ( 'Location' ) ?? '' , headersSize : this . request . responseHeadersText ?. length ?? - 1 , bodySize : this . getResponseBodySize ( ) , _transferSize : this . request . transferSize } ; } private async buildContent ( ) { const data = await this . request . contentData ( ) ; return Object . assign ( { size : this . request . resourceSize , mimeType : this . request . mimeType || 'x-unknown' , compression : this . getResponseCompression ( ) ?? undefined } , data ) ; } private buildTimings ( ) { const timing = this . request . timing ; const issueTime = this . request . issueTime ; const startTime = this . request . startTime ; const result = { blocked : - 1 , dns : - 1 , ssl : - 1 , connect : - 1 , send : 0 , wait : 0 , receive : 0 } ; const queuedTime = issueTime < startTime ? startTime - issueTime : - 1 ; result . blocked = this . toMilliseconds ( queuedTime ) ; let highestTime = 0 ; if ( timing ) { const blockedStart = this . leastNonNegative ( [ timing . dnsStart , timing . connectStart , timing . sendStart ] ) ; if ( blockedStart !== Infinity ) { result . blocked += blockedStart ; } const dnsStart = timing . dnsEnd >= 0 ? blockedStart : 0 ; const dnsEnd = timing . dnsEnd >= 0 ? timing . dnsEnd : - 1 ; result . dns = dnsEnd - dnsStart ; const sslStart = timing . sslEnd > 0 ? timing . sslStart : 0 ; const sslEnd = timing . sslEnd > 0 ? timing . sslEnd : - 1 ; result . ssl = sslEnd - sslStart ; const connectStart = timing . connectEnd >= 0 ? this . leastNonNegative ( [ dnsEnd , blockedStart ] ) : 0 ; const connectEnd = timing . connectEnd >= 0 ? timing . connectEnd : - 1 ; result . connect = connectEnd - connectStart ; const sendStart = timing . sendEnd >= 0 ? Math . max ( connectEnd , dnsEnd , blockedStart ) : 0 ; const sendEnd = timing . sendEnd >= 0 ? timing . sendEnd : 0 ; result . send = sendEnd - sendStart ; if ( result . send < 0 ) { result . send = 0 ; } highestTime = Math . max ( sendEnd , connectEnd , sslEnd , dnsEnd , blockedStart , 0 ) ; } else if ( this . request . responseReceivedTime === - 1 ) { result . blocked = this . request . endTime - issueTime ; return result ; } const requestTime = timing ?. requestTime ?? startTime ; const waitStart = highestTime ; const waitEnd = this . toMilliseconds ( this . request . responseReceivedTime - requestTime ) ; result . wait = waitEnd - waitStart ; const receiveStart = waitEnd ; const receiveEnd = this . toMilliseconds ( this . request . endTime - requestTime ) ; result . receive = Math . max ( receiveEnd - receiveStart , 0 ) ; return result ; } private leastNonNegative ( values ) { const value = values . find ( ( item ) : boolean => item >= 0 ) ; return value ?? - 1 ; } private async buildPostData ( ) { const postData = await this . request . requestFormData ( ) ; if ( ! postData ) { return ; } const res = { mimeType : this . request . requestContentType ?? '' , text : postData } ; const formParameters = await this . request . formParameters ( ) ; if ( formParameters ) { res . params = [ ... formParameters ] as never ; } return res as PostData ; } private buildRequestURL ( url ) { return url . split ( '#' , 2 ) [ 0 ] ; } private buildCookies ( cookies ) { return cookies . map ( this . buildCookie . bind ( this ) ) ; } private buildCookie ( cookie ) { return { name : cookie . name , value : cookie . value , path : cookie . path , domain : cookie . domain , expires : cookie . expiresDate ( new Date ( this . request . getWallTime ( this . request . startTime ) * 1000 ) ) ?. toJSON ( ) , httpOnly : cookie . httpOnly , secure : cookie . secure } ; } private async requestBodySize ( ) { const postData = await this . request . requestFormData ( ) ; if ( ! postData ) { return 0 ; } return this . request . contentLength ; } }