Skip to content

Commit

Permalink
Allow StatusCode to support 600-999 range
Browse files Browse the repository at this point in the history
Additionally, reduces the size of the static memory included, and reduces some of the complicated macro usage resulting in faster compiles!

Co-authored-by: quininer <quininer@live.com>
Co-authored-by: David Kellum <dek-oss@gravitext.com>
  • Loading branch information
dekellum and quininer authored Dec 8, 2020
1 parent b00473c commit be05ed2
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 104 deletions.
146 changes: 87 additions & 59 deletions src/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,22 @@
//! ```

use std::convert::TryFrom;
use std::num::NonZeroU16;
use std::error::Error;
use std::fmt;
use std::str::FromStr;

/// An HTTP status code (`status-code` in RFC 7230 et al.).
///
/// This type contains constants for all common status codes.
/// It allows status codes in the range [100, 599].
/// Constants are provided for known status codes, including those in the IANA
/// [HTTP Status Code Registry](
/// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml).
///
/// IANA maintain the [Hypertext Transfer Protocol (HTTP) Status Code
/// Registry](http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml) which is
/// the source for this enum (with one exception, 418 I'm a teapot, which is
/// inexplicably not in the register).
/// Status code values in the range 100-999 (inclusive) are supported by this
/// type. Values in the range 100-599 are semantically classified by the most
/// significant digit. See [`StatusCode::is_success`], etc. Values above 599
/// are unclassified but allowed for legacy compatibility, though their use is
/// discouraged. Applications may interpret such values as protocol errors.
///
/// # Examples
///
Expand All @@ -39,12 +42,12 @@ use std::str::FromStr;
/// assert!(StatusCode::OK.is_success());
/// ```
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StatusCode(u16);
pub struct StatusCode(NonZeroU16);

/// A possible error value when converting a `StatusCode` from a `u16` or `&str`
///
/// This error indicates that the supplied input was not a valid number, was less
/// than 100, or was greater than 599.
/// than 100, or was greater than 999.
pub struct InvalidStatusCode {
_priv: (),
}
Expand All @@ -53,7 +56,7 @@ impl StatusCode {
/// Converts a u16 to a status code.
///
/// The function validates the correctness of the supplied u16. It must be
/// greater or equal to 100 but less than 600.
/// greater or equal to 100 and less than 1000.
///
/// # Example
///
Expand All @@ -68,11 +71,13 @@ impl StatusCode {
/// ```
#[inline]
pub fn from_u16(src: u16) -> Result<StatusCode, InvalidStatusCode> {
if src < 100 || src >= 600 {
if src < 100 || src >= 1000 {
return Err(InvalidStatusCode::new());
}

Ok(StatusCode(src))
NonZeroU16::new(src)
.map(StatusCode)
.ok_or_else(InvalidStatusCode::new)
}

/// Converts a &[u8] to a status code
Expand All @@ -85,12 +90,14 @@ impl StatusCode {
let b = src[1].wrapping_sub(b'0') as u16;
let c = src[2].wrapping_sub(b'0') as u16;

if a == 0 || a > 5 || b > 9 || c > 9 {
if a == 0 || a > 9 || b > 9 || c > 9 {
return Err(InvalidStatusCode::new());
}

let status = (a * 100) + (b * 10) + c;
Ok(StatusCode(status))
NonZeroU16::new(status)
.map(StatusCode)
.ok_or_else(InvalidStatusCode::new)
}

/// Returns the `u16` corresponding to this `StatusCode`.
Expand Down Expand Up @@ -126,7 +133,17 @@ impl StatusCode {
/// ```
#[inline]
pub fn as_str(&self) -> &str {
CODES_AS_STR[(self.0 - 100) as usize]
let offset = (self.0.get() - 100) as usize;
let offset = offset * 3;

// Invariant: self has checked range [100, 999] and CODE_DIGITS is
// ASCII-only, of length 900 * 3 = 2700 bytes

#[cfg(debug_assertions)]
{ &CODE_DIGITS[offset..offset+3] }

#[cfg(not(debug_assertions))]
unsafe { CODE_DIGITS.get_unchecked(offset..offset+3) }
}

/// Get the standardised `reason-phrase` for this status code.
Expand All @@ -148,37 +165,37 @@ impl StatusCode {
/// assert_eq!(status.canonical_reason(), Some("OK"));
/// ```
pub fn canonical_reason(&self) -> Option<&'static str> {
canonical_reason(self.0)
canonical_reason(self.0.get())
}

/// Check if status is within 100-199.
#[inline]
pub fn is_informational(&self) -> bool {
200 > self.0 && self.0 >= 100
200 > self.0.get() && self.0.get() >= 100
}

/// Check if status is within 200-299.
#[inline]
pub fn is_success(&self) -> bool {
300 > self.0 && self.0 >= 200
300 > self.0.get() && self.0.get() >= 200
}

/// Check if status is within 300-399.
#[inline]
pub fn is_redirection(&self) -> bool {
400 > self.0 && self.0 >= 300
400 > self.0.get() && self.0.get() >= 300
}

/// Check if status is within 400-499.
#[inline]
pub fn is_client_error(&self) -> bool {
500 > self.0 && self.0 >= 400
500 > self.0.get() && self.0.get() >= 400
}

/// Check if status is within 500-599.
#[inline]
pub fn is_server_error(&self) -> bool {
600 > self.0 && self.0 >= 500
600 > self.0.get() && self.0.get() >= 500
}
}

Expand Down Expand Up @@ -231,7 +248,7 @@ impl PartialEq<StatusCode> for u16 {
impl From<StatusCode> for u16 {
#[inline]
fn from(status: StatusCode) -> u16 {
status.0
status.0.get()
}
}

Expand Down Expand Up @@ -287,7 +304,7 @@ macro_rules! status_codes {
impl StatusCode {
$(
$(#[$docs])*
pub const $konst: StatusCode = StatusCode($num);
pub const $konst: StatusCode = StatusCode(unsafe { NonZeroU16::new_unchecked($num) });
)+

}
Expand Down Expand Up @@ -521,40 +538,51 @@ impl fmt::Display for InvalidStatusCode {

impl Error for InvalidStatusCode {}

macro_rules! status_code_strs {
($($num:expr,)+) => {
const CODES_AS_STR: [&'static str; 500] = [ $( stringify!($num), )+ ];
}
}

status_code_strs!(
100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,

200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,

300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359,
360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379,
380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399,

400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419,
420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439,
440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459,
460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479,
480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499,

500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519,
520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539,
540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559,
560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579,
580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599,
);
// A string of packed 3-ASCII-digit status code values for the supported range
// of [100, 999] (900 codes, 2700 bytes).
const CODE_DIGITS: &'static str = "\
100101102103104105106107108109110111112113114115116117118119\
120121122123124125126127128129130131132133134135136137138139\
140141142143144145146147148149150151152153154155156157158159\
160161162163164165166167168169170171172173174175176177178179\
180181182183184185186187188189190191192193194195196197198199\
200201202203204205206207208209210211212213214215216217218219\
220221222223224225226227228229230231232233234235236237238239\
240241242243244245246247248249250251252253254255256257258259\
260261262263264265266267268269270271272273274275276277278279\
280281282283284285286287288289290291292293294295296297298299\
300301302303304305306307308309310311312313314315316317318319\
320321322323324325326327328329330331332333334335336337338339\
340341342343344345346347348349350351352353354355356357358359\
360361362363364365366367368369370371372373374375376377378379\
380381382383384385386387388389390391392393394395396397398399\
400401402403404405406407408409410411412413414415416417418419\
420421422423424425426427428429430431432433434435436437438439\
440441442443444445446447448449450451452453454455456457458459\
460461462463464465466467468469470471472473474475476477478479\
480481482483484485486487488489490491492493494495496497498499\
500501502503504505506507508509510511512513514515516517518519\
520521522523524525526527528529530531532533534535536537538539\
540541542543544545546547548549550551552553554555556557558559\
560561562563564565566567568569570571572573574575576577578579\
580581582583584585586587588589590591592593594595596597598599\
600601602603604605606607608609610611612613614615616617618619\
620621622623624625626627628629630631632633634635636637638639\
640641642643644645646647648649650651652653654655656657658659\
660661662663664665666667668669670671672673674675676677678679\
680681682683684685686687688689690691692693694695696697698699\
700701702703704705706707708709710711712713714715716717718719\
720721722723724725726727728729730731732733734735736737738739\
740741742743744745746747748749750751752753754755756757758759\
760761762763764765766767768769770771772773774775776777778779\
780781782783784785786787788789790791792793794795796797798799\
800801802803804805806807808809810811812813814815816817818819\
820821822823824825826827828829830831832833834835836837838839\
840841842843844845846847848849850851852853854855856857858859\
860861862863864865866867868869870871872873874875876877878879\
880881882883884885886887888889890891892893894895896897898899\
900901902903904905906907908909910911912913914915916917918919\
920921922923924925926927928929930931932933934935936937938939\
940941942943944945946947948949950951952953954955956957958959\
960961962963964965966967968969970971972973974975976977978979\
980981982983984985986987988989990991992993994995996997998999";
54 changes: 9 additions & 45 deletions tests/status_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ use http::*;
#[test]
fn from_bytes() {
for ok in &[
"100", "101", "199", "200", "250", "299", "321", "399", "499", "599",
"100", "101", "199", "200", "250", "299", "321", "399", "499", "599", "600", "999"
] {
assert!(StatusCode::from_bytes(ok.as_bytes()).is_ok());
}

for not_ok in &[
"0", "00", "10", "40", "99", "000", "010", "099", "600", "610", "999",
"0", "00", "10", "40", "99", "000", "010", "099", "1000", "1999",
] {
assert!(StatusCode::from_bytes(not_ok.as_bytes()).is_err());
}
Expand All @@ -22,48 +22,12 @@ fn equates_with_u16() {
assert_eq!(status, 200u16);
}

macro_rules! test_round_trip {
($($num:expr,)+) => {
#[test]
fn roundtrip() {
$(
let status = StatusCode::from_bytes(stringify!($num).as_bytes()).unwrap();
let expect = $num;

assert_eq!(u16::from(status), expect);
)+
}
#[test]
fn roundtrip() {
for s in 100..1000 {
let sstr = s.to_string();
let status = StatusCode::from_bytes(sstr.as_bytes()).unwrap();
assert_eq!(s, u16::from(status));
assert_eq!(sstr, status.as_str());
}
}

test_round_trip!(
100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,

200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,

300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359,
360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379,
380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399,

400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419,
420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439,
440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459,
460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479,
480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499,

500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519,
520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539,
540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559,
560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579,
580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599,
);

0 comments on commit be05ed2

Please sign in to comment.