diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index fcb05dd37..b2c6b8e0a 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -101,3 +101,7 @@ rust-tls = { version="0.17", package = "rustls" } [[bench]] name = "content-length" harness = false + +[[bench]] +name = "status-line" +harness = false diff --git a/actix-http/benches/status-line.rs b/actix-http/benches/status-line.rs new file mode 100644 index 000000000..51f840f89 --- /dev/null +++ b/actix-http/benches/status-line.rs @@ -0,0 +1,222 @@ +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; + +use bytes::BytesMut; +use http::Version; + +const CODES: &[u16] = &[201, 303, 404, 515]; + +fn bench_write_status_line_11(c: &mut Criterion) { + let mut group = c.benchmark_group("write_status_line v1.1"); + + let version = Version::HTTP_11; + + for i in CODES.iter() { + group.bench_with_input(BenchmarkId::new("Original (unsafe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _original::write_status_line(version, i, &mut b); + }) + }); + + group.bench_with_input(BenchmarkId::new("New (safe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _new::write_status_line(version, i, &mut b); + }) + }); + + group.bench_with_input(BenchmarkId::new("Naive", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _naive::write_status_line(version, i, &mut b); + }) + }); + } + + group.finish(); +} + +fn bench_write_status_line_10(c: &mut Criterion) { + let mut group = c.benchmark_group("write_status_line v1.0"); + + let version = Version::HTTP_10; + + for i in CODES.iter() { + group.bench_with_input(BenchmarkId::new("Original (unsafe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _original::write_status_line(version, i, &mut b); + }) + }); + + group.bench_with_input(BenchmarkId::new("New (safe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _new::write_status_line(version, i, &mut b); + }) + }); + + group.bench_with_input(BenchmarkId::new("Naive", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _naive::write_status_line(version, i, &mut b); + }) + }); + } + + group.finish(); +} + +fn bench_write_status_line_09(c: &mut Criterion) { + let mut group = c.benchmark_group("write_status_line v0.9"); + + let version = Version::HTTP_09; + + for i in CODES.iter() { + group.bench_with_input(BenchmarkId::new("Original (unsafe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _original::write_status_line(version, i, &mut b); + }) + }); + + group.bench_with_input(BenchmarkId::new("New (safe)", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _new::write_status_line(version, i, &mut b); + }) + }); + + group.bench_with_input(BenchmarkId::new("Naive", i), i, |b, &i| { + b.iter(|| { + let mut b = BytesMut::with_capacity(35); + _naive::write_status_line(version, i, &mut b); + }) + }); + } + + group.finish(); +} + +criterion_group!( + benches, + bench_write_status_line_11, + bench_write_status_line_10, + bench_write_status_line_09 +); +criterion_main!(benches); + +mod _naive { + use bytes::{BufMut, BytesMut}; + use http::Version; + + pub(crate) fn write_status_line(version: Version, n: u16, bytes: &mut BytesMut) { + match version { + Version::HTTP_11 => bytes.put_slice(b"HTTP/1.1 "), + Version::HTTP_10 => bytes.put_slice(b"HTTP/1.0 "), + Version::HTTP_09 => bytes.put_slice(b"HTTP/0.9 "), + _ => { + // other HTTP version handlers do not use this method + } + } + + bytes.put_slice(n.to_string().as_bytes()); + } +} + +mod _new { + use bytes::{BufMut, BytesMut}; + use http::Version; + + const DIGITS_START: u8 = b'0'; + + pub(crate) fn write_status_line(version: Version, n: u16, bytes: &mut BytesMut) { + match version { + Version::HTTP_11 => bytes.put_slice(b"HTTP/1.1 "), + Version::HTTP_10 => bytes.put_slice(b"HTTP/1.0 "), + Version::HTTP_09 => bytes.put_slice(b"HTTP/0.9 "), + _ => { + // other HTTP version handlers do not use this method + } + } + + let d100 = (n / 100) as u8; + let d10 = ((n / 10) % 10) as u8; + let d1 = (n % 10) as u8; + + bytes.put_u8(DIGITS_START + d100); + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); + + bytes.put_u8(b' '); + } +} + +mod _original { + use std::ptr; + + use bytes::{BufMut, BytesMut}; + use http::Version; + + const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\ + 2021222324252627282930313233343536373839\ + 4041424344454647484950515253545556575859\ + 6061626364656667686970717273747576777879\ + 8081828384858687888990919293949596979899"; + + pub(crate) const STATUS_LINE_BUF_SIZE: usize = 13; + + pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesMut) { + let mut buf: [u8; STATUS_LINE_BUF_SIZE] = *b"HTTP/1.1 "; + + match version { + Version::HTTP_2 => buf[5] = b'2', + Version::HTTP_10 => buf[7] = b'0', + Version::HTTP_09 => { + buf[5] = b'0'; + buf[7] = b'9'; + } + _ => (), + } + + let mut curr: isize = 12; + let buf_ptr = buf.as_mut_ptr(); + let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + let four = n > 999; + + // decode 2 more chars, if > 2 chars + let d1 = (n % 100) << 1; + n /= 100; + curr -= 2; + unsafe { + ptr::copy_nonoverlapping( + lut_ptr.offset(d1 as isize), + buf_ptr.offset(curr), + 2, + ); + } + + // decode last 1 or 2 chars + if n < 10 { + curr -= 1; + unsafe { + *buf_ptr.offset(curr) = (n as u8) + b'0'; + } + } else { + let d1 = n << 1; + curr -= 2; + unsafe { + ptr::copy_nonoverlapping( + lut_ptr.offset(d1 as isize), + buf_ptr.offset(curr), + 2, + ); + } + } + + bytes.put_slice(&buf); + if four { + bytes.put_u8(b' '); + } + } +} diff --git a/actix-http/src/extensions.rs b/actix-http/src/extensions.rs index 6a4a034a4..4e3918537 100644 --- a/actix-http/src/extensions.rs +++ b/actix-http/src/extensions.rs @@ -6,6 +6,8 @@ use fxhash::FxHashMap; #[derive(Default)] /// A type map of request extensions. pub struct Extensions { + /// Use FxHasher with a std HashMap with for faster + /// lookups on the small `TypeId` (u64 equivalent) keys. map: FxHashMap>, } diff --git a/actix-http/src/header/common/range.rs b/actix-http/src/header/common/range.rs index fc1bc8159..f9e203bb2 100644 --- a/actix-http/src/header/common/range.rs +++ b/actix-http/src/header/common/range.rs @@ -183,13 +183,13 @@ impl fmt::Display for Range { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Range::Bytes(ref ranges) => { - try!(write!(f, "bytes=")); + write!(f, "bytes=")?; for (i, range) in ranges.iter().enumerate() { if i != 0 { - try!(f.write_str(",")); + f.write_str(",")?; } - try!(Display::fmt(range, f)); + Display::fmt(range, f)?; } Ok(()) } @@ -214,9 +214,9 @@ impl FromStr for Range { } Ok(Range::Bytes(ranges)) } - (Some(unit), Some(range_str)) if unit != "" && range_str != "" => Ok( - Range::Unregistered(unit.to_owned(), range_str.to_owned()), - ), + (Some(unit), Some(range_str)) if unit != "" && range_str != "" => { + Ok(Range::Unregistered(unit.to_owned(), range_str.to_owned())) + } _ => Err(::Error::Header), } } @@ -229,7 +229,8 @@ impl FromStr for ByteRangeSpec { let mut parts = s.splitn(2, '-'); match (parts.next(), parts.next()) { - (Some(""), Some(end)) => end.parse() + (Some(""), Some(end)) => end + .parse() .or(Err(::Error::Header)) .map(ByteRangeSpec::Last), (Some(start), Some("")) => start @@ -272,163 +273,138 @@ impl Header for Range { } } -#[test] -fn test_parse_bytes_range_valid() { - let r: Range = Header::parse_header(&"bytes=1-100".into()).unwrap(); - let r2: Range = Header::parse_header(&"bytes=1-100,-".into()).unwrap(); - let r3 = Range::bytes(1, 100); - assert_eq!(r, r2); - assert_eq!(r2, r3); +#[cfg(test)] +mod tests { + use super::*; - let r: Range = Header::parse_header(&"bytes=1-100,200-".into()).unwrap(); - let r2: Range = - Header::parse_header(&"bytes= 1-100 , 101-xxx, 200- ".into()).unwrap(); - let r3 = Range::Bytes(vec![ - ByteRangeSpec::FromTo(1, 100), - ByteRangeSpec::AllFrom(200), - ]); - assert_eq!(r, r2); - assert_eq!(r2, r3); + #[test] + fn test_parse_bytes_range_valid() { + let r: Range = Header::parse_header(&"bytes=1-100".into()).unwrap(); + let r2: Range = Header::parse_header(&"bytes=1-100,-".into()).unwrap(); + let r3 = Range::bytes(1, 100); + assert_eq!(r, r2); + assert_eq!(r2, r3); - let r: Range = Header::parse_header(&"bytes=1-100,-100".into()).unwrap(); - let r2: Range = Header::parse_header(&"bytes=1-100, ,,-100".into()).unwrap(); - let r3 = Range::Bytes(vec![ - ByteRangeSpec::FromTo(1, 100), - ByteRangeSpec::Last(100), - ]); - assert_eq!(r, r2); - assert_eq!(r2, r3); + let r: Range = Header::parse_header(&"bytes=1-100,200-".into()).unwrap(); + let r2: Range = + Header::parse_header(&"bytes= 1-100 , 101-xxx, 200- ".into()).unwrap(); + let r3 = Range::Bytes(vec![ + ByteRangeSpec::FromTo(1, 100), + ByteRangeSpec::AllFrom(200), + ]); + assert_eq!(r, r2); + assert_eq!(r2, r3); - let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap(); - let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); - assert_eq!(r, r2); -} - -#[test] -fn test_parse_unregistered_range_valid() { - let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap(); - let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); - assert_eq!(r, r2); - - let r: Range = Header::parse_header(&"custom=abcd".into()).unwrap(); - let r2 = Range::Unregistered("custom".to_owned(), "abcd".to_owned()); - assert_eq!(r, r2); - - let r: Range = Header::parse_header(&"custom=xxx-yyy".into()).unwrap(); - let r2 = Range::Unregistered("custom".to_owned(), "xxx-yyy".to_owned()); - assert_eq!(r, r2); -} - -#[test] -fn test_parse_invalid() { - let r: ::Result = Header::parse_header(&"bytes=1-a,-".into()); - assert_eq!(r.ok(), None); - - let r: ::Result = Header::parse_header(&"bytes=1-2-3".into()); - assert_eq!(r.ok(), None); - - let r: ::Result = Header::parse_header(&"abc".into()); - assert_eq!(r.ok(), None); - - let r: ::Result = Header::parse_header(&"bytes=1-100=".into()); - assert_eq!(r.ok(), None); - - let r: ::Result = Header::parse_header(&"bytes=".into()); - assert_eq!(r.ok(), None); - - let r: ::Result = Header::parse_header(&"custom=".into()); - assert_eq!(r.ok(), None); - - let r: ::Result = Header::parse_header(&"=1-100".into()); - assert_eq!(r.ok(), None); -} - -#[test] -fn test_fmt() { - use header::Headers; - - let mut headers = Headers::new(); - - headers.set(Range::Bytes(vec![ - ByteRangeSpec::FromTo(0, 1000), - ByteRangeSpec::AllFrom(2000), - ])); - assert_eq!(&headers.to_string(), "Range: bytes=0-1000,2000-\r\n"); - - headers.clear(); - headers.set(Range::Bytes(vec![])); - - assert_eq!(&headers.to_string(), "Range: bytes=\r\n"); - - headers.clear(); - headers.set(Range::Unregistered( - "custom".to_owned(), - "1-xxx".to_owned(), - )); - - assert_eq!(&headers.to_string(), "Range: custom=1-xxx\r\n"); -} - -#[test] -fn test_byte_range_spec_to_satisfiable_range() { - assert_eq!( - Some((0, 0)), - ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(3) - ); - assert_eq!( - Some((1, 2)), - ByteRangeSpec::FromTo(1, 2).to_satisfiable_range(3) - ); - assert_eq!( - Some((1, 2)), - ByteRangeSpec::FromTo(1, 5).to_satisfiable_range(3) - ); - assert_eq!( - None, - ByteRangeSpec::FromTo(3, 3).to_satisfiable_range(3) - ); - assert_eq!( - None, - ByteRangeSpec::FromTo(2, 1).to_satisfiable_range(3) - ); - assert_eq!( - None, - ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(0) - ); - - assert_eq!( - Some((0, 2)), - ByteRangeSpec::AllFrom(0).to_satisfiable_range(3) - ); - assert_eq!( - Some((2, 2)), - ByteRangeSpec::AllFrom(2).to_satisfiable_range(3) - ); - assert_eq!( - None, - ByteRangeSpec::AllFrom(3).to_satisfiable_range(3) - ); - assert_eq!( - None, - ByteRangeSpec::AllFrom(5).to_satisfiable_range(3) - ); - assert_eq!( - None, - ByteRangeSpec::AllFrom(0).to_satisfiable_range(0) - ); - - assert_eq!( - Some((1, 2)), - ByteRangeSpec::Last(2).to_satisfiable_range(3) - ); - assert_eq!( - Some((2, 2)), - ByteRangeSpec::Last(1).to_satisfiable_range(3) - ); - assert_eq!( - Some((0, 2)), - ByteRangeSpec::Last(5).to_satisfiable_range(3) - ); - assert_eq!(None, ByteRangeSpec::Last(0).to_satisfiable_range(3)); - assert_eq!(None, ByteRangeSpec::Last(2).to_satisfiable_range(0)); + let r: Range = Header::parse_header(&"bytes=1-100,-100".into()).unwrap(); + let r2: Range = Header::parse_header(&"bytes=1-100, ,,-100".into()).unwrap(); + let r3 = Range::Bytes(vec![ + ByteRangeSpec::FromTo(1, 100), + ByteRangeSpec::Last(100), + ]); + assert_eq!(r, r2); + assert_eq!(r2, r3); + + let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap(); + let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); + assert_eq!(r, r2); + } + + #[test] + fn test_parse_unregistered_range_valid() { + let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap(); + let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned()); + assert_eq!(r, r2); + + let r: Range = Header::parse_header(&"custom=abcd".into()).unwrap(); + let r2 = Range::Unregistered("custom".to_owned(), "abcd".to_owned()); + assert_eq!(r, r2); + + let r: Range = Header::parse_header(&"custom=xxx-yyy".into()).unwrap(); + let r2 = Range::Unregistered("custom".to_owned(), "xxx-yyy".to_owned()); + assert_eq!(r, r2); + } + + #[test] + fn test_parse_invalid() { + let r: ::Result = Header::parse_header(&"bytes=1-a,-".into()); + assert_eq!(r.ok(), None); + + let r: ::Result = Header::parse_header(&"bytes=1-2-3".into()); + assert_eq!(r.ok(), None); + + let r: ::Result = Header::parse_header(&"abc".into()); + assert_eq!(r.ok(), None); + + let r: ::Result = Header::parse_header(&"bytes=1-100=".into()); + assert_eq!(r.ok(), None); + + let r: ::Result = Header::parse_header(&"bytes=".into()); + assert_eq!(r.ok(), None); + + let r: ::Result = Header::parse_header(&"custom=".into()); + assert_eq!(r.ok(), None); + + let r: ::Result = Header::parse_header(&"=1-100".into()); + assert_eq!(r.ok(), None); + } + + #[test] + fn test_fmt() { + use header::Headers; + + let mut headers = Headers::new(); + + headers.set(Range::Bytes(vec![ + ByteRangeSpec::FromTo(0, 1000), + ByteRangeSpec::AllFrom(2000), + ])); + assert_eq!(&headers.to_string(), "Range: bytes=0-1000,2000-\r\n"); + + headers.clear(); + headers.set(Range::Bytes(vec![])); + + assert_eq!(&headers.to_string(), "Range: bytes=\r\n"); + + headers.clear(); + headers.set(Range::Unregistered("custom".to_owned(), "1-xxx".to_owned())); + + assert_eq!(&headers.to_string(), "Range: custom=1-xxx\r\n"); + } + + #[test] + fn test_byte_range_spec_to_satisfiable_range() { + assert_eq!( + Some((0, 0)), + ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(3) + ); + assert_eq!( + Some((1, 2)), + ByteRangeSpec::FromTo(1, 2).to_satisfiable_range(3) + ); + assert_eq!( + Some((1, 2)), + ByteRangeSpec::FromTo(1, 5).to_satisfiable_range(3) + ); + assert_eq!(None, ByteRangeSpec::FromTo(3, 3).to_satisfiable_range(3)); + assert_eq!(None, ByteRangeSpec::FromTo(2, 1).to_satisfiable_range(3)); + assert_eq!(None, ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(0)); + + assert_eq!( + Some((0, 2)), + ByteRangeSpec::AllFrom(0).to_satisfiable_range(3) + ); + assert_eq!( + Some((2, 2)), + ByteRangeSpec::AllFrom(2).to_satisfiable_range(3) + ); + assert_eq!(None, ByteRangeSpec::AllFrom(3).to_satisfiable_range(3)); + assert_eq!(None, ByteRangeSpec::AllFrom(5).to_satisfiable_range(3)); + assert_eq!(None, ByteRangeSpec::AllFrom(0).to_satisfiable_range(0)); + + assert_eq!(Some((1, 2)), ByteRangeSpec::Last(2).to_satisfiable_range(3)); + assert_eq!(Some((2, 2)), ByteRangeSpec::Last(1).to_satisfiable_range(3)); + assert_eq!(Some((0, 2)), ByteRangeSpec::Last(5).to_satisfiable_range(3)); + assert_eq!(None, ByteRangeSpec::Last(0).to_satisfiable_range(3)); + assert_eq!(None, ByteRangeSpec::Last(2).to_satisfiable_range(0)); + } } diff --git a/actix-http/src/header/shared/charset.rs b/actix-http/src/header/shared/charset.rs index 6ddfa03ea..00e7309d4 100644 --- a/actix-http/src/header/shared/charset.rs +++ b/actix-http/src/header/shared/charset.rs @@ -137,17 +137,22 @@ impl FromStr for Charset { } } -#[test] -fn test_parse() { - assert_eq!(Us_Ascii, "us-ascii".parse().unwrap()); - assert_eq!(Us_Ascii, "US-Ascii".parse().unwrap()); - assert_eq!(Us_Ascii, "US-ASCII".parse().unwrap()); - assert_eq!(Shift_Jis, "Shift-JIS".parse().unwrap()); - assert_eq!(Ext("ABCD".to_owned()), "abcd".parse().unwrap()); -} +#[cfg(test)] +mod tests { + use super::*; -#[test] -fn test_display() { - assert_eq!("US-ASCII", format!("{}", Us_Ascii)); - assert_eq!("ABCD", format!("{}", Ext("ABCD".to_owned()))); + #[test] + fn test_parse() { + assert_eq!(Us_Ascii, "us-ascii".parse().unwrap()); + assert_eq!(Us_Ascii, "US-Ascii".parse().unwrap()); + assert_eq!(Us_Ascii, "US-ASCII".parse().unwrap()); + assert_eq!(Shift_Jis, "Shift-JIS".parse().unwrap()); + assert_eq!(Ext("ABCD".to_owned()), "abcd".parse().unwrap()); + } + + #[test] + fn test_display() { + assert_eq!("US-ASCII", format!("{}", Us_Ascii)); + assert_eq!("ABCD", format!("{}", Ext("ABCD".to_owned()))); + } } diff --git a/actix-http/src/helpers.rs b/actix-http/src/helpers.rs index 86f8250b6..ff647e72b 100644 --- a/actix-http/src/helpers.rs +++ b/actix-http/src/helpers.rs @@ -1,69 +1,34 @@ -use std::{io, ptr}; +use std::io; use bytes::{BufMut, BytesMut}; use http::Version; use crate::extensions::Extensions; -const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\ - 2021222324252627282930313233343536373839\ - 4041424344454647484950515253545556575859\ - 6061626364656667686970717273747576777879\ - 8081828384858687888990919293949596979899"; - -pub(crate) const STATUS_LINE_BUF_SIZE: usize = 13; - -pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesMut) { - let mut buf: [u8; STATUS_LINE_BUF_SIZE] = *b"HTTP/1.1 "; - match version { - Version::HTTP_2 => buf[5] = b'2', - Version::HTTP_10 => buf[7] = b'0', - Version::HTTP_09 => { - buf[5] = b'0'; - buf[7] = b'9'; - } - _ => (), - } - - let mut curr: isize = 12; - let buf_ptr = buf.as_mut_ptr(); - let lut_ptr = DEC_DIGITS_LUT.as_ptr(); - let four = n > 999; - - // decode 2 more chars, if > 2 chars - let d1 = (n % 100) << 1; - n /= 100; - curr -= 2; - unsafe { - ptr::copy_nonoverlapping(lut_ptr.offset(d1 as isize), buf_ptr.offset(curr), 2); - } - - // decode last 1 or 2 chars - if n < 10 { - curr -= 1; - unsafe { - *buf_ptr.offset(curr) = (n as u8) + b'0'; - } - } else { - let d1 = n << 1; - curr -= 2; - unsafe { - ptr::copy_nonoverlapping( - lut_ptr.offset(d1 as isize), - buf_ptr.offset(curr), - 2, - ); - } - } - - bytes.put_slice(&buf); - if four { - bytes.put_u8(b' '); - } -} - const DIGITS_START: u8 = b'0'; +pub(crate) fn write_status_line(version: Version, n: u16, bytes: &mut BytesMut) { + match version { + Version::HTTP_11 => bytes.put_slice(b"HTTP/1.1 "), + Version::HTTP_10 => bytes.put_slice(b"HTTP/1.0 "), + Version::HTTP_09 => bytes.put_slice(b"HTTP/0.9 "), + _ => { + // other HTTP version handlers do not use this method + } + } + + let d100 = (n / 100) as u8; + let d10 = ((n / 10) % 10) as u8; + let d1 = (n % 10) as u8; + + bytes.put_u8(DIGITS_START + d100); + bytes.put_u8(DIGITS_START + d10); + bytes.put_u8(DIGITS_START + d1); + + // trailing space before reason + bytes.put_u8(b' '); +} + /// NOTE: bytes object has to contain enough space pub fn write_content_length(n: usize, bytes: &mut BytesMut) { bytes.put_slice(b"\r\ncontent-length: "); @@ -189,8 +154,28 @@ impl DataFactory for Data { #[cfg(test)] mod tests { + use std::str::from_utf8; + use super::*; + #[test] + fn test_status_line() { + let mut bytes = BytesMut::new(); + bytes.reserve(50); + write_status_line(Version::HTTP_11, 200, &mut bytes); + assert_eq!(from_utf8(&bytes.split().freeze()).unwrap(), "HTTP/1.1 200 "); + + let mut bytes = BytesMut::new(); + bytes.reserve(50); + write_status_line(Version::HTTP_09, 404, &mut bytes); + assert_eq!(from_utf8(&bytes.split().freeze()).unwrap(), "HTTP/0.9 404 "); + + let mut bytes = BytesMut::new(); + bytes.reserve(50); + write_status_line(Version::HTTP_09, 515, &mut bytes); + assert_eq!(from_utf8(&bytes.split().freeze()).unwrap(), "HTTP/0.9 515 "); + } + #[test] fn test_write_content_length() { let mut bytes = BytesMut::new(); diff --git a/src/config.rs b/src/config.rs index 6db378c7b..19a5ccc7b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -123,6 +123,7 @@ impl AppService { } } +/// Application connection config #[derive(Clone)] pub struct AppConfig(Rc);