mirror of https://github.com/fafhrd91/actix-web
Merge branch 'master' into fix/new-remove-pin-unsafe
This commit is contained in:
commit
4e72932809
|
@ -3,13 +3,17 @@
|
||||||
|
|
||||||
## [2.0.NEXT] - 2020-01-xx
|
## [2.0.NEXT] - 2020-01-xx
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add helper function for creating routes with `TRACE` method guard `web::trace()`
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
* Use `sha-1` crate instead of unmaintained `sha1` crate
|
* Use `sha-1` crate instead of unmaintained `sha1` crate
|
||||||
|
|
||||||
* Skip empty chunks when returning response from a `Stream` #1308
|
* Skip empty chunks when returning response from a `Stream` #1308
|
||||||
|
|
||||||
* Update the `time` dependency to 0.2.5
|
* Update the `time` dependency to 0.2.7
|
||||||
|
|
||||||
## [2.0.0] - 2019-12-25
|
## [2.0.0] - 2019-12-25
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,7 @@ regex = "1.3"
|
||||||
serde = { version = "1.0", features=["derive"] }
|
serde = { version = "1.0", features=["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_urlencoded = "0.6.1"
|
serde_urlencoded = "0.6.1"
|
||||||
time = { version = "0.2.5", default-features = false, features = ["std"] }
|
time = { version = "0.2.7", default-features = false, features = ["std"] }
|
||||||
url = "2.1"
|
url = "2.1"
|
||||||
open-ssl = { version="0.10", package = "openssl", optional = true }
|
open-ssl = { version="0.10", package = "openssl", optional = true }
|
||||||
rust-tls = { version = "0.16.0", package = "rustls", optional = true }
|
rust-tls = { version = "0.16.0", package = "rustls", optional = true }
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
* Update the `time` dependency to 0.2.5
|
* Update the `time` dependency to 0.2.7
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ serde_json = "1.0"
|
||||||
sha-1 = "0.8"
|
sha-1 = "0.8"
|
||||||
slab = "0.4"
|
slab = "0.4"
|
||||||
serde_urlencoded = "0.6.1"
|
serde_urlencoded = "0.6.1"
|
||||||
time = { version = "0.2.5", default-features = false, features = ["std"] }
|
time = { version = "0.2.7", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
# for secure cookie
|
# for secure cookie
|
||||||
ring = { version = "0.16.9", optional = true }
|
ring = { version = "0.16.9", optional = true }
|
||||||
|
@ -93,8 +93,13 @@ actix-server = "1.0.0"
|
||||||
actix-connect = { version = "1.0.0", features=["openssl"] }
|
actix-connect = { version = "1.0.0", features=["openssl"] }
|
||||||
actix-http-test = { version = "1.0.0", features=["openssl"] }
|
actix-http-test = { version = "1.0.0", features=["openssl"] }
|
||||||
actix-tls = { version = "1.0.0", features=["openssl"] }
|
actix-tls = { version = "1.0.0", features=["openssl"] }
|
||||||
|
criterion = "0.3"
|
||||||
futures = "0.3.1"
|
futures = "0.3.1"
|
||||||
env_logger = "0.6"
|
env_logger = "0.6"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
open-ssl = { version="0.10", package = "openssl" }
|
open-ssl = { version="0.10", package = "openssl" }
|
||||||
rust-tls = { version="0.16", package = "rustls" }
|
rust-tls = { version="0.16", package = "rustls" }
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "content-length"
|
||||||
|
harness = false
|
||||||
|
|
|
@ -0,0 +1,267 @@
|
||||||
|
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||||
|
|
||||||
|
use bytes::BytesMut;
|
||||||
|
|
||||||
|
// benchmark sending all requests at the same time
|
||||||
|
fn bench_write_content_length(c: &mut Criterion) {
|
||||||
|
let mut group = c.benchmark_group("write_content_length");
|
||||||
|
|
||||||
|
let sizes = [
|
||||||
|
0, 1, 11, 83, 101, 653, 1001, 6323, 10001, 56329, 100001, 123456, 98724245,
|
||||||
|
4294967202,
|
||||||
|
];
|
||||||
|
|
||||||
|
for i in sizes.iter() {
|
||||||
|
group.bench_with_input(BenchmarkId::new("Original (unsafe)", i), i, |b, &i| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut b = BytesMut::with_capacity(35);
|
||||||
|
_original::write_content_length(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_content_length(i, &mut b)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, bench_write_content_length);
|
||||||
|
criterion_main!(benches);
|
||||||
|
|
||||||
|
mod _new {
|
||||||
|
use bytes::{BufMut, BytesMut};
|
||||||
|
|
||||||
|
const DIGITS_START: u8 = b'0';
|
||||||
|
|
||||||
|
/// NOTE: bytes object has to contain enough space
|
||||||
|
pub fn write_content_length(n: usize, bytes: &mut BytesMut) {
|
||||||
|
if n == 0 {
|
||||||
|
bytes.put_slice(b"\r\ncontent-length: 0\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes.put_slice(b"\r\ncontent-length: ");
|
||||||
|
|
||||||
|
if n < 10 {
|
||||||
|
bytes.put_u8(DIGITS_START + (n as u8));
|
||||||
|
} else if n < 100 {
|
||||||
|
let n = n as u8;
|
||||||
|
|
||||||
|
let d10 = n / 10;
|
||||||
|
let d1 = n % 10;
|
||||||
|
|
||||||
|
bytes.put_u8(DIGITS_START + d10);
|
||||||
|
bytes.put_u8(DIGITS_START + d1);
|
||||||
|
} else if n < 1000 {
|
||||||
|
let n = n as u16;
|
||||||
|
|
||||||
|
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);
|
||||||
|
} else if n < 10_000 {
|
||||||
|
let n = n as u16;
|
||||||
|
|
||||||
|
let d1000 = (n / 1000) as u8;
|
||||||
|
let d100 = ((n / 100) % 10) as u8;
|
||||||
|
let d10 = ((n / 10) % 10) as u8;
|
||||||
|
let d1 = (n % 10) as u8;
|
||||||
|
|
||||||
|
bytes.put_u8(DIGITS_START + d1000);
|
||||||
|
bytes.put_u8(DIGITS_START + d100);
|
||||||
|
bytes.put_u8(DIGITS_START + d10);
|
||||||
|
bytes.put_u8(DIGITS_START + d1);
|
||||||
|
} else if n < 100_000 {
|
||||||
|
let n = n as u32;
|
||||||
|
|
||||||
|
let d10000 = (n / 10000) as u8;
|
||||||
|
let d1000 = ((n / 1000) % 10) as u8;
|
||||||
|
let d100 = ((n / 100) % 10) as u8;
|
||||||
|
let d10 = ((n / 10) % 10) as u8;
|
||||||
|
let d1 = (n % 10) as u8;
|
||||||
|
|
||||||
|
bytes.put_u8(DIGITS_START + d10000);
|
||||||
|
bytes.put_u8(DIGITS_START + d1000);
|
||||||
|
bytes.put_u8(DIGITS_START + d100);
|
||||||
|
bytes.put_u8(DIGITS_START + d10);
|
||||||
|
bytes.put_u8(DIGITS_START + d1);
|
||||||
|
} else if n < 1_000_000 {
|
||||||
|
let n = n as u32;
|
||||||
|
|
||||||
|
let d100000 = (n / 100000) as u8;
|
||||||
|
let d10000 = ((n / 10000) % 10) as u8;
|
||||||
|
let d1000 = ((n / 1000) % 10) as u8;
|
||||||
|
let d100 = ((n / 100) % 10) as u8;
|
||||||
|
let d10 = ((n / 10) % 10) as u8;
|
||||||
|
let d1 = (n % 10) as u8;
|
||||||
|
|
||||||
|
bytes.put_u8(DIGITS_START + d100000);
|
||||||
|
bytes.put_u8(DIGITS_START + d10000);
|
||||||
|
bytes.put_u8(DIGITS_START + d1000);
|
||||||
|
bytes.put_u8(DIGITS_START + d100);
|
||||||
|
bytes.put_u8(DIGITS_START + d10);
|
||||||
|
bytes.put_u8(DIGITS_START + d1);
|
||||||
|
} else {
|
||||||
|
write_usize(n, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes.put_slice(b"\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_usize(n: usize, bytes: &mut BytesMut) {
|
||||||
|
let mut n = n;
|
||||||
|
|
||||||
|
// 20 chars is max length of a usize (2^64)
|
||||||
|
// digits will be added to the buffer from lsd to msd
|
||||||
|
let mut buf = BytesMut::with_capacity(20);
|
||||||
|
|
||||||
|
while n > 9 {
|
||||||
|
// "pop" the least-significant digit
|
||||||
|
let lsd = (n % 10) as u8;
|
||||||
|
|
||||||
|
// remove the lsd from n
|
||||||
|
n = n / 10;
|
||||||
|
|
||||||
|
buf.put_u8(DIGITS_START + lsd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// put msd to result buffer
|
||||||
|
bytes.put_u8(DIGITS_START + (n as u8));
|
||||||
|
|
||||||
|
// put, in reverse (msd to lsd), remaining digits to buffer
|
||||||
|
for i in (0..buf.len()).rev() {
|
||||||
|
bytes.put_u8(buf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod _original {
|
||||||
|
use std::{mem, ptr, slice};
|
||||||
|
|
||||||
|
use bytes::{BufMut, BytesMut};
|
||||||
|
|
||||||
|
const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\
|
||||||
|
2021222324252627282930313233343536373839\
|
||||||
|
4041424344454647484950515253545556575859\
|
||||||
|
6061626364656667686970717273747576777879\
|
||||||
|
8081828384858687888990919293949596979899";
|
||||||
|
|
||||||
|
/// NOTE: bytes object has to contain enough space
|
||||||
|
pub fn write_content_length(mut n: usize, bytes: &mut BytesMut) {
|
||||||
|
if n < 10 {
|
||||||
|
let mut buf: [u8; 21] = [
|
||||||
|
b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l',
|
||||||
|
b'e', b'n', b'g', b't', b'h', b':', b' ', b'0', b'\r', b'\n',
|
||||||
|
];
|
||||||
|
buf[18] = (n as u8) + b'0';
|
||||||
|
bytes.put_slice(&buf);
|
||||||
|
} else if n < 100 {
|
||||||
|
let mut buf: [u8; 22] = [
|
||||||
|
b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l',
|
||||||
|
b'e', b'n', b'g', b't', b'h', b':', b' ', b'0', b'0', b'\r', b'\n',
|
||||||
|
];
|
||||||
|
let d1 = n << 1;
|
||||||
|
unsafe {
|
||||||
|
ptr::copy_nonoverlapping(
|
||||||
|
DEC_DIGITS_LUT.as_ptr().add(d1),
|
||||||
|
buf.as_mut_ptr().offset(18),
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
bytes.put_slice(&buf);
|
||||||
|
} else if n < 1000 {
|
||||||
|
let mut buf: [u8; 23] = [
|
||||||
|
b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l',
|
||||||
|
b'e', b'n', b'g', b't', b'h', b':', b' ', b'0', b'0', b'0', b'\r',
|
||||||
|
b'\n',
|
||||||
|
];
|
||||||
|
// decode 2 more chars, if > 2 chars
|
||||||
|
let d1 = (n % 100) << 1;
|
||||||
|
n /= 100;
|
||||||
|
unsafe {
|
||||||
|
ptr::copy_nonoverlapping(
|
||||||
|
DEC_DIGITS_LUT.as_ptr().add(d1),
|
||||||
|
buf.as_mut_ptr().offset(19),
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// decode last 1
|
||||||
|
buf[18] = (n as u8) + b'0';
|
||||||
|
|
||||||
|
bytes.put_slice(&buf);
|
||||||
|
} else {
|
||||||
|
bytes.put_slice(b"\r\ncontent-length: ");
|
||||||
|
convert_usize(n, bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn convert_usize(mut n: usize, bytes: &mut BytesMut) {
|
||||||
|
let mut curr: isize = 39;
|
||||||
|
let mut buf: [u8; 41] = unsafe { mem::MaybeUninit::uninit().assume_init() };
|
||||||
|
buf[39] = b'\r';
|
||||||
|
buf[40] = b'\n';
|
||||||
|
let buf_ptr = buf.as_mut_ptr();
|
||||||
|
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
|
||||||
|
|
||||||
|
// eagerly decode 4 characters at a time
|
||||||
|
while n >= 10_000 {
|
||||||
|
let rem = (n % 10_000) as isize;
|
||||||
|
n /= 10_000;
|
||||||
|
|
||||||
|
let d1 = (rem / 100) << 1;
|
||||||
|
let d2 = (rem % 100) << 1;
|
||||||
|
curr -= 4;
|
||||||
|
unsafe {
|
||||||
|
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
|
||||||
|
ptr::copy_nonoverlapping(
|
||||||
|
lut_ptr.offset(d2),
|
||||||
|
buf_ptr.offset(curr + 2),
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we reach here numbers are <= 9999, so at most 4 chars long
|
||||||
|
let mut n = n as isize; // possibly reduce 64bit math
|
||||||
|
|
||||||
|
// decode 2 more chars, if > 2 chars
|
||||||
|
if n >= 100 {
|
||||||
|
let d1 = (n % 100) << 1;
|
||||||
|
n /= 100;
|
||||||
|
curr -= 2;
|
||||||
|
unsafe {
|
||||||
|
ptr::copy_nonoverlapping(lut_ptr.offset(d1), 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), buf_ptr.offset(curr), 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
bytes.extend_from_slice(slice::from_raw_parts(
|
||||||
|
buf_ptr.offset(curr),
|
||||||
|
41 - curr as usize,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -990,7 +990,7 @@ impl<'a, 'b> PartialEq<Cookie<'b>> for Cookie<'a> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Cookie, SameSite};
|
use super::{Cookie, SameSite};
|
||||||
use time::{offset, PrimitiveDateTime};
|
use time::PrimitiveDateTime;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn format() {
|
fn format() {
|
||||||
|
@ -1015,7 +1015,7 @@ mod tests {
|
||||||
assert_eq!(&cookie.to_string(), "foo=bar; Domain=www.rust-lang.org");
|
assert_eq!(&cookie.to_string(), "foo=bar; Domain=www.rust-lang.org");
|
||||||
|
|
||||||
let time_str = "Wed, 21 Oct 2015 07:28:00 GMT";
|
let time_str = "Wed, 21 Oct 2015 07:28:00 GMT";
|
||||||
let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().using_offset(offset!(UTC));
|
let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().assume_utc();
|
||||||
let cookie = Cookie::build("foo", "bar").expires(expires).finish();
|
let cookie = Cookie::build("foo", "bar").expires(expires).finish();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&cookie.to_string(),
|
&cookie.to_string(),
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::fmt;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
|
|
||||||
use percent_encoding::percent_decode;
|
use percent_encoding::percent_decode;
|
||||||
use time::{Duration, offset};
|
use time::Duration;
|
||||||
|
|
||||||
use super::{Cookie, CookieStr, SameSite};
|
use super::{Cookie, CookieStr, SameSite};
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
|
||||||
.or_else(|| time::parse(v, "%a, %d-%b-%Y %H:%M:%S").ok());
|
.or_else(|| time::parse(v, "%a, %d-%b-%Y %H:%M:%S").ok());
|
||||||
|
|
||||||
if let Some(time) = tm {
|
if let Some(time) = tm {
|
||||||
cookie.expires = Some(time.using_offset(offset!(UTC)))
|
cookie.expires = Some(time.assume_utc())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -216,7 +216,7 @@ where
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Cookie, SameSite};
|
use super::{Cookie, SameSite};
|
||||||
use time::{offset, Duration, PrimitiveDateTime};
|
use time::{Duration, PrimitiveDateTime};
|
||||||
|
|
||||||
macro_rules! assert_eq_parse {
|
macro_rules! assert_eq_parse {
|
||||||
($string:expr, $expected:expr) => {
|
($string:expr, $expected:expr) => {
|
||||||
|
@ -376,7 +376,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let time_str = "Wed, 21 Oct 2015 07:28:00 GMT";
|
let time_str = "Wed, 21 Oct 2015 07:28:00 GMT";
|
||||||
let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().using_offset(offset!(UTC));
|
let expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%M:%S").unwrap().assume_utc();
|
||||||
expected.set_expires(expires);
|
expected.set_expires(expires);
|
||||||
assert_eq_parse!(
|
assert_eq_parse!(
|
||||||
" foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
|
" foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
|
||||||
|
@ -385,7 +385,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
unexpected.set_domain("foo.com");
|
unexpected.set_domain("foo.com");
|
||||||
let bad_expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%S:%M").unwrap().using_offset(offset!(UTC));
|
let bad_expires = PrimitiveDateTime::parse(time_str, "%a, %d %b %Y %H:%S:%M").unwrap().assume_utc();
|
||||||
expected.set_expires(bad_expires);
|
expected.set_expires(bad_expires);
|
||||||
assert_ne_parse!(
|
assert_ne_parse!(
|
||||||
" foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
|
" foo=bar ;HttpOnly; Secure; Max-Age=4; Path=/foo; \
|
||||||
|
|
|
@ -63,7 +63,7 @@ header! {
|
||||||
(AcceptCharset, ACCEPT_CHARSET) => (QualityItem<Charset>)+
|
(AcceptCharset, ACCEPT_CHARSET) => (QualityItem<Charset>)+
|
||||||
|
|
||||||
test_accept_charset {
|
test_accept_charset {
|
||||||
/// Test case from RFC
|
// Test case from RFC
|
||||||
test_header!(test1, vec![b"iso-8859-5, unicode-1-1;q=0.8"]);
|
test_header!(test1, vec![b"iso-8859-5, unicode-1-1;q=0.8"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ impl FromStr for HttpDate {
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<HttpDate, ParseError> {
|
fn from_str(s: &str) -> Result<HttpDate, ParseError> {
|
||||||
match time_parser::parse_http_date(s) {
|
match time_parser::parse_http_date(s) {
|
||||||
Some(t) => Ok(HttpDate(t.using_offset(offset!(UTC)))),
|
Some(t) => Ok(HttpDate(t.assume_utc())),
|
||||||
None => Err(ParseError::Header)
|
None => Err(ParseError::Header)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ impl From<OffsetDateTime> for HttpDate {
|
||||||
|
|
||||||
impl From<SystemTime> for HttpDate {
|
impl From<SystemTime> for HttpDate {
|
||||||
fn from(sys: SystemTime) -> HttpDate {
|
fn from(sys: SystemTime) -> HttpDate {
|
||||||
HttpDate(PrimitiveDateTime::from(sys).using_offset(offset!(UTC)))
|
HttpDate(PrimitiveDateTime::from(sys).assume_utc())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,14 +66,14 @@ impl From<HttpDate> for SystemTime {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::HttpDate;
|
use super::HttpDate;
|
||||||
use time::{PrimitiveDateTime, date, time, offset};
|
use time::{PrimitiveDateTime, date, time};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_date() {
|
fn test_date() {
|
||||||
let nov_07 = HttpDate(PrimitiveDateTime::new(
|
let nov_07 = HttpDate(PrimitiveDateTime::new(
|
||||||
date!(1994-11-07),
|
date!(1994-11-07),
|
||||||
time!(8:48:37)
|
time!(8:48:37)
|
||||||
).using_offset(offset!(UTC)));
|
).assume_utc());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"Sun, 07 Nov 1994 08:48:37 GMT".parse::<HttpDate>().unwrap(),
|
"Sun, 07 Nov 1994 08:48:37 GMT".parse::<HttpDate>().unwrap(),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{io, mem, ptr, slice};
|
use std::{io, ptr};
|
||||||
|
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
use http::Version;
|
use http::Version;
|
||||||
|
@ -14,9 +14,7 @@ const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\
|
||||||
pub(crate) const STATUS_LINE_BUF_SIZE: usize = 13;
|
pub(crate) const STATUS_LINE_BUF_SIZE: usize = 13;
|
||||||
|
|
||||||
pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesMut) {
|
pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesMut) {
|
||||||
let mut buf: [u8; STATUS_LINE_BUF_SIZE] = [
|
let mut buf: [u8; STATUS_LINE_BUF_SIZE] = *b"HTTP/1.1 ";
|
||||||
b'H', b'T', b'T', b'P', b'/', b'1', b'.', b'1', b' ', b' ', b' ', b' ', b' ',
|
|
||||||
];
|
|
||||||
match version {
|
match version {
|
||||||
Version::HTTP_2 => buf[5] = b'2',
|
Version::HTTP_2 => buf[5] = b'2',
|
||||||
Version::HTTP_10 => buf[7] = b'0',
|
Version::HTTP_10 => buf[7] = b'0',
|
||||||
|
@ -64,109 +62,104 @@ pub(crate) fn write_status_line(version: Version, mut n: u16, bytes: &mut BytesM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DIGITS_START: u8 = b'0';
|
||||||
|
|
||||||
/// NOTE: bytes object has to contain enough space
|
/// NOTE: bytes object has to contain enough space
|
||||||
pub fn write_content_length(mut n: usize, bytes: &mut BytesMut) {
|
pub fn write_content_length(n: usize, bytes: &mut BytesMut) {
|
||||||
|
bytes.put_slice(b"\r\ncontent-length: ");
|
||||||
|
|
||||||
if n < 10 {
|
if n < 10 {
|
||||||
let mut buf: [u8; 21] = [
|
bytes.put_u8(DIGITS_START + (n as u8));
|
||||||
b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l', b'e',
|
|
||||||
b'n', b'g', b't', b'h', b':', b' ', b'0', b'\r', b'\n',
|
|
||||||
];
|
|
||||||
buf[18] = (n as u8) + b'0';
|
|
||||||
bytes.put_slice(&buf);
|
|
||||||
} else if n < 100 {
|
} else if n < 100 {
|
||||||
let mut buf: [u8; 22] = [
|
let n = n as u8;
|
||||||
b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l', b'e',
|
|
||||||
b'n', b'g', b't', b'h', b':', b' ', b'0', b'0', b'\r', b'\n',
|
let d10 = n / 10;
|
||||||
];
|
let d1 = n % 10;
|
||||||
let d1 = n << 1;
|
|
||||||
unsafe {
|
bytes.put_u8(DIGITS_START + d10);
|
||||||
ptr::copy_nonoverlapping(
|
bytes.put_u8(DIGITS_START + d1);
|
||||||
DEC_DIGITS_LUT.as_ptr().add(d1),
|
|
||||||
buf.as_mut_ptr().offset(18),
|
|
||||||
2,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
bytes.put_slice(&buf);
|
|
||||||
} else if n < 1000 {
|
} else if n < 1000 {
|
||||||
let mut buf: [u8; 23] = [
|
let n = n as u16;
|
||||||
b'\r', b'\n', b'c', b'o', b'n', b't', b'e', b'n', b't', b'-', b'l', b'e',
|
|
||||||
b'n', b'g', b't', b'h', b':', b' ', b'0', b'0', b'0', b'\r', b'\n',
|
|
||||||
];
|
|
||||||
// decode 2 more chars, if > 2 chars
|
|
||||||
let d1 = (n % 100) << 1;
|
|
||||||
n /= 100;
|
|
||||||
unsafe {
|
|
||||||
ptr::copy_nonoverlapping(
|
|
||||||
DEC_DIGITS_LUT.as_ptr().add(d1),
|
|
||||||
buf.as_mut_ptr().offset(19),
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// decode last 1
|
let d100 = (n / 100) as u8;
|
||||||
buf[18] = (n as u8) + b'0';
|
let d10 = ((n / 10) % 10) as u8;
|
||||||
|
let d1 = (n % 10) as u8;
|
||||||
|
|
||||||
bytes.put_slice(&buf);
|
bytes.put_u8(DIGITS_START + d100);
|
||||||
|
bytes.put_u8(DIGITS_START + d10);
|
||||||
|
bytes.put_u8(DIGITS_START + d1);
|
||||||
|
} else if n < 10_000 {
|
||||||
|
let n = n as u16;
|
||||||
|
|
||||||
|
let d1000 = (n / 1000) as u8;
|
||||||
|
let d100 = ((n / 100) % 10) as u8;
|
||||||
|
let d10 = ((n / 10) % 10) as u8;
|
||||||
|
let d1 = (n % 10) as u8;
|
||||||
|
|
||||||
|
bytes.put_u8(DIGITS_START + d1000);
|
||||||
|
bytes.put_u8(DIGITS_START + d100);
|
||||||
|
bytes.put_u8(DIGITS_START + d10);
|
||||||
|
bytes.put_u8(DIGITS_START + d1);
|
||||||
|
} else if n < 100_000 {
|
||||||
|
let n = n as u32;
|
||||||
|
|
||||||
|
let d10000 = (n / 10000) as u8;
|
||||||
|
let d1000 = ((n / 1000) % 10) as u8;
|
||||||
|
let d100 = ((n / 100) % 10) as u8;
|
||||||
|
let d10 = ((n / 10) % 10) as u8;
|
||||||
|
let d1 = (n % 10) as u8;
|
||||||
|
|
||||||
|
bytes.put_u8(DIGITS_START + d10000);
|
||||||
|
bytes.put_u8(DIGITS_START + d1000);
|
||||||
|
bytes.put_u8(DIGITS_START + d100);
|
||||||
|
bytes.put_u8(DIGITS_START + d10);
|
||||||
|
bytes.put_u8(DIGITS_START + d1);
|
||||||
|
} else if n < 1_000_000 {
|
||||||
|
let n = n as u32;
|
||||||
|
|
||||||
|
let d100000 = (n / 100000) as u8;
|
||||||
|
let d10000 = ((n / 10000) % 10) as u8;
|
||||||
|
let d1000 = ((n / 1000) % 10) as u8;
|
||||||
|
let d100 = ((n / 100) % 10) as u8;
|
||||||
|
let d10 = ((n / 10) % 10) as u8;
|
||||||
|
let d1 = (n % 10) as u8;
|
||||||
|
|
||||||
|
bytes.put_u8(DIGITS_START + d100000);
|
||||||
|
bytes.put_u8(DIGITS_START + d10000);
|
||||||
|
bytes.put_u8(DIGITS_START + d1000);
|
||||||
|
bytes.put_u8(DIGITS_START + d100);
|
||||||
|
bytes.put_u8(DIGITS_START + d10);
|
||||||
|
bytes.put_u8(DIGITS_START + d1);
|
||||||
} else {
|
} else {
|
||||||
bytes.put_slice(b"\r\ncontent-length: ");
|
write_usize(n, bytes);
|
||||||
convert_usize(n, bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes.put_slice(b"\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn convert_usize(mut n: usize, bytes: &mut BytesMut) {
|
pub(crate) fn write_usize(n: usize, bytes: &mut BytesMut) {
|
||||||
let mut curr: isize = 39;
|
let mut n = n;
|
||||||
let mut buf: [u8; 41] = unsafe { mem::MaybeUninit::uninit().assume_init() };
|
|
||||||
buf[39] = b'\r';
|
|
||||||
buf[40] = b'\n';
|
|
||||||
let buf_ptr = buf.as_mut_ptr();
|
|
||||||
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
|
|
||||||
|
|
||||||
// eagerly decode 4 characters at a time
|
// 20 chars is max length of a usize (2^64)
|
||||||
while n >= 10_000 {
|
// digits will be added to the buffer from lsd to msd
|
||||||
let rem = (n % 10_000) as isize;
|
let mut buf = BytesMut::with_capacity(20);
|
||||||
n /= 10_000;
|
|
||||||
|
|
||||||
let d1 = (rem / 100) << 1;
|
while n > 9 {
|
||||||
let d2 = (rem % 100) << 1;
|
// "pop" the least-significant digit
|
||||||
curr -= 4;
|
let lsd = (n % 10) as u8;
|
||||||
unsafe {
|
|
||||||
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
|
// remove the lsd from n
|
||||||
ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
|
n = n / 10;
|
||||||
}
|
|
||||||
|
buf.put_u8(DIGITS_START + lsd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we reach here numbers are <= 9999, so at most 4 chars long
|
// put msd to result buffer
|
||||||
let mut n = n as isize; // possibly reduce 64bit math
|
bytes.put_u8(DIGITS_START + (n as u8));
|
||||||
|
|
||||||
// decode 2 more chars, if > 2 chars
|
// put, in reverse (msd to lsd), remaining digits to buffer
|
||||||
if n >= 100 {
|
for i in (0..buf.len()).rev() {
|
||||||
let d1 = (n % 100) << 1;
|
bytes.put_u8(buf[i]);
|
||||||
n /= 100;
|
|
||||||
curr -= 2;
|
|
||||||
unsafe {
|
|
||||||
ptr::copy_nonoverlapping(lut_ptr.offset(d1), 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), buf_ptr.offset(curr), 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
bytes.extend_from_slice(slice::from_raw_parts(
|
|
||||||
buf_ptr.offset(curr),
|
|
||||||
41 - curr as usize,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,5 +224,48 @@ mod tests {
|
||||||
bytes.reserve(50);
|
bytes.reserve(50);
|
||||||
write_content_length(5909, &mut bytes);
|
write_content_length(5909, &mut bytes);
|
||||||
assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 5909\r\n"[..]);
|
assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 5909\r\n"[..]);
|
||||||
|
bytes.reserve(50);
|
||||||
|
write_content_length(9999, &mut bytes);
|
||||||
|
assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 9999\r\n"[..]);
|
||||||
|
bytes.reserve(50);
|
||||||
|
write_content_length(10001, &mut bytes);
|
||||||
|
assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 10001\r\n"[..]);
|
||||||
|
bytes.reserve(50);
|
||||||
|
write_content_length(59094, &mut bytes);
|
||||||
|
assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 59094\r\n"[..]);
|
||||||
|
bytes.reserve(50);
|
||||||
|
write_content_length(99999, &mut bytes);
|
||||||
|
assert_eq!(bytes.split().freeze(), b"\r\ncontent-length: 99999\r\n"[..]);
|
||||||
|
|
||||||
|
bytes.reserve(50);
|
||||||
|
write_content_length(590947, &mut bytes);
|
||||||
|
assert_eq!(
|
||||||
|
bytes.split().freeze(),
|
||||||
|
b"\r\ncontent-length: 590947\r\n"[..]
|
||||||
|
);
|
||||||
|
bytes.reserve(50);
|
||||||
|
write_content_length(999999, &mut bytes);
|
||||||
|
assert_eq!(
|
||||||
|
bytes.split().freeze(),
|
||||||
|
b"\r\ncontent-length: 999999\r\n"[..]
|
||||||
|
);
|
||||||
|
bytes.reserve(50);
|
||||||
|
write_content_length(5909471, &mut bytes);
|
||||||
|
assert_eq!(
|
||||||
|
bytes.split().freeze(),
|
||||||
|
b"\r\ncontent-length: 5909471\r\n"[..]
|
||||||
|
);
|
||||||
|
bytes.reserve(50);
|
||||||
|
write_content_length(59094718, &mut bytes);
|
||||||
|
assert_eq!(
|
||||||
|
bytes.split().freeze(),
|
||||||
|
b"\r\ncontent-length: 59094718\r\n"[..]
|
||||||
|
);
|
||||||
|
bytes.reserve(50);
|
||||||
|
write_content_length(4294973728, &mut bytes);
|
||||||
|
assert_eq!(
|
||||||
|
bytes.split().freeze(),
|
||||||
|
b"\r\ncontent-length: 4294973728\r\n"[..]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use time::{PrimitiveDateTime, Date};
|
use time::{OffsetDateTime, PrimitiveDateTime, Date};
|
||||||
|
|
||||||
/// Attempt to parse a `time` string as one of either RFC 1123, RFC 850, or asctime.
|
/// Attempt to parse a `time` string as one of either RFC 1123, RFC 850, or asctime.
|
||||||
pub fn parse_http_date(time: &str) -> Option<PrimitiveDateTime> {
|
pub fn parse_http_date(time: &str) -> Option<PrimitiveDateTime> {
|
||||||
|
@ -19,7 +19,7 @@ fn try_parse_rfc_850(time: &str) -> Option<PrimitiveDateTime> {
|
||||||
// If the `time` string contains a two-digit year, then as per RFC 2616 § 19.3,
|
// If the `time` string contains a two-digit year, then as per RFC 2616 § 19.3,
|
||||||
// we consider the year as part of this century if it's within the next 50 years,
|
// we consider the year as part of this century if it's within the next 50 years,
|
||||||
// otherwise we consider as part of the previous century.
|
// otherwise we consider as part of the previous century.
|
||||||
let now = PrimitiveDateTime::now();
|
let now = OffsetDateTime::now();
|
||||||
let century_start_year = (now.year() / 100) * 100;
|
let century_start_year = (now.year() / 100) * 100;
|
||||||
let mut expanded_year = century_start_year + dt.year();
|
let mut expanded_year = century_start_year + dt.year();
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## [0.2.NEXT] - 2020-xx-xx
|
## [0.2.1] - 2020-02-25
|
||||||
|
|
||||||
* Allow the handler function to be named as `config` #1290
|
* Add `#[allow(missing_docs)]` attribute to generated structs [#1368]
|
||||||
|
* Allow the handler function to be named as `config` [#1290]
|
||||||
|
|
||||||
|
[#1368]: https://github.com/actix/actix-web/issues/1368
|
||||||
|
[#1290]: https://github.com/actix/actix-web/issues/1290
|
||||||
|
|
||||||
## [0.2.0] - 2019-12-13
|
## [0.2.0] - 2019-12-13
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-web-codegen"
|
name = "actix-web-codegen"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
description = "Actix web proc macros"
|
description = "Actix web proc macros"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
|
@ -17,6 +17,6 @@ syn = { version = "^1", features = ["full", "parsing"] }
|
||||||
proc-macro2 = "^1"
|
proc-macro2 = "^1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = { version = "1.0.0" }
|
actix-rt = "1.0.0"
|
||||||
actix-web = { version = "2.0.0-rc" }
|
actix-web = "2.0.0"
|
||||||
futures = { version = "0.3.1" }
|
futures = "0.3.1"
|
||||||
|
|
18
src/web.rs
18
src/web.rs
|
@ -193,6 +193,24 @@ pub fn head() -> Route {
|
||||||
method(Method::HEAD)
|
method(Method::HEAD)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create *route* with `TRACE` method guard.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use actix_web::{web, App, HttpResponse};
|
||||||
|
///
|
||||||
|
/// let app = App::new().service(
|
||||||
|
/// web::resource("/{project_id}")
|
||||||
|
/// .route(web::trace().to(|| HttpResponse::Ok()))
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// In the above example, one `HEAD` route gets added:
|
||||||
|
/// * /{project_id}
|
||||||
|
///
|
||||||
|
pub fn trace() -> Route {
|
||||||
|
method(Method::TRACE)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create *route* and add method guard.
|
/// Create *route* and add method guard.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## [Unreleased] - 2020-xx-xx
|
## [Unreleased] - 2020-xx-xx
|
||||||
|
|
||||||
* Update the `time` dependency to 0.2.5
|
* Update the `time` dependency to 0.2.7
|
||||||
|
|
||||||
|
|
||||||
## [1.0.0] - 2019-12-13
|
## [1.0.0] - 2019-12-13
|
||||||
|
|
|
@ -51,7 +51,7 @@ serde_json = "1.0"
|
||||||
sha1 = "0.6"
|
sha1 = "0.6"
|
||||||
slab = "0.4"
|
slab = "0.4"
|
||||||
serde_urlencoded = "0.6.1"
|
serde_urlencoded = "0.6.1"
|
||||||
time = { version = "0.2.5", default-features = false, features = ["std"] }
|
time = { version = "0.2.7", default-features = false, features = ["std"] }
|
||||||
open-ssl = { version="0.10", package="openssl", optional = true }
|
open-ssl = { version="0.10", package="openssl", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
Loading…
Reference in New Issue