Skip to content

Websocket breaking change on actix-http 3.13.0 #4115

Description

@Slixe

Expected Behavior

Websocket connection is correctly upgraded.

Current Behavior

WebSocket protocol error: No "Connection: upgrade" header

Possible Solution

downgrade actix-http to =3.12.1

Steps to Reproduce (for bugs)

use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
use futures_util::{SinkExt, StreamExt};
use std::net::TcpListener;
use tokio_tungstenite_wasm::{connect, Message};

async fn ws(req: HttpRequest, body: web::Payload) -> actix_web::Result<HttpResponse> {
    let (response, mut session, mut stream) = actix_ws::handle(&req, body)?;

    actix_web::rt::spawn(async move {
        while let Some(Ok(msg)) = stream.next().await {
            match msg {
                actix_ws::Message::Text(text) => {
                    let _ = session.text(text).await;
                }
                actix_ws::Message::Close(reason) => {
                    let _ = session.close(reason).await;
                    return;
                }
                _ => {}
            }
        }
    });

    Ok(response)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let listener = TcpListener::bind(("127.0.0.1", 0))?;
    let addr = listener.local_addr()?;
    let server = HttpServer::new(|| App::new().route("/json_rpc", web::get().to(ws)))
        .listen(listener)?
        .run();

    actix_web::rt::spawn(server);

    let url = format!("ws://{addr}/json_rpc");
    let mut websocket = connect(&url)
        .await
        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;

    websocket
        .send(Message::Text("ping".into()))
        .await
        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;

    match websocket.next().await {
        Some(Ok(Message::Text(text))) if text == "ping" => Ok(()),
        other => Err(std::io::Error::new(
            std::io::ErrorKind::Other,
            format!("unexpected websocket response: {other:?}"),
        )),
    }
}
actix-web = "=4.13.0"
actix-ws = "=0.3.1"
actix-http = "3"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
tokio-tungstenite-wasm = { version = "=0.8.2", features = ["rustls-tls-webpki-roots"] }
futures-util = "0.3"
cargo update
    Updating crates.io index
     Locking 2 packages to latest compatible versions
    Updating actix-http v3.12.1 -> v3.13.0
      Adding foldhash v0.2.0
note: pass `--verbose` to see 3 unchanged dependencies behind latest

cargo run   
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.10s
     Running `target/debug/ws-repro`
Error: Custom { kind: Other, error: Protocol(MissingConnectionUpgradeHeader) }

Context

Support WebSocket on my API, right after updating the dependencies, my client couldn't connect to the server anymore.

Your Environment

  • Rust Version (I.e, output of rustc -V): rustc 1.96.0 (ac68faa20 2026-05-25)
  • Actix Web Version: 4.14.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions