1414 * limitations under the License.
1515 */
1616
17+ import { URL } from 'url' ;
1718import fs from 'fs' ;
1819import { BrowserContext } from '../../browserContext' ;
1920import { helper } from '../../helper' ;
2021import * as network from '../../network' ;
2122import { Page } from '../../page' ;
2223import * as har from './har' ;
24+ import * as types from '../../types' ;
25+
26+ const FALLBACK_HTTP_VERSION = 'HTTP/1.1' ;
2327
2428type HarOptions = {
2529 path : string ;
@@ -51,6 +55,7 @@ export class HarTracer {
5155 } ;
5256 context . on ( BrowserContext . Events . Page , ( page : Page ) => this . _ensurePageEntry ( page ) ) ;
5357 context . on ( BrowserContext . Events . Request , ( request : network . Request ) => this . _onRequest ( request ) ) ;
58+ context . on ( BrowserContext . Events . RequestFinished , ( request : network . Request ) => this . _onRequestFinished ( request ) . catch ( ( ) => { } ) ) ;
5459 context . on ( BrowserContext . Events . Response , ( response : network . Response ) => this . _onResponse ( response ) ) ;
5560 }
5661
@@ -128,27 +133,28 @@ export class HarTracer {
128133 request : {
129134 method : request . method ( ) ,
130135 url : request . url ( ) ,
131- httpVersion : 'HTTP/1.1' ,
136+ httpVersion : FALLBACK_HTTP_VERSION ,
132137 cookies : [ ] ,
133138 headers : [ ] ,
134139 queryString : [ ...url . searchParams ] . map ( e => ( { name : e [ 0 ] , value : e [ 1 ] } ) ) ,
135- postData : undefined ,
140+ postData : postDataForHar ( request ) ,
136141 headersSize : - 1 ,
137- bodySize : - 1 ,
142+ bodySize : calculateRequestBodySize ( request ) || 0 ,
138143 } ,
139144 response : {
140145 status : - 1 ,
141146 statusText : '' ,
142- httpVersion : 'HTTP/1.1' ,
147+ httpVersion : FALLBACK_HTTP_VERSION ,
143148 cookies : [ ] ,
144149 headers : [ ] ,
145150 content : {
146151 size : - 1 ,
147- mimeType : request . headerValue ( 'content-type' ) || 'application/octet-stream ' ,
152+ mimeType : request . headerValue ( 'content-type' ) || 'x-unknown ' ,
148153 } ,
149154 headersSize : - 1 ,
150155 bodySize : - 1 ,
151- redirectURL : ''
156+ redirectURL : '' ,
157+ _transferSize : - 1
152158 } ,
153159 cache : {
154160 beforeRequest : null ,
@@ -168,29 +174,63 @@ export class HarTracer {
168174 this . _entries . set ( request , harEntry ) ;
169175 }
170176
177+ private async _onRequestFinished ( request : network . Request ) {
178+ const page = request . frame ( ) . _page ;
179+ const harEntry = this . _entries . get ( request ) ! ;
180+ const response = await request . response ( ) ;
181+
182+ if ( ! response )
183+ return ;
184+
185+ const httpVersion = normaliseHttpVersion ( response . _httpVersion ) ;
186+ const transferSize = response . _transferSize || - 1 ;
187+ const headersSize = calculateResponseHeadersSize ( httpVersion , response . status ( ) , response . statusText ( ) , response . headers ( ) ) ;
188+ const bodySize = transferSize !== - 1 ? transferSize - headersSize : - 1 ;
189+
190+ harEntry . request . httpVersion = httpVersion ;
191+ harEntry . response . bodySize = bodySize ;
192+ harEntry . response . headersSize = headersSize ;
193+ harEntry . response . _transferSize = transferSize ;
194+ harEntry . request . headersSize = calculateRequestHeadersSize ( request . method ( ) , request . url ( ) , httpVersion , request . headers ( ) ) ;
195+
196+ const promise = response . body ( ) . then ( buffer => {
197+ const content = harEntry . response . content ;
198+ content . size = buffer . length ;
199+ content . compression = harEntry . response . bodySize !== - 1 ? buffer . length - harEntry . response . bodySize : 0 ;
200+
201+ if ( ! this . _options . omitContent && buffer && buffer . length > 0 ) {
202+ content . text = buffer . toString ( 'base64' ) ;
203+ content . encoding = 'base64' ;
204+ }
205+ } ) . catch ( ( ) => { } ) ;
206+ this . _addBarrier ( page , promise ) ;
207+ }
208+
171209 private _onResponse ( response : network . Response ) {
172210 const page = response . frame ( ) . _page ;
173211 const pageEntry = this . _ensurePageEntry ( page ) ;
174212 const harEntry = this . _entries . get ( response . request ( ) ) ! ;
175213 // Rewrite provisional headers with actual
176214 const request = response . request ( ) ;
215+
177216 harEntry . request . headers = request . headers ( ) . map ( header => ( { name : header . name , value : header . value } ) ) ;
178217 harEntry . request . cookies = cookiesForHar ( request . headerValue ( 'cookie' ) , ';' ) ;
179- harEntry . request . postData = postDataForHar ( request ) || undefined ;
218+ harEntry . request . postData = postDataForHar ( request ) ;
180219
181220 harEntry . response = {
182221 status : response . status ( ) ,
183222 statusText : response . statusText ( ) ,
184- httpVersion : 'HTTP/1.1' ,
223+ httpVersion : normaliseHttpVersion ( response . _httpVersion ) ,
185224 cookies : cookiesForHar ( response . headerValue ( 'set-cookie' ) , '\n' ) ,
186225 headers : response . headers ( ) . map ( header => ( { name : header . name , value : header . value } ) ) ,
187226 content : {
188227 size : - 1 ,
189- mimeType : response . headerValue ( 'content-type' ) || 'application/octet-stream ' ,
228+ mimeType : response . headerValue ( 'content-type' ) || 'x-unknown ' ,
190229 } ,
191230 headersSize : - 1 ,
192231 bodySize : - 1 ,
193- redirectURL : ''
232+ redirectURL : '' ,
233+ _transferSize : - 1
194234 } ;
195235 const timing = response . timing ( ) ;
196236 if ( pageEntry . startedDateTime . valueOf ( ) > timing . startTime )
@@ -220,14 +260,6 @@ export class HarTracer {
220260 if ( details )
221261 harEntry . _securityDetails = details ;
222262 } ) ) ;
223-
224- if ( ! this . _options . omitContent && response . status ( ) === 200 ) {
225- const promise = response . body ( ) . then ( buffer => {
226- harEntry . response . content . text = buffer . toString ( 'base64' ) ;
227- harEntry . response . content . encoding = 'base64' ;
228- } ) . catch ( ( ) => { } ) ;
229- this . _addBarrier ( page , promise ) ;
230- }
231263 }
232264
233265 async flush ( ) {
@@ -246,10 +278,10 @@ export class HarTracer {
246278 }
247279}
248280
249- function postDataForHar ( request : network . Request ) : har . PostData | null {
281+ function postDataForHar ( request : network . Request ) : har . PostData | undefined {
250282 const postData = request . postDataBuffer ( ) ;
251283 if ( ! postData )
252- return null ;
284+ return ;
253285
254286 const contentType = request . headerValue ( 'content-type' ) || 'application/octet-stream' ;
255287 const result : har . PostData = {
@@ -305,3 +337,33 @@ function parseCookie(c: string): har.Cookie {
305337 }
306338 return cookie ;
307339}
340+
341+ function calculateResponseHeadersSize ( protocol : string , status : number , statusText : string , headers : types . HeadersArray ) {
342+ let rawHeaders = `${ protocol } ${ status } ${ statusText } \r\n` ;
343+ for ( const header of headers )
344+ rawHeaders += `${ header . name } : ${ header . value } \r\n` ;
345+ rawHeaders += '\r\n' ;
346+ return rawHeaders . length ;
347+ }
348+
349+ function calculateRequestHeadersSize ( method : string , url : string , httpVersion : string , headers : types . HeadersArray ) {
350+ let rawHeaders = `${ method } ${ ( new URL ( url ) ) . pathname } ${ httpVersion } \r\n` ;
351+ for ( const header of headers )
352+ rawHeaders += `${ header . name } : ${ header . value } \r\n` ;
353+ return rawHeaders . length ;
354+ }
355+
356+ function normaliseHttpVersion ( httpVersion ?: string ) {
357+ if ( ! httpVersion )
358+ return FALLBACK_HTTP_VERSION ;
359+ if ( httpVersion === 'http/1.1' )
360+ return 'HTTP/1.1' ;
361+ return httpVersion ;
362+ }
363+
364+ function calculateRequestBodySize ( request : network . Request ) : number | undefined {
365+ const postData = request . postDataBuffer ( ) ;
366+ if ( ! postData )
367+ return ;
368+ return new TextEncoder ( ) . encode ( postData . toString ( 'utf8' ) ) . length ;
369+ }
0 commit comments