1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-23 23:51:06 +01:00

Do not use as it can not parse some valid paths

This commit is contained in:
Nikolay Kim 2017-10-27 22:19:00 -07:00
parent 76ffc60971
commit d93244aa4f
9 changed files with 91 additions and 58 deletions

View File

@ -3,6 +3,8 @@
## 0.2.0 (2017-10-xx)
* Do not use `http::Uri` as it can not parse some valid paths
* Refactor response `Body`
* Refactor `RouteRecognizer` usability

View File

@ -41,6 +41,7 @@ regex = "0.2"
slab = "0.4"
sha1 = "0.2"
url = "1.5"
percent-encoding = "1.0"
# tokio
bytes = "0.4"

View File

@ -4,7 +4,7 @@ use std::collections::HashMap;
use bytes::BytesMut;
use futures::{Async, Future, Stream, Poll};
use url::form_urlencoded;
use http::{header, Method, Version, Uri, HeaderMap, Extensions};
use http::{header, Method, Version, HeaderMap, Extensions};
use {Cookie, CookieParseError};
use {HttpRange, HttpRangeParseError};
@ -18,7 +18,8 @@ use multipart::{Multipart, MultipartError};
pub struct HttpRequest {
version: Version,
method: Method,
uri: Uri,
path: String,
query: String,
headers: HeaderMap,
params: Params,
cookies: Vec<Cookie<'static>>,
@ -28,10 +29,13 @@ pub struct HttpRequest {
impl HttpRequest {
/// Construct a new Request.
#[inline]
pub fn new(method: Method, uri: Uri, version: Version, headers: HeaderMap) -> Self {
pub fn new(method: Method, path: String,
version: Version, headers: HeaderMap, query: String) -> Self
{
HttpRequest {
method: method,
uri: uri,
path: path,
query: query,
version: version,
headers: headers,
params: Params::empty(),
@ -43,7 +47,8 @@ impl HttpRequest {
pub(crate) fn for_error() -> HttpRequest {
HttpRequest {
method: Method::GET,
uri: Uri::default(),
path: String::new(),
query: String::new(),
version: Version::HTTP_11,
headers: HeaderMap::new(),
params: Params::empty(),
@ -58,10 +63,6 @@ impl HttpRequest {
&mut self.extensions
}
/// Read the Request Uri.
#[inline]
pub fn uri(&self) -> &Uri { &self.uri }
/// Read the Request method.
#[inline]
pub fn method(&self) -> &Method { &self.method }
@ -79,13 +80,13 @@ impl HttpRequest {
/// The target path of this Request.
#[inline]
pub fn path(&self) -> &str {
self.uri.path()
&self.path
}
/// Return a new iterator that yields pairs of `Cow<str>` for query parameters
#[inline]
pub fn query(&self) -> form_urlencoded::Parse {
form_urlencoded::parse(self.query_string().as_ref())
form_urlencoded::parse(self.query.as_ref())
}
/// The query string in the URL.
@ -93,7 +94,7 @@ impl HttpRequest {
/// E.g., id=10
#[inline]
pub fn query_string(&self) -> &str {
self.uri.query().unwrap_or("")
&self.query
}
/// Return request cookies.
@ -238,7 +239,8 @@ impl HttpRequest {
impl fmt::Debug for HttpRequest {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let res = write!(f, "\nHttpRequest {:?} {}:{}\n", self.version, self.method, self.uri);
let res = write!(f, "\nHttpRequest {:?} {}:{}\n",
self.version, self.method, self.path);
if !self.params.is_empty() {
let _ = write!(f, " params: {:?}\n", self.params);
}
@ -289,9 +291,6 @@ impl Future for UrlEncoded {
#[cfg(test)]
mod tests {
use super::*;
use http::{Uri, HttpTryFrom};
// use futures::future::{lazy, result};
// use tokio_core::reactor::Core;
use payload::Payload;
#[test]
@ -300,7 +299,7 @@ mod tests {
headers.insert(header::TRANSFER_ENCODING,
header::HeaderValue::from_static("chunked"));
let req = HttpRequest::new(
Method::GET, Uri::try_from("/").unwrap(), Version::HTTP_11, headers);
Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
let (_, payload) = Payload::new(false);
assert!(req.urlencoded(payload).is_err());
@ -311,7 +310,7 @@ mod tests {
headers.insert(header::CONTENT_LENGTH,
header::HeaderValue::from_static("xxxx"));
let req = HttpRequest::new(
Method::GET, Uri::try_from("/").unwrap(), Version::HTTP_11, headers);
Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
let (_, payload) = Payload::new(false);
assert!(req.urlencoded(payload).is_err());
@ -322,7 +321,7 @@ mod tests {
headers.insert(header::CONTENT_LENGTH,
header::HeaderValue::from_static("1000000"));
let req = HttpRequest::new(
Method::GET, Uri::try_from("/").unwrap(), Version::HTTP_11, headers);
Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
let (_, payload) = Payload::new(false);
assert!(req.urlencoded(payload).is_err());
@ -333,7 +332,7 @@ mod tests {
headers.insert(header::CONTENT_LENGTH,
header::HeaderValue::from_static("10"));
let req = HttpRequest::new(
Method::GET, Uri::try_from("/").unwrap(), Version::HTTP_11, headers);
Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
let (_, payload) = Payload::new(false);
assert!(req.urlencoded(payload).is_err());

View File

@ -19,6 +19,7 @@ extern crate http_range;
extern crate mime;
extern crate mime_guess;
extern crate url;
extern crate percent_encoding;
extern crate actix;
mod application;

View File

@ -52,7 +52,13 @@ impl Logger {
match *text {
FormatText::Str(ref string) => fmt.write_str(string),
FormatText::Method => req.method().fmt(fmt),
FormatText::URI => req.uri().fmt(fmt),
FormatText::URI => {
if req.query_string().is_empty() {
fmt.write_fmt(format_args!("{}", req.path()))
} else {
fmt.write_fmt(format_args!("{}?{}", req.path(), req.query_string()))
}
},
FormatText::Status => resp.status().fmt(fmt),
FormatText::ResponseTime =>
fmt.write_fmt(format_args!("{} ms", response_time_ms)),

View File

@ -1,11 +1,12 @@
use std::{self, io, ptr};
use httparse;
use http::{Method, Version, Uri, HttpTryFrom, HeaderMap};
use http::{Method, Version, HttpTryFrom, HeaderMap};
use http::header::{self, HeaderName, HeaderValue};
use bytes::{BytesMut, BufMut};
use futures::{Async, Poll};
use tokio_io::AsyncRead;
use percent_encoding;
use error::ParseError;
use decode::Decoder;
@ -248,8 +249,31 @@ impl Reader {
let slice = buf.split_to(len).freeze();
let path = slice.slice(path.0, path.1);
// path was found to be utf8 by httparse
let uri = Uri::from_shared(path).map_err(|_| ParseError::Uri)?;
// manually split path, path was found to be utf8 by httparse
let uri = {
if let Ok(path) = percent_encoding::percent_decode(&path).decode_utf8() {
let parts: Vec<&str> = path.splitn(2, '?').collect();
if parts.len() == 2 {
Some((parts[0].to_owned(), parts[1][1..].to_owned()))
} else {
Some((parts[0].to_owned(), String::new()))
}
} else {
None
}
};
let (path, query) = if let Some(uri) = uri {
uri
} else {
let parts: Vec<&str> = unsafe{
std::str::from_utf8_unchecked(&path)}.splitn(2, '?').collect();
if parts.len() == 2 {
(parts[0].to_owned(), parts[1][1..].to_owned())
} else {
(parts[0].to_owned(), String::new())
}
};
// convert headers
let mut headers = HeaderMap::with_capacity(headers_len);
@ -267,7 +291,7 @@ impl Reader {
}
}
let msg = HttpRequest::new(method, uri, version, headers);
let msg = HttpRequest::new(method, path, version, headers, query);
let decoder = if msg.upgrade() {
Some(Decoder::eof())

View File

@ -325,20 +325,20 @@ impl WsWriter {
#[cfg(test)]
mod tests {
use http::{Method, HeaderMap, StatusCode, Uri, Version, HttpTryFrom, header};
use http::{Method, HeaderMap, StatusCode, Version, header};
use super::{HttpRequest, SEC_WEBSOCKET_VERSION, SEC_WEBSOCKET_KEY, handshake};
#[test]
fn test_handshake() {
let req = HttpRequest::new(Method::POST, Uri::try_from("/").unwrap(),
Version::HTTP_11, HeaderMap::new());
let req = HttpRequest::new(Method::POST, "/".to_owned(),
Version::HTTP_11, HeaderMap::new(), String::new());
match handshake(&req) {
Err(err) => assert_eq!(err.status(), StatusCode::METHOD_NOT_ALLOWED),
_ => panic!("should not happen"),
}
let req = HttpRequest::new(Method::GET, Uri::try_from("/").unwrap(),
Version::HTTP_11, HeaderMap::new());
let req = HttpRequest::new(Method::GET, "/".to_owned(),
Version::HTTP_11, HeaderMap::new(), String::new());
match handshake(&req) {
Err(err) => assert_eq!(err.status(), StatusCode::METHOD_NOT_ALLOWED),
_ => panic!("should not happen"),
@ -347,8 +347,8 @@ mod tests {
let mut headers = HeaderMap::new();
headers.insert(header::UPGRADE,
header::HeaderValue::from_static("test"));
let req = HttpRequest::new(Method::GET, Uri::try_from("/").unwrap(),
Version::HTTP_11, headers);
let req = HttpRequest::new(Method::GET, "/".to_owned(),
Version::HTTP_11, headers, String::new());
match handshake(&req) {
Err(err) => assert_eq!(err.status(), StatusCode::METHOD_NOT_ALLOWED),
_ => panic!("should not happen"),
@ -357,8 +357,8 @@ mod tests {
let mut headers = HeaderMap::new();
headers.insert(header::UPGRADE,
header::HeaderValue::from_static("websocket"));
let req = HttpRequest::new(Method::GET, Uri::try_from("/").unwrap(),
Version::HTTP_11, headers);
let req = HttpRequest::new(Method::GET, "/".to_owned(),
Version::HTTP_11, headers, String::new());
match handshake(&req) {
Err(err) => assert_eq!(err.status(), StatusCode::BAD_REQUEST),
_ => panic!("should not happen"),
@ -369,8 +369,8 @@ mod tests {
header::HeaderValue::from_static("websocket"));
headers.insert(header::CONNECTION,
header::HeaderValue::from_static("upgrade"));
let req = HttpRequest::new(Method::GET, Uri::try_from("/").unwrap(),
Version::HTTP_11, headers);
let req = HttpRequest::new(Method::GET, "/".to_owned(),
Version::HTTP_11, headers, String::new());
match handshake(&req) {
Err(err) => assert_eq!(err.status(), StatusCode::BAD_REQUEST),
_ => panic!("should not happen"),
@ -383,8 +383,8 @@ mod tests {
header::HeaderValue::from_static("upgrade"));
headers.insert(SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("5"));
let req = HttpRequest::new(Method::GET, Uri::try_from("/").unwrap(),
Version::HTTP_11, headers);
let req = HttpRequest::new(Method::GET, "/".to_owned(),
Version::HTTP_11, headers, String::new());
match handshake(&req) {
Err(err) => assert_eq!(err.status(), StatusCode::BAD_REQUEST),
_ => panic!("should not happen"),
@ -397,8 +397,8 @@ mod tests {
header::HeaderValue::from_static("upgrade"));
headers.insert(SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("13"));
let req = HttpRequest::new(Method::GET, Uri::try_from("/").unwrap(),
Version::HTTP_11, headers);
let req = HttpRequest::new(Method::GET, "/".to_owned(),
Version::HTTP_11, headers, String::new());
match handshake(&req) {
Err(err) => assert_eq!(err.status(), StatusCode::BAD_REQUEST),
_ => panic!("should not happen"),
@ -413,8 +413,8 @@ mod tests {
header::HeaderValue::from_static("13"));
headers.insert(SEC_WEBSOCKET_KEY,
header::HeaderValue::from_static("13"));
let req = HttpRequest::new(Method::GET, Uri::try_from("/").unwrap(),
Version::HTTP_11, headers);
let req = HttpRequest::new(Method::GET, "/".to_owned(),
Version::HTTP_11, headers, String::new());
match handshake(&req) {
Ok(resp) => {
assert_eq!(resp.status(), StatusCode::SWITCHING_PROTOCOLS)

View File

@ -4,13 +4,13 @@ extern crate time;
use std::str;
use actix_web::*;
use http::{header, Method, Uri, Version, HeaderMap, HttpTryFrom};
use http::{header, Method, Version, HeaderMap};
#[test]
fn test_no_request_cookies() {
let mut req = HttpRequest::new(
Method::GET, Uri::try_from("/").unwrap(), Version::HTTP_11, HeaderMap::new());
Method::GET, "/".to_owned(), Version::HTTP_11, HeaderMap::new(), String::new());
assert!(req.cookies().is_empty());
let _ = req.load_cookies();
assert!(req.cookies().is_empty());
@ -23,7 +23,7 @@ fn test_request_cookies() {
header::HeaderValue::from_static("cookie1=value1; cookie2=value2"));
let mut req = HttpRequest::new(
Method::GET, Uri::try_from("/").unwrap(), Version::HTTP_11, headers);
Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
assert!(req.cookies().is_empty());
{
let cookies = req.load_cookies().unwrap();
@ -46,8 +46,8 @@ fn test_request_cookies() {
#[test]
fn test_no_request_range_header() {
let req = HttpRequest::new(Method::GET, Uri::try_from("/").unwrap(),
Version::HTTP_11, HeaderMap::new());
let req = HttpRequest::new(Method::GET, "/".to_owned(),
Version::HTTP_11, HeaderMap::new(), String::new());
let ranges = req.range(100).unwrap();
assert!(ranges.is_empty());
}
@ -58,8 +58,8 @@ fn test_request_range_header() {
headers.insert(header::RANGE,
header::HeaderValue::from_static("bytes=0-4"));
let req = HttpRequest::new(Method::GET, Uri::try_from("/").unwrap(),
Version::HTTP_11, headers);
let req = HttpRequest::new(Method::GET, "/".to_owned(),
Version::HTTP_11, headers, String::new());
let ranges = req.range(100).unwrap();
assert_eq!(ranges.len(), 1);
assert_eq!(ranges[0].start, 0);
@ -68,8 +68,8 @@ fn test_request_range_header() {
#[test]
fn test_request_query() {
let req = HttpRequest::new(Method::GET, Uri::try_from("/?id=test").unwrap(),
Version::HTTP_11, HeaderMap::new());
let req = HttpRequest::new(Method::GET, "/".to_owned(),
Version::HTTP_11, HeaderMap::new(), "id=test".to_owned());
assert_eq!(req.query_string(), "id=test");
let query: Vec<_> = req.query().collect();
@ -79,8 +79,8 @@ fn test_request_query() {
#[test]
fn test_request_match_info() {
let mut req = HttpRequest::new(Method::GET, Uri::try_from("/value/?id=test").unwrap(),
Version::HTTP_11, HeaderMap::new());
let mut req = HttpRequest::new(Method::GET, "/value/".to_owned(),
Version::HTTP_11, HeaderMap::new(), "?id=test".to_owned());
let rec = RouteRecognizer::new("/".to_owned(), vec![("/{key}/".to_owned(), 1)]);
let (params, _) = rec.recognize(req.path()).unwrap();
@ -93,14 +93,14 @@ fn test_request_match_info() {
#[test]
fn test_chunked() {
let req = HttpRequest::new(
Method::GET, Uri::try_from("/").unwrap(), Version::HTTP_11, HeaderMap::new());
Method::GET, "/".to_owned(), Version::HTTP_11, HeaderMap::new(), String::new());
assert!(!req.chunked().unwrap());
let mut headers = HeaderMap::new();
headers.insert(header::TRANSFER_ENCODING,
header::HeaderValue::from_static("chunked"));
let req = HttpRequest::new(
Method::GET, Uri::try_from("/").unwrap(), Version::HTTP_11, headers);
Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
assert!(req.chunked().unwrap());
let mut headers = HeaderMap::new();
@ -109,6 +109,6 @@ fn test_chunked() {
headers.insert(header::TRANSFER_ENCODING,
header::HeaderValue::from_str(s).unwrap());
let req = HttpRequest::new(
Method::GET, Uri::try_from("/").unwrap(), Version::HTTP_11, headers);
Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
assert!(req.chunked().is_err());
}

View File

@ -4,7 +4,7 @@ extern crate time;
use actix_web::*;
use time::Duration;
use http::{header, Method, Uri, Version, HeaderMap, HttpTryFrom};
use http::{header, Method, Version, HeaderMap};
#[test]
@ -14,7 +14,7 @@ fn test_response_cookies() {
header::HeaderValue::from_static("cookie1=value1; cookie2=value2"));
let mut req = HttpRequest::new(
Method::GET, Uri::try_from("/").unwrap(), Version::HTTP_11, headers);
Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
let cookies = req.load_cookies().unwrap();
let resp = httpcodes::HTTPOk