mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 20:27:35 +03:00
implement parsing and writing of GOAWAY frames
This commit is contained in:
parent
4b2ab55435
commit
8fa3e62de9
6 changed files with 168 additions and 9 deletions
|
@ -67,10 +67,7 @@ func (f *ConnectionCloseFrame) Write(b *bytes.Buffer, version protocol.VersionNu
|
|||
|
||||
reasonPhraseLen := uint16(len(f.ReasonPhrase))
|
||||
utils.WriteUint16(b, reasonPhraseLen)
|
||||
|
||||
for i := 0; i < int(reasonPhraseLen); i++ {
|
||||
b.WriteByte(uint8(f.ReasonPhrase[i]))
|
||||
}
|
||||
b.WriteString(f.ReasonPhrase)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
73
frames/goaway_frame.go
Normal file
73
frames/goaway_frame.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
package frames
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
"github.com/lucas-clemente/quic-go/utils"
|
||||
)
|
||||
|
||||
// A GoawayFrame is a GOAWAY frame
|
||||
type GoawayFrame struct {
|
||||
ErrorCode qerr.ErrorCode
|
||||
LastGoodStream protocol.StreamID
|
||||
ReasonPhrase string
|
||||
}
|
||||
|
||||
// ParseGoawayFrame parses a GOAWAY frame
|
||||
func ParseGoawayFrame(r *bytes.Reader) (*GoawayFrame, error) {
|
||||
frame := &GoawayFrame{}
|
||||
|
||||
_, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
errorCode, err := utils.ReadUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
frame.ErrorCode = qerr.ErrorCode(errorCode)
|
||||
|
||||
lastGoodStream, err := utils.ReadUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
frame.LastGoodStream = protocol.StreamID(lastGoodStream)
|
||||
|
||||
reasonPhraseLen, err := utils.ReadUint16(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if reasonPhraseLen > uint16(protocol.MaxPacketSize) {
|
||||
return nil, qerr.Error(qerr.InvalidGoawayData, "reason phrase too long")
|
||||
}
|
||||
|
||||
reasonPhrase := make([]byte, reasonPhraseLen)
|
||||
if _, err := io.ReadFull(r, reasonPhrase); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
frame.ReasonPhrase = string(reasonPhrase)
|
||||
|
||||
return frame, nil
|
||||
}
|
||||
|
||||
func (f *GoawayFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error {
|
||||
typeByte := uint8(0x03)
|
||||
b.WriteByte(typeByte)
|
||||
|
||||
utils.WriteUint32(b, uint32(f.ErrorCode))
|
||||
utils.WriteUint32(b, uint32(f.LastGoodStream))
|
||||
utils.WriteUint16(b, uint16(len(f.ReasonPhrase)))
|
||||
b.WriteString(f.ReasonPhrase)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MinLength of a written frame
|
||||
func (f *GoawayFrame) MinLength() (protocol.ByteCount, error) {
|
||||
return protocol.ByteCount(1 + 4 + 4 + 2 + len(f.ReasonPhrase)), nil
|
||||
}
|
70
frames/goaway_frame_test.go
Normal file
70
frames/goaway_frame_test.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package frames
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/protocol"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("GoawayFrame", func() {
|
||||
Context("when parsing", func() {
|
||||
It("accepts sample frame", func() {
|
||||
b := bytes.NewReader([]byte{
|
||||
0x03,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00,
|
||||
'f', 'o', 'o',
|
||||
})
|
||||
frame, err := ParseGoawayFrame(b)
|
||||
Expect(frame).To(Equal(&GoawayFrame{
|
||||
ErrorCode: 1,
|
||||
LastGoodStream: 2,
|
||||
ReasonPhrase: "foo",
|
||||
}))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(b.Len()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("rejects long reason phrases", func() {
|
||||
b := bytes.NewReader([]byte{
|
||||
0x03,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff,
|
||||
})
|
||||
_, err := ParseGoawayFrame(b)
|
||||
Expect(err).To(MatchError(qerr.Error(qerr.InvalidGoawayData, "reason phrase too long")))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when writing", func() {
|
||||
It("writes a sample frame", func() {
|
||||
b := &bytes.Buffer{}
|
||||
frame := GoawayFrame{
|
||||
ErrorCode: 1,
|
||||
LastGoodStream: 2,
|
||||
ReasonPhrase: "foo",
|
||||
}
|
||||
frame.Write(b, 0)
|
||||
Expect(b.Bytes()).To(Equal([]byte{
|
||||
0x03,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00,
|
||||
'f', 'o', 'o',
|
||||
}))
|
||||
})
|
||||
|
||||
It("has the correct min length", func() {
|
||||
frame := GoawayFrame{
|
||||
ReasonPhrase: "foo",
|
||||
}
|
||||
Expect(frame.MinLength()).To(Equal(protocol.ByteCount(14)))
|
||||
})
|
||||
})
|
||||
})
|
|
@ -73,7 +73,10 @@ ReadLoop:
|
|||
err = qerr.Error(qerr.InvalidConnectionCloseData, err.Error())
|
||||
}
|
||||
case 0x03:
|
||||
err = errors.New("unimplemented: GOAWAY")
|
||||
frame, err = frames.ParseGoawayFrame(r)
|
||||
if err != nil {
|
||||
err = qerr.Error(qerr.InvalidGoawayData, err.Error())
|
||||
}
|
||||
case 0x04:
|
||||
frame, err = frames.ParseWindowUpdateFrame(r)
|
||||
if err != nil {
|
||||
|
|
|
@ -110,10 +110,23 @@ var _ = Describe("Packet unpacker", func() {
|
|||
Expect(packet.frames).To(Equal([]frames.Frame{f}))
|
||||
})
|
||||
|
||||
It("errors on GOAWAY frames", func() {
|
||||
setReader([]byte{0x03})
|
||||
_, err := unpacker.Unpack(hdrBin, hdr, r)
|
||||
Expect(err).To(MatchError("unimplemented: GOAWAY"))
|
||||
It("accepts GOAWAY frames", func() {
|
||||
setReader([]byte{
|
||||
0x03,
|
||||
0x01, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00,
|
||||
'f', 'o', 'o',
|
||||
})
|
||||
packet, err := unpacker.Unpack(hdrBin, hdr, r)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(packet.frames).To(Equal([]frames.Frame{
|
||||
&frames.GoawayFrame{
|
||||
ErrorCode: 1,
|
||||
LastGoodStream: 2,
|
||||
ReasonPhrase: "foo",
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
It("accepts WINDOW_UPDATE frames", func() {
|
||||
|
|
|
@ -250,6 +250,9 @@ func (s *Session) handlePacketImpl(remoteAddr interface{}, hdr *publicHeader, da
|
|||
case *frames.ConnectionCloseFrame:
|
||||
utils.Debugf("\t<- %#v", frame)
|
||||
s.closeImpl(qerr.Error(frame.ErrorCode, frame.ReasonPhrase), true)
|
||||
case *frames.GoawayFrame:
|
||||
utils.Debugf("\t<- %#v", frame)
|
||||
err = errors.New("unimplemented: handling GOAWAY frames")
|
||||
case *frames.StopWaitingFrame:
|
||||
utils.Debugf("\t<- %#v", frame)
|
||||
err = s.receivedPacketHandler.ReceivedStopWaiting(frame)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue