|
| 1 | +'use strict'; |
| 2 | + |
| 3 | +const{ |
| 4 | + ArrayIsArray, |
| 5 | + DateNow, |
| 6 | + ObjectEntries, |
| 7 | + String, |
| 8 | + Symbol, |
| 9 | +}=primordials; |
| 10 | + |
| 11 | +const{ |
| 12 | + kInspectorRequestId, |
| 13 | + kResourceType, |
| 14 | + getMonotonicTime, |
| 15 | + getNextRequestId, |
| 16 | + sniffMimeType, |
| 17 | +}=require('internal/inspector/network'); |
| 18 | +constdc=require('diagnostics_channel'); |
| 19 | +const{ Network }=require('inspector'); |
| 20 | +const{ |
| 21 | +HTTP2_HEADER_AUTHORITY, |
| 22 | +HTTP2_HEADER_CONTENT_TYPE, |
| 23 | +HTTP2_HEADER_COOKIE, |
| 24 | +HTTP2_HEADER_METHOD, |
| 25 | +HTTP2_HEADER_PATH, |
| 26 | +HTTP2_HEADER_SCHEME, |
| 27 | +HTTP2_HEADER_SET_COOKIE, |
| 28 | +HTTP2_HEADER_STATUS, |
| 29 | +NGHTTP2_NO_ERROR, |
| 30 | +}=internalBinding('http2').constants; |
| 31 | + |
| 32 | +constkRequestUrl=Symbol('kRequestUrl'); |
| 33 | + |
| 34 | +// Convert a Headers object (Map<string, number | string | string[]>) to a plain object (Map<string, string>) |
| 35 | +functionconvertHeaderObject(headers={}){ |
| 36 | +letscheme; |
| 37 | +letauthority; |
| 38 | +letpath; |
| 39 | +letmethod; |
| 40 | +letstatusCode; |
| 41 | +letcharset; |
| 42 | +letmimeType; |
| 43 | +constdict={}; |
| 44 | + |
| 45 | +for(const{0: key,1: value}ofObjectEntries(headers)){ |
| 46 | +constlowerCasedKey=key.toLowerCase(); |
| 47 | + |
| 48 | +if(lowerCasedKey===HTTP2_HEADER_SCHEME){ |
| 49 | +scheme=value; |
| 50 | +}elseif(lowerCasedKey===HTTP2_HEADER_AUTHORITY){ |
| 51 | +authority=value; |
| 52 | +}elseif(lowerCasedKey===HTTP2_HEADER_PATH){ |
| 53 | +path=value; |
| 54 | +}elseif(lowerCasedKey===HTTP2_HEADER_METHOD){ |
| 55 | +method=value; |
| 56 | +}elseif(lowerCasedKey===HTTP2_HEADER_STATUS){ |
| 57 | +statusCode=value; |
| 58 | +}elseif(lowerCasedKey===HTTP2_HEADER_CONTENT_TYPE){ |
| 59 | +constresult=sniffMimeType(value); |
| 60 | +charset=result.charset; |
| 61 | +mimeType=result.mimeType; |
| 62 | +} |
| 63 | + |
| 64 | +if(typeofvalue==='string'){ |
| 65 | +dict[key]=value; |
| 66 | +}elseif(ArrayIsArray(value)){ |
| 67 | +if(lowerCasedKey===HTTP2_HEADER_COOKIE)dict[key]=value.join('; '); |
| 68 | +// ChromeDevTools frontend treats 'set-cookie' as a special case |
| 69 | +// https://github.com/ChromeDevTools/devtools-frontend/blob/4275917f84266ef40613db3c1784a25f902ea74e/front_end/core/sdk/NetworkRequest.ts#L1368 |
| 70 | +elseif(lowerCasedKey===HTTP2_HEADER_SET_COOKIE)dict[key]=value.join('\n'); |
| 71 | +elsedict[key]=value.join(', '); |
| 72 | +}else{ |
| 73 | +dict[key]=String(value); |
| 74 | +} |
| 75 | +} |
| 76 | + |
| 77 | +consturl=`${scheme}://${authority}${path}`; |
| 78 | + |
| 79 | +return[dict,url,method,statusCode,charset,mimeType]; |
| 80 | +} |
| 81 | + |
| 82 | +/** |
| 83 | + * When a client stream is created, emit Network.requestWillBeSent event. |
| 84 | + * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-requestWillBeSent |
| 85 | + * @param {{ stream: import('http2').ClientHttp2Stream, headers: object }} event |
| 86 | + */ |
| 87 | +functiononClientStreamCreated({ stream, headers }){ |
| 88 | +stream[kInspectorRequestId]=getNextRequestId(); |
| 89 | + |
| 90 | +const{0: convertedHeaderObject,1: url,2: method,4: charset}=convertHeaderObject(headers); |
| 91 | +stream[kRequestUrl]=url; |
| 92 | + |
| 93 | +Network.requestWillBeSent({ |
| 94 | +requestId: stream[kInspectorRequestId], |
| 95 | +timestamp: getMonotonicTime(), |
| 96 | +wallTime: DateNow(), |
| 97 | + charset, |
| 98 | +request: { |
| 99 | + url, |
| 100 | + method, |
| 101 | +headers: convertedHeaderObject, |
| 102 | +}, |
| 103 | +}); |
| 104 | +} |
| 105 | + |
| 106 | +/** |
| 107 | + * When a client stream errors, emit Network.loadingFailed event. |
| 108 | + * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-loadingFailed |
| 109 | + * @param {{ stream: import('http2').ClientHttp2Stream, error: any }} event |
| 110 | + */ |
| 111 | +functiononClientStreamError({ stream, error }){ |
| 112 | +if(typeofstream[kInspectorRequestId]!=='string'){ |
| 113 | +return; |
| 114 | +} |
| 115 | + |
| 116 | +Network.loadingFailed({ |
| 117 | +requestId: stream[kInspectorRequestId], |
| 118 | +timestamp: getMonotonicTime(), |
| 119 | +type: kResourceType.Other, |
| 120 | +errorText: error.message, |
| 121 | +}); |
| 122 | +} |
| 123 | + |
| 124 | +/** |
| 125 | + * When response headers are received, emit Network.responseReceived event. |
| 126 | + * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-responseReceived |
| 127 | + * @param {{ stream: import('http2').ClientHttp2Stream, headers: object }} event |
| 128 | + */ |
| 129 | +functiononClientStreamFinish({ stream, headers }){ |
| 130 | +if(typeofstream[kInspectorRequestId]!=='string'){ |
| 131 | +return; |
| 132 | +} |
| 133 | + |
| 134 | +const{0: convertedHeaderObject,3: statusCode,4: charset,5: mimeType}=convertHeaderObject(headers); |
| 135 | + |
| 136 | +Network.responseReceived({ |
| 137 | +requestId: stream[kInspectorRequestId], |
| 138 | +timestamp: getMonotonicTime(), |
| 139 | +type: kResourceType.Other, |
| 140 | +response: { |
| 141 | +url: stream[kRequestUrl], |
| 142 | +status: statusCode, |
| 143 | +statusText: '', |
| 144 | +headers: convertedHeaderObject, |
| 145 | + mimeType, |
| 146 | + charset, |
| 147 | +}, |
| 148 | +}); |
| 149 | +} |
| 150 | + |
| 151 | +/** |
| 152 | + * When user code completes consuming the response body, emit Network.loadingFinished event. |
| 153 | + * https://chromedevtools.github.io/devtools-protocol/1-3/Network/#event-loadingFinished |
| 154 | + * @param {{ stream: import('http2').ClientHttp2Stream }} event |
| 155 | + */ |
| 156 | +functiononClientStreamClose({ stream }){ |
| 157 | +if(typeofstream[kInspectorRequestId]!=='string'){ |
| 158 | +return; |
| 159 | +} |
| 160 | + |
| 161 | +if(stream.rstCode!==NGHTTP2_NO_ERROR){ |
| 162 | +// This is an error case, so only Network.loadingFailed should be emitted |
| 163 | +// which is already done by onClientStreamError(). |
| 164 | +return; |
| 165 | +} |
| 166 | + |
| 167 | +Network.loadingFinished({ |
| 168 | +requestId: stream[kInspectorRequestId], |
| 169 | +timestamp: getMonotonicTime(), |
| 170 | +}); |
| 171 | +} |
| 172 | + |
| 173 | +functionenable(){ |
| 174 | +dc.subscribe('http2.client.stream.created',onClientStreamCreated); |
| 175 | +dc.subscribe('http2.client.stream.error',onClientStreamError); |
| 176 | +dc.subscribe('http2.client.stream.finish',onClientStreamFinish); |
| 177 | +dc.subscribe('http2.client.stream.close',onClientStreamClose); |
| 178 | +} |
| 179 | + |
| 180 | +functiondisable(){ |
| 181 | +dc.unsubscribe('http2.client.stream.created',onClientStreamCreated); |
| 182 | +dc.unsubscribe('http2.client.stream.error',onClientStreamError); |
| 183 | +dc.unsubscribe('http2.client.stream.finish',onClientStreamFinish); |
| 184 | +dc.unsubscribe('http2.client.stream.close',onClientStreamClose); |
| 185 | +} |
| 186 | + |
| 187 | +module.exports={ |
| 188 | + enable, |
| 189 | + disable, |
| 190 | +}; |
0 commit comments