diff --git a/connection_close_frame.go b/connection_close_frame.go new file mode 100644 index 000000000..d9eca2bc9 --- /dev/null +++ b/connection_close_frame.go @@ -0,0 +1,51 @@ +package quic + +import ( + "bytes" + "errors" + + "github.com/lucas-clemente/quic-go/utils" +) + +// A ConnectionCloseFrame in QUIC +type ConnectionCloseFrame struct { + ErrorCode uint32 + ReasonPhrase string +} + +// Write writes an ACK frame. +func (f *ConnectionCloseFrame) Write(b *bytes.Buffer) error { + return errors.New("ConnectionCloseFrame: Write not yet implemented") +} + +// ParseConnectionCloseFrame reads an ACK frame +func ParseConnectionCloseFrame(r *bytes.Reader) (*ConnectionCloseFrame, error) { + frame := &ConnectionCloseFrame{} + + // read the TypeByte + _, err := r.ReadByte() + if err != nil { + return nil, err + } + + frame.ErrorCode, err = utils.ReadUint32(r) + if err != nil { + return nil, err + } + + reasonPhraseLen, err := utils.ReadUint16(r) + if err != nil { + return nil, err + } + + for i := 0; i < int(reasonPhraseLen); i++ { + val, err := r.ReadByte() + if err != nil { + return nil, err + } + + frame.ReasonPhrase += string(val) + } + + return frame, nil +} diff --git a/connection_close_frame_test.go b/connection_close_frame_test.go new file mode 100644 index 000000000..cb94a87e8 --- /dev/null +++ b/connection_close_frame_test.go @@ -0,0 +1,30 @@ +package quic + +import ( + "bytes" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("AckFrame", func() { + Context("when parsing", func() { + It("accepts sample frame", func() { + b := bytes.NewReader([]byte{0x40, 0x19, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x4e, 0x6f, 0x20, 0x72, 0x65, 0x63, 0x65, 0x6e, 0x74, 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x2e}) + frame, err := ParseConnectionCloseFrame(b) + Expect(err).ToNot(HaveOccurred()) + Expect(frame.ErrorCode).To(Equal(uint32(0x19))) + Expect(frame.ReasonPhrase).To(Equal("No recent network activity.")) + Expect(b.Len()).To(Equal(0)) + }) + + It("parses a frame without a reason phrase", func() { + b := bytes.NewReader([]byte{0x02, 0xAD, 0xFB, 0xCA, 0xDE, 0x00, 0x00}) + frame, err := ParseConnectionCloseFrame(b) + Expect(err).ToNot(HaveOccurred()) + Expect(frame.ErrorCode).To(Equal(uint32(0xDECAFBAD))) + Expect(len(frame.ReasonPhrase)).To(Equal(0)) + Expect(b.Len()).To(Equal(0)) + }) + }) +}) diff --git a/session.go b/session.go index 2a04a8bd7..51f2c101b 100644 --- a/session.go +++ b/session.go @@ -114,11 +114,19 @@ func (s *Session) HandlePacket(addr *net.UDPAddr, publicHeaderBinary []byte, pub return err } // ToDo: react to receiving this frame + } else if typeByte&0x02 == 0x02 { // CONNECTION_CLOSE + fmt.Println("Detected CONNECTION_CLOSE") + frame, err := ParseConnectionCloseFrame(r) + if err != nil { + return err + } + fmt.Printf("%#v\n", frame) } else if typeByte == 0 { // PAD return nil + } else { + return errors.New("Session: invalid Frame Type Field") } - return fmt.Errorf("Session: invalid frame type %x", typeByte) } return nil }