@@ -81,19 +81,20 @@ console.log("Server running at http://localhost:3000");
8181
8282### 🔐 Authentication
8383
84- - ** Bearer Auth** - JWT/API token authentication
85- - ** Basic Auth** - HTTP Basic authentication
84+ - [ ** Bearer Auth** ] ( #bearer-auth ) - JWT/API token authentication
85+ - [ ** Basic Auth** ] ( #basic-auth ) - HTTP Basic authentication
8686
8787### 🛡️ Security
8888
89- - ** CORS** - Cross-Origin Resource Sharing
90- - ** Rate Limiting ** - Request rate limiting with multiple algorithms
89+ - [ ** CORS** ] ( #cors ) - Cross-Origin Resource Sharing
90+ - [ ** Rate Limit ** ] ( #rate-limit ) - Request rate limiting with multiple algorithms
9191
9292### 📊 Utils
9393
94- - ** Logger** - HTTP request/response logging with customizable formats
95- - ** Cache** - Response caching using pluggable backends like in-memory, LRU, or Redis for improved performance and reduced server load.
96- - ** IP Extract** - Parses the incoming request's IP address, respecting common proxy headers (X-Forwarded-For, X-Real-IP) and attaches it to the request context.
94+ - [ ** Logger** ] ( #logger ) - HTTP request/response logging with customizable formats
95+ - [ ** Body Limit** ] ( #body-limit ) - Limit the file size of the request body
96+ - [ ** Cache** ] ( #cache ) - Response caching using pluggable backends like in-memory, LRU, or Redis for improved performance and reduced server load
97+ - [ ** IP Extract** ] ( #ip-extract ) - Parses the incoming request's IP address, respecting common proxy headers (X-Forwarded-For, X-Real-IP) and attaches it to the request context
9798
9899## 📚 Middleware Documentation
99100
@@ -349,6 +350,113 @@ app.use(cache({ storage: new CustomStorage() }));
349350- Distributed caching with Redis backend
350351- Memory efficient with configurable storage limits
351352
353+ ### Body Limit
354+
355+ Protect your server from large request payloads with configurable size limits. Prevents memory exhaustion and ensures fair resource usage.
356+
357+ ` ` ` js
358+ import { bodyLimit } from " @rabbit-company/web-middleware/body-limit" ;
359+
360+ // Basic usage - 1MB limit (default)
361+ app .use (bodyLimit ());
362+
363+ // Custom size limit
364+ app .use (
365+ bodyLimit ({
366+ maxSize: " 5mb" , // or 5242880 for bytes
367+ })
368+ );
369+
370+ // Different limits for different routes
371+ app .post (" /api/upload/avatar" , bodyLimit ({ maxSize: " 2mb" }), uploadAvatar);
372+
373+ app .post (" /api/upload/video" , bodyLimit ({ maxSize: " 100mb" }), uploadVideo);
374+
375+ // Limit only specific content types
376+ app .use (
377+ bodyLimit ({
378+ maxSize: " 10mb" ,
379+ contentTypes: [" application/json" , " application/xml" ],
380+ message: " JSON/XML payload too large" ,
381+ })
382+ );
383+
384+ // Skip limit for premium users
385+ app .use (
386+ bodyLimit ({
387+ maxSize: " 5mb" ,
388+ skip: async (ctx ) => {
389+ const user = ctx .get (" user" );
390+ return user? .plan === " premium" ;
391+ },
392+ })
393+ );
394+
395+ // Custom error handling
396+ app .use (
397+ bodyLimit ({
398+ maxSize: " 1mb" ,
399+ message : (size , limit ) => ` Payload too large: ${ (size / 1024 ).toFixed (2 )} KB exceeds ${ (limit / 1024 ).toFixed (2 )} KB limit` ,
400+ statusCode: 400 , // Use 400 instead of default 413
401+ })
402+ );
403+
404+ // Include headers in size calculation
405+ app .use (
406+ bodyLimit ({
407+ maxSize: " 10kb" ,
408+ includeHeaders: true , // Total request size including headers
409+ })
410+ );
411+
412+ // File upload endpoint with strict limit
413+ app .post (
414+ " /api/documents" ,
415+ bodyLimit ({
416+ maxSize: " 10mb" ,
417+ contentTypes: [" multipart/form-data" ],
418+ message: " Document size must not exceed 10MB" ,
419+ }),
420+ async (ctx ) => {
421+ const formData = await ctx .req .formData ();
422+ const file = formData .get (" document" );
423+ // Process file...
424+ return ctx .json ({ success: true });
425+ }
426+ );
427+ ` ` `
428+
429+ #### Options:
430+
431+ - ` maxSize` : Maximum allowed body size (number in bytes or string with units: "1kb", "5mb", "1gb")
432+ - ` includeHeaders` : Include header size in limit calculation (default: false)
433+ - ` message` : Error message - string or function(size, limit)
434+ - ` statusCode` : HTTP status code when limit exceeded (default: 413)
435+ - ` contentTypes` : Array of content types to apply limit to (default: all)
436+ - ` skip` : Function to conditionally skip limit check
437+
438+ #### Size Format Examples:
439+
440+ - ` 100 ` or ` " 100" ` - 100 bytes
441+ - ` " 100b" ` - 100 bytes
442+ - ` " 10kb" ` - 10 kilobytes (10,240 bytes)
443+ - ` " 5.5mb" ` - 5.5 megabytes
444+ - ` " 1gb" ` - 1 gigabyte
445+
446+ #### Security Benefits:
447+
448+ - Prevents memory exhaustion attacks
449+ - Protects against slowloris-style attacks
450+ - Ensures fair resource allocation
451+ - Reduces attack surface for buffer overflow exploits
452+
453+ #### Performance Notes:
454+
455+ - Uses Content-Length header for efficient early rejection
456+ - No body parsing required - fails fast for oversized requests
457+ - Minimal memory overhead
458+ - Works with streaming and non-streaming requests
459+
352460### Bearer Auth
353461
354462Token-based authentication for APIs.
@@ -567,7 +675,7 @@ app.use(
567675- ` preflightContinue` : Pass OPTIONS requests to next handler
568676- ` optionsSuccessStatus` : Status code for successful OPTIONS
569677
570- ### Rate Limiting
678+ ### Rate Limit
571679
572680Advanced rate limiting with multiple algorithms to prevent API abuse and ensure fair usage..
573681
@@ -724,6 +832,115 @@ console.log(`Active rate limits: ${limiter.getSize()}`);
724832- ` RateLimit- Algorithm` : Algorithm used
725833- ` Retry- After` : Seconds until retry (when limited)
726834
835+ ### IP Extract
836+
837+ Securely extract client IP addresses from requests, handling various proxy configurations and preventing IP spoofing attacks.
838+
839+ ` ` ` js
840+ import { ipExtract , getClientIp } from " @rabbit-company/web-middleware/ip-extract" ;
841+
842+ // Direct connection (no proxy)
843+ app .use (ipExtract (" direct" ));
844+
845+ // Behind Cloudflare
846+ app .use (ipExtract (" cloudflare" ));
847+
848+ // Behind AWS Load Balancer
849+ app .use (ipExtract (" aws" ));
850+
851+ // Behind nginx reverse proxy
852+ app .use (ipExtract (" nginx" ));
853+
854+ // Custom configuration
855+ app .use (
856+ ipExtract ({
857+ trustProxy: true ,
858+ trustedProxies: [" 10.0.0.0/8" , " 172.16.0.0/12" ],
859+ trustedHeaders: [" x-real-ip" , " x-forwarded-for" ],
860+ maxProxyChain: 3 ,
861+ logWarnings: true ,
862+ })
863+ );
864+
865+ // Access the extracted IP
866+ app .get (" /api/info" , (ctx ) => {
867+ const ip = getClientIp (ctx);
868+ // or directly: ctx.clientIp
869+ return ctx .json ({
870+ clientIp: ip,
871+ country: geoip .lookup (ip)? .country ,
872+ });
873+ });
874+
875+ // Rate limiting by IP
876+ app .use (ipExtract (" cloudflare" ));
877+ app .use (
878+ rateLimit ({
879+ keyGenerator : (ctx ) => getClientIp (ctx) || " unknown" ,
880+ })
881+ );
882+
883+ // Logging with real IPs
884+ app .use (ipExtract (" nginx" ));
885+ app .use (
886+ logger ({
887+ getUserId : (ctx ) => getClientIp (ctx),
888+ })
889+ );
890+
891+ // IP-based access control
892+ const ipWhitelist = [" 192.168.1.0/24" , " 10.0.0.0/8" ];
893+
894+ app .use (" /admin" , ipExtract (" direct" ), (ctx , next ) => {
895+ const ip = getClientIp (ctx);
896+ if (! ip || ! isIpInWhitelist (ip, ipWhitelist)) {
897+ return ctx .text (" Access denied" , 403 );
898+ }
899+ return next ();
900+ });
901+
902+ // Development mode (trusts all headers - NOT for production!)
903+ if (process .env .NODE_ENV === " development" ) {
904+ app .use (ipExtract (" development" ));
905+ }
906+ ` ` `
907+
908+ #### Presets:
909+
910+ - ` " direct" ` - No proxy, direct connections only
911+ - ` " cloudflare" ` - Behind Cloudflare (auto-configures CF IPs)
912+ - ` " aws" ` - Behind AWS ALB/ELB
913+ - ` " gcp" ` - Behind Google Cloud Load Balancer
914+ - ` " azure" ` - Behind Azure Application Gateway
915+ - ` " vercel" ` - Behind Vercel's edge network
916+ - ` " nginx" ` - Behind nginx reverse proxy
917+ - ` " development" ` - Trusts all headers (NEVER use in production!)
918+
919+ #### Options:
920+
921+ - ` trustProxy` : Enable proxy header parsing (default: false)
922+ - ` trustedProxies` : List of trusted proxy IPs/CIDR ranges
923+ - ` trustedHeaders` : Headers to check in order (default: ["x-forwarded-for", "x-real-ip"])
924+ - ` maxProxyChain` : Maximum proxy chain length to prevent attacks (default: 5)
925+ - ` cloudProvider` : Auto-configure for cloud provider ("aws", "cloudflare", "gcp", "azure", "vercel")
926+ - ` logWarnings` : Log suspicious activity (default: false)
927+
928+ #### Security Features:
929+
930+ - **IP Spoofing Prevention**: Only trusts headers from configured proxies
931+ - **Chain Length Limits**: Prevents long X-Forwarded-For chains
932+ - **CIDR Support**: Configure trusted proxy ranges (e.g., "10.0.0.0/8")
933+ - **Cloud Provider Detection**: Pre-configured secure settings for major providers
934+ - **IPv4/IPv6 Support**: Full support for both protocols
935+
936+ #### Important Notes:
937+
938+ - Always use HTTPS in production to prevent header injection
939+ - Configure ` trustedProxies` to match your infrastructure
940+ - The ` development` preset is insecure - only for local testing
941+ - Test your configuration with tools like ` curl - H " X-Forwarded-For: fake" `
942+ - Consider using cloud provider presets for automatic secure configuration
943+
727944## 📦 Dependencies
728945
729946- ` @rabbit- company/ web` - Core web framework (peer dependency)
0 commit comments