@@ -14,11 +14,6 @@ const http = require('http');
1414const url = require ( 'url' ) ;
1515const zlib = require ( 'zlib' ) ;
1616
17- // external libs
18- const multipart_parse = require ( 'parse-multipart-data' ) . parse ;
19- const querystring_parse = require ( 'querystring' ) . decode ;
20- const request_ip_get = require ( 'ipware' ) ( ) . get_ip ;
21-
2217// constants
2318const COMPRESS_METHOD_NONE = 0 ;
2419const COMPRESS_METHOD_GZIP = 1 ;
@@ -720,7 +715,7 @@ const parse_old_map = data => (
720715 entry . length > 0 &&
721716 entry . charCodeAt ( 0 ) !== 35
722717 )
723- . map ( entry => entry . split ( ':' ) )
718+ . map ( entry => entry . split ( ':' , 2 ) )
724719 )
725720)
726721const config_path_check = ( path , allow_empty = false ) => {
@@ -784,10 +779,190 @@ const spam = (type, data) => {
784779 }
785780}
786781
782+ const multipart_parse = ( body_raw , boundary , result ) => {
783+ boundary = Buffer . from ( boundary ) ;
784+ if ( boundary [ 0 ] === 34 ) {
785+ boundary = boundary . subarray ( 1 , - 1 ) ;
786+ }
787+ const boundary_length = boundary . length ;
788+ /// -- + boundary + \r\n
789+ const boundary_length_full = boundary_length + 4 ;
790+ /// last possible index for full boundary
791+ const boundary_index_limit = body_raw . length - boundary_length_full ;
792+ let index_start = 0 ;
793+ let index_end = 0 ;
794+ while (
795+ // find next boundary
796+ (
797+ index_end = body_raw . indexOf ( 45 , index_end ) // -
798+ ) >= 0 &&
799+ index_end <= boundary_index_limit
800+ ) {
801+ // check if it actually is a boundary
802+ if (
803+ body_raw [ index_end + 1 ] !== 45 || // -
804+ index_end > 0 && body_raw [ index_end - 2 ] !== 13 || // \r
805+ index_end > 0 && body_raw [ index_end - 1 ] !== 10 || // \n
806+ body_raw . compare (
807+ boundary ,
808+ 0 ,
809+ boundary_length ,
810+ index_end + 2 ,
811+ index_end + 2 + boundary_length
812+ ) !== 0
813+ ) {
814+ ++ index_end ;
815+ continue ;
816+ }
817+ // skip first boundary
818+ if ( index_start > 0 ) {
819+ if ( index_end - index_start < 20 ) {
820+ throw 'part too short' ;
821+ }
822+ // find end of header
823+ let index_header_end = index_start ;
824+ while (
825+ (
826+ index_header_end = body_raw . indexOf ( 13 , index_header_end ) // \r
827+ ) >= 0 &&
828+ body_raw [ index_header_end + 1 ] !== 10 || // \n
829+ body_raw [ index_header_end + 2 ] !== 13 || // \r
830+ body_raw [ index_header_end + 3 ] !== 10 // \n
831+ ) {
832+ ++ index_header_end ;
833+ }
834+ if ( index_header_end < 0 ) {
835+ throw 'part header end not found' ;
836+ }
837+ // parse header
838+ let header_disposition = '' ;
839+ let header_content_type = '' ;
840+ for (
841+ const header_line of
842+ body_raw
843+ . toString (
844+ 'utf8' ,
845+ index_start ,
846+ index_header_end
847+ )
848+ . split ( '\r\n' , 2 )
849+ ) {
850+ if ( ! header_line . startsWith ( 'Content-' ) ) {
851+ throw 'illegal part header' ;
852+ }
853+ const [ name , value ] = header_line . slice ( 8 ) . split ( ': ' , 2 ) ;
854+ if ( name === 'Disposition' ) {
855+ if ( ! value . startsWith ( 'form-data; ' ) ) {
856+ throw 'illegal disposition part header' ;
857+ }
858+ header_disposition = value . slice ( 11 ) ;
859+ }
860+ else if ( name === 'Type' ) {
861+ header_content_type = value ;
862+ }
863+ // else if (name === 'Transfer-Encoding') {}
864+ else {
865+ throw 'illegal part header' ;
866+ }
867+ }
868+ if ( ! header_disposition ) {
869+ throw 'part disposition header missing' ;
870+ }
871+ let header_disposition_name = '' ;
872+ let header_disposition_filename = '' ;
873+ for ( const header_disposition_entry of header_disposition . split ( '; ' ) ) {
874+ let [ name , value ] = header_disposition_entry . split ( '=' , 2 ) ;
875+ if ( value . charCodeAt ( 0 ) === 34 ) {
876+ value = value . slice ( 1 , - 1 ) ;
877+ }
878+ if ( name === 'name' ) {
879+ header_disposition_name = value ;
880+ }
881+ else if ( name === 'filename' ) {
882+ header_disposition_filename = value ;
883+ }
884+ else {
885+ throw 'illegal disposition part header' ;
886+ }
887+ }
888+ if ( ! header_disposition_name ) {
889+ throw 'part name header missing' ;
890+ }
891+ const part_result = (
892+ header_content_type
893+ ? {
894+ filename : header_disposition_filename ,
895+ data : body_raw . subarray (
896+ index_header_end + 4 ,
897+ index_end - 2
898+ ) ,
899+ type : header_content_type ,
900+ }
901+ : (
902+ body_raw
903+ . toString (
904+ 'utf8' ,
905+ index_header_end + 4 ,
906+ index_end - 2
907+ )
908+ )
909+ ) ;
910+ if ( header_disposition_name . endsWith ( '[]' ) ) {
911+ header_disposition_name = header_disposition_name . slice ( 0 , - 2 ) ;
912+ if ( header_disposition_name in result ) {
913+ if ( ! ( result [ header_disposition_name ] instanceof Array ) ) {
914+ throw 'duplicate part name' ;
915+ }
916+ result [ header_disposition_name ] . push ( part_result ) ;
917+ }
918+ else {
919+ result [ header_disposition_name ] = [ part_result ] ;
920+ }
921+ }
922+ else {
923+ if ( header_disposition_name in result ) {
924+ throw 'duplicate part name' ;
925+ }
926+ result [ header_disposition_name ] = part_result ;
927+ }
928+ }
929+ index_start = index_end + boundary_length_full ;
930+ index_end = index_start + 1 ;
931+ }
932+ }
933+
934+ const querystring_parse = ( querystring , result ) => {
935+ for ( const entry of querystring . split ( '&' ) ) {
936+ let [ key , value ] = entry . split ( '=' , 2 ) ;
937+ const array = key . endsWith ( '[]' ) ;
938+ if ( array ) {
939+ key = key . slice ( 0 , - 2 ) ;
940+ }
941+ if ( ! key ) {
942+ throw 'key is empty' ;
943+ }
944+ value = decodeURIComponent ( value || '' ) ;
945+ if ( key in result ) {
946+ if ( ( result [ key ] instanceof Array ) !== array ) {
947+ throw 'type mismatch' ;
948+ }
949+ if ( array ) {
950+ result [ key ] . push ( value ) ;
951+ }
952+ else {
953+ result [ key ] = value ;
954+ }
955+ }
956+ else {
957+ result [ key ] = array ? [ value ] : value ;
958+ }
959+ }
960+ }
961+
787962const request_handle = async ( request , response , https ) => {
788963 const request_method = request . method ;
789964 const request_headers = request . headers ;
790- const request_ip = request_ip_get ( request ) . clientIp ;
965+ const request_ip = request_headers [ 'x-forwarded-for' ] || request . socket . remoteAddress ;
791966 const request_method_head = request_method === 'HEAD' ;
792967
793968 if ( 'x-forwarded-proto' in request_headers ) {
@@ -1077,21 +1252,21 @@ const request_handle = async (request, response, https) => {
10771252 }
10781253
10791254 if ( request_headers [ 'x-input' ] ) {
1080- Object . assign (
1081- file_function_input ,
1082- querystring_parse ( request_headers [ 'x-input' ] )
1083- ) ;
1255+ try {
1256+ querystring_parse ( request_headers [ 'x-input' ] , file_function_input ) ;
1257+ }
1258+ catch ( err ) {
1259+ log ( `[error] ${ path } request x-input: ${ err } ` ) ;
1260+ throw 400 ;
1261+ }
10841262 }
10851263
10861264 if ( request_url_parsed . query ) {
10871265 try {
1088- Object . assign (
1089- file_function_input ,
1090- querystring_parse ( request_url_parsed . query )
1091- ) ;
1266+ querystring_parse ( request_url_parsed . query , file_function_input ) ;
10921267 }
10931268 catch ( err ) {
1094- log ( `[error] ${ path } request query: ${ err . message } ` ) ;
1269+ log ( `[error] ${ path } request query: ${ err } ` ) ;
10951270 throw 400 ;
10961271 }
10971272 }
@@ -1100,37 +1275,32 @@ const request_handle = async (request, response, https) => {
11001275 try {
11011276 const content_type = request . headers [ 'content-type' ] || '' ;
11021277 const body_raw = file_function_input [ 'body' ] = await request_body_promise ;
1103- let body = null ;
1104- switch ( content_type . split ( ';' ) [ 0 ] ) {
1105- case 'application/x-www-form-urlencoded' :
1106- body = querystring_parse ( body_raw . toString ( ) ) ;
1107- break ;
1108- case 'application/json' :
1109- body = JSON . parse ( body_raw . toString ( ) ) ;
1110- break ;
1111- case 'multipart/form-data' : {
1112- body = Object . fromEntries (
1113- multipart_parse (
1114- body_raw ,
1115- content_type . split ( 'boundary=' ) [ 1 ] . split ( ';' ) [ 0 ]
1116- )
1117- . map ( value => {
1118- const { name} = value ;
1119- delete value . name ;
1120- return [
1121- name ,
1122- value . type ? value : value . data . toString ( )
1123- ] ;
1124- } )
1125- ) ;
1126- }
1127- }
1128- if ( body ) {
1129- Object . assign ( file_function_input , /** @type {!Object } */ ( body ) ) ;
1278+ switch (
1279+ content_type . split ( ';' , 1 ) [ 0 ]
1280+ ) {
1281+ case 'application/json' :
1282+ Object . assign (
1283+ file_function_input ,
1284+ /** @type {!Object } */ (
1285+ JSON . parse ( body_raw . toString ( ) )
1286+ )
1287+ ) ;
1288+ break ;
1289+ case 'application/x-www-form-urlencoded' :
1290+ querystring_parse ( body_raw . toString ( ) , file_function_input ) ;
1291+ break ;
1292+ case 'multipart/form-data' :
1293+ multipart_parse (
1294+ body_raw ,
1295+ content_type
1296+ . split ( 'boundary=' , 2 ) [ 1 ]
1297+ . split ( ';' , 1 ) [ 0 ] ,
1298+ file_function_input
1299+ ) ;
11301300 }
11311301 }
11321302 catch ( err ) {
1133- log ( `[error] ${ path } request body: ${ err . message } ` ) ;
1303+ log ( `[error] ${ path } request body: ${ err . message || err } ` ) ;
11341304 throw 400 ;
11351305 }
11361306 }
@@ -1178,7 +1348,10 @@ const request_handle = async (request, response, https) => {
11781348 ) . pipe ( response ) ;
11791349 }
11801350 }
1181- else if ( encodings . includes ( 'gzip' ) ) {
1351+ else if (
1352+ GZIP_OPTIONS . level > 0 &&
1353+ encodings . includes ( 'gzip' )
1354+ ) {
11821355 file_compression = COMPRESS_METHOD_GZIP ;
11831356 response . setHeader ( 'Content-Encoding' , 'gzip' ) ;
11841357 if ( ! request_method_head ) {
0 commit comments