k8s-operator/sessionrecording/ws: unify Read/Write frame parsing (#19227)
Consolidate the duplicated WebSocket frame-parsing logic from Read and Write into a shared processFrames loop, fixing several bugs in the process: - Mixed control and data frames in a single Read/Write call buffer were not handled: a control frame would cause merged data frames to be skipped. - Multiple data frames into one Write call weren't being correctly parsed: only the first frame was processed, ignoring the rest in the buffer. - msg.isFinalized was being set before confirming the fragment was complete, so an incomplete msg fragment, could've been sometimes marked as finalized. - Continuation frames without any payload were being treated as if they didn't have stream ID, even thought the id is already known from the initial fragment. Fixes tailscale/corp#39583 Signed-off-by: Fernando Serboncini <fserb@tailscale.com> Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk> Co-authored-by: chaosinthecrd <tom@tmlabs.co.uk>
This commit is contained in:
committed by
GitHub
parent
8a7e160a6e
commit
07399275f1
@@ -99,19 +99,19 @@ func (msg *message) Parse(b []byte, log *zap.SugaredLogger) (bool, error) {
|
||||
}
|
||||
isInitialFragment := len(msg.raw) == 0
|
||||
|
||||
msg.isFinalized = isFinalFragment(b)
|
||||
|
||||
finalized := isFinalFragment(b)
|
||||
maskSet := isMasked(b)
|
||||
|
||||
payloadLength, payloadOffset, maskOffset, err := fragmentDimensions(b, maskSet)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error determining payload length: %w", err)
|
||||
}
|
||||
log.Debugf("parse: parsing a message fragment with payload length: %d payload offset: %d maskOffset: %d mask set: %t, is finalized: %t, is initial fragment: %t", payloadLength, payloadOffset, maskOffset, maskSet, msg.isFinalized, isInitialFragment)
|
||||
log.Debugf("parse: parsing a message fragment with payload length: %d payload offset: %d maskOffset: %d mask set: %t, is finalized: %t, is initial fragment: %t", payloadLength, payloadOffset, maskOffset, maskSet, finalized, isInitialFragment)
|
||||
|
||||
if len(b) < int(payloadOffset+payloadLength) { // incomplete fragment
|
||||
return false, nil
|
||||
}
|
||||
msg.isFinalized = finalized
|
||||
// TODO (irbekrm): perhaps only do this extra allocation if we know we
|
||||
// will need to unmask?
|
||||
msg.raw = make([]byte, int(payloadOffset)+int(payloadLength))
|
||||
@@ -136,6 +136,13 @@ func (msg *message) Parse(b []byte, log *zap.SugaredLogger) (bool, error) {
|
||||
// message payload.
|
||||
// https://github.com/kubernetes/apimachinery/commit/73d12d09c5be8703587b5127416eb83dc3b7e182#diff-291f96e8632d04d2d20f5fb00f6b323492670570d65434e8eac90c7a442d13bdR23-R36
|
||||
if len(msgPayload) == 0 {
|
||||
if !isInitialFragment {
|
||||
// Continuation frame with zero payload. The stream ID is
|
||||
// already known from the initial fragment, so this is not
|
||||
// fatal, just unusual.
|
||||
log.Infof("[unexpected] received a continuation fragment with no payload")
|
||||
return true, nil
|
||||
}
|
||||
return false, errors.New("[unexpected] received a message fragment with no stream ID")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user