Skip to content

WebSocketServer handshake handling#64

Merged
aecsocket merged 1 commit intoaecsocket:mainfrom
liamaharon:ws-handshake-validation
Sep 25, 2025
Merged

WebSocketServer handshake handling#64
aecsocket merged 1 commit intoaecsocket:mainfrom
liamaharon:ws-handshake-validation

Conversation

@liamaharon
Copy link
Copy Markdown
Contributor

Hi, thanks for the crate!

I've added a HandshakeHandler to the ws ServerConfig, allowing auth check(s) during http handshake rather than needing to wait until the connection is upgraded.

Useful for implementing low cost auth check like the netcode connect tokens.

The new config entry is optional, so no breaking changes.

Example creating a WebSocketServer with a custom HandshakeHandler:

#[derive(Resource, Default)]
struct Counter(Arc<RwLock<u32>>);

fn start(mut commands: Commands, counter: Res<Counter>) {
    let counter = counter.0.clone();
    let predicate = move |req: &Request, mut resp: Response| {
        // Use outside state
        let mut counter = counter.write().unwrap();
        info!("Call {}", counter);
        *counter = counter.wrapping_add(1);

        // Validate the Request however we want
        let auth_header = req
            .headers()
            .get("Authorization")
            .ok_or_else(|| ErrorResponse::new(Some("missing authorization header".to_string())))?;
        if auth_header != "123" {
            return Err(ErrorResponse::new(Some("unauthorized".to_string())));
        }

        // Optionally modify the response before returning
        resp.headers_mut()
            .insert("X-Something", HeaderValue::from_static("Something"));

        Ok(resp)
    };
    let handshake_handler = HandshakeHandler::from(Arc::new(predicate));

    let config = ServerConfig::builder()
        .with_bind_default(GAME_SERVER_PORT)
        .with_no_encryption()
        .with_handshake_handler(handshake_handler);

    commands.spawn_empty().queue(WebSocketServer::open(config));
}

Copy link
Copy Markdown
Owner

@aecsocket aecsocket left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! This is something that I've been meaning to get around to doing, you can tell by the TODO, but I never found the time to implement it. The method of passing a callback isn't ideal - if possible I would like to use the ECS directly here like running a system or a trigger - but I don't think that's really possible to achieve with how tungstenite works, so I'm happy with the callback approach.

I'm very happy with the PR overall, just would like some minor tweaks to docs and implementation.

Comment thread crates/aeronet_websocket/src/server/backend.rs Outdated
Comment thread crates/aeronet_websocket/src/server/backend.rs Outdated
Comment thread crates/aeronet_websocket/src/server/config.rs Outdated
Comment thread crates/aeronet_websocket/src/server/config.rs Outdated
Comment thread crates/aeronet_websocket/src/server/config.rs
Comment thread crates/aeronet_websocket/src/server/config.rs
Comment thread crates/aeronet_websocket/src/server/config.rs Outdated
Comment thread crates/aeronet_websocket/src/server/config.rs
Comment thread crates/aeronet_websocket/src/server/config.rs
@aecsocket
Copy link
Copy Markdown
Owner

Also please make sure that CI clippy and doctests pass

@liamaharon liamaharon force-pushed the ws-handshake-validation branch from 25a03df to 7a5c6e5 Compare September 24, 2025 18:57
@liamaharon
Copy link
Copy Markdown
Contributor Author

Great suggestions, thanks @aecsocket.

@aecsocket
Copy link
Copy Markdown
Owner

Thanks for the fixes! I'll merge it now and do a 0.16.1 release

@aecsocket aecsocket merged commit b9f1641 into aecsocket:main Sep 25, 2025
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants