@@ -4,6 +4,88 @@ var url = require('url');
44var hash = require ( 'crypto-js/md5' ) ;
55var isCreditCard = require ( 'card-validator' ) ;
66var assign = require ( 'lodash/assign' ) ;
7+ var zlib = require ( 'zlib' ) ;
8+ // Helper to check if buffer is binary (not utf8 string)
9+ function isBinaryBuffer ( buf ) {
10+ if ( ! Buffer . isBuffer ( buf ) ) return false ;
11+ // Heuristic: if buffer contains non-printable characters, treat as binary
12+ var text = buf . toString ( 'utf8' ) ;
13+ // If conversion to string produces replacement chars, it's likely binary
14+ return text . includes ( '\uFFFD' ) || / [ \x00 - \x08 \x0E - \x1F ] / . test ( text ) ;
15+ }
16+
17+ // Decompress if needed based on headers and heuristics
18+ function decompressIfNeeded ( body , headers ) {
19+ if ( ! body ) return body ;
20+ var buf = Buffer . isBuffer ( body ) ? body : Buffer . from ( body ) ;
21+ var encoding = ( headers && ( headers [ 'content-encoding' ] || headers [ 'Content-Encoding' ] ) ) || '' ;
22+ var server = ( headers && ( headers [ 'server' ] || headers [ 'Server' ] ) ) || '' ;
23+ var contentType = ( headers && ( headers [ 'content-type' ] || headers [ 'Content-Type' ] ) ) || '' ;
24+ var decompressed = null ;
25+ var isBinary = isBinaryBuffer ( buf ) ;
26+
27+ // Only decompress if content-type is json, xml, or text
28+ var isTextType = false ;
29+ if ( contentType ) {
30+ var ct = contentType . toLowerCase ( ) ;
31+ if ( ct . indexOf ( 'json' ) !== - 1 || ct . indexOf ( 'xml' ) !== - 1 || ct . indexOf ( 'text' ) !== - 1 ) {
32+ isTextType = true ;
33+ }
34+ }
35+ if ( ! isTextType ) {
36+ // Not a text type, skip decompression
37+ return body ;
38+ }
39+
40+ try {
41+ if ( encoding && isBinary ) {
42+ if ( encoding . indexOf ( 'gzip' ) !== - 1 ) {
43+ decompressed = zlib . gunzipSync ( buf ) ;
44+ } else if ( encoding . indexOf ( 'deflate' ) !== - 1 ) {
45+ decompressed = zlib . inflateSync ( buf ) ;
46+ } else if ( encoding . indexOf ( 'br' ) !== - 1 ) {
47+ decompressed = zlib . brotliDecompressSync ( buf ) ;
48+ }
49+ } else if ( isBinary ) {
50+ // Heuristic: try brotli if server is cloudflare
51+ if ( server . toLowerCase ( ) . indexOf ( 'cloudflare' ) !== - 1 ) {
52+ try {
53+ decompressed = zlib . brotliDecompressSync ( buf ) ;
54+ } catch ( e ) {
55+ // fallback below
56+ }
57+ }
58+ // Try gzip as fallback
59+ if ( ! decompressed ) {
60+ try {
61+ decompressed = zlib . gunzipSync ( buf ) ;
62+ } catch ( e ) { }
63+ }
64+ // Try deflate as fallback
65+ if ( ! decompressed ) {
66+ try {
67+ decompressed = zlib . inflateSync ( buf ) ;
68+ } catch ( e ) { }
69+ }
70+ // As last resort, try brotli if not already tried
71+ if ( ! decompressed && server . toLowerCase ( ) . indexOf ( 'cloudflare' ) === - 1 ) {
72+ try {
73+ decompressed = zlib . brotliDecompressSync ( buf ) ;
74+ } catch ( e ) {
75+ // fallback below
76+ }
77+ }
78+ }
79+ if ( decompressed ) {
80+ // Try to return as string if possible
81+ return decompressed . toString ( 'utf8' ) ;
82+ }
83+ } catch ( err ) {
84+ // fallback below
85+ }
86+ // If not decompressed, return original
87+ return body ;
88+ }
789
890var logMessage = function ( debug , functionName , message , details ) {
991 if ( debug ) {
@@ -18,8 +100,7 @@ var logMessage = function (debug, functionName, message, details) {
18100 finalMessage = message + '\n' + JSON . stringify ( details ) ;
19101 }
20102 }
21- } catch ( err ) {
22- }
103+ } catch ( err ) { }
23104 console . log ( 'MOESIF: [' + functionName + '] ' + finalMessage ) ;
24105 }
25106} ;
@@ -38,11 +119,7 @@ function _hashSensitive(jsonBody, debug) {
38119 if ( itemType === 'number' || itemType === 'string' ) {
39120 var creditCardCheck = isCreditCard . number ( '' + item ) ;
40121 if ( creditCardCheck . isValid ) {
41- logMessage (
42- debug ,
43- 'hashSensitive' ,
44- 'looks like a credit card, performing hash.'
45- ) ;
122+ logMessage ( debug , 'hashSensitive' , 'looks like a credit card, performing hash.' ) ;
46123 return hash ( item ) . toString ( ) ;
47124 }
48125 }
@@ -58,24 +135,13 @@ function _hashSensitive(jsonBody, debug) {
58135 var innerVal = jsonBody [ key ] ;
59136 var innerValType = typeof innerVal ;
60137
61- if (
62- key . toLowerCase ( ) . indexOf ( 'password' ) !== - 1 &&
63- typeof innerVal === 'string'
64- ) {
65- logMessage (
66- debug ,
67- 'hashSensitive' ,
68- 'key is password, so hashing the value.'
69- ) ;
138+ if ( key . toLowerCase ( ) . indexOf ( 'password' ) !== - 1 && typeof innerVal === 'string' ) {
139+ logMessage ( debug , 'hashSensitive' , 'key is password, so hashing the value.' ) ;
70140 returnObject [ key ] = hash ( jsonBody [ key ] ) . toString ( ) ;
71141 } else if ( innerValType === 'number' || innerValType === 'string' ) {
72142 var creditCardCheck = isCreditCard . number ( '' + innerVal ) ;
73143 if ( creditCardCheck . isValid ) {
74- logMessage (
75- debug ,
76- 'hashSensitive' ,
77- 'a field looks like credit card, performing hash.'
78- ) ;
144+ logMessage ( debug , 'hashSensitive' , 'a field looks like credit card, performing hash.' ) ;
79145 returnObject [ key ] = hash ( jsonBody [ key ] ) . toString ( ) ;
80146 } else {
81147 returnObject [ key ] = _hashSensitive ( innerVal , debug ) ;
@@ -97,10 +163,10 @@ function _getUrlFromRequestOptions(options, request) {
97163 options = url . parse ( options ) ;
98164 } else {
99165 // Avoid modifying the original options object.
100- let originalOptions = options ;
166+ var originalOptions = options ;
101167 options = { } ;
102168 if ( originalOptions ) {
103- Object . keys ( originalOptions ) . forEach ( ( key ) => {
169+ Object . keys ( originalOptions ) . forEach ( function ( key ) {
104170 options [ key ] = originalOptions [ key ] ;
105171 } ) ;
106172 }
@@ -130,8 +196,7 @@ function _getUrlFromRequestOptions(options, request) {
130196 }
131197
132198 // Mix in default values used by http.request and others
133- options . protocol =
134- options . protocol || ( request . agent && request . agent . protocol ) || undefined ;
199+ options . protocol = options . protocol || ( request . agent && request . agent . protocol ) || undefined ;
135200 options . hostname = options . hostname || 'localhost' ;
136201
137202 return url . format ( options ) ;
@@ -156,15 +221,15 @@ function isPlainObject(value) {
156221 if ( Object . prototype . toString . call ( value ) !== '[object Object]' ) {
157222 return false ;
158223 }
159- const prototype = Object . getPrototypeOf ( value ) ;
224+ var prototype = Object . getPrototypeOf ( value ) ;
160225 return prototype === null || prototype === Object . prototype ;
161226}
162227
163228function isPlainObjectOrPrimitive ( value ) {
164229 if ( isPlainObject ( value ) ) {
165230 return true ;
166231 }
167- const type = typeof value ;
232+ var type = typeof value ;
168233 return (
169234 type === 'number' ||
170235 type === 'boolean' ||
@@ -184,11 +249,7 @@ function _safeJsonParse(body) {
184249 transferEncoding : undefined ,
185250 } ;
186251 }
187- if (
188- Array . isArray ( body ) &&
189- body . every &&
190- body . every ( isPlainObjectOrPrimitive )
191- ) {
252+ if ( Array . isArray ( body ) && body . every && body . every ( isPlainObjectOrPrimitive ) ) {
192253 return {
193254 body : body ,
194255 transferEncoding : undefined ,
@@ -241,6 +302,7 @@ function getRequestHeaders(requestOptions, request) {
241302 return { } ;
242303}
243304
305+ // this is mostly for outgoing data capture.
244306function _getEventModelFromRequestAndResponse (
245307 requestOptions ,
246308 request ,
@@ -260,17 +322,23 @@ function _getEventModelFromRequestAndResponse(
260322 logData . request . headers = getRequestHeaders ( requestOptions , request ) ;
261323 logData . request . time = requestTime ;
262324
263- if ( requestBody ) {
264- var isReqBodyMaybeJson = _startWithJson ( requestBody ) ;
325+ // Decompress requestBody if needed
326+ var reqHeaders = logData . request . headers || { } ;
327+ var decompressedRequestBody = requestBody
328+ ? decompressIfNeeded ( requestBody , reqHeaders )
329+ : requestBody ;
330+
331+ if ( decompressedRequestBody ) {
332+ var isReqBodyMaybeJson = _startWithJson ( decompressedRequestBody ) ;
265333
266334 if ( isReqBodyMaybeJson ) {
267- var parsedReqBody = _safeJsonParse ( requestBody ) ;
335+ var parsedReqBody = _safeJsonParse ( decompressedRequestBody ) ;
268336
269337 logData . request . transferEncoding = parsedReqBody . transferEncoding ;
270338 logData . request . body = parsedReqBody . body ;
271339 } else {
272340 logData . request . transferEncoding = 'base64' ;
273- logData . request . body = _bodyToBase64 ( requestBody ) ;
341+ logData . request . body = _bodyToBase64 ( decompressedRequestBody ) ;
274342 }
275343 }
276344
@@ -279,17 +347,23 @@ function _getEventModelFromRequestAndResponse(
279347 logData . response . status = ( response && ( response . statusCode || response . status ) ) || 599 ;
280348 logData . response . headers = assign ( { } , ( response && response . headers ) || { } ) ;
281349
282- if ( responseBody ) {
283- var isResBodyMaybeJson = _startWithJson ( responseBody ) ;
350+ // Decompress responseBody if needed
351+ var resHeaders = logData . response . headers || { } ;
352+ var decompressedResponseBody = responseBody
353+ ? decompressIfNeeded ( responseBody , resHeaders )
354+ : responseBody ;
355+
356+ if ( decompressedResponseBody ) {
357+ var isResBodyMaybeJson = _startWithJson ( decompressedResponseBody ) ;
284358
285359 if ( isResBodyMaybeJson ) {
286- var parsedResBody = _safeJsonParse ( responseBody ) ;
360+ var parsedResBody = _safeJsonParse ( decompressedResponseBody ) ;
287361
288362 logData . response . transferEncoding = parsedResBody . transferEncoding ;
289363 logData . response . body = parsedResBody . body ;
290364 } else {
291365 logData . response . transferEncoding = 'base64' ;
292- logData . response . body = _bodyToBase64 ( responseBody ) ;
366+ logData . response . body = _bodyToBase64 ( decompressedResponseBody ) ;
293367 }
294368 }
295369
@@ -302,10 +376,7 @@ function isJsonHeader(msg) {
302376 if ( headers [ 'content-encoding' ] ) {
303377 return false ;
304378 }
305- if (
306- headers [ 'content-type' ] &&
307- headers [ 'content-type' ] . indexOf ( 'json' ) >= 0
308- ) {
379+ if ( headers [ 'content-type' ] && headers [ 'content-type' ] . indexOf ( 'json' ) >= 0 ) {
309380 return true ;
310381 }
311382 }
@@ -314,7 +385,7 @@ function isJsonHeader(msg) {
314385
315386function approximateObjectSize ( obj ) {
316387 try {
317- const str = JSON . stringify ( obj ) ;
388+ var str = JSON . stringify ( obj ) ;
318389 return str . length ;
319390 } catch ( err ) {
320391 return 0 ;
@@ -347,9 +418,7 @@ function appendChunk(buf, chunk) {
347418 }
348419 } else if ( typeof chunk === 'string' ) {
349420 try {
350- return buf
351- ? Buffer . concat ( [ buf , Buffer . from ( chunk ) ] )
352- : Buffer . from ( chunk ) ;
421+ return buf ? Buffer . concat ( [ buf , Buffer . from ( chunk ) ] ) : Buffer . from ( chunk ) ;
353422 } catch ( err ) {
354423 return buf ;
355424 }
@@ -401,14 +470,14 @@ function getReqHeaders(req) {
401470}
402471
403472function generateUUIDv4 ( ) {
404- let timeNow = new Date ( ) . getTime ( ) ; // Current time in milliseconds
405- let timeRandom = timeNow + Math . random ( ) ; // Combine time and random number
406-
407- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' . replace ( / [ x y ] / g, function ( c ) {
408- let r = ( timeRandom + Math . random ( ) * 16 ) % 16 | 0 ; // Mix in timeRandom for extra entropy
409- timeRandom = Math . floor ( timeRandom / 16 ) ;
410- let v = c === 'x' ? r : ( r & 0x3 | 0x8 ) ; // Handle the fixed bits for UUIDv4
411- return v . toString ( 16 ) ;
473+ let timeNow = new Date ( ) . getTime ( ) ; // Current time in milliseconds
474+ let timeRandom = timeNow + Math . random ( ) ; // Combine time and random number
475+
476+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' . replace ( / [ x y ] / g, function ( c ) {
477+ let r = ( timeRandom + Math . random ( ) * 16 ) % 16 | 0 ; // Mix in timeRandom for extra entropy
478+ timeRandom = Math . floor ( timeRandom / 16 ) ;
479+ let v = c === 'x' ? r : ( r & 0x3 ) | 0x8 ; // Handle the fixed bits for UUIDv4
480+ return v . toString ( 16 ) ;
412481 } ) ;
413482}
414483
@@ -428,4 +497,5 @@ module.exports = {
428497 ensureToString : ensureToString ,
429498 getReqHeaders : getReqHeaders ,
430499 generateUUIDv4 : generateUUIDv4 ,
500+ decompressIfNeeded : decompressIfNeeded ,
431501} ;
0 commit comments