quic-go/quic-go

SetReadDeadline behavior

Closed this issue · 1 comments

Context

I am developing a QUIC client that establishes a connection and opens a single SendStream. This client continually sends packets with high frequency into this stream over an extended period:

// QUIC Client

func (c *QUICClient) write(msg []byte) error {
    // This is a stream from `OpenUniStreamSync`
	err := c.stream.SetWriteDeadline(time.Now().Add(c.writeTimeout))
	if err != nil {
		loggerClient.Error("Failed to set write deadline", "err", err)
		return ErrOpenStream
	}

	_, err = c.stream.Write(msg)
	if err != nil {
		loggerClient.Error("Failed to write to stream", "err", err)
		return ErrSendPatch
	}

	return nil
}

On the server side, I handle this stream to process packets over time, with the goal of keeping the stream open for a prolonged period:

// QUIC Server

func (s *Server) handleStream(
	ctx context.Context,
	stream quicgo.ReceiveStream,
	conn quicgo.Connection,
) {
	for {
		err := stream.SetReadDeadline(time.Now().Add(ReadTimeout))
		if err != nil {
			slog.Error("Failed to set read deadline for QUIC stream", "err", err)
		}

		// Read the patch length
		lengthBuffer := make([]byte, PatchLengthPrefix)
		_, err = io.ReadFull(stream, lengthBuffer)
		if err != nil {
			slog.Error("Failed to QUIC read patch length", "err", err)

			return
		}
		patchLength := binary.BigEndian.Uint32(lengthBuffer)
		slog.Info("Got patch length", "length", patchLength)

		// Read the whole patch
		buffer := make([]byte, patchLength)
		_, err = io.ReadFull(stream, buffer)
		if err != nil {
			slog.Error("Failed to read complete QUIC patch", "err", err)

			return
		}

		// Handle the patch
		go func(patch []byte) {
			err := s.handlePatch(ctx, conn, patch)
			if err != nil {
				slog.Error("Error handling QUIC patch", "err", err)

				return
			}
			slog.Info("Successfully handled QUIC patch from client")
		}(buffer)
	}
}

Question

How does SetReadDeadline behave in this case? Specifically:

  1. When a packet arrives, does SetReadDeadline allow reading from it for the entire ReadTimeout duration, after which it checks for new packets if the current one was unreadable in that time frame?
  2. Alternatively, does SetReadDeadline simply wait for a packet for up to ReadTimeout and time out if none arrives?

I'm uncertain how to configure SetReadDeadline on the server side to ensure the server continues processing incoming packets within an open SendStream over an extended time.

I expected:

  • The server can process packets over a long duration on a single open SendStream from the client.
  • The SetReadDeadline only times out when absolutely no packets have been received within ReadTimeout- not if a packet takes slightly longer to be fully read.

P.S Big thanks for building this library and being so helpful! ❤

Deadlines behave exactly as they do in the Go standard library. They're purely a local operation to make calls to Read and Write return once the deadline is reached.