uquic/client_test.go
2017-01-14 18:52:24 +07:00

184 lines
5.9 KiB
Go

package quic
import (
"bytes"
"encoding/binary"
"net"
"github.com/lucas-clemente/quic-go/protocol"
"github.com/lucas-clemente/quic-go/qerr"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Client", func() {
var client *Client
var session *mockSession
BeforeEach(func() {
client = &Client{}
session = &mockSession{connectionID: 0x1337}
client.connectionID = 0x1337
client.session = session
client.version = protocol.Version36
})
startUDPConn := func() {
var err error
client.addr, err = net.ResolveUDPAddr("udp", "127.0.0.1:0")
Expect(err).ToNot(HaveOccurred())
client.conn, err = net.ListenUDP("udp", client.addr)
Expect(err).NotTo(HaveOccurred())
}
It("errors on invalid public header", func() {
err := client.handlePacket(nil)
Expect(err.(*qerr.QuicError).ErrorCode).To(Equal(qerr.InvalidPacketHeader))
})
It("errors on large packets", func() {
err := client.handlePacket(bytes.Repeat([]byte{'a'}, int(protocol.MaxPacketSize)+1))
Expect(err).To(MatchError(qerr.PacketTooLarge))
})
It("closes sessions when Close is called", func() {
addr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
Expect(err).ToNot(HaveOccurred())
client.conn, err = net.ListenUDP("udp", addr)
Expect(err).ToNot(HaveOccurred())
err = client.Close()
Expect(err).ToNot(HaveOccurred())
Expect(session.closed).To(BeTrue())
Expect(session.closeReason).To(BeNil())
})
It("creates new sessions with the right parameters", func() {
startUDPConn()
client.session = nil
client.hostname = "hostname"
err := client.createNewSession()
Expect(err).ToNot(HaveOccurred())
Expect(client.session).ToNot(BeNil())
Expect(client.session.(*Session).connectionID).To(Equal(client.connectionID))
Expect(client.session.(*Session).version).To(Equal(client.version))
})
Context("handling packets", func() {
It("errors on too large packets", func() {
err := client.handlePacket(bytes.Repeat([]byte{'f'}, int(protocol.MaxPacketSize+1)))
Expect(err).To(MatchError(qerr.PacketTooLarge))
})
It("handles packets", func(done Done) {
startUDPConn()
serverConn, err := net.DialUDP("udp", nil, client.conn.LocalAddr().(*net.UDPAddr))
Expect(err).NotTo(HaveOccurred())
go func() {
defer GinkgoRecover()
listenErr := client.Listen()
Expect(listenErr).ToNot(HaveOccurred())
close(done)
}()
Expect(session.packetCount).To(BeZero())
ph := PublicHeader{
PacketNumber: 1,
PacketNumberLen: protocol.PacketNumberLen2,
ConnectionID: 0x1337,
}
b := &bytes.Buffer{}
err = ph.Write(b, protocol.Version36, protocol.PerspectiveServer)
Expect(err).ToNot(HaveOccurred())
_, err = serverConn.Write(b.Bytes())
Expect(err).ToNot(HaveOccurred())
Eventually(func() int { return session.packetCount }).Should(Equal(1))
Expect(session.closed).To(BeFalse())
err = client.Close()
Expect(err).ToNot(HaveOccurred())
})
It("closes the session when encountering an error while handling a packet", func(done Done) {
startUDPConn()
serverConn, err := net.DialUDP("udp", nil, client.conn.LocalAddr().(*net.UDPAddr))
Expect(err).NotTo(HaveOccurred())
var listenErr error
go func() {
defer GinkgoRecover()
listenErr = client.Listen()
Expect(listenErr).To(HaveOccurred())
close(done)
}()
// cause a PacketTooLarge error
_, err = serverConn.Write(bytes.Repeat([]byte{'f'}, 100))
Expect(err).ToNot(HaveOccurred())
Eventually(func() bool { return session.closed }).Should(BeTrue())
Expect(session.closeReason).To(MatchError(listenErr))
err = client.Close()
Expect(err).ToNot(HaveOccurred())
})
})
Context("version negotiation", func() {
getVersionNegotiation := func(versions []protocol.VersionNumber) []byte {
oldVersionNegotiationPacket := composeVersionNegotiation(0x1337)
oldSupportVersionTags := protocol.SupportedVersionsAsTags
var b bytes.Buffer
for _, v := range versions {
s := make([]byte, 4)
binary.LittleEndian.PutUint32(s, protocol.VersionNumberToTag(v))
b.Write(s)
}
protocol.SupportedVersionsAsTags = b.Bytes()
packet := composeVersionNegotiation(client.connectionID)
protocol.SupportedVersionsAsTags = oldSupportVersionTags
Expect(composeVersionNegotiation(0x1337)).To(Equal(oldVersionNegotiationPacket))
return packet
}
It("changes the version after receiving a version negotiation packet", func() {
startUDPConn()
newVersion := protocol.Version35
Expect(newVersion).ToNot(Equal(client.version))
Expect(session.packetCount).To(BeZero())
err := client.handlePacket(getVersionNegotiation([]protocol.VersionNumber{newVersion}))
Expect(client.version).To(Equal(newVersion))
Expect(client.versionNegotiated).To(BeTrue())
// it swapped the sessions
Expect(client.session).ToNot(Equal(session))
Expect(err).ToNot(HaveOccurred())
// it didn't pass the version negoation packet to the session (since it has no payload)
Expect(session.packetCount).To(BeZero())
err = client.Close()
Expect(err).ToNot(HaveOccurred())
})
It("errors if no matching version is found", func() {
err := client.handlePacket(getVersionNegotiation([]protocol.VersionNumber{1}))
Expect(err).To(MatchError(qerr.VersionNegotiationMismatch))
})
It("ignores delayed version negotiation packets", func() {
// if the version was not yet negotiated, handlePacket would return a VersionNegotiationMismatch error, see above test
client.versionNegotiated = true
Expect(session.packetCount).To(BeZero())
err := client.handlePacket(getVersionNegotiation([]protocol.VersionNumber{1}))
Expect(err).ToNot(HaveOccurred())
Expect(client.versionNegotiated).To(BeTrue())
Expect(session.packetCount).To(BeZero())
})
It("errors if the server should have accepted the offered version", func() {
err := client.handlePacket(getVersionNegotiation([]protocol.VersionNumber{client.version}))
Expect(err).To(MatchError(errInvalidVersionNegotiation))
})
})
})