Skip to content
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

Expose websocket handshake state #6

Merged
merged 2 commits into from
Dec 29, 2024

Conversation

tthebst
Copy link
Contributor

@tthebst tthebst commented Dec 27, 2024

According to the web socket spec:

Once the client's opening handshake has been sent, the client MUST wait for a response from the server before sending any further data.

With the current API it is not possible to know if the handshake is completed and send_text can be called. This MR adds a handshake_completed function to know if the handshake completed.

The other API change is to introduce an error if send_text is called before the handshake is completed. In almost any case this is not what the user wants since the endpoint can respond in arbitrary ways (in my case it was an EOF) which can cause painful debugging. This change might is breaking and I can remove it.

@HaveFunTrading
Copy link
Owner

HaveFunTrading commented Dec 28, 2024

Thanks @tthebst for raising the PR. We can certainly add pub fn handshake_complete(&self) -> bool function (just mark it as const).

As for returning an error when trying to send message when the handshake did not finish this would lead to increased complexity and break the existing API. Better way of addressing it would be to have pending_msg_buffer on the Websocket, e.g.

#[derive(Debug)]
pub struct Websocket<S> {
    stream: S,
    handshaker: Handshaker,
    frame: Decoder,
    closed: bool,
    pending_pong: bool,
    pong_payload: Vec<u8>,
    pending_msg_buffer: VecDeque<(u8, bool, Option<Vec<u8>>)>,
}

Then, when we try to send_text and the handshake is not done we would buffer the message.

#[inline]
pub fn send_text(&mut self, fin: bool, body: Option<&[u8]>) -> Result<(), Error> {
    if !self.handshake_complete() {
        self.buffer_message(fin, protocol::op::TEXT_FRAME, body);
    }
    self.send(fin, protocol::op::TEXT_FRAME, body)
}

#[cold]
fn buffer_message(&mut self, fin: bool, op: u8, body: Option<&[u8]>) {
    let body = body.map(|body| body.to_vec());
    self.pending_msg_buffer.push_back((op, fin, body))
}

Finally, the pending_msg_buffer would be cleared when handshake completes.

#[cold]
#[inline(never)]
fn perform_handshake(&mut self) -> Result<Option<WebsocketFrame>, Error> {
    match self.handshaker.await_handshake(&mut self.stream) {
        Ok(()) => {
            // drain pending message buffer
            while let Some((op, fin, body)) = self.pending_msg_buffer.pop_front() {
                let body = body.as_ref().map(|body| body.as_slice());
                self.send(fin, op, body)?;
            }
            Ok(None)
        },
        Err(err) if err.kind() == WouldBlock => Ok(None),
        Err(err) => {
            self.closed = true;
            Err(err)?
        }
    }
}

Feel free to include pending_msg_buffer on your PR; otherwise please commit handshake_complete function and I can add the buffering on the next PR.

@tthebst
Copy link
Contributor Author

tthebst commented Dec 29, 2024

Thanks for the quick response. I removed the change tosend_text and only added the handshake_complete function. Agree that what you suggest is a better API und would be simpler.

With what you suggest the handshake_complete wouldn't be needed. So I could only add what you suggest and it would work fine.

@HaveFunTrading HaveFunTrading merged commit a1e14e3 into HaveFunTrading:main Dec 29, 2024
4 checks passed
@HaveFunTrading
Copy link
Owner

@tthebst the above change plus buffering messages before handshake has been released in 0.0.27.

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