mirror of
https://github.com/fafhrd91/actix-web
synced 2025-07-03 17:41:30 +02:00
Compare commits
10 Commits
codegen-v0
...
web-v1.0.9
Author | SHA1 | Date | |
---|---|---|---|
0212c618c6 | |||
88110ed268 | |||
fba02fdd8c | |||
b2934ad8d2 | |||
f7f410d033 | |||
885ff7396e | |||
61b38e8d0d | |||
edcde67076 | |||
f0612f7570 | |||
ace98e3a1e |
@ -1,11 +1,16 @@
|
||||
# Changes
|
||||
|
||||
## [1.0.9] - 2019-xx-xx
|
||||
## [1.0.9] - 2019-11-14
|
||||
|
||||
### Added
|
||||
|
||||
* Add `Payload::into_inner` method and make stored `def::Payload` public. (#1110)
|
||||
|
||||
### Changed
|
||||
|
||||
* Support `Host` guards when the `Host` header is unset (e.g. HTTP/2 requests) (#1129)
|
||||
|
||||
|
||||
## [1.0.8] - 2019-09-25
|
||||
|
||||
### Added
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-web"
|
||||
version = "1.0.8"
|
||||
version = "1.0.9"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Actix web is a simple, pragmatic and extremely fast web framework for Rust."
|
||||
readme = "README.md"
|
||||
@ -78,7 +78,7 @@ actix-utils = "0.4.4"
|
||||
actix-router = "0.1.5"
|
||||
actix-rt = "0.2.4"
|
||||
actix-web-codegen = "0.1.2"
|
||||
actix-http = "0.2.9"
|
||||
actix-http = "0.2.11"
|
||||
actix-server = "0.6.1"
|
||||
actix-server-config = "0.1.2"
|
||||
actix-testing = "0.1.0"
|
||||
@ -89,7 +89,7 @@ bytes = "0.4"
|
||||
derive_more = "0.15.0"
|
||||
encoding_rs = "0.8"
|
||||
futures = "0.1.25"
|
||||
hashbrown = "0.5.0"
|
||||
hashbrown = "0.6.3"
|
||||
log = "0.4"
|
||||
mime = "0.3"
|
||||
net2 = "0.2.33"
|
||||
|
@ -1,5 +1,9 @@
|
||||
# Changes
|
||||
|
||||
## [0.1.7] - 2019-11-06
|
||||
|
||||
* Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151)
|
||||
|
||||
## [0.1.6] - 2019-10-14
|
||||
|
||||
* Add option to redirect to a slash-ended path `Files` #1132
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-files"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Static files support for actix web."
|
||||
readme = "README.md"
|
||||
@ -19,7 +19,7 @@ path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
actix-web = { version = "1.0.8", default-features = false }
|
||||
actix-http = "0.2.9"
|
||||
actix-http = "0.2.11"
|
||||
actix-service = "0.4.1"
|
||||
bitflags = "1"
|
||||
bytes = "0.4"
|
||||
|
@ -733,6 +733,31 @@ mod tests {
|
||||
assert!(resp.headers().get(header::CONTENT_DISPOSITION).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_non_ascii_file_name() {
|
||||
let mut file =
|
||||
NamedFile::from_file(File::open("Cargo.toml").unwrap(), "貨物.toml")
|
||||
.unwrap();
|
||||
{
|
||||
file.file();
|
||||
let _f: &File = &file;
|
||||
}
|
||||
{
|
||||
let _f: &mut File = &mut file;
|
||||
}
|
||||
|
||||
let req = TestRequest::default().to_http_request();
|
||||
let resp = file.respond_to(&req).unwrap();
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_TYPE).unwrap(),
|
||||
"text/x-toml"
|
||||
);
|
||||
assert_eq!(
|
||||
resp.headers().get(header::CONTENT_DISPOSITION).unwrap(),
|
||||
"inline; filename=\"貨物.toml\"; filename*=UTF-8''%E8%B2%A8%E7%89%A9.toml"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_named_file_set_content_type() {
|
||||
let mut file = NamedFile::open("Cargo.toml")
|
||||
|
@ -13,7 +13,7 @@ use mime_guess::from_path;
|
||||
|
||||
use actix_http::body::SizedStream;
|
||||
use actix_web::http::header::{
|
||||
self, ContentDisposition, DispositionParam, DispositionType,
|
||||
self, Charset, ContentDisposition, DispositionParam, DispositionType, ExtendedValue,
|
||||
};
|
||||
use actix_web::http::{ContentEncoding, StatusCode};
|
||||
use actix_web::middleware::BodyEncoding;
|
||||
@ -93,9 +93,18 @@ impl NamedFile {
|
||||
mime::IMAGE | mime::TEXT | mime::VIDEO => DispositionType::Inline,
|
||||
_ => DispositionType::Attachment,
|
||||
};
|
||||
let mut parameters =
|
||||
vec![DispositionParam::Filename(String::from(filename.as_ref()))];
|
||||
if !filename.is_ascii() {
|
||||
parameters.push(DispositionParam::FilenameExt(ExtendedValue {
|
||||
charset: Charset::Ext(String::from("UTF-8")),
|
||||
language_tag: None,
|
||||
value: filename.into_owned().into_bytes(),
|
||||
}))
|
||||
}
|
||||
let cd = ContentDisposition {
|
||||
disposition: disposition_type,
|
||||
parameters: vec![DispositionParam::Filename(filename.into_owned())],
|
||||
parameters: parameters,
|
||||
};
|
||||
(ct, cd)
|
||||
};
|
||||
|
@ -1,11 +1,15 @@
|
||||
# Changes
|
||||
|
||||
## Not released yet
|
||||
## [0.2.11] - 2019-11-06
|
||||
|
||||
### Added
|
||||
|
||||
* Add support for serde_json::Value to be passed as argument to ResponseBuilder.body()
|
||||
|
||||
* Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151)
|
||||
|
||||
* Allow to use `std::convert::Infallible` as `actix_http::error::Error`
|
||||
|
||||
### Fixed
|
||||
|
||||
* To be compatible with non-English error responses, `ResponseError` rendered with `text/plain; charset=utf-8` header #1118
|
||||
@ -17,9 +21,6 @@
|
||||
|
||||
* Add support for sending HTTP requests with `Rc<RequestHead>` in addition to sending HTTP requests with `RequestHead`
|
||||
|
||||
* Allow to use `std::convert::Infallible` as `actix_http::error::Error`
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
* h2 will use error response #1080
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-http"
|
||||
version = "0.2.10"
|
||||
version = "0.2.11"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Actix http primitives"
|
||||
readme = "README.md"
|
||||
@ -62,7 +62,7 @@ derive_more = "0.15.0"
|
||||
either = "1.5.2"
|
||||
encoding_rs = "0.8"
|
||||
futures = "0.1.25"
|
||||
hashbrown = "0.5.0"
|
||||
hashbrown = "0.6.3"
|
||||
h2 = "0.1.16"
|
||||
http = "0.1.17"
|
||||
httparse = "1.3"
|
||||
|
@ -548,10 +548,11 @@ mod tests {
|
||||
ConnectionType::Close,
|
||||
&ServiceConfig::default(),
|
||||
);
|
||||
assert_eq!(
|
||||
bytes.take().freeze(),
|
||||
Bytes::from_static(b"\r\nContent-Length: 0\r\nConnection: close\r\nDate: date\r\nContent-Type: plain/text\r\n\r\n")
|
||||
);
|
||||
let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap();
|
||||
assert!(data.contains("Content-Length: 0\r\n"));
|
||||
assert!(data.contains("Connection: close\r\n"));
|
||||
assert!(data.contains("Content-Type: plain/text\r\n"));
|
||||
assert!(data.contains("Date: date\r\n"));
|
||||
|
||||
let _ = head.encode_headers(
|
||||
&mut bytes,
|
||||
@ -560,10 +561,10 @@ mod tests {
|
||||
ConnectionType::KeepAlive,
|
||||
&ServiceConfig::default(),
|
||||
);
|
||||
assert_eq!(
|
||||
bytes.take().freeze(),
|
||||
Bytes::from_static(b"\r\nTransfer-Encoding: chunked\r\nDate: date\r\nContent-Type: plain/text\r\n\r\n")
|
||||
);
|
||||
let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap();
|
||||
assert!(data.contains("Transfer-Encoding: chunked\r\n"));
|
||||
assert!(data.contains("Content-Type: plain/text\r\n"));
|
||||
assert!(data.contains("Date: date\r\n"));
|
||||
|
||||
let _ = head.encode_headers(
|
||||
&mut bytes,
|
||||
@ -572,10 +573,10 @@ mod tests {
|
||||
ConnectionType::KeepAlive,
|
||||
&ServiceConfig::default(),
|
||||
);
|
||||
assert_eq!(
|
||||
bytes.take().freeze(),
|
||||
Bytes::from_static(b"\r\nContent-Length: 100\r\nDate: date\r\nContent-Type: plain/text\r\n\r\n")
|
||||
);
|
||||
let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap();
|
||||
assert!(data.contains("Content-Length: 100\r\n"));
|
||||
assert!(data.contains("Content-Type: plain/text\r\n"));
|
||||
assert!(data.contains("Date: date\r\n"));
|
||||
|
||||
let mut head = RequestHead::default();
|
||||
head.set_camel_case_headers(false);
|
||||
@ -586,7 +587,6 @@ mod tests {
|
||||
.append(CONTENT_TYPE, HeaderValue::from_static("xml"));
|
||||
|
||||
let mut head = RequestHeadType::Owned(head);
|
||||
|
||||
let _ = head.encode_headers(
|
||||
&mut bytes,
|
||||
Version::HTTP_11,
|
||||
@ -594,10 +594,11 @@ mod tests {
|
||||
ConnectionType::KeepAlive,
|
||||
&ServiceConfig::default(),
|
||||
);
|
||||
assert_eq!(
|
||||
bytes.take().freeze(),
|
||||
Bytes::from_static(b"\r\ntransfer-encoding: chunked\r\ndate: date\r\ncontent-type: xml\r\ncontent-type: plain/text\r\n\r\n")
|
||||
);
|
||||
let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap();
|
||||
assert!(data.contains("transfer-encoding: chunked\r\n"));
|
||||
assert!(data.contains("content-type: xml\r\n"));
|
||||
assert!(data.contains("content-type: plain/text\r\n"));
|
||||
assert!(data.contains("date: date\r\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -626,9 +627,10 @@ mod tests {
|
||||
ConnectionType::Close,
|
||||
&ServiceConfig::default(),
|
||||
);
|
||||
assert_eq!(
|
||||
bytes.take().freeze(),
|
||||
Bytes::from_static(b"\r\ncontent-length: 0\r\nconnection: close\r\nauthorization: another authorization\r\ndate: date\r\n\r\n")
|
||||
);
|
||||
let data = String::from_utf8(Vec::from(bytes.take().freeze().as_ref())).unwrap();
|
||||
assert!(data.contains("content-length: 0\r\n"));
|
||||
assert!(data.contains("connection: close\r\n"));
|
||||
assert!(data.contains("authorization: another authorization\r\n"));
|
||||
assert!(data.contains("date: date\r\n"));
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,11 @@ pub enum DispositionParam {
|
||||
/// the form.
|
||||
Name(String),
|
||||
/// A plain file name.
|
||||
///
|
||||
/// It is [not supposed](https://tools.ietf.org/html/rfc6266#appendix-D) to contain any
|
||||
/// non-ASCII characters when used in a *Content-Disposition* HTTP response header, where
|
||||
/// [`FilenameExt`](DispositionParam::FilenameExt) with charset UTF-8 may be used instead
|
||||
/// in case there are Unicode characters in file names.
|
||||
Filename(String),
|
||||
/// An extended file name. It must not exist for `ContentType::Formdata` according to
|
||||
/// [RFC7578 Section 4.2](https://tools.ietf.org/html/rfc7578#section-4.2).
|
||||
@ -220,7 +225,16 @@ impl DispositionParam {
|
||||
/// ext-token = <the characters in token, followed by "*">
|
||||
/// ```
|
||||
///
|
||||
/// **Note**: filename* [must not](https://tools.ietf.org/html/rfc7578#section-4.2) be used within
|
||||
/// # Note
|
||||
///
|
||||
/// filename is [not supposed](https://tools.ietf.org/html/rfc6266#appendix-D) to contain any
|
||||
/// non-ASCII characters when used in a *Content-Disposition* HTTP response header, where
|
||||
/// filename* with charset UTF-8 may be used instead in case there are Unicode characters in file
|
||||
/// names.
|
||||
/// filename is [acceptable](https://tools.ietf.org/html/rfc7578#section-4.2) to be UTF-8 encoded
|
||||
/// directly in a *Content-Disposition* header for *multipart/form-data*, though.
|
||||
///
|
||||
/// filename* [must not](https://tools.ietf.org/html/rfc7578#section-4.2) be used within
|
||||
/// *multipart/form-data*.
|
||||
///
|
||||
/// # Example
|
||||
@ -251,6 +265,22 @@ impl DispositionParam {
|
||||
/// };
|
||||
/// assert_eq!(cd2.get_name(), Some("file")); // field name
|
||||
/// assert_eq!(cd2.get_filename(), Some("bill.odt"));
|
||||
///
|
||||
/// // HTTP response header with Unicode characters in file names
|
||||
/// let cd3 = ContentDisposition {
|
||||
/// disposition: DispositionType::Attachment,
|
||||
/// parameters: vec![
|
||||
/// DispositionParam::FilenameExt(ExtendedValue {
|
||||
/// charset: Charset::Ext(String::from("UTF-8")),
|
||||
/// language_tag: None,
|
||||
/// value: String::from("\u{1f600}.svg").into_bytes(),
|
||||
/// }),
|
||||
/// // fallback for better compatibility
|
||||
/// DispositionParam::Filename(String::from("Grinning-Face-Emoji.svg"))
|
||||
/// ],
|
||||
/// };
|
||||
/// assert_eq!(cd3.get_filename_ext().map(|ev| ev.value.as_ref()),
|
||||
/// Some("\u{1f600}.svg".as_bytes()));
|
||||
/// ```
|
||||
///
|
||||
/// # WARN
|
||||
@ -333,15 +363,17 @@ impl ContentDisposition {
|
||||
// token: won't contains semicolon according to RFC 2616 Section 2.2
|
||||
let (token, new_left) = split_once_and_trim(left, ';');
|
||||
left = new_left;
|
||||
if token.is_empty() {
|
||||
// quoted-string can be empty, but token cannot be empty
|
||||
return Err(crate::error::ParseError::Header);
|
||||
}
|
||||
token.to_owned()
|
||||
};
|
||||
if value.is_empty() {
|
||||
return Err(crate::error::ParseError::Header);
|
||||
}
|
||||
|
||||
let param = if param_name.eq_ignore_ascii_case("name") {
|
||||
DispositionParam::Name(value)
|
||||
} else if param_name.eq_ignore_ascii_case("filename") {
|
||||
// See also comments in test_from_raw_uncessary_percent_decode.
|
||||
DispositionParam::Filename(value)
|
||||
} else {
|
||||
DispositionParam::Unknown(param_name.to_owned(), value)
|
||||
@ -466,11 +498,40 @@ impl fmt::Display for DispositionType {
|
||||
|
||||
impl fmt::Display for DispositionParam {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// All ASCII control charaters (0-30, 127) excepting horizontal tab, double quote, and
|
||||
// All ASCII control characters (0-30, 127) including horizontal tab, double quote, and
|
||||
// backslash should be escaped in quoted-string (i.e. "foobar").
|
||||
// Ref: RFC6266 S4.1 -> RFC2616 S2.2; RFC 7578 S4.2 -> RFC2183 S2 -> ... .
|
||||
// Ref: RFC6266 S4.1 -> RFC2616 S3.6
|
||||
// filename-parm = "filename" "=" value
|
||||
// value = token | quoted-string
|
||||
// quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
|
||||
// qdtext = <any TEXT except <">>
|
||||
// quoted-pair = "\" CHAR
|
||||
// TEXT = <any OCTET except CTLs,
|
||||
// but including LWS>
|
||||
// LWS = [CRLF] 1*( SP | HT )
|
||||
// OCTET = <any 8-bit sequence of data>
|
||||
// CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
// CTL = <any US-ASCII control character
|
||||
// (octets 0 - 31) and DEL (127)>
|
||||
//
|
||||
// Ref: RFC7578 S4.2 -> RFC2183 S2 -> RFC2045 S5.1
|
||||
// parameter := attribute "=" value
|
||||
// attribute := token
|
||||
// ; Matching of attributes
|
||||
// ; is ALWAYS case-insensitive.
|
||||
// value := token / quoted-string
|
||||
// token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
|
||||
// or tspecials>
|
||||
// tspecials := "(" / ")" / "<" / ">" / "@" /
|
||||
// "," / ";" / ":" / "\" / <">
|
||||
// "/" / "[" / "]" / "?" / "="
|
||||
// ; Must be in quoted-string,
|
||||
// ; to use within parameter values
|
||||
//
|
||||
//
|
||||
// See also comments in test_from_raw_uncessary_percent_decode.
|
||||
lazy_static! {
|
||||
static ref RE: Regex = Regex::new("[\x01-\x08\x10\x1F\x7F\"\\\\]").unwrap();
|
||||
static ref RE: Regex = Regex::new("[\x00-\x08\x10-\x1F\x7F\"\\\\]").unwrap();
|
||||
}
|
||||
match self {
|
||||
DispositionParam::Name(ref value) => write!(f, "name={}", value),
|
||||
@ -774,8 +835,18 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_from_raw_uncessary_percent_decode() {
|
||||
// In fact, RFC7578 (multipart/form-data) Section 2 and 4.2 suggests that filename with
|
||||
// non-ASCII characters MAY be percent-encoded.
|
||||
// On the contrary, RFC6266 or other RFCs related to Content-Disposition response header
|
||||
// do not mention such percent-encoding.
|
||||
// So, it appears to be undecidable whether to percent-decode or not without
|
||||
// knowing the usage scenario (multipart/form-data v.s. HTTP response header) and
|
||||
// inevitable to unnecessarily percent-decode filename with %XX in the former scenario.
|
||||
// Fortunately, it seems that almost all mainstream browsers just send UTF-8 encoded file
|
||||
// names in quoted-string format (tested on Edge, IE11, Chrome and Firefox) without
|
||||
// percent-encoding. So we do not bother to attempt to percent-decode.
|
||||
let a = HeaderValue::from_static(
|
||||
"form-data; name=photo; filename=\"%74%65%73%74%2e%70%6e%67\"", // Should not be decoded!
|
||||
"form-data; name=photo; filename=\"%74%65%73%74%2e%70%6e%67\"",
|
||||
);
|
||||
let a: ContentDisposition = ContentDisposition::from_raw(&a).unwrap();
|
||||
let b = ContentDisposition {
|
||||
@ -811,6 +882,9 @@ mod tests {
|
||||
|
||||
let a = HeaderValue::from_static("inline; filename= ");
|
||||
assert!(ContentDisposition::from_raw(&a).is_err());
|
||||
|
||||
let a = HeaderValue::from_static("inline; filename=\"\"");
|
||||
assert!(ContentDisposition::from_raw(&a).expect("parse cd").get_filename().expect("filename").is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1,5 +1,9 @@
|
||||
# Changes
|
||||
|
||||
## [0.2.8] - 2019-11-06
|
||||
|
||||
* Add support for setting query from Serialize type for client request.
|
||||
|
||||
|
||||
## [0.2.7] - 2019-09-25
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "awc"
|
||||
version = "0.2.7"
|
||||
version = "0.2.8"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||
description = "Actix http client."
|
||||
readme = "README.md"
|
||||
@ -44,7 +44,7 @@ flate2-rust = ["actix-http/flate2-rust"]
|
||||
[dependencies]
|
||||
actix-codec = "0.1.2"
|
||||
actix-service = "0.4.1"
|
||||
actix-http = "0.2.10"
|
||||
actix-http = "0.2.11"
|
||||
base64 = "0.10.1"
|
||||
bytes = "0.4"
|
||||
derive_more = "0.15.0"
|
||||
@ -62,8 +62,8 @@ rustls = { version = "0.15.2", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "0.2.2"
|
||||
actix-web = { version = "1.0.0", features=["ssl"] }
|
||||
actix-http = { version = "0.2.10", features=["ssl"] }
|
||||
actix-web = { version = "1.0.8", features=["ssl"] }
|
||||
actix-http = { version = "0.2.11", features=["ssl"] }
|
||||
actix-http-test = { version = "0.2.0", features=["ssl"] }
|
||||
actix-utils = "0.4.1"
|
||||
actix-server = { version = "0.6.0", features=["ssl", "rust-tls"] }
|
||||
|
@ -382,6 +382,27 @@ impl ClientRequest {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the query part of the request
|
||||
pub fn query<T: Serialize>(
|
||||
mut self,
|
||||
query: &T,
|
||||
) -> Result<Self, serde_urlencoded::ser::Error> {
|
||||
let mut parts = self.head.uri.clone().into_parts();
|
||||
|
||||
if let Some(path_and_query) = parts.path_and_query {
|
||||
let query = serde_urlencoded::to_string(query)?;
|
||||
let path = path_and_query.path();
|
||||
parts.path_and_query = format!("{}?{}", path, query).parse().ok();
|
||||
|
||||
match Uri::from_parts(parts) {
|
||||
Ok(uri) => self.head.uri = uri,
|
||||
Err(e) => self.err = Some(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Freeze request builder and construct `FrozenClientRequest`,
|
||||
/// which could be used for sending same request multiple times.
|
||||
pub fn freeze(self) -> Result<FrozenClientRequest, FreezeRequestError> {
|
||||
@ -690,4 +711,13 @@ mod tests {
|
||||
"Bearer someS3cr3tAutht0k3n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_query() {
|
||||
let req = Client::new()
|
||||
.get("/")
|
||||
.query(&[("key1", "val1"), ("key2", "val2")])
|
||||
.unwrap();
|
||||
assert_eq!(req.get_uri().query().unwrap(), "key1=val1&key2=val2");
|
||||
}
|
||||
}
|
||||
|
35
src/guard.rs
35
src/guard.rs
@ -276,10 +276,12 @@ pub fn Host<H: AsRef<str>>(host: H) -> HostGuard {
|
||||
|
||||
fn get_host_uri(req: &RequestHead) -> Option<Uri> {
|
||||
use core::str::FromStr;
|
||||
let host_value = req.headers.get(header::HOST)?;
|
||||
let host = host_value.to_str().ok()?;
|
||||
let uri = Uri::from_str(host).ok()?;
|
||||
Some(uri)
|
||||
req.headers
|
||||
.get(header::HOST)
|
||||
.and_then(|host_value| host_value.to_str().ok())
|
||||
.or_else(|| req.uri.host())
|
||||
.map(|host: &str| Uri::from_str(host).ok())
|
||||
.and_then(|host_success| host_success)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@ -400,6 +402,31 @@ mod tests {
|
||||
assert!(!pred.check(req.head()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_host_without_header() {
|
||||
let req = TestRequest::default()
|
||||
.uri("www.rust-lang.org")
|
||||
.to_http_request();
|
||||
|
||||
let pred = Host("www.rust-lang.org");
|
||||
assert!(pred.check(req.head()));
|
||||
|
||||
let pred = Host("www.rust-lang.org").scheme("https");
|
||||
assert!(pred.check(req.head()));
|
||||
|
||||
let pred = Host("blog.rust-lang.org");
|
||||
assert!(!pred.check(req.head()));
|
||||
|
||||
let pred = Host("blog.rust-lang.org").scheme("https");
|
||||
assert!(!pred.check(req.head()));
|
||||
|
||||
let pred = Host("crates.io");
|
||||
assert!(!pred.check(req.head()));
|
||||
|
||||
let pred = Host("localhost");
|
||||
assert!(!pred.check(req.head()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_methods() {
|
||||
let req = TestRequest::default().to_http_request();
|
||||
|
@ -162,6 +162,12 @@ impl ConnectionInfo {
|
||||
/// - Forwarded
|
||||
/// - X-Forwarded-For
|
||||
/// - peer name of opened socket
|
||||
///
|
||||
/// # Security
|
||||
/// Do not use this function for security purposes, unless you can ensure the Forwarded and
|
||||
/// X-Forwarded-For headers cannot be spoofed by the client. If you want the client's socket
|
||||
/// address explicitly, use
|
||||
/// [`HttpRequest::peer_addr()`](../web/struct.HttpRequest.html#method.peer_addr) instead.
|
||||
#[inline]
|
||||
pub fn remote(&self) -> Option<&str> {
|
||||
if let Some(ref r) = self.remote {
|
||||
|
@ -5,6 +5,7 @@
|
||||
### Changed
|
||||
|
||||
* Update serde_urlencoded to "0.6.1"
|
||||
* Increase TestServerRuntime timeouts from 500ms to 3000ms
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -144,7 +144,7 @@ impl TestServer {
|
||||
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
|
||||
Connector::new()
|
||||
.conn_lifetime(time::Duration::from_secs(0))
|
||||
.timeout(time::Duration::from_millis(500))
|
||||
.timeout(time::Duration::from_millis(3000))
|
||||
.ssl(builder.build())
|
||||
.finish()
|
||||
}
|
||||
@ -152,7 +152,7 @@ impl TestServer {
|
||||
{
|
||||
Connector::new()
|
||||
.conn_lifetime(time::Duration::from_secs(0))
|
||||
.timeout(time::Duration::from_millis(500))
|
||||
.timeout(time::Duration::from_millis(3000))
|
||||
.finish()
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user