| 188 | } |
| 189 | |
| 190 | pub fn set<'js>(&mut self, ctx: Ctx<'js>, key: String, value: Value<'js>) -> Result<()> { |
| 191 | let key = lower_key(key); |
| 192 | if !is_http_header_name(&key) { |
| 193 | return Err(Exception::throw_type(&ctx, "Invalid key")); |
| 194 | } |
| 195 | if self.guard == HeadersGuard::Immutable { |
| 196 | return Err(Exception::throw_type(&ctx, "Headers are immutable")); |
| 197 | } |
| 198 | if matches!( |
| 199 | self.guard, |
| 200 | HeadersGuard::Request | HeadersGuard::RequestNoCors |
| 201 | ) && is_forbidden_request_header(&key) |
| 202 | { |
| 203 | return Ok(()); |
| 204 | } |
| 205 | if self.guard == HeadersGuard::Response && key.as_ref() == SET_COOKIE.as_str() { |
| 206 | return Ok(()); |
| 207 | } |
| 208 | |
| 209 | let mut value = coerce_to_string(&ctx, value)?; |
| 210 | // Reject values containing null bytes or bare CR/LF; |
| 211 | // `normalize_header_value_inplace` silently strips them, but |
| 212 | // `header-setcookie` expects a TypeError for such values. |
| 213 | if value.contains('\0') || has_bare_cr_lf(&value) { |
| 214 | return Err(Exception::throw_type(&ctx, "Invalid header value")); |
| 215 | } |
| 216 | normalize_header_value_inplace(&ctx, &mut value)?; |
| 217 | // Value-based forbidden header check (must run after value normalisation). |
| 218 | if matches!( |
| 219 | self.guard, |
| 220 | HeadersGuard::Request | HeadersGuard::RequestNoCors |
| 221 | ) && is_forbidden_method_override(&key, &value) |
| 222 | { |
| 223 | return Ok(()); |
| 224 | } |
| 225 | if self.guard == HeadersGuard::RequestNoCors { |
| 226 | let val = value.split(',').next().unwrap_or("").trim(); |
| 227 | if !is_cors_safelisted_request_header(&key, val) { |
| 228 | return Ok(()); // silently ignore disallowed header |
| 229 | } |
| 230 | value = val.into(); |
| 231 | } |
| 232 | if !is_http_header_value(&value) { |
| 233 | return Err(Exception::throw_type(&ctx, "Invalid value of key")); |
| 234 | } |
| 235 | |
| 236 | if key.as_ref() == SET_COOKIE.as_str() { |
| 237 | self.headers.retain(|(k, _)| k != &key); |
| 238 | self.headers.push((key, value.into())); |
| 239 | } else { |
| 240 | match self.headers.iter_mut().find(|(k, _)| k == &key) { |
| 241 | Some((_, existing_value)) => *existing_value = value.into(), |
| 242 | None => { |
| 243 | self.headers.push((key, value.into())); |
| 244 | }, |
| 245 | } |
| 246 | } |
| 247 | Ok(()) |