LiteServer

Lightweight HttpServer wrapper for Dart.

Features

  • Easy to use
  • Lightweight
  • Dynamic Path Support /user/<id>
  • Nested Routes
  • Static file support with security features
  • MultipartFile support with security checks
  • Support Custom Guard Controllers, manipulate request if need
  • Request timeout protection
  • Request size limits
  • Directory traversal protection
  • CORS support
  • Comprehensive logging

Security Features

  • Request timeout protection (default: 30 seconds)
  • Request size limits (default: 10MB)
  • Directory traversal attack prevention
  • File extension restrictions for static routes
  • Input sanitization and validation
  • Error handling with graceful degradation

Usage

void main(List<String> arguments) async {
  final liteServer = LiteServer(
    routes: [
      homeRoute,
      HttpRoute.get(
        '/',
        handler: (request, payload) {
          final cwd = Directory.current.path;
          request.response.file('$cwd/assets/web/images/512.png');
        },
        routes: [
          HttpRoute.get(
            'api',
            routes: [
              HttpRoute.get(
                'users',
                handler: (request, payload) {
                  request.response.json([]);
                },
              ),
            ],
          )
        ],
      ),
      HttpRoute.post(
        '/user/<id>',
        handler: (request, payload) async {
          print(jsonDecode(await request.readBodyAsString()));
          await request.response.json(payload.pathParameters);
        },
      ),
      HttpRoute.post(
        '/post',
        handler: (request, payload) async {
          print(jsonDecode(await request.readBodyAsString()));
          await request.response.ok('posted');
        },
      ),
      HttpRoute.post(
        '/upload',
        handler: (request, payload) async {
          await for (final entry in request.multipartData()) {
            print(entry.info);

            if (!entry.info.containsKey('content-type')) {
              print(utf8.decode(entry.bytes));
            }
          }

          await request.response.ok('uploaded');
        },
      ),
      HttpStaticRoute(
        '/images',
        directoryPath: 'assets/images/',
        listDirectory: true,
        allowedExtensions: ['jpg', 'png', 'gif'], // Optional file restrictions
      ),
      HttpStaticRoute(
        '/web',
        directoryPath: 'assets/web/',
        defaultDocument: 'index.html',
      ),
    ],
    maxRequestSize: 5 * 1024 * 1024, // 5MB limit
    requestTimeout: Duration(seconds: 60), // 60 second timeout
  );

  for (var i = 0; i < 6; i++) {
    await Isolate.spawn(startServer, liteServer);
  }

  print(liteServer.routeMap.keys.join('\n'));
  await startServer(liteServer);
}

Future<void> startServer(LiteServer liteServer) async {
  final server = await HttpServer.bind(
    InternetAddress.anyIPv4,
    9080,
    shared: true,
  )
    ..autoCompress = true
    ..serverHeader = Isolate.current.hashCode.toString();

  liteServer.listen(
    server,
    controllers: [
      LoggerController(level: LogLevel.errors),
      CorsOriginController(
        allowedMethods: {'GET', 'POST', 'OPTIONS'},
        allowedOrigins: {'http://localhost:3000', 'https://yourdomain.com'},
      ),
    ],
  );
}

## Configuration Options

### LiteServer Constructor
- `maxRequestSize`: Maximum request size in bytes (default: 10MB)
- `requestTimeout`: Request timeout duration (default: 30 seconds)

### HttpStaticRoute
- `allowedExtensions`: List of allowed file extensions for security
- `listDirectory`: Enable directory listing
- `defaultDocument`: Default document to serve

### LoggerController
- `level`: Logging level (all, errors, requests, none)
- `path`: Log directory path

### CorsOriginController
- `allowedOrigins`: Set of allowed origins
- `allowedMethods`: Set of allowed HTTP methods
- `allowedHeaders`: Set of allowed headers
- `maxAge`: CORS cache duration
- `allowCredentials`: Allow credentials
- `exposeHeaders`: Headers to expose

Libraries

lite_server