I'm encountering a problem where I can connect to a server fine with tokio-tungstenite and maintain that connection, but at some point the stream gets reset (presumably due to load or the server being restarted) and my client disconnects. When my reconnection logic runs, I am generally seeing the error WebSocket protocol error: HTTP version must be 1.1 or higher:
|
/// Wrong HTTP version used (the WebSocket protocol requires version 1.1 or higher). |
|
#[error("HTTP version must be 1.1 or higher")] |
|
WrongHttpVersion, |
AFAICT this error is raised in two places during the client-side handshake:
- A check on client requests (which I'm confident are using HTTP 1.1 because I'm calling
tokio_tungstenite::connect_async with a string URL, so the client HTTP version should always be the default of 1.1):
|
if request.version() < http::Version::HTTP_11 { |
|
return Err(Error::Protocol(ProtocolError::WrongHttpVersion)); |
|
} |
- A check on server responses:
|
impl<'h, 'b: 'h> FromHttparse<httparse::Response<'h, 'b>> for Response { |
|
fn from_httparse(raw: httparse::Response<'h, 'b>) -> Result<Self> { |
|
if raw.version.expect("Bug: no HTTP version") < /*1.*/1 { |
|
return Err(Error::Protocol(ProtocolError::WrongHttpVersion)); |
|
} |
|
|
|
let headers = HeaderMap::from_httparse(raw.headers)?; |
|
|
|
let mut response = Response::new(None); |
|
*response.status_mut() = StatusCode::from_u16(raw.code.expect("Bug: no HTTP status code"))?; |
|
*response.headers_mut() = headers; |
|
// TODO: httparse only supports HTTP 0.9/1.0/1.1 but not HTTP 2.0 |
|
// so the only valid value we could get in the response would be 1.1. |
|
*response.version_mut() = http::Version::HTTP_11; |
|
|
|
Ok(response) |
|
} |
|
} |
Looking at the server response, I noticed that the HTTP version check occurs before the status code is inspected. HTTP 1.0 does have status codes, and my current hypothesis is that the server (or maybe the reverse proxy in front of it) is returning a 500 error like 503 while the underlying server is down, and doing so with HTTP 1.0 instead of HTTP 1.1 (maybe because the HTTP server generating the error is written for a trivial baseline). But because tungstenite is checking the HTTP version first and rejecting on that, the HTTP status code is completely dropped.
Given that the HTTP version of an HTTP 1.* server is "negotiated" in its first reply, it would be very helpful if tungstenite returned more details in this situation. I see two possible resolutions:
- It could return the server's status code inside
ProtocolError::WrongHttpVersion.
- It could perform the HTTP version check in two parts:
- Check for HTTP 1.* (so that status codes exist).
- Check for a WebSocket-compatible status code appropriate to the handshake phase (i.e. 101 Switching Protocols for the first response message), and return a
ProtocolError if the wrong status code is observed (e.g. 200, or 503).
- Check for HTTP 1.1 (so that Upgrade is supported).
I'm encountering a problem where I can connect to a server fine with
tokio-tungsteniteand maintain that connection, but at some point the stream gets reset (presumably due to load or the server being restarted) and my client disconnects. When my reconnection logic runs, I am generally seeing the errorWebSocket protocol error: HTTP version must be 1.1 or higher:tungstenite-rs/src/error.rs
Lines 176 to 178 in 255aaa2
AFAICT this error is raised in two places during the client-side handshake:
tokio_tungstenite::connect_asyncwith a string URL, so the client HTTP version should always be the default of 1.1):tungstenite-rs/src/handshake/client.rs
Lines 50 to 52 in 255aaa2
tungstenite-rs/src/handshake/client.rs
Lines 311 to 328 in 255aaa2
Looking at the server response, I noticed that the HTTP version check occurs before the status code is inspected. HTTP 1.0 does have status codes, and my current hypothesis is that the server (or maybe the reverse proxy in front of it) is returning a 500 error like 503 while the underlying server is down, and doing so with HTTP 1.0 instead of HTTP 1.1 (maybe because the HTTP server generating the error is written for a trivial baseline). But because
tungsteniteis checking the HTTP version first and rejecting on that, the HTTP status code is completely dropped.Given that the HTTP version of an HTTP 1.* server is "negotiated" in its first reply, it would be very helpful if
tungstenitereturned more details in this situation. I see two possible resolutions:ProtocolError::WrongHttpVersion.ProtocolErrorif the wrong status code is observed (e.g. 200, or 503).