|
1 | 1 | use bytes::{Bytes, BytesMut}; |
2 | 2 |
|
| 3 | +use core::ops::RangeBounds; |
3 | 4 | use std::convert::TryFrom; |
4 | 5 | use std::error::Error; |
5 | 6 | use std::fmt::Write; |
@@ -152,6 +153,62 @@ impl HeaderValue { |
152 | 153 | HeaderValue::try_from_generic(src, Bytes::copy_from_slice) |
153 | 154 | } |
154 | 155 |
|
| 156 | + /// Convert a [`Bytes`] into a `HeaderValue`. |
| 157 | + /// |
| 158 | + /// This function will not perform any copying. |
| 159 | + /// |
| 160 | + /// If the argument contains invalid header value bytes, an error is |
| 161 | + /// returned. Only byte values between 32 and 255 (inclusive) are permitted, |
| 162 | + /// excluding byte 127 (DEL). |
| 163 | + /// |
| 164 | + /// # Examples |
| 165 | + /// |
| 166 | + /// ``` |
| 167 | + /// # use http::header::HeaderValue; |
| 168 | + /// # use bytes::Bytes; |
| 169 | + /// let bytes = Bytes::from_static(b"hello\xfa"); |
| 170 | + /// let val = HeaderValue::from_shared(bytes).unwrap(); |
| 171 | + /// assert_eq!(val, &b"hello\xfa"[..]); |
| 172 | + /// ``` |
| 173 | + pub fn from_shared(src: Bytes) -> Result<HeaderValue, InvalidHeaderValue> { |
| 174 | + for &b in src.as_ref() { |
| 175 | + if !is_valid(b) { |
| 176 | + return Err(InvalidHeaderValue { _priv: () }); |
| 177 | + } |
| 178 | + } |
| 179 | + Ok(HeaderValue { |
| 180 | + inner: src, |
| 181 | + is_sensitive: false, |
| 182 | + }) |
| 183 | + } |
| 184 | + |
| 185 | + /// Convert a [`Bytes`] directly into a `HeaderValue` without validating. |
| 186 | + /// |
| 187 | + /// This function does NOT validate that illegal bytes are not contained |
| 188 | + /// within the buffer. |
| 189 | + /// |
| 190 | + /// ## Panics |
| 191 | + /// In a debug build this will panic if `src` is not valid UTF-8. |
| 192 | + /// |
| 193 | + /// ## Safety |
| 194 | + /// `src` must contain valid UTF-8. In a release build it is undefined |
| 195 | + /// behaviour to call this with `src` that is not valid UTF-8. |
| 196 | + pub unsafe fn from_shared_unchecked(src: Bytes) -> HeaderValue { |
| 197 | + if cfg!(debug_assertions) { |
| 198 | + match HeaderValue::from_shared(src) { |
| 199 | + Ok(val) => val, |
| 200 | + Err(_err) => { |
| 201 | + panic!("HeaderValue::from_shared_unchecked() with invalid bytes"); |
| 202 | + } |
| 203 | + } |
| 204 | + } else { |
| 205 | + return HeaderValue { |
| 206 | + inner: src, |
| 207 | + is_sensitive: false, |
| 208 | + }; |
| 209 | + } |
| 210 | + } |
| 211 | + |
155 | 212 | /// Attempt to convert a `Bytes` buffer to a `HeaderValue`. |
156 | 213 | /// |
157 | 214 | /// This will try to prevent a copy if the type passed is the type used |
@@ -205,10 +262,6 @@ impl HeaderValue { |
205 | 262 | } |
206 | 263 | } |
207 | 264 |
|
208 | | - fn from_shared(src: Bytes) -> Result<HeaderValue, InvalidHeaderValue> { |
209 | | - HeaderValue::try_from_generic(src, std::convert::identity) |
210 | | - } |
211 | | - |
212 | 265 | fn try_from_generic<T: AsRef<[u8]>, F: FnOnce(T) -> Bytes>( |
213 | 266 | src: T, |
214 | 267 | into: F, |
@@ -296,6 +349,148 @@ impl HeaderValue { |
296 | 349 | self.as_ref() |
297 | 350 | } |
298 | 351 |
|
| 352 | + /// Creates a new [`Bytes`] object from this `HeaderValue`. |
| 353 | + /// |
| 354 | + /// This function will not perform any copying. Rather, it allocates a new, |
| 355 | + /// owned `Bytes` object (which will be, at the time of writing, roughly 4 |
| 356 | + /// pointers in size) which will claim a reference-counted borrow of the |
| 357 | + /// shared memory region that backs this `HeaderValue`. Because of the |
| 358 | + /// Arc-like semantics of `Bytes` and `HeaderValue`s, this `HeaderValue` and |
| 359 | + /// the returned `Bytes` can be dropped in any order and the shared memory |
| 360 | + /// region will remain valid for the other. |
| 361 | + /// |
| 362 | + /// # Examples |
| 363 | + /// |
| 364 | + /// ``` |
| 365 | + /// # use http::header::HeaderValue; |
| 366 | + /// # use bytes::Bytes; |
| 367 | + /// let val = HeaderValue::from_static("hello"); |
| 368 | + /// assert_eq!(val.as_shared(), Bytes::from_static(b"hello")); |
| 369 | + /// ``` |
| 370 | + #[inline] |
| 371 | + pub fn as_shared(&self) -> Bytes { |
| 372 | + self.inner.clone() |
| 373 | + } |
| 374 | + |
| 375 | + /// Return the inner [`Bytes`] object, consuming this `HeaderValue`. |
| 376 | + /// |
| 377 | + /// Unlike [`as_shared()`], this method will not increment the reference |
| 378 | + /// count of the shared memory region that backs this `HeaderValue`. This |
| 379 | + /// method consumes `self` to do so. |
| 380 | + /// |
| 381 | + /// # Examples |
| 382 | + /// |
| 383 | + /// ``` |
| 384 | + /// # use http::header::HeaderValue; |
| 385 | + /// # use bytes::Bytes; |
| 386 | + /// let val = HeaderValue::from_static("hello"); |
| 387 | + /// assert_eq!(val.into_shared(), Bytes::from_static(b"hello")); |
| 388 | + /// ``` |
| 389 | + /// |
| 390 | + /// [`as_shared()`]: Self::as_shared() |
| 391 | + #[inline] |
| 392 | + pub fn into_shared(self) -> Bytes { |
| 393 | + self.inner |
| 394 | + } |
| 395 | + |
| 396 | + /// Returns a slice of `self` over the provided range. |
| 397 | + /// |
| 398 | + /// |
| 399 | + /// This function will not perform any copying. Rather, it allocates a new, |
| 400 | + /// owned `HeaderValue` object (which will be, at the time of writing, |
| 401 | + /// roughly 5 pointers in size) which will claim a reference-counted borrow |
| 402 | + /// of the shared memory region that backs this `HeaderValue`. Because of the |
| 403 | + /// Arc-like semantics of`HeaderValue`s, this and the resulting |
| 404 | + /// `HeaderValue` can be dropped in any order and the shared memory region |
| 405 | + /// will remain valid for the other. |
| 406 | + /// |
| 407 | + /// If the `range` would be out of bounds, this function will return `None`. |
| 408 | + /// |
| 409 | + /// # Examples |
| 410 | + /// |
| 411 | + /// ``` |
| 412 | + /// # use http::header::HeaderValue; |
| 413 | + /// let val = HeaderValue::from_static(r#"W/"67ab43", "54ed21", "7892dd""#); |
| 414 | + /// let slice_1 = val.try_slice(0..10).unwrap(); |
| 415 | + /// let slice_3 = val.try_slice(22..30).unwrap(); |
| 416 | + /// assert_eq!(slice_1, r#"W/"67ab43""#); |
| 417 | + /// assert_eq!(slice_3, "\"7892dd\""); |
| 418 | + /// assert!(val.try_slice(0..31).is_none()); |
| 419 | + /// ``` |
| 420 | + #[inline] |
| 421 | + pub fn try_slice(&self, range: impl RangeBounds<usize>) -> Option<HeaderValue> { |
| 422 | + use core::ops::Bound; |
| 423 | + |
| 424 | + let len = self.len(); |
| 425 | + |
| 426 | + let begin = match range.start_bound() { |
| 427 | + Bound::Included(&n) => n, |
| 428 | + Bound::Excluded(&n) => { |
| 429 | + if let Some(n) = n.checked_add(1) { |
| 430 | + n |
| 431 | + } else { |
| 432 | + return None; |
| 433 | + } |
| 434 | + } |
| 435 | + Bound::Unbounded => 0, |
| 436 | + }; |
| 437 | + |
| 438 | + let end = match range.end_bound() { |
| 439 | + Bound::Included(&n) => { |
| 440 | + if let Some(n) = n.checked_add(1) { |
| 441 | + n |
| 442 | + } else { |
| 443 | + return None; |
| 444 | + } |
| 445 | + } |
| 446 | + Bound::Excluded(&n) => n, |
| 447 | + Bound::Unbounded => len, |
| 448 | + }; |
| 449 | + |
| 450 | + if begin > end { |
| 451 | + return None; |
| 452 | + } |
| 453 | + if end > len { |
| 454 | + return None; |
| 455 | + } |
| 456 | + |
| 457 | + Some(self.slice(range)) |
| 458 | + } |
| 459 | + |
| 460 | + /// Returns a slice of `self` over the provided range. |
| 461 | + /// |
| 462 | + /// |
| 463 | + /// This function will not perform any copying. Rather, it allocates a new, |
| 464 | + /// owned `HeaderValue` object (which will be, at the time of writing, |
| 465 | + /// roughly 5 pointers in size) which will claim a reference-counted borrow |
| 466 | + /// of the shared memory region that backs this `HeaderValue`. Because of the |
| 467 | + /// Arc-like semantics of`HeaderValue`s, this and the resulting |
| 468 | + /// `HeaderValue` can be dropped in any order and the shared memory region |
| 469 | + /// will remain valid for the other. |
| 470 | + /// |
| 471 | + /// # Panics |
| 472 | + /// |
| 473 | + /// Requires that `begin <= end` and `end <= self.len()`, otherwise slicing |
| 474 | + /// will panic. |
| 475 | + /// |
| 476 | + /// # Examples |
| 477 | + /// |
| 478 | + /// ``` |
| 479 | + /// # use http::header::HeaderValue; |
| 480 | + /// let val = HeaderValue::from_static(r#"W/"67ab43", "54ed21", "7892dd""#); |
| 481 | + /// let slice_1 = val.slice(0..10); |
| 482 | + /// let slice_3 = val.slice(22..30); |
| 483 | + /// assert_eq!(slice_1, r#"W/"67ab43""#); |
| 484 | + /// assert_eq!(slice_3, "\"7892dd\""); |
| 485 | + /// ``` |
| 486 | + #[inline] |
| 487 | + pub fn slice(&self, range: impl RangeBounds<usize>) -> HeaderValue { |
| 488 | + let inner_slice = self.inner.slice(range); |
| 489 | + // SAFETY: Any subslice of `self.inner` should be just as valid for use |
| 490 | + // as a HeaderValue as the full `self.inner` buffer. |
| 491 | + unsafe { HeaderValue::from_shared_unchecked(inner_slice) } |
| 492 | + } |
| 493 | + |
299 | 494 | /// Mark that the header value represents sensitive information. |
300 | 495 | /// |
301 | 496 | /// # Examples |
@@ -386,7 +581,7 @@ impl From<HeaderName> for HeaderValue { |
386 | 581 | #[inline] |
387 | 582 | fn from(h: HeaderName) -> HeaderValue { |
388 | 583 | HeaderValue { |
389 | | - inner: h.into_bytes(), |
| 584 | + inner: h.into_shared(), |
390 | 585 | is_sensitive: false, |
391 | 586 | } |
392 | 587 | } |
|
0 commit comments