Normalize HTTP request path. (#228)
The HTTP/2.0 specification requires that the path pseudo header is never empty for requests unless the request uses the OPTIONS method. This is currently not correctly enforced. This patch provides a test and a fix.
This commit is contained in:
@@ -440,10 +440,14 @@ impl Pseudo {
|
|||||||
pub fn request(method: Method, uri: Uri) -> Self {
|
pub fn request(method: Method, uri: Uri) -> Self {
|
||||||
let parts = uri::Parts::from(uri);
|
let parts = uri::Parts::from(uri);
|
||||||
|
|
||||||
let path = parts
|
let mut path = parts
|
||||||
.path_and_query
|
.path_and_query
|
||||||
.map(|v| v.into())
|
.map(|v| v.into())
|
||||||
.unwrap_or_else(|| Bytes::from_static(b"/"));
|
.unwrap_or_else(|| Bytes::new());
|
||||||
|
|
||||||
|
if path.is_empty() && method != Method::OPTIONS {
|
||||||
|
path = Bytes::from_static(b"/");
|
||||||
|
}
|
||||||
|
|
||||||
let mut pseudo = Pseudo {
|
let mut pseudo = Pseudo {
|
||||||
method: Some(method),
|
method: Some(method),
|
||||||
|
|||||||
@@ -708,6 +708,72 @@ fn recv_too_big_headers() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn request_without_path() {
|
||||||
|
let _ = ::env_logger::try_init();
|
||||||
|
let (io, srv) = mock::new();
|
||||||
|
|
||||||
|
let srv = srv.assert_client_handshake()
|
||||||
|
.unwrap()
|
||||||
|
.recv_settings()
|
||||||
|
.recv_frame(frames::headers(1).request("GET", "http://example.com/").eos())
|
||||||
|
.send_frame(frames::headers(1).response(200).eos())
|
||||||
|
.close();
|
||||||
|
|
||||||
|
let client = client::handshake(io)
|
||||||
|
.expect("handshake")
|
||||||
|
.and_then(move |(mut client, conn)| {
|
||||||
|
// Note the lack of trailing slash.
|
||||||
|
let request = Request::get("http://example.com")
|
||||||
|
.body(())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (response, _) = client.send_request(request, true).unwrap();
|
||||||
|
|
||||||
|
conn.drive(response)
|
||||||
|
});
|
||||||
|
|
||||||
|
client.join(srv).wait().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn request_options_with_star() {
|
||||||
|
let _ = ::env_logger::try_init();
|
||||||
|
let (io, srv) = mock::new();
|
||||||
|
|
||||||
|
// Note the lack of trailing slash.
|
||||||
|
let uri = uri::Uri::from_parts({
|
||||||
|
let mut parts = uri::Parts::default();
|
||||||
|
parts.scheme = Some(uri::Scheme::HTTP);
|
||||||
|
parts.authority = Some(uri::Authority::from_shared("example.com".into()).unwrap());
|
||||||
|
parts.path_and_query = Some(uri::PathAndQuery::from_static("*"));
|
||||||
|
parts
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
let srv = srv.assert_client_handshake()
|
||||||
|
.unwrap()
|
||||||
|
.recv_settings()
|
||||||
|
.recv_frame(frames::headers(1).request("OPTIONS", uri.clone()).eos())
|
||||||
|
.send_frame(frames::headers(1).response(200).eos())
|
||||||
|
.close();
|
||||||
|
|
||||||
|
let client = client::handshake(io)
|
||||||
|
.expect("handshake")
|
||||||
|
.and_then(move |(mut client, conn)| {
|
||||||
|
let request = Request::builder()
|
||||||
|
.method(Method::OPTIONS)
|
||||||
|
.uri(uri)
|
||||||
|
.body(())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (response, _) = client.send_request(request, true).unwrap();
|
||||||
|
|
||||||
|
conn.drive(response)
|
||||||
|
});
|
||||||
|
|
||||||
|
client.join(srv).wait().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0];
|
const SETTINGS: &'static [u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0];
|
||||||
const SETTINGS_ACK: &'static [u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0];
|
const SETTINGS_ACK: &'static [u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0];
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ pub use self::futures::{Future, IntoFuture, Sink, Stream};
|
|||||||
pub use super::future_ext::{FutureExt, Unwrap};
|
pub use super::future_ext::{FutureExt, Unwrap};
|
||||||
|
|
||||||
// Re-export HTTP types
|
// Re-export HTTP types
|
||||||
pub use self::http::{HeaderMap, Method, Request, Response, StatusCode, Version};
|
pub use self::http::{uri, HeaderMap, Method, Request, Response, StatusCode, Version};
|
||||||
|
|
||||||
pub use self::bytes::{Buf, BufMut, Bytes, BytesMut, IntoBuf};
|
pub use self::bytes::{Buf, BufMut, Bytes, BytesMut, IntoBuf};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user