11import * as _ from 'lodash' ;
22import * as WebSocket from 'isomorphic-ws' ;
3+ import { PassThrough } from 'stream' ;
34
45import {
56 getLocal ,
@@ -10,7 +11,8 @@ import {
1011 delay ,
1112 expect ,
1213 fetch ,
13- isNode
14+ isNode ,
15+ nodeOnly
1416} from "../../test-utils" ;
1517
1618describe ( "Rule event susbcriptions" , ( ) => {
@@ -227,6 +229,80 @@ describe("Rule event susbcriptions", () => {
227229 expect ( responseBodyEvent . rawBody . toString ( 'utf8' ) ) . to . equal ( 'Original response body' ) ;
228230 } ) ;
229231
232+
233+ it ( "should fire abort event if upstream body response fails" , async ( ) => {
234+ await remoteServer . forAnyRequest ( ) . thenCloseConnection ( ) ;
235+ const forwardingRule = await server . forAnyRequest ( ) . thenForwardTo ( remoteServer . url ) ;
236+
237+ const ruleEvents : RuleEvent < any > [ ] = [ ] ;
238+ await server . on ( 'rule-event' , ( e ) => ruleEvents . push ( e ) ) ;
239+
240+ await fetch ( server . url ) . catch ( ( ) => { } ) ;
241+
242+ await delay ( 100 ) ;
243+ expect ( ruleEvents . length ) . to . equal ( 3 ) ;
244+
245+ const requestId = ( await forwardingRule . getSeenRequests ( ) ) [ 0 ] . id ;
246+ ruleEvents . forEach ( ( event ) => {
247+ expect ( event . ruleId ) . to . equal ( forwardingRule . id ) ;
248+ expect ( event . requestId ) . to . equal ( requestId ) ;
249+ } ) ;
250+
251+ expect ( ruleEvents . map ( e => e . eventType ) ) . to . deep . equal ( [
252+ 'passthrough-request-head' ,
253+ 'passthrough-request-body' ,
254+ 'passthrough-abort'
255+ ] ) ;
256+
257+ const responseAbortEvent = ruleEvents [ 2 ] . eventData ;
258+ expect ( responseAbortEvent . error . name ) . to . equal ( 'Error' ) ;
259+ expect ( responseAbortEvent . error . message ) . to . equal ( 'socket hang up' ) ;
260+ } ) ;
261+
262+ nodeOnly ( ( ) => {
263+ it ( "should fire abort event if upstream body response fails" , async ( ) => {
264+ const stream = new PassThrough ( ) ;
265+ await remoteServer . forAnyRequest ( ) . thenStream ( 200 , stream ) ;
266+ const forwardingRule = await server . forAnyRequest ( ) . thenForwardTo ( remoteServer . url , {
267+ transformResponse : {
268+ replaceBody : 'replaced body'
269+ }
270+ } ) ;
271+
272+ const ruleEvents : RuleEvent < any > [ ] = [ ] ;
273+ await server . on ( 'rule-event' , ( e ) => ruleEvents . push ( e ) ) ;
274+
275+ const response = await fetch ( server . url ) ;
276+ expect ( response . status ) . to . equal ( 200 ) ;
277+
278+ stream . emit ( 'error' , new Error ( ) ) ; // Hard-fail part way through the body response
279+ await delay ( 10 ) ;
280+
281+ expect ( ruleEvents . length ) . to . equal ( 4 ) ;
282+
283+ const requestId = ( await forwardingRule . getSeenRequests ( ) ) [ 0 ] . id ;
284+ ruleEvents . forEach ( ( event ) => {
285+ expect ( event . ruleId ) . to . equal ( forwardingRule . id ) ;
286+ expect ( event . requestId ) . to . equal ( requestId ) ;
287+ } ) ;
288+
289+ expect ( ruleEvents . map ( e => e . eventType ) ) . to . deep . equal ( [
290+ 'passthrough-request-head' ,
291+ 'passthrough-request-body' ,
292+ 'passthrough-response-head' ,
293+ 'passthrough-abort'
294+ ] ) ;
295+
296+ const responseHeadEvent = ruleEvents [ 2 ] . eventData ;
297+ expect ( responseHeadEvent . statusCode ) . to . equal ( 200 ) ; // <-- Original status
298+
299+ const responseAbortEvent = ruleEvents [ 3 ] . eventData ;
300+ expect ( responseAbortEvent . error . name ) . to . equal ( 'Error' ) ;
301+ expect ( responseAbortEvent . error . code ) . to . equal ( 'ECONNRESET' ) ;
302+ expect ( responseAbortEvent . error . message ) . to . equal ( 'aborted' ) ;
303+ } ) ;
304+ } ) ;
305+
230306 it ( "should fire for proxied websockets" , async ( ) => {
231307 await remoteServer . forAnyWebSocket ( ) . thenPassivelyListen ( ) ;
232308 const forwardingRule = await server . forAnyWebSocket ( ) . thenForwardTo ( remoteServer . url ) ;
0 commit comments