github.com/ooni/psiphon/tunnel-core@v0.0.0-20230105123940-fe12a24c96ee/oovendor/quic-go/http3/body.go (about)

     1  package http3
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  
     7  	"github.com/ooni/psiphon/tunnel-core/oovendor/quic-go"
     8  )
     9  
    10  // The body of a http.Request or http.Response.
    11  type 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  
    25  var _ io.ReadCloser = &body{}
    26  
    27  func newRequestBody(str quic.Stream, onFrameError func()) *body {
    28  	return &body{
    29  		str:          str,
    30  		onFrameError: onFrameError,
    31  	}
    32  }
    33  
    34  func 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  
    42  func (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  
    50  func (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  
    85  func (r *body) requestDone() {
    86  	if r.reqDoneClosed || r.reqDone == nil {
    87  		return
    88  	}
    89  	close(r.reqDone)
    90  	r.reqDoneClosed = true
    91  }
    92  
    93  func (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  }