-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Malformed HTTP/2 may cause 'assertion failed' #1994
Comments
It seems related to this. I am wondering what is the correct HTTP error status for this purpose. // In debug, make sure we agree with Hyper. Otherwise, cross our fingers
// and trust that it only gives us valid URIs like it's supposed to.
// TODO: Keep around not just the path/query, but the rest, if there?
let uri = hyper.uri.path_and_query().ok_or(Error::InvalidUri(&hyper.uri))?;
debug_assert!(Origin::parse(uri.as_str()).is_ok());
let uri = Origin::new(uri.path(), uri.query().map(Cow::Borrowed)); Should we report it to the Hyper issue tracker and maybe return an |
Related to #1831 |
What is here the difference between HTTP/1.1 and 2 in hyper? I'm asking because I am not sure at the moment what would be the correct handling... [src/bin/client.rs:33] &req = Request {
method: GET,
uri: http://127.0.0.1:8000/hello/world,
version: HTTP/1.1,
headers: {},
body: Body(
Empty,
),
}
[src/bin/client.rs:35] &res = Response {
status: 200,
version: HTTP/2.0,
headers: {
"content-type": "text/plain; charset=utf-8",
"server": "Rocket",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"permissions-policy": "interest-cohort=()",
"content-length": "13",
"date": "Sun, 16 Jan 2022 15:36:27 GMT",
},
body: Body(
Streaming,
),
}
[src/bin/client.rs:40] &req = Request {
method: GET,
uri: http://127.0.0.1:8000/%2Fhello%2Fworld,
version: HTTP/1.1,
headers: {},
body: Body(
Empty,
),
}
[src/bin/client.rs:42] &res = Response {
status: 404,
version: HTTP/2.0,
headers: {
"content-type": "text/html; charset=utf-8",
"server": "Rocket",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"permissions-policy": "interest-cohort=()",
"content-length": "383",
"date": "Sun, 16 Jan 2022 15:36:27 GMT",
},
body: Body(
Streaming,
),
}
[src/bin/client.rs:58] &req = Request {
method: GET,
uri: http://127.0.0.1:8000/%2Fhello%2Fworld,
version: HTTP/1.1,
headers: {},
body: Body(
Empty,
),
}
[src/bin/client.rs:61] &res = Response {
status: 404,
version: HTTP/2.0,
headers: {
"content-type": "text/html; charset=utf-8",
"server": "Rocket",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"permissions-policy": "interest-cohort=()",
"content-length": "383",
"date": "Sun, 16 Jan 2022 15:36:27 GMT",
},
body: Body(
Streaming,
),
}
[src/bin/client.rs:80] &req = Request {
method: GET,
uri: http://127.0.0.1:8000%2Fhello%2Fworld,
version: HTTP/1.1,
headers: {},
body: Body(
Empty,
),
}
Error: hyper::Error(Http2, Error { kind: Reset(StreamId(7), INTERNAL_ERROR, Remote) }) With HTTP/1.1... [src/bin/client.rs:34] &req = Request {
method: GET,
uri: http://127.0.0.1:8000/hello/world,
version: HTTP/1.1,
headers: {},
body: Body(
Empty,
),
}
[src/bin/client.rs:36] &res = Response {
status: 200,
version: HTTP/1.1,
headers: {
"content-type": "text/plain; charset=utf-8",
"server": "Rocket",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"permissions-policy": "interest-cohort=()",
"content-length": "13",
"date": "Sun, 16 Jan 2022 15:41:22 GMT",
},
body: Body(
Streaming,
),
}
[src/bin/client.rs:41] &req = Request {
method: GET,
uri: http://127.0.0.1:8000/%2Fhello%2Fworld,
version: HTTP/1.1,
headers: {},
body: Body(
Empty,
),
}
[src/bin/client.rs:43] &res = Response {
status: 404,
version: HTTP/1.1,
headers: {
"content-type": "text/html; charset=utf-8",
"server": "Rocket",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"permissions-policy": "interest-cohort=()",
"content-length": "383",
"date": "Sun, 16 Jan 2022 15:41:22 GMT",
},
body: Body(
Streaming,
),
}
[src/bin/client.rs:59] &req = Request {
method: GET,
uri: http://127.0.0.1:8000/%2Fhello%2Fworld,
version: HTTP/1.1,
headers: {},
body: Body(
Empty,
),
}
[src/bin/client.rs:62] &res = Response {
status: 404,
version: HTTP/1.1,
headers: {
"content-type": "text/html; charset=utf-8",
"server": "Rocket",
"x-content-type-options": "nosniff",
"x-frame-options": "SAMEORIGIN",
"permissions-policy": "interest-cohort=()",
"content-length": "383",
"date": "Sun, 16 Jan 2022 15:41:22 GMT",
},
body: Body(
Streaming,
),
}
[src/bin/client.rs:81] &req = Request {
method: GET,
uri: http://127.0.0.1:8000%2Fhello%2Fworld,
version: HTTP/1.1,
headers: {},
body: Body(
Empty,
),
}
[src/bin/client.rs:84] &res = Response {
status: 400,
version: HTTP/1.1,
headers: {
"content-length": "0",
"date": "Sun, 16 Jan 2022 15:41:22 GMT",
},
body: Body(
Empty,
),
} |
"Funny" stuff... |
@heikki-heikkila Can you please edit the title and change the HTTP to HTTP/2! |
// src/main.rs
#[macro_use]
extern crate rocket;
#[get("/")]
fn index() -> &'static str {
"Hello, world!"
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![index])
} # Cargo.toml
[package]
name = "untitled"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rocket = {git = "https://github.com/SergioBenitez/Rocket.git", rev = "8cae077ba1d54b92cdef3e171a730b819d5eeb8e"} Command curl -v 'localhost:8000/a/\/b' Output
However, this is with HTTP/1.1 and not HTTP/2. I'm not sure if this is a related issue, the same issue, or an entirely different issue. |
Closing in favor of #1831. See my comment in #1831 (comment) for an explanation of what's going on. In short: Hyper needs to be fixed. |
Description
With a certain malformed HTTP request, a HTTP server that runs Rocket will panic with 'assertion failed' (details below). The whole HTTP server does not crash, however; the server SW catches the panic and keeps running.
This should be seen as a bug, because 'assertion failed' should never happen in production software.
To Reproduce
The issue can be reproduced in a crate with three files, src/server.rs, src/client.rs and Cargo.toml, as follows. See instructions after the file listings.
// server.rs
// A very simple textbook server
// Instructions for use:
// Run "cargo run --bin http_server" in one command window. The HTTP
// server starts and waits for requests.
// In another command window, run "cargo run --bin http_client". The client
// should send one query to the server and print the outcome.
// The server runs in debug mode and prints useful diagnostics to the
// terminal screen.
#[macro_use] extern crate rocket;
#[get("/world")]
fn world() -> &'static str {
"Hello, world!"
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/hello", routes![world])
}
// end of file server.rs
// client.rs
// A very simple HTTP client that produces an invalid HTTP request.
// In "path and query", where slash '/' is supposed to be a separator,
// we do erroneous percent encoding "/" -> "%2F", just to see what the
// HTTP server does.
// Instructions for use:
// Run "cargo run --bin http_server" in one command window. The HTTP
// server starts and waits for requests.
// In another command window, run "cargo run --bin http_client". The client
// should send one query to the server and print the outcome.
#[tokio::main]
async fn main() {
}
// end of file client.rs
Cargo.toml
[package]
name = "http_panic_test"
version = "0.1.0"
edition = "2021"
authors = ["H. Heikkilä"]
description = "Test panic condition in Rocket HTTP server"
See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
http = "0.2.5"
mac_address = "1.1.2"
rocket = "0.5.0-rc.1"
futures = "0.3"
hyper = { version = "0.14.13", features = [
"http2", "server", "client", "tcp" ] }
tokio = { version = "1.12.0", features = [
"sync", "rt", "net", "time" ] }
[[bin]]
name = "http_server"
path = "src/server.rs"
[[bin]]
name = "http_client"
path = "src/client.rs"
end of Cargo.toml
Instructions for reproducing the issue:
Set up a cargo crate with files ./src/server.rs, ./src/client.rs and ./Cargo.toml
Build SW as usual: cargo build
Open one command window and do "cargo run --bin http_server". This starts an HTTP server, which starts waiting for requests. (The server reserves socket address 128.0.0.1:8000.)
Open another command window and do "cargo run --bin http_client". This runs an HTTP client, which sends one request towards the HTTP server.
In the HTTP server window, a message like the following is seen:
thread 'rocket-worker-thread' panicked at 'assertion failed: Origin::parse(uri.as_str()).is_ok()', . . . cache/registry/src/github.com-1ecc6299db9ec823/rocket-0.5.0-rc.1/src/request/request.rs:846:9
note: run with
RUST_BACKTRACE=1
environment variable to display a backtraceExpected Behavior
I expect that HTTP servers are robust, so that 'assertion failed' never happens, even if the HTTP request message is malformed or perhaps even malicious. (Admittedly, the HTTP server catches the assertion gracefully.)
Environment:
Additional Context
I discovered this behaviour by chance. I was doing percent encoding to HTTP request in client SW -- which is required in some cases -- but erroneously applied percent encoding to the slash separators.
The text was updated successfully, but these errors were encountered: