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.StreamErrorCode(errorRequestCanceled)) 97 return nil 98} 99