@@ -1005,6 +1005,90 @@ function connectionCorkNT(conn) {
1005
1005
conn . uncork ( ) ;
1006
1006
}
1007
1007
1008
+ OutgoingMessage . prototype . sendFile = function sendFile ( path , optsOrCallback , callback ) {
1009
+ let start = 0 ;
1010
+ let end = null ;
1011
+ let fd = null ;
1012
+
1013
+ if ( typeof optsOrCallback === 'function' ) {
1014
+ callback = optsOrCallback ;
1015
+ opts = null ;
1016
+ } else if ( opts != null ) {
1017
+ start = opts . start ?? 0 ;
1018
+ end = opts . end ;
1019
+ fd = opts . fd ;
1020
+ }
1021
+
1022
+ // TODO (fix): Validate path, fd, start, end
1023
+
1024
+ if ( fd ) {
1025
+ _sendFile ( fd , dst , start , end , callback ) ;
1026
+ } else {
1027
+ fs . open ( path , ( err , fd ) => {
1028
+ if ( err ) {
1029
+ callback ( err ) ;
1030
+ } else {
1031
+ _sendFile ( fd , dst , start , end , callback ) ;
1032
+ }
1033
+ } ) ;
1034
+ }
1035
+ }
1036
+
1037
+ function _sendFile ( fd , dst , start , end , callback ) {
1038
+ let pos = start ;
1039
+ let drain = null ;
1040
+
1041
+ const next = ( err , bytesRead , buf ) => {
1042
+ pos += bytesRead ;
1043
+
1044
+ if ( err != null && err . code === 'EAGAIN' ) {
1045
+ setImmediate ( ( ) => read ( buf ) ) ;
1046
+ return ;
1047
+ } else if ( err ) {
1048
+ // Fallthrough
1049
+ } else if ( bytesRead === 0 ) {
1050
+ // Fallthrough
1051
+ } else if ( dst . write ( bytesRead < buf . byteLength ? buf . subarray ( 0 , bytesRead ) : buf ) ) {
1052
+ // TODO (perf): Use FastBuffer instead of subarray.
1053
+ // TODO (perf): Is there a way we can re-use the buffer once we
1054
+ // know for sure it's been flushed through the socket?
1055
+ // TODO (perf): Avoid creating subarray and use offset parameter in fs.read
1056
+ // instead.
1057
+ read ( bytesRead < buf . byteLength < buf . subarray ( bytesRead ) , null ) ;
1058
+ return ;
1059
+ } else if ( dst . destroyed ) {
1060
+ // Fallthrough
1061
+ } else if ( drain == null ) {
1062
+ drain = read ;
1063
+ dst . on ( 'drain' , drain ) ;
1064
+ return ;
1065
+ }
1066
+
1067
+ if ( drain ) {
1068
+ dst . off ( 'drain' , drain ) ;
1069
+ }
1070
+
1071
+ fs . close ( fd , er => {
1072
+ if ( err && er ) {
1073
+ err = new AggregateError ( [ err , er ] ) ;
1074
+ }
1075
+ callback ( err , pos ) ;
1076
+ } ) ;
1077
+ } ;
1078
+
1079
+ // TODO (perf): Avoid closure...
1080
+ const read = ( buf ) => {
1081
+ const n = end ? Math . min ( end - pos , Buffer . poolSize ) : Buffer . poolSize ;
1082
+ if ( n <= 0 ) {
1083
+ next ( null , 0 , null )
1084
+ } else {
1085
+ fs . read ( fd , buf ?? Buffer . allocUnsafe ( n ) , 0 , n , pos , next ) ;
1086
+ }
1087
+ } ;
1088
+
1089
+ read ( null ) ;
1090
+ }
1091
+
1008
1092
OutgoingMessage . prototype . addTrailers = function addTrailers ( headers ) {
1009
1093
this . _trailer = '' ;
1010
1094
const keys = ObjectKeys ( headers ) ;
0 commit comments