using Astralis; using Invercargill; using Invercargill.DataStructures; /** * FileServer Example * * A static file server that serves files from a directory specified * on the command line. Supports compression via gzip, brotli, and zstd. * * Usage: file-server [port] * * Examples: * file-server /var/www/html * file-server ./public 8080 */ // Root endpoint that shows server info class ServerInfoEndpoint : Object, Endpoint { private string served_directory; private int port; public ServerInfoEndpoint(string served_directory, int port) { this.served_directory = served_directory; this.port = port; } public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error { var info = @"Astralis File Server Serving: $served_directory Port: $port Available compression: gzip, brotli, zstd Endpoints: /** - Serves files from the directory /__server_info - This information page Try: curl http://localhost:$port/ curl --compressed http://localhost:$port/style.css "; return new HttpStringResult(info) .set_header("Content-Type", "text/plain"); } } void main(string[] args) { // Parse command line arguments if (args.length < 2) { printerr("Usage: %s [port]\n", args[0]); printerr("\nServes files from the specified directory over HTTP.\n"); printerr("Supports compression: gzip, brotli, zstd\n"); printerr("\nExamples:\n"); printerr(" %s /var/www/html\n", args[0]); printerr(" %s ./public 8080\n", args[0]); Process.exit(1); } string directory = args[1]; int port = args.length > 2 ? int.parse(args[2]) : 8080; // Validate directory exists var dir_file = File.new_for_path(directory); if (!dir_file.query_exists()) { printerr("Error: Directory '%s' does not exist\n", directory); Process.exit(1); } // Check if it's actually a directory try { var info = dir_file.query_info("standard::type", FileQueryInfoFlags.NONE); if (info.get_file_type() != FileType.DIRECTORY) { printerr("Error: '%s' is not a directory\n", directory); Process.exit(1); } } catch (Error e) { printerr("Error checking directory: %s\n", e.message); Process.exit(1); } // Resolve to absolute path string absolute_path = dir_file.get_path(); // Create the filesystem resource with deep matching FilesystemResource file_resource; try { file_resource = new FilesystemResource("/**", absolute_path); file_resource.allow_directory_listing = true; file_resource.index_file = "index.html"; } catch (FilesystemResourceError e) { printerr("Error creating file resource: %s\n", e.message); Process.exit(1); } // Print startup information print("╔══════════════════════════════════════════════════════════════╗\n"); print("║ Astralis File Server ║\n"); print("╠══════════════════════════════════════════════════════════════╣\n"); print(@"║ Serving: $(absolute_path)"); if (absolute_path.length < 50) { for (int i = 0; i < 50 - absolute_path.length; i++) print(" "); } print(" ║\n"); print(@"║ Port: $port"); for (int i = 0; i < 50 - port.to_string().length; i++) print(" "); print(" ║\n"); print("╠══════════════════════════════════════════════════════════════╣\n"); print("║ Compression: gzip, brotli, zstd ║\n"); print("╠══════════════════════════════════════════════════════════════╣\n"); print("║ Try: ║\n"); print(@"║ http://localhost:$port/"); for (int i = 0; i < 50 - 21 - port.to_string().length; i++) print(" "); print(" ║\n"); print(@"║ http://localhost:$port/__server_info"); for (int i = 0; i < 50 - 35 - port.to_string().length; i++) print(" "); print(" ║\n"); print("╚══════════════════════════════════════════════════════════════╝\n"); print("\nPress Ctrl+C to stop the server\n\n"); // Create the application and register endpoints var application = new WebApplication(port); // Register compression components application.container.register_singleton(() => new GzipCompressor()); application.container.register_singleton(() => new ZstdCompressor()); application.container.register_singleton(() => new BrotliCompressor()); // Register server info endpoint application.container.register_scoped(() => new ServerInfoEndpoint(absolute_path, port)) .with_metadata(new EndpointRoute("/__server_info")); // Register filesystem resource endpoint as singleton (holds configuration) // The factory creates the instance and the container caches it application.container.register_singleton(() => file_resource) .with_metadata(new EndpointRoute("/**")); // Run the server application.run(); }