| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- package h2quic
- import (
- "bytes"
- "net/http"
- "strconv"
- "strings"
- "sync"
- quic "github.com/lucas-clemente/quic-go"
- "github.com/lucas-clemente/quic-go/internal/protocol"
- "github.com/lucas-clemente/quic-go/internal/utils"
- "golang.org/x/net/http2"
- "golang.org/x/net/http2/hpack"
- )
- type responseWriter struct {
- dataStreamID protocol.StreamID
- dataStream quic.Stream
- headerStream quic.Stream
- headerStreamMutex *sync.Mutex
- header http.Header
- status int // status code passed to WriteHeader
- headerWritten bool
- logger utils.Logger
- }
- func newResponseWriter(
- headerStream quic.Stream,
- headerStreamMutex *sync.Mutex,
- dataStream quic.Stream,
- dataStreamID protocol.StreamID,
- logger utils.Logger,
- ) *responseWriter {
- return &responseWriter{
- header: http.Header{},
- headerStream: headerStream,
- headerStreamMutex: headerStreamMutex,
- dataStream: dataStream,
- dataStreamID: dataStreamID,
- logger: logger,
- }
- }
- func (w *responseWriter) Header() http.Header {
- return w.header
- }
- func (w *responseWriter) WriteHeader(status int) {
- if w.headerWritten {
- return
- }
- w.headerWritten = true
- w.status = status
- var headers bytes.Buffer
- enc := hpack.NewEncoder(&headers)
- enc.WriteField(hpack.HeaderField{Name: ":status", Value: strconv.Itoa(status)})
- for k, v := range w.header {
- for index := range v {
- enc.WriteField(hpack.HeaderField{Name: strings.ToLower(k), Value: v[index]})
- }
- }
- w.logger.Infof("Responding with %d", status)
- w.headerStreamMutex.Lock()
- defer w.headerStreamMutex.Unlock()
- h2framer := http2.NewFramer(w.headerStream, nil)
- err := h2framer.WriteHeaders(http2.HeadersFrameParam{
- StreamID: uint32(w.dataStreamID),
- EndHeaders: true,
- BlockFragment: headers.Bytes(),
- })
- if err != nil {
- w.logger.Errorf("could not write h2 header: %s", err.Error())
- }
- }
- func (w *responseWriter) Write(p []byte) (int, error) {
- if !w.headerWritten {
- w.WriteHeader(200)
- }
- if !bodyAllowedForStatus(w.status) {
- return 0, http.ErrBodyNotAllowed
- }
- return w.dataStream.Write(p)
- }
- func (w *responseWriter) Flush() {}
- // This is a NOP. Use http.Request.Context
- func (w *responseWriter) CloseNotify() <-chan bool { return make(<-chan bool) }
- // test that we implement http.Flusher
- var _ http.Flusher = &responseWriter{}
- // copied from http2/http2.go
- // bodyAllowedForStatus reports whether a given response status code
- // permits a body. See RFC 2616, section 4.4.
- func bodyAllowedForStatus(status int) bool {
- switch {
- case status >= 100 && status <= 199:
- return false
- case status == 204:
- return false
- case status == 304:
- return false
- }
- return true
- }
|