forked from quic-go/quic-go
add h2quic package with response writer
This commit is contained in:
13
h2quic/h2quic_suite_test.go
Normal file
13
h2quic/h2quic_suite_test.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package h2quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestH2quic(t *testing.T) {
|
||||||
|
RegisterFailHandler(Fail)
|
||||||
|
RunSpecs(t, "H2quic Suite")
|
||||||
|
}
|
||||||
78
h2quic/response_writer.go
Normal file
78
h2quic/response_writer.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package h2quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/lucas-clemente/quic-go"
|
||||||
|
"github.com/lucas-clemente/quic-go/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/utils"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"golang.org/x/net/http2/hpack"
|
||||||
|
)
|
||||||
|
|
||||||
|
type responseWriter struct {
|
||||||
|
session *quic.Session
|
||||||
|
dataStreamID protocol.StreamID
|
||||||
|
headerStream utils.Stream
|
||||||
|
dataStream utils.Stream
|
||||||
|
|
||||||
|
header http.Header
|
||||||
|
headerWritten bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newResponseWriter(headerStream utils.Stream, dataStreamID protocol.StreamID, session *quic.Session) *responseWriter {
|
||||||
|
return &responseWriter{
|
||||||
|
header: http.Header{},
|
||||||
|
headerStream: headerStream,
|
||||||
|
dataStreamID: dataStreamID,
|
||||||
|
session: session,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *responseWriter) Header() http.Header {
|
||||||
|
return w.header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *responseWriter) WriteHeader(status int) {
|
||||||
|
w.headerWritten = true
|
||||||
|
|
||||||
|
var headers bytes.Buffer
|
||||||
|
enc := hpack.NewEncoder(&headers)
|
||||||
|
enc.WriteField(hpack.HeaderField{Name: ":status", Value: strconv.Itoa(status)})
|
||||||
|
|
||||||
|
for k, v := range w.header {
|
||||||
|
enc.WriteField(hpack.HeaderField{Name: k, Value: v[0]})
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Responding with %d %#v\n", status, w.header)
|
||||||
|
h2framer := http2.NewFramer(w.headerStream, nil)
|
||||||
|
err := h2framer.WriteHeaders(http2.HeadersFrameParam{
|
||||||
|
StreamID: uint32(w.dataStreamID),
|
||||||
|
EndHeaders: true,
|
||||||
|
BlockFragment: headers.Bytes(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *responseWriter) Write(p []byte) (int, error) {
|
||||||
|
if !w.headerWritten {
|
||||||
|
w.WriteHeader(200)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) != 0 {
|
||||||
|
if w.dataStream == nil {
|
||||||
|
var err error
|
||||||
|
w.dataStream, err = w.session.NewStream(w.dataStreamID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("error creating data stream: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w.dataStream.Write(p)
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
44
h2quic/response_writer_test.go
Normal file
44
h2quic/response_writer_test.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package h2quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockStream struct {
|
||||||
|
bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mockStream) Close() error { return nil }
|
||||||
|
|
||||||
|
var _ = Describe("Response Writer", func() {
|
||||||
|
var (
|
||||||
|
w *responseWriter
|
||||||
|
headerStream *mockStream
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
headerStream = &mockStream{}
|
||||||
|
w = newResponseWriter(headerStream, 5, nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes status", func() {
|
||||||
|
w.WriteHeader(http.StatusTeapot)
|
||||||
|
Expect(headerStream.Bytes()).To(Equal([]byte{
|
||||||
|
0x0, 0x0, 0x5, 0x1, 0x4, 0x0, 0x0, 0x0, 0x5, 'H', 0x3, '4', '1', '8',
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("writes headers", func() {
|
||||||
|
w.Header().Add("content-length", "42")
|
||||||
|
w.WriteHeader(http.StatusTeapot)
|
||||||
|
Expect(headerStream.Bytes()).To(Equal([]byte{
|
||||||
|
0x0, 0x0, 0x14, 0x1, 0x4, 0x0, 0x0, 0x0, 0x5, 0x48, 0x3, 0x34, 0x31, 0x38,
|
||||||
|
0x40, 0x8a, 0xbc, 0x7a, 0x92, 0x5a, 0x92, 0xb6, 0x72, 0xd5, 0x32, 0x67,
|
||||||
|
0x2, 0x34, 0x32,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user