Skip to content

Commit 6fe2c5b

Browse files
authored
add function to set cors headers consitently in local_webserver.rs (#2975)
<!-- CURSOR_SUMMARY --> > [!NOTE] > Introduce add_cors_headers helper and apply it across consumption proxy, OPTIONS, and workflow endpoints to consistently set CORS (including OPTIONS). > > - **Server (local_webserver.rs)**: > - **CORS**: > - Add `add_cors_headers` helper to standardize CORS headers (`Access-Control-Allow-*`). > - Apply to responses in `get_consumption_api_res` (including UNAUTHORIZED and proxied responses). > - Apply to `options_route` (explicitly supports `OPTIONS`). > - Apply to workflow endpoints: `workflows_history_route`, `workflows_trigger_route`, `workflows_terminate_route` for both success and error paths. > - Normalize allowed methods to `GET, POST, OPTIONS` where applicable. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 778dda9. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent e3dc317 commit 6fe2c5b

File tree

1 file changed

+24
-31
lines changed

1 file changed

+24
-31
lines changed

apps/framework-cli/src/cli/local_webserver.rs

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,17 @@ impl Default for LocalWebserverConfig {
401401
}
402402
}
403403

404+
/// Helper function to add CORS headers to a response builder
405+
fn add_cors_headers(builder: hyper::http::response::Builder) -> hyper::http::response::Builder {
406+
builder
407+
.header("Access-Control-Allow-Origin", "*")
408+
.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
409+
.header(
410+
"Access-Control-Allow-Headers",
411+
"Authorization, Content-Type, baggage, sentry-trace, traceparent, tracestate",
412+
)
413+
}
414+
404415
#[tracing::instrument(skip(consumption_apis, req, is_prod), fields(uri = %req.uri(), method = %req.method(), headers = ?req.headers()))]
405416
async fn get_consumption_api_res(
406417
http_client: Arc<Client>,
@@ -415,7 +426,7 @@ async fn get_consumption_api_res(
415426

416427
// JWT config for consumption api is handled in user's api files
417428
if !check_authorization(auth_header, &MOOSE_CONSUMPTION_API_KEY, &None).await {
418-
return Ok(Response::builder()
429+
return Ok(add_cors_headers(Response::builder())
419430
.status(StatusCode::UNAUTHORIZED)
420431
.body(Full::new(Bytes::from(
421432
"Unauthorized: Invalid or missing token",
@@ -475,14 +486,8 @@ async fn get_consumption_api_res(
475486
let status = res.status();
476487
let body = res.bytes().await?;
477488

478-
let returned_response = Response::builder()
489+
let returned_response = add_cors_headers(Response::builder())
479490
.status(status)
480-
.header("Access-Control-Allow-Origin", "*")
481-
.header("Access-Control-Allow-Methods", "GET, POST")
482-
.header(
483-
"Access-Control-Allow-Headers",
484-
"Authorization, Content-Type, baggage, sentry-trace, traceparent, tracestate",
485-
)
486491
.header("Content-Type", "application/json")
487492
.body(Full::new(body))
488493
.unwrap();
@@ -629,14 +634,8 @@ impl<I: InfraMapProvider + Clone + Send + 'static> Service<Request<Incoming>>
629634
}
630635

631636
fn options_route() -> Result<Response<Full<Bytes>>, hyper::http::Error> {
632-
let response = Response::builder()
637+
let response = add_cors_headers(Response::builder())
633638
.status(StatusCode::OK)
634-
.header("Access-Control-Allow-Origin", "*")
635-
.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
636-
.header(
637-
"Access-Control-Allow-Headers",
638-
"Authorization, Content-Type, baggage, sentry-trace, traceparent, tracestate",
639-
)
640639
.body(Full::new(Bytes::from("Success")))
641640
.unwrap();
642641

@@ -657,7 +656,7 @@ async fn workflows_history_route(
657656
if is_prod {
658657
let auth_header = req.headers().get(hyper::header::AUTHORIZATION);
659658
if !check_authorization(auth_header, &MOOSE_CONSUMPTION_API_KEY, &None).await {
660-
return Response::builder()
659+
return add_cors_headers(Response::builder())
661660
.status(StatusCode::UNAUTHORIZED)
662661
.body(Full::new(Bytes::from(
663662
"Unauthorized: Invalid or missing token",
@@ -678,10 +677,9 @@ async fn workflows_history_route(
678677
let json_string =
679678
serde_json::to_string(&workflows).unwrap_or_else(|_| "[]".to_string());
680679

681-
Response::builder()
680+
add_cors_headers(Response::builder())
682681
.status(StatusCode::OK)
683682
.header("Content-Type", "application/json")
684-
.header("Access-Control-Allow-Origin", "*")
685683
.body(Full::new(Bytes::from(json_string)))
686684
}
687685
Err(e) => {
@@ -691,7 +689,7 @@ async fn workflows_history_route(
691689
"details": format!("{:?}", e)
692690
});
693691

694-
Response::builder()
692+
add_cors_headers(Response::builder())
695693
.status(StatusCode::INTERNAL_SERVER_ERROR)
696694
.header("Content-Type", "application/json")
697695
.body(Full::new(Bytes::from(
@@ -711,7 +709,7 @@ async fn workflows_trigger_route(
711709
if is_prod {
712710
let auth_header = req.headers().get(hyper::header::AUTHORIZATION);
713711
if !check_authorization(auth_header, &MOOSE_CONSUMPTION_API_KEY, &None).await {
714-
return Response::builder()
712+
return add_cors_headers(Response::builder())
715713
.status(StatusCode::UNAUTHORIZED)
716714
.body(Full::new(Bytes::from(
717715
"Unauthorized: Invalid or missing token",
@@ -729,10 +727,9 @@ async fn workflows_trigger_route(
729727
Err(e) => match e.classify() {
730728
serde_json::error::Category::Eof => None,
731729
_ => {
732-
return Response::builder()
730+
return add_cors_headers(Response::builder())
733731
.status(StatusCode::BAD_REQUEST)
734732
.header("Content-Type", "application/json")
735-
.header("Access-Control-Allow-Origin", "*")
736733
.body(Full::new(Bytes::from(
737734
serde_json::to_string(&serde_json::json!({
738735
"error": "Invalid JSON body",
@@ -755,18 +752,16 @@ async fn workflows_trigger_route(
755752
payload["dashboardUrl"] = serde_json::Value::String(dashboard_url);
756753
}
757754

758-
Response::builder()
755+
add_cors_headers(Response::builder())
759756
.status(StatusCode::OK)
760757
.header("Content-Type", "application/json")
761-
.header("Access-Control-Allow-Origin", "*")
762758
.body(Full::new(Bytes::from(
763759
serde_json::to_string(&payload).unwrap(),
764760
)))
765761
}
766-
Err(e) => Response::builder()
762+
Err(e) => add_cors_headers(Response::builder())
767763
.status(StatusCode::BAD_REQUEST)
768764
.header("Content-Type", "application/json")
769-
.header("Access-Control-Allow-Origin", "*")
770765
.body(Full::new(Bytes::from(
771766
serde_json::to_string(&serde_json::json!({
772767
"error": "Failed to start workflow",
@@ -786,7 +781,7 @@ async fn workflows_terminate_route(
786781
if is_prod {
787782
let auth_header = req.headers().get(hyper::header::AUTHORIZATION);
788783
if !check_authorization(auth_header, &MOOSE_CONSUMPTION_API_KEY, &None).await {
789-
return Response::builder()
784+
return add_cors_headers(Response::builder())
790785
.status(StatusCode::UNAUTHORIZED)
791786
.body(Full::new(Bytes::from(
792787
"Unauthorized: Invalid or missing token",
@@ -795,21 +790,19 @@ async fn workflows_terminate_route(
795790
}
796791

797792
match terminate_workflow(&project, &workflow_name).await {
798-
Ok(success) => Response::builder()
793+
Ok(success) => add_cors_headers(Response::builder())
799794
.status(StatusCode::OK)
800795
.header("Content-Type", "application/json")
801-
.header("Access-Control-Allow-Origin", "*")
802796
.body(Full::new(Bytes::from(
803797
serde_json::to_string(&serde_json::json!({
804798
"status": "terminated",
805799
"message": success.message.details,
806800
}))
807801
.unwrap(),
808802
))),
809-
Err(err) => Response::builder()
803+
Err(err) => add_cors_headers(Response::builder())
810804
.status(StatusCode::BAD_REQUEST)
811805
.header("Content-Type", "application/json")
812-
.header("Access-Control-Allow-Origin", "*")
813806
.body(Full::new(Bytes::from(
814807
serde_json::to_string(&serde_json::json!({
815808
"error": "Failed to terminate workflow",

0 commit comments

Comments
 (0)