@@ -140,7 +140,15 @@ function isSerializedBuffer(obj: any): obj is SerializedBuffer {
140
140
}
141
141
142
142
export interface RequestHandler extends RequestHandlerDefinition {
143
- handle ( request : OngoingRequest , response : OngoingResponse ) : Promise < void > ;
143
+ handle (
144
+ request : OngoingRequest ,
145
+ response : OngoingResponse ,
146
+ options : RequestHandlerOptions
147
+ ) : Promise < void > ;
148
+ }
149
+
150
+ export interface RequestHandlerOptions {
151
+ emitEventCallback ?: ( type : string , event : unknown ) => void ;
144
152
}
145
153
146
154
export class SimpleHandler extends SimpleHandlerDefinition {
@@ -380,7 +388,11 @@ export class PassThroughHandler extends PassThroughHandlerDefinition {
380
388
return this . _trustedCACertificates ;
381
389
}
382
390
383
- async handle ( clientReq : OngoingRequest , clientRes : OngoingResponse ) {
391
+ async handle (
392
+ clientReq : OngoingRequest ,
393
+ clientRes : OngoingResponse ,
394
+ options : RequestHandlerOptions
395
+ ) {
384
396
// Don't let Node add any default standard headers - we want full control
385
397
dropDefaultHeaders ( clientRes ) ;
386
398
@@ -735,8 +747,24 @@ export class PassThroughHandler extends PassThroughHandlerDefinition {
735
747
let serverStatusCode = serverRes . statusCode ! ;
736
748
let serverStatusMessage = serverRes . statusMessage
737
749
let serverRawHeaders = pairFlatRawHeaders ( serverRes . rawHeaders ) ;
750
+
751
+ // This is only set if we need to read the body here, for a callback or similar. If so,
752
+ // we keep the buffer in case we need it afterwards (if the cb doesn't replace it).
753
+ let originalBody : Buffer | undefined ;
754
+
755
+ // This is set when we override the body data. Note that this doesn't mean we actually
756
+ // read & buffered the original data! With a fixed replacement body we can skip that.
738
757
let resBodyOverride : Uint8Array | undefined ;
739
758
759
+ if ( options . emitEventCallback ) {
760
+ options . emitEventCallback ( 'passthrough-response-head' , {
761
+ statusCode : serverStatusCode ,
762
+ statusMessage : serverStatusMessage ,
763
+ httpVersion : serverRes . httpVersion ,
764
+ rawHeaders : serverRawHeaders
765
+ } ) ;
766
+ }
767
+
740
768
if ( isH2Downstream ) {
741
769
serverRawHeaders = h1HeadersToH2 ( serverRawHeaders ) ;
742
770
}
@@ -775,8 +803,8 @@ export class PassThroughHandler extends PassThroughHandlerDefinition {
775
803
} else if ( replaceBodyFromFile ) {
776
804
resBodyOverride = await fs . readFile ( replaceBodyFromFile ) ;
777
805
} else if ( updateJsonBody ) {
778
- const rawBody = await streamToBuffer ( serverRes ) ;
779
- const realBody = buildBodyReader ( rawBody , serverRes . headers ) ;
806
+ originalBody = await streamToBuffer ( serverRes ) ;
807
+ const realBody = buildBodyReader ( originalBody , serverRes . headers ) ;
780
808
781
809
if ( await realBody . getJson ( ) === undefined ) {
782
810
throw new Error ( "Can't transform non-JSON response body" ) ;
@@ -795,26 +823,27 @@ export class PassThroughHandler extends PassThroughHandlerDefinition {
795
823
796
824
resBodyOverride = asBuffer ( JSON . stringify ( updatedBody ) ) ;
797
825
} else if ( matchReplaceBody ) {
798
- const rawBody = await streamToBuffer ( serverRes ) ;
799
- const realBody = buildBodyReader ( rawBody , serverRes . headers ) ;
826
+ originalBody = await streamToBuffer ( serverRes ) ;
827
+ const realBody = buildBodyReader ( originalBody , serverRes . headers ) ;
800
828
801
- const originalBody = await realBody . getText ( ) ;
802
- if ( originalBody === undefined ) {
829
+ const originalBodyText = await realBody . getText ( ) ;
830
+ if ( originalBodyText === undefined ) {
803
831
throw new Error ( "Can't match & replace non-decodeable response body" ) ;
804
832
}
805
833
806
- let replacedBody = originalBody ;
834
+ let replacedBody = originalBodyText ;
807
835
for ( let [ match , result ] of matchReplaceBody ) {
808
836
replacedBody = replacedBody ! . replace ( match , result ) ;
809
837
}
810
838
811
- if ( replacedBody !== originalBody ) {
839
+ if ( replacedBody !== originalBodyText ) {
812
840
resBodyOverride = asBuffer ( replacedBody ) ;
813
841
}
814
842
}
815
843
816
844
if ( resBodyOverride ) {
817
- // We always re-encode the body to match the resulting content-encoding header:
845
+ // In the above cases, the overriding data is assumed to always be in decoded form,
846
+ // so we re-encode the body to match the resulting content-encoding header:
818
847
resBodyOverride = await encodeBodyBuffer (
819
848
resBodyOverride ,
820
849
serverHeaders
@@ -833,9 +862,8 @@ export class PassThroughHandler extends PassThroughHandlerDefinition {
833
862
serverRawHeaders = objectHeadersToRaw ( serverHeaders ) ;
834
863
} else if ( this . beforeResponse ) {
835
864
let modifiedRes : CallbackResponseResult | void ;
836
- let body : Buffer ;
837
865
838
- body = await streamToBuffer ( serverRes ) ;
866
+ originalBody = await streamToBuffer ( serverRes ) ;
839
867
let serverHeaders = rawHeadersToObject ( serverRawHeaders ) ;
840
868
841
869
modifiedRes = await this . beforeResponse ( {
@@ -844,19 +872,14 @@ export class PassThroughHandler extends PassThroughHandlerDefinition {
844
872
statusMessage : serverRes . statusMessage ,
845
873
headers : serverHeaders ,
846
874
rawHeaders : _ . cloneDeep ( serverRawHeaders ) ,
847
- body : buildBodyReader ( body , serverHeaders )
875
+ body : buildBodyReader ( originalBody , serverHeaders )
848
876
} ) ;
849
877
850
878
if ( modifiedRes === 'close' ) {
851
- // Dump the real response data and kill the client socket:
852
- serverRes . resume ( ) ;
853
879
( clientReq as any ) . socket . end ( ) ;
854
880
throw new AbortError ( 'Connection closed intentionally by rule' ) ;
855
881
} else if ( modifiedRes === 'reset' ) {
856
882
requireSocketResetSupport ( ) ;
857
-
858
- // Dump the real response data and kill the client socket:
859
- serverRes . resume ( ) ;
860
883
resetOrDestroy ( clientReq ) ;
861
884
throw new AbortError ( 'Connection reset intentionally by rule' ) ;
862
885
}
@@ -880,11 +903,6 @@ export class PassThroughHandler extends PassThroughHandlerDefinition {
880
903
modifiedRes ?. headers ,
881
904
method === 'HEAD' // HEAD responses are allowed mismatched content-length
882
905
) ;
883
- } else {
884
- // If you don't specify a body override, we need to use the real
885
- // body anyway, because as we've read it already streaming it to
886
- // the response won't work
887
- resBodyOverride = body ;
888
906
}
889
907
890
908
serverRawHeaders = objectHeadersToRaw ( serverHeaders ) ;
@@ -910,14 +928,40 @@ export class PassThroughHandler extends PassThroughHandlerDefinition {
910
928
if ( resBodyOverride ) {
911
929
// Return the override data to the client:
912
930
clientRes . end ( resBodyOverride ) ;
913
- // Dump the real response data:
914
- serverRes . resume ( ) ;
915
931
932
+ // Dump the real response data, in case that body wasn't read yet:
933
+ serverRes . resume ( ) ;
934
+ resolve ( ) ;
935
+ } else if ( originalBody ) {
936
+ // If the original body was read, and not overridden, then send it
937
+ // onward directly:
938
+ clientRes . end ( originalBody ) ;
916
939
resolve ( ) ;
917
940
} else {
941
+ // Otherwise the body hasn't been read - stream it live:
918
942
serverRes . pipe ( clientRes ) ;
919
943
serverRes . once ( 'end' , resolve ) ;
920
944
}
945
+
946
+ if ( options . emitEventCallback ) {
947
+ if ( ! ! resBodyOverride ) {
948
+ ( originalBody
949
+ ? Promise . resolve ( originalBody )
950
+ : streamToBuffer ( serverRes )
951
+ ) . then ( ( upstreamBody ) => {
952
+ options . emitEventCallback ! ( 'passthrough-response-body' , {
953
+ overridden : true ,
954
+ rawBody : upstreamBody
955
+ } ) ;
956
+ } ) ;
957
+ } else {
958
+ options . emitEventCallback ( 'passthrough-response-body' , {
959
+ overridden : false
960
+ // We don't bother buffering & re-sending the body if
961
+ // it's the same as the one being sent to the client.
962
+ } ) ;
963
+ }
964
+ }
921
965
} ) ( ) . catch ( reject ) ) ;
922
966
923
967
serverReq . once ( 'socket' , ( socket : net . Socket ) => {
@@ -979,6 +1023,30 @@ export class PassThroughHandler extends PassThroughHandlerDefinition {
979
1023
980
1024
// For similar reasons, we don't want any buffering on outgoing data at all if possible:
981
1025
serverReq . setNoDelay ( true ) ;
1026
+
1027
+ // Fire rule events, to allow in-depth debugging of upstream traffic & modifications,
1028
+ // so anybody interested can see _exactly_ what we're sending upstream here:
1029
+ if ( options . emitEventCallback ) {
1030
+ options . emitEventCallback ( 'passthrough-request-head' , {
1031
+ method,
1032
+ protocol : protocol ! . replace ( / : $ / , '' ) ,
1033
+ hostname,
1034
+ port,
1035
+ path,
1036
+ rawHeaders
1037
+ } ) ;
1038
+
1039
+ if ( ! ! reqBodyOverride ) {
1040
+ options . emitEventCallback ( 'passthrough-request-body' , {
1041
+ overridden : true ,
1042
+ rawBody : reqBodyOverride
1043
+ } ) ;
1044
+ } else {
1045
+ options . emitEventCallback ! ( 'passthrough-request-body' , {
1046
+ overridden : false
1047
+ } ) ;
1048
+ }
1049
+ }
982
1050
} ) ( ) . catch ( reject )
983
1051
) . catch ( ( e : ErrorLike ) => {
984
1052
// All errors anywhere above (thrown or from explicit reject()) should end up here.
0 commit comments