diff --git a/session.go b/session.go index 1ed80132a..09af8c09d 100644 --- a/session.go +++ b/session.go @@ -394,6 +394,12 @@ runLoop: s.handshakeComplete = true handshakeEvent = nil // prevent this case from ever being selected again s.sentPacketHandler.SetHandshakeComplete() + if !s.version.UsesTLS() && s.perspective == protocol.PerspectiveClient { + // In gQUIC, there's no equivalent to the Finished message in TLS + // The server knows that the handshake is complete when it receives the first forward-secure packet sent by the client. + // We need to make sure that the client actually sends such a packet. + s.packer.QueueControlFrame(&wire.PingFrame{}) + } close(s.handshakeChan) } else { s.tryDecryptingQueuedPackets() diff --git a/session_test.go b/session_test.go index a51b87751..6438b45db 100644 --- a/session_test.go +++ b/session_test.go @@ -1470,6 +1470,22 @@ var _ = Describe("Client Session", func() { newCryptoSetupClient = handshake.NewCryptoSetupClient }) + It("sends a forward-secure packet when the handshake completes", func() { + sess.packer.hasSentPacket = true + done := make(chan struct{}) + go func() { + defer GinkgoRecover() + err := sess.run() + Expect(err).ToNot(HaveOccurred()) + close(done) + }() + close(handshakeChan) + Eventually(mconn.written).Should(Receive()) + //make sure the go routine returns + Expect(sess.Close(nil)).To(Succeed()) + Eventually(done).Should(BeClosed()) + }) + Context("receiving packets", func() { var hdr *wire.Header