@@ -92,13 +92,18 @@ pub struct StreamableHttpServerConfig {
9292 pub allowed_hosts : Vec < String > ,
9393 /// Allowed browser origins for inbound `Origin` validation.
9494 ///
95- /// Defaults to an empty list, which disables Origin validation. When
96- /// non-empty, requests carrying an `Origin` header must match per RFC 6454
97- /// `(scheme, host, port)`; missing-`Origin` requests still pass. Entries
98- /// must include a scheme; `"null"` matches the browser's `Origin: null`.
95+ /// Validation is enabled by default. Requests carrying an `Origin` header
96+ /// must match per RFC 6454
97+ /// `(scheme, host, port)`; missing-`Origin` requests still pass. An empty
98+ /// list allows no present Origin values. Entries must include a scheme;
99+ /// `"null"` matches the browser's `Origin: null`.
100+ ///
101+ /// Call [`StreamableHttpServerConfig::disable_allowed_origins`] to
102+ /// explicitly disable Origin validation.
99103 /// examples:
100104 /// allowed_origins = ["https://app.example.com", "http://localhost:8080"]
101105 pub allowed_origins : Vec < String > ,
106+ origin_validation_enabled : bool ,
102107 /// Optional external session store for cross-instance recovery.
103108 ///
104109 /// When set, [`SessionState`] (the client's `initialize` parameters) is
@@ -171,6 +176,7 @@ impl Default for StreamableHttpServerConfig {
171176 cancellation_token : CancellationToken :: new ( ) ,
172177 allowed_hosts : vec ! [ "localhost" . into( ) , "127.0.0.1" . into( ) , "::1" . into( ) ] ,
173178 allowed_origins : vec ! [ ] ,
179+ origin_validation_enabled : true ,
174180 session_store : None ,
175181 max_request_body_bytes : DEFAULT_MAX_REQUEST_BODY_BYTES ,
176182 stateless_protocol_metadata_required : false ,
@@ -196,11 +202,13 @@ impl StreamableHttpServerConfig {
196202 allowed_origins : impl IntoIterator < Item = impl Into < String > > ,
197203 ) -> Self {
198204 self . allowed_origins = allowed_origins. into_iter ( ) . map ( Into :: into) . collect ( ) ;
205+ self . origin_validation_enabled = true ;
199206 self
200207 }
201- /// Disable Origin validation, reverting to the default ignore- Origin behavior .
208+ /// Disable Origin validation, allowing requests with any ` Origin` header .
202209 pub fn disable_allowed_origins ( mut self ) -> Self {
203210 self . allowed_origins . clear ( ) ;
211+ self . origin_validation_enabled = false ;
204212 self
205213 }
206214 pub fn with_sse_keep_alive ( mut self , duration : Option < Duration > ) -> Self {
@@ -797,9 +805,6 @@ fn parse_origin_value(value: &str) -> Option<NormalizedOrigin> {
797805}
798806
799807fn origin_is_allowed ( origin : & NormalizedOrigin , allowed_origins : & [ String ] ) -> bool {
800- if allowed_origins. is_empty ( ) {
801- return true ;
802- }
803808 allowed_origins
804809 . iter ( )
805810 . filter_map ( |raw| parse_origin_value ( raw) )
@@ -874,15 +879,15 @@ fn validate_dns_rebinding_headers(
874879 ) ;
875880 return Err ( forbidden_response ( "Forbidden: Host header is not allowed" ) ) ;
876881 }
877- validate_origin_header ( headers, & config. allowed_origins ) ?;
882+ validate_origin_header ( headers, config) ?;
878883 Ok ( ( ) )
879884}
880885
881886fn validate_origin_header (
882887 headers : & HeaderMap ,
883- allowed_origins : & [ String ] ,
888+ config : & StreamableHttpServerConfig ,
884889) -> Result < ( ) , BoxResponse > {
885- if allowed_origins . is_empty ( ) {
890+ if !config . origin_validation_enabled {
886891 return Ok ( ( ) ) ;
887892 }
888893 let Some ( origin_header) = headers. get ( http:: header:: ORIGIN ) else {
@@ -893,15 +898,15 @@ fn validate_origin_header(
893898 . inspect_err ( |_| {
894899 tracing:: warn!( origin = ?origin_header, "rejected request with non-UTF-8 Origin header" ) ;
895900 } )
896- . map_err ( |_| bad_request_response ( "Bad Request : Invalid Origin header encoding") ) ?;
901+ . map_err ( |_| forbidden_response ( "Forbidden : Invalid Origin header encoding") ) ?;
897902 let origin = parse_origin_value ( origin_str) . ok_or_else ( || {
898903 tracing:: warn!(
899904 origin = origin_str,
900905 "rejected request with malformed Origin header" ,
901906 ) ;
902- bad_request_response ( "Bad Request : Invalid Origin header")
907+ forbidden_response ( "Forbidden : Invalid Origin header")
903908 } ) ?;
904- if !origin_is_allowed ( & origin, allowed_origins) {
909+ if !origin_is_allowed ( & origin, & config . allowed_origins ) {
905910 tracing:: warn!(
906911 origin = ?origin,
907912 "rejected request with disallowed Origin header (possible cross-origin attack)" ,
0 commit comments