@@ -14,12 +14,24 @@ async function toArray(chunks) {
1414 return arr ;
1515}
1616
17- const chunks = await toArray ( serializer ( [
18- { type : 'test:diagnostic' , data : { nesting : 0 , details : { } , message : 'diagnostic' } } ,
19- ] ) ) ;
17+ const diagnosticEvent = {
18+ type : 'test:diagnostic' ,
19+ data : { nesting : 0 , details : { } , message : 'diagnostic' } ,
20+ } ;
21+ const chunks = await toArray ( serializer ( [ diagnosticEvent ] ) ) ;
2022const defaultSerializer = new DefaultSerializer ( ) ;
2123defaultSerializer . writeHeader ( ) ;
2224const headerLength = defaultSerializer . releaseBuffer ( ) . length ;
25+ const headerOnly = Buffer . from ( [ 0xff , 0x0f ] ) ;
26+ const oversizedLengthHeader = Buffer . from ( [ 0xff , 0x0f , 0x7f , 0xff , 0xff , 0xff ] ) ;
27+ const truncatedLengthHeader = Buffer . from ( [ 0xff , 0x0f , 0x00 , 0x01 , 0x00 , 0x00 ] ) ;
28+
29+ function collectStdout ( reported ) {
30+ return reported
31+ . filter ( ( event ) => event . type === 'test:stdout' )
32+ . map ( ( event ) => event . data . message )
33+ . join ( '' ) ;
34+ }
2335
2436describe ( 'v8 deserializer' , common . mustCall ( ( ) => {
2537 let fileTest ;
@@ -56,35 +68,83 @@ describe('v8 deserializer', common.mustCall(() => {
5668
5769 it ( 'should deserialize a serialized chunk' , async ( ) => {
5870 const reported = await collectReported ( chunks ) ;
59- assert . deepStrictEqual ( reported , [
60- { data : { nesting : 0 , details : { } , message : 'diagnostic' } , type : 'test:diagnostic' } ,
61- ] ) ;
71+ assert . deepStrictEqual ( reported , [ diagnosticEvent ] ) ;
6272 } ) ;
6373
6474 it ( 'should deserialize a serialized chunk after non-serialized chunk' , async ( ) => {
6575 const reported = await collectReported ( [ Buffer . concat ( [ Buffer . from ( 'unknown' ) , ...chunks ] ) ] ) ;
6676 assert . deepStrictEqual ( reported , [
6777 { data : { __proto__ : null , file : 'filetest' , message : 'unknown' } , type : 'test:stdout' } ,
68- { data : { nesting : 0 , details : { } , message : 'diagnostic' } , type : 'test:diagnostic' } ,
78+ diagnosticEvent ,
6979 ] ) ;
7080 } ) ;
7181
7282 it ( 'should deserialize a serialized chunk before non-serialized output' , async ( ) => {
7383 const reported = await collectReported ( [ Buffer . concat ( [ ...chunks , Buffer . from ( 'unknown' ) ] ) ] ) ;
7484 assert . deepStrictEqual ( reported , [
75- { data : { nesting : 0 , details : { } , message : 'diagnostic' } , type : 'test:diagnostic' } ,
85+ diagnosticEvent ,
7686 { data : { __proto__ : null , file : 'filetest' , message : 'unknown' } , type : 'test:stdout' } ,
7787 ] ) ;
7888 } ) ;
7989
90+ it ( 'should not hang when buffer starts with v8Header followed by oversized length' , async ( ) => {
91+ // Regression test for https://github.com/nodejs/node/issues/62693
92+ // FF 0F is the v8 serializer header; the next 4 bytes are read as a
93+ // big-endian message size. 0x7FFFFFFF far exceeds any actual buffer
94+ // size, causing #processRawBuffer to make no progress and
95+ // #drainRawBuffer to loop forever without the no-progress guard.
96+ const reported = await collectReported ( [ oversizedLengthHeader ] ) ;
97+ assert ( reported . every ( ( event ) => event . type === 'test:stdout' ) ) ;
98+ assert . strictEqual ( collectStdout ( reported ) , oversizedLengthHeader . toString ( 'utf8' ) ) ;
99+ } ) ;
100+
101+ it ( 'should flush incomplete v8 frame as stdout and keep prior valid data' , async ( ) => {
102+ // A valid non-serialized message followed by bytes that look like
103+ // a v8 header with a truncated/oversized length.
104+ const reported = await collectReported ( [
105+ Buffer . from ( 'hello' ) ,
106+ truncatedLengthHeader ,
107+ ] ) ;
108+ assert . strictEqual ( collectStdout ( reported ) , `hello${ truncatedLengthHeader . toString ( 'utf8' ) } ` ) ;
109+ } ) ;
110+
111+ it ( 'should flush v8Header-only bytes as stdout when stream ends' , async ( ) => {
112+ // Just the two-byte v8 header with no size field at all.
113+ const reported = await collectReported ( [ headerOnly ] ) ;
114+ assert ( reported . every ( ( event ) => event . type === 'test:stdout' ) ) ;
115+ assert . strictEqual ( collectStdout ( reported ) , headerOnly . toString ( 'utf8' ) ) ;
116+ } ) ;
117+
118+ it ( 'should resync and parse valid messages after false v8 header' , async ( ) => {
119+ // A false v8 header (FF 0F + oversized length) followed by a
120+ // legitimate serialized message. The parser must skip the corrupt
121+ // bytes and still deserialize the real message.
122+ const reported = await collectReported ( [
123+ oversizedLengthHeader ,
124+ ...chunks ,
125+ ] ) ;
126+ assert . deepStrictEqual ( reported . at ( - 1 ) , diagnosticEvent ) ;
127+ assert . strictEqual ( reported . filter ( ( event ) => event . type === 'test:diagnostic' ) . length , 1 ) ;
128+ assert . strictEqual ( collectStdout ( reported ) , oversizedLengthHeader . toString ( 'utf8' ) ) ;
129+ } ) ;
130+
131+ it ( 'should preserve a false v8 header split across chunks' , async ( ) => {
132+ const reported = await collectReported ( [
133+ oversizedLengthHeader . subarray ( 0 , 1 ) ,
134+ oversizedLengthHeader . subarray ( 1 ) ,
135+ ] ) ;
136+ assert ( reported . every ( ( event ) => event . type === 'test:stdout' ) ) ;
137+ assert . strictEqual ( collectStdout ( reported ) , oversizedLengthHeader . toString ( 'utf8' ) ) ;
138+ } ) ;
139+
80140 const headerPosition = headerLength * 2 + 4 ;
81141 for ( let i = 0 ; i < headerPosition + 5 ; i ++ ) {
82142 const message = `should deserialize a serialized message split into two chunks {...${ i } ,${ i + 1 } ...}` ;
83143 it ( message , async ( ) => {
84144 const data = chunks [ 0 ] ;
85145 const reported = await collectReported ( [ data . subarray ( 0 , i ) , data . subarray ( i ) ] ) ;
86146 assert . deepStrictEqual ( reported , [
87- { data : { nesting : 0 , details : { } , message : 'diagnostic' } , type : 'test:diagnostic' } ,
147+ diagnosticEvent ,
88148 ] ) ;
89149 } ) ;
90150
@@ -96,7 +156,7 @@ describe('v8 deserializer', common.mustCall(() => {
96156 ] ) ;
97157 assert . deepStrictEqual ( reported , [
98158 { data : { __proto__ : null , file : 'filetest' , message : 'unknown' } , type : 'test:stdout' } ,
99- { data : { nesting : 0 , details : { } , message : 'diagnostic' } , type : 'test:diagnostic' } ,
159+ diagnosticEvent ,
100160 { data : { __proto__ : null , file : 'filetest' , message : 'unknown' } , type : 'test:stdout' } ,
101161 ] ) ;
102162 }
0 commit comments