1package http3
2
3import (
4	"fmt"
5	"io"
6
7	"github.com/lucas-clemente/quic-go"
8)
9
10// The body of a http.Request or http.Response.
11type body struct {
12	str quic.Stream
13
14	// only set for the http.Response
15	// The channel is closed when the user is done with this response:
16	// either when Read() errors, or when Close() is called.
17	reqDone       chan<- struct{}
18	reqDoneClosed bool
19
20	onFrameError func()
21
22	bytesRemainingInFrame uint64
23}
24
25var _ io.ReadCloser = &body{}
26
27func newRequestBody(str quic.Stream, onFrameError func()) *body {
28	return &body{
29		str:          str,
30		onFrameError: onFrameError,
31	}
32}
33
34func newResponseBody(str quic.Stream, done chan<- struct{}, onFrameError func()) *body {
35	return &body{
36		str:          str,
37		onFrameError: onFrameError,
38		reqDone:      done,
39	}
40}
41
42func (r *body) Read(b []byte) (int, error) {
43	n, err := r.readImpl(b)
44	if err != nil {
45		r.requestDone()
46	}
47	return n, err
48}
49
50func (r *body) readImpl(b []byte) (int, error) {
51	if r.bytesRemainingInFrame == 0 {
52	parseLoop:
53		for {
54			frame, err := parseNextFrame(r.str)
55			if err != nil {
56				return 0, err
57			}
58			switch f := frame.(type) {
59			case *headersFrame:
60				// skip HEADERS frames
61				continue
62			case *dataFrame:
63				r.bytesRemainingInFrame = f.Length
64				break parseLoop
65			default:
66				r.onFrameError()
67				// parseNextFrame skips over unknown frame types
68				// Therefore, this condition is only entered when we parsed another known frame type.
69				return 0, fmt.Errorf("peer sent an unexpected frame: %T", f)
70			}
71		}
72	}
73
74	var n int
75	var err error
76	if r.bytesRemainingInFrame < uint64(len(b)) {
77		n, err = r.str.Read(b[:r.bytesRemainingInFrame])
78	} else {
79		n, err = r.str.Read(b)
80	}
81	r.bytesRemainingInFrame -= uint64(n)
82	return n, err
83}
84
85func (r *body) requestDone() {
86	if r.reqDoneClosed || r.reqDone == nil {
87		return
88	}
89	close(r.reqDone)
90	r.reqDoneClosed = true
91}
92
93func (r *body) Close() error {
94	r.requestDone()
95	// If the EOF was read, CancelRead() is a no-op.
96	r.str.CancelRead(quic.ErrorCode(errorRequestCanceled))
97	return nil
98}
99