@@ -99,6 +99,38 @@ const _corsAnyOriginHeaders = {
9999 'Access-Control-Allow-Headers' : '*' ,
100100};
101101
102+ Response _buildOptionsCorsResponse (
103+ Request request,
104+ List <String > allowedOrigins,
105+ ) => Response .ok ('' , headers: corsHeadersFor (request, allowedOrigins));
106+
107+ Response _applyCorsHeaders (
108+ Request request,
109+ Response response,
110+ List <String > allowedOrigins,
111+ ) => response.change (headers: corsHeadersFor (request, allowedOrigins));
112+
113+ @visibleForTesting
114+ Map <String , String > corsHeadersFor (
115+ Request request,
116+ List <String > allowedOrigins,
117+ ) {
118+ if (allowedOrigins.contains ('*' )) {
119+ return _corsAnyOriginHeaders;
120+ }
121+
122+ final origin = request.headers['origin' ];
123+ if (origin != null && allowedOrigins.contains (origin)) {
124+ return {
125+ 'Access-Control-Allow-Origin' : origin,
126+ 'Access-Control-Allow-Methods' : '*' ,
127+ 'Access-Control-Allow-Headers' : '*' ,
128+ };
129+ }
130+
131+ return const {};
132+ }
133+
102134/// Routes incoming requests to the appropriate function handler.
103135FutureOr <Response > _routeRequest (
104136 Request request,
@@ -144,7 +176,7 @@ FutureOr<Response> _routeToTargetFunction(
144176 Firebase firebase,
145177 FirebaseEnv env,
146178 String functionTarget,
147- ) {
179+ ) async {
148180 final functions = firebase.functions;
149181
150182 // Find the function with matching name
@@ -165,6 +197,11 @@ FutureOr<Response> _routeToTargetFunction(
165197 // from the Node.js model does not apply here.
166198
167199 // Validate HTTP method for event functions
200+ if (request.method.toUpperCase () == 'OPTIONS' &&
201+ targetFunction.allowedOrigins != null ) {
202+ return _buildOptionsCorsResponse (request, targetFunction.allowedOrigins! );
203+ }
204+
168205 if (! targetFunction.external && request.method.toUpperCase () != 'POST' ) {
169206 return Response (
170207 405 ,
@@ -173,10 +210,12 @@ FutureOr<Response> _routeToTargetFunction(
173210 );
174211 }
175212
176- // Execute the target function (all requests go to this function)
177- // Wrap with onInit to ensure initialization callback runs before first execution
178213 final wrappedHandler = withInit (targetFunction.handler);
179- return wrappedHandler (request);
214+ final response = await wrappedHandler (request);
215+ if (targetFunction.allowedOrigins != null ) {
216+ return _applyCorsHeaders (request, response, targetFunction.allowedOrigins! );
217+ }
218+ return response;
180219}
181220
182221/// Routes request by path matching (development/shared process mode).
@@ -215,16 +254,29 @@ FutureOr<Response> _routeByPath(
215254
216255 // Try to find a matching function by name
217256 for (final function in functions) {
218- // Internal functions (events) only accept POST requests
219- if (! function.external && currentRequest.method.toUpperCase () != 'POST' ) {
220- continue ;
221- }
222-
223- // Match by function name
224257 if (functionName == function.name) {
225- // Wrap with onInit to ensure initialization callback runs before first execution
258+ if (currentRequest.method.toUpperCase () == 'OPTIONS' &&
259+ function.allowedOrigins != null ) {
260+ return _buildOptionsCorsResponse (
261+ currentRequest,
262+ function.allowedOrigins! ,
263+ );
264+ }
265+
266+ if (! function.external && currentRequest.method.toUpperCase () != 'POST' ) {
267+ continue ;
268+ }
269+
226270 final wrappedHandler = withInit (function.handler);
227- return wrappedHandler (currentRequest);
271+ final response = await wrappedHandler (currentRequest);
272+ if (function.allowedOrigins != null ) {
273+ return _applyCorsHeaders (
274+ currentRequest,
275+ response,
276+ function.allowedOrigins! ,
277+ );
278+ }
279+ return response;
228280 }
229281 }
230282
0 commit comments