mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 20:09:58 +00:00
TUN-5621: Correctly manage QUIC stream closing
Until this PR, we were naively closing the quic.Stream whenever the callstack for handling the request (HTTP or TCP) finished. However, our proxy handler may still be reading or writing from the quic.Stream at that point, because we return the callstack if either side finishes, but not necessarily both. This is a problem for quic-go library because quic.Stream#Close cannot be called concurrently with quic.Stream#Write Furthermore, we also noticed that quic.Stream#Close does nothing to do receiving stream (since, underneath, quic.Stream has 2 streams, 1 for each direction), thus leaking memory, as explained in: https://github.com/lucas-clemente/quic-go/issues/3322 This PR addresses both problems by wrapping the quic.Stream that is passed down to the proxying logic and handle all these concerns.
This commit is contained in:
43
quic/safe_stream.go
Normal file
43
quic/safe_stream.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
)
|
||||
|
||||
type SafeStreamCloser struct {
|
||||
lock sync.Mutex
|
||||
stream quic.Stream
|
||||
}
|
||||
|
||||
func NewSafeStreamCloser(stream quic.Stream) *SafeStreamCloser {
|
||||
return &SafeStreamCloser{
|
||||
stream: stream,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SafeStreamCloser) Read(p []byte) (n int, err error) {
|
||||
return s.stream.Read(p)
|
||||
}
|
||||
|
||||
func (s *SafeStreamCloser) Write(p []byte) (n int, err error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
return s.stream.Write(p)
|
||||
}
|
||||
|
||||
func (s *SafeStreamCloser) Close() error {
|
||||
// Make sure a possible writer does not block the lock forever. We need it, so we can close the writer
|
||||
// side of the stream safely.
|
||||
_ = s.stream.SetWriteDeadline(time.Now())
|
||||
|
||||
// This lock is eventually acquired despite Write also acquiring it, because we set a deadline to writes.
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
// We have to clean up the receiving stream ourselves since the Close in the bottom does not handle that.
|
||||
s.stream.CancelRead(0)
|
||||
return s.stream.Close()
|
||||
}
|
Reference in New Issue
Block a user