diff --git a/lib/firebase_functions.dart b/lib/firebase_functions.dart index fa6c873..aac4c3b 100644 --- a/lib/firebase_functions.dart +++ b/lib/firebase_functions.dart @@ -123,7 +123,7 @@ export 'src/remote_config/remote_config.dart'; // Experimental: Scheduler triggers (not yet supported in production or emulator) export 'src/scheduler/scheduler.dart'; // Core runtime -export 'src/server.dart' show fireUp, runFunctions; +export 'src/server.dart' show RunFunctionsOptions, fireUp, runFunctions; // Experimental: Storage triggers (emulator only) export 'src/storage/storage.dart'; // Experimental: Task queue triggers (not yet supported in production or emulator) diff --git a/lib/src/server.dart b/lib/src/server.dart index 5744621..6e41f62 100644 --- a/lib/src/server.dart +++ b/lib/src/server.dart @@ -30,6 +30,18 @@ import 'logger/logger.dart'; /// Callback type for the user's function registration code. typedef FunctionsRunner = FutureOr Function(Firebase firebase); +/// Runtime configuration options for [runFunctions]. +class RunFunctionsOptions { + const RunFunctionsOptions({this.poweredByHeader}); + + /// Value for the `x-powered-by` response header. + /// + /// Defaults to `null`, which omits the header entirely. Pass a string to set + /// a custom value. This applies to all responses, including + /// internally-generated shelf error responses. + final String? poweredByHeader; +} + /// Starts the Firebase Functions runtime. /// /// This is the main entry point for a Firebase Functions application. @@ -64,7 +76,10 @@ Future fireUp(List args, FunctionsRunner runner) => /// }); /// } /// ``` -Future runFunctions(FunctionsRunner runner) async { +Future runFunctions( + FunctionsRunner runner, { + RunFunctionsOptions options = const RunFunctionsOptions(), +}) async { final firebase = createFirebaseInternal(); final env = firebase.$env; final projectId = env.projectId; @@ -95,7 +110,12 @@ Future runFunctions(FunctionsRunner runner) async { }); // Start HTTP server - await shelf_io.serve(handler, InternetAddress.anyIPv4, env.port); + await shelf_io.serve( + handler, + InternetAddress.anyIPv4, + env.port, + poweredByHeader: options.poweredByHeader, + ); }); } diff --git a/test/unit/server_test.dart b/test/unit/server_test.dart index c881b5b..fd9059b 100644 --- a/test/unit/server_test.dart +++ b/test/unit/server_test.dart @@ -51,6 +51,18 @@ void main() { }); }); + group('RunFunctionsOptions', () { + test('defaults to null (no header)', () { + const opts = RunFunctionsOptions(); + expect(opts.poweredByHeader, isNull); + }); + + test('accepts a custom header value', () { + const opts = RunFunctionsOptions(poweredByHeader: 'MyApp/1.0'); + expect(opts.poweredByHeader, 'MyApp/1.0'); + }); + }); + group('corsHeadersFor', () { test('returns asterisk when allowedOrigins contains asterisk', () { final request = Request('GET', Uri.parse('http://localhost/test'));