mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-30 18:34:36 +01:00
fix router cannot parse Non-ASCII characters in URL #137
This commit is contained in:
parent
4f7d45ee9c
commit
90e3aaaf8a
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
* Added `HttpReuqest::extract_xxx()`, type safe path/query information extractor
|
* Added `HttpReuqest::extract_xxx()`, type safe path/query information extractor
|
||||||
|
|
||||||
|
* Router cannot parse Non-ASCII characters in URL #137
|
||||||
|
|
||||||
* Fix long client urls #129
|
* Fix long client urls #129
|
||||||
|
|
||||||
* Fix panic on invalid URL characters #130
|
* Fix panic on invalid URL characters #130
|
||||||
|
@ -11,6 +11,7 @@ use http::header::{self, HeaderName, HeaderValue};
|
|||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use url::Url;
|
||||||
use percent_encoding::{USERINFO_ENCODE_SET, percent_encode};
|
use percent_encoding::{USERINFO_ENCODE_SET, percent_encode};
|
||||||
|
|
||||||
use body::Body;
|
use body::Body;
|
||||||
@ -66,35 +67,35 @@ impl Default for ClientRequest {
|
|||||||
impl ClientRequest {
|
impl ClientRequest {
|
||||||
|
|
||||||
/// Create request builder for `GET` request
|
/// Create request builder for `GET` request
|
||||||
pub fn get<U>(uri: U) -> ClientRequestBuilder where Uri: HttpTryFrom<U> {
|
pub fn get<U: AsRef<str>>(uri: U) -> ClientRequestBuilder {
|
||||||
let mut builder = ClientRequest::build();
|
let mut builder = ClientRequest::build();
|
||||||
builder.method(Method::GET).uri(uri);
|
builder.method(Method::GET).uri(uri);
|
||||||
builder
|
builder
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create request builder for `HEAD` request
|
/// Create request builder for `HEAD` request
|
||||||
pub fn head<U>(uri: U) -> ClientRequestBuilder where Uri: HttpTryFrom<U> {
|
pub fn head<U: AsRef<str>>(uri: U) -> ClientRequestBuilder {
|
||||||
let mut builder = ClientRequest::build();
|
let mut builder = ClientRequest::build();
|
||||||
builder.method(Method::HEAD).uri(uri);
|
builder.method(Method::HEAD).uri(uri);
|
||||||
builder
|
builder
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create request builder for `POST` request
|
/// Create request builder for `POST` request
|
||||||
pub fn post<U>(uri: U) -> ClientRequestBuilder where Uri: HttpTryFrom<U> {
|
pub fn post<U: AsRef<str>>(uri: U) -> ClientRequestBuilder {
|
||||||
let mut builder = ClientRequest::build();
|
let mut builder = ClientRequest::build();
|
||||||
builder.method(Method::POST).uri(uri);
|
builder.method(Method::POST).uri(uri);
|
||||||
builder
|
builder
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create request builder for `PUT` request
|
/// Create request builder for `PUT` request
|
||||||
pub fn put<U>(uri: U) -> ClientRequestBuilder where Uri: HttpTryFrom<U> {
|
pub fn put<U: AsRef<str>>(uri: U) -> ClientRequestBuilder {
|
||||||
let mut builder = ClientRequest::build();
|
let mut builder = ClientRequest::build();
|
||||||
builder.method(Method::PUT).uri(uri);
|
builder.method(Method::PUT).uri(uri);
|
||||||
builder
|
builder
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create request builder for `DELETE` request
|
/// Create request builder for `DELETE` request
|
||||||
pub fn delete<U>(uri: U) -> ClientRequestBuilder where Uri: HttpTryFrom<U> {
|
pub fn delete<U: AsRef<str>>(uri: U) -> ClientRequestBuilder {
|
||||||
let mut builder = ClientRequest::build();
|
let mut builder = ClientRequest::build();
|
||||||
builder.method(Method::DELETE).uri(uri);
|
builder.method(Method::DELETE).uri(uri);
|
||||||
builder
|
builder
|
||||||
@ -255,8 +256,15 @@ pub struct ClientRequestBuilder {
|
|||||||
impl ClientRequestBuilder {
|
impl ClientRequestBuilder {
|
||||||
/// Set HTTP uri of request.
|
/// Set HTTP uri of request.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn uri<U>(&mut self, uri: U) -> &mut Self where Uri: HttpTryFrom<U> {
|
pub fn uri<U: AsRef<str>>(&mut self, uri: U) -> &mut Self {
|
||||||
match Uri::try_from(uri) {
|
match Url::parse(uri.as_ref()) {
|
||||||
|
Ok(url) => self._uri(url.as_str()),
|
||||||
|
Err(_) => self._uri(uri.as_ref()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _uri(&mut self, url: &str) -> &mut Self {
|
||||||
|
match Uri::try_from(url) {
|
||||||
Ok(uri) => {
|
Ok(uri) => {
|
||||||
// set request host header
|
// set request host header
|
||||||
if let Some(host) = uri.host() {
|
if let Some(host) = uri.host() {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
use std::{io, cmp, str, fmt, mem};
|
use std::{io, cmp, str, fmt, mem};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use std::borrow::Cow;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use cookie::Cookie;
|
use cookie::Cookie;
|
||||||
use futures::{Async, Future, Stream, Poll};
|
use futures::{Async, Future, Stream, Poll};
|
||||||
@ -11,6 +12,7 @@ use url::{Url, form_urlencoded};
|
|||||||
use http::{header, Uri, Method, Version, HeaderMap, Extensions, StatusCode};
|
use http::{header, Uri, Method, Version, HeaderMap, Extensions, StatusCode};
|
||||||
use tokio_io::AsyncRead;
|
use tokio_io::AsyncRead;
|
||||||
use serde::de;
|
use serde::de;
|
||||||
|
use percent_encoding::percent_decode;
|
||||||
|
|
||||||
use body::Body;
|
use body::Body;
|
||||||
use info::ConnectionInfo;
|
use info::ConnectionInfo;
|
||||||
@ -257,6 +259,12 @@ impl<S> HttpRequest<S> {
|
|||||||
self.uri().path()
|
self.uri().path()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Percent decoded path of this Request.
|
||||||
|
#[inline]
|
||||||
|
pub fn path_decoded(&self) -> Cow<str> {
|
||||||
|
percent_decode(self.uri().path().as_bytes()).decode_utf8().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get *ConnectionInfo* for correct request.
|
/// Get *ConnectionInfo* for correct request.
|
||||||
pub fn connection_info(&self) -> &ConnectionInfo {
|
pub fn connection_info(&self) -> &ConnectionInfo {
|
||||||
if self.as_ref().info.is_none() {
|
if self.as_ref().info.is_none() {
|
||||||
@ -598,7 +606,7 @@ impl<S> AsyncRead for HttpRequest<S> {}
|
|||||||
impl<S> fmt::Debug for HttpRequest<S> {
|
impl<S> fmt::Debug for HttpRequest<S> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let res = write!(f, "\nHttpRequest {:?} {}:{}\n",
|
let res = write!(f, "\nHttpRequest {:?} {}:{}\n",
|
||||||
self.as_ref().version, self.as_ref().method, self.as_ref().uri);
|
self.as_ref().version, self.as_ref().method, self.path_decoded());
|
||||||
if !self.query_string().is_empty() {
|
if !self.query_string().is_empty() {
|
||||||
let _ = write!(f, " query: ?{:?}\n", self.query_string());
|
let _ = write!(f, " query: ?{:?}\n", self.query_string());
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ use std::hash::{Hash, Hasher};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use regex::{Regex, escape};
|
use regex::{Regex, escape};
|
||||||
|
use percent_encoding::percent_decode;
|
||||||
|
|
||||||
use error::UrlGenerationError;
|
use error::UrlGenerationError;
|
||||||
use param::Params;
|
use param::Params;
|
||||||
@ -70,9 +71,10 @@ impl Router {
|
|||||||
}
|
}
|
||||||
let path: &str = unsafe{mem::transmute(&req.path()[self.0.prefix_len..])};
|
let path: &str = unsafe{mem::transmute(&req.path()[self.0.prefix_len..])};
|
||||||
let route_path = if path.is_empty() { "/" } else { path };
|
let route_path = if path.is_empty() { "/" } else { path };
|
||||||
|
let p = percent_decode(route_path.as_bytes()).decode_utf8().unwrap();
|
||||||
|
|
||||||
for (idx, pattern) in self.0.patterns.iter().enumerate() {
|
for (idx, pattern) in self.0.patterns.iter().enumerate() {
|
||||||
if pattern.match_with_params(route_path, req.match_info_mut()) {
|
if pattern.match_with_params(p.as_ref(), req.match_info_mut()) {
|
||||||
return Some(idx)
|
return Some(idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,3 +61,20 @@ fn test_query_extractor() {
|
|||||||
let response = srv.execute(request.send()).unwrap();
|
let response = srv.execute(request.send()).unwrap();
|
||||||
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_non_ascii_route() {
|
||||||
|
let mut srv = test::TestServer::new(|app| {
|
||||||
|
app.resource("/中文/index.html", |r| r.f(|_| "success"));
|
||||||
|
});
|
||||||
|
|
||||||
|
// client request
|
||||||
|
let request = srv.get().uri(srv.url("/中文/index.html"))
|
||||||
|
.finish().unwrap();
|
||||||
|
let response = srv.execute(request.send()).unwrap();
|
||||||
|
assert!(response.status().is_success());
|
||||||
|
|
||||||
|
// read response
|
||||||
|
let bytes = srv.execute(response.body()).unwrap();
|
||||||
|
assert_eq!(bytes, Bytes::from_static(b"success"));
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user