create Client in main package

This commit is contained in:
Marten Seemann 2016-11-07 17:51:39 +07:00
parent 6189df2d37
commit afa71d52f1
No known key found for this signature in database
GPG key ID: 3603F40B121FCDEA
3 changed files with 137 additions and 75 deletions

102
client.go Normal file
View file

@ -0,0 +1,102 @@
package quic
import (
"bytes"
"math/rand"
"net"
"time"
"github.com/lucas-clemente/quic-go/protocol"
"github.com/lucas-clemente/quic-go/qerr"
"github.com/lucas-clemente/quic-go/utils"
)
// A Client of QUIC
type Client struct {
addr *net.UDPAddr
conn *net.UDPConn
connectionID protocol.ConnectionID
version protocol.VersionNumber
session *Session
}
// NewClient makes a new client
func NewClient(addr *net.UDPAddr) (*Client, error) {
conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
if err != nil {
return nil, err
}
// TODO: generate cryptographically secure random ConnectionID
rand.Seed(time.Now().UTC().UnixNano())
connectionID := protocol.ConnectionID(rand.Int63())
client := &Client{
addr: addr,
conn: conn,
version: protocol.Version36,
connectionID: connectionID,
}
streamCallback := func(session *Session, stream utils.Stream) {}
client.session, err = newClientSession(conn, addr, client.version, client.connectionID, streamCallback, client.closeCallback)
if err != nil {
return nil, err
}
return client, nil
}
// Listen listens
func (c *Client) Listen() {
go c.session.run()
for {
data := getPacketBuffer()
data = data[:protocol.MaxPacketSize]
n, _, err := c.conn.ReadFromUDP(data)
utils.Debugf("%d", n)
if err != nil {
panic(err)
}
data = data[:n]
err = c.handlePacket(data)
if err != nil {
utils.Errorf("error handling packet: %s", err.Error())
}
}
}
func (c *Client) handlePacket(packet []byte) error {
if protocol.ByteCount(len(packet)) > protocol.MaxPacketSize {
return qerr.PacketTooLarge
}
rcvTime := time.Now()
r := bytes.NewReader(packet)
hdr, err := ParsePublicHeader(r)
if err != nil {
return qerr.Error(qerr.InvalidPacketHeader, err.Error())
}
hdr.Raw = packet[:len(packet)-r.Len()]
c.session.handlePacket(&receivedPacket{
remoteAddr: c.addr,
publicHeader: hdr,
data: packet[len(packet)-r.Len():],
rcvTime: rcvTime,
})
return nil
}
func (c *Client) closeCallback(id protocol.ConnectionID) {
utils.Infof("Connection %x closed.", id)
c.conn.Close()
}

29
client_test.go Normal file
View file

@ -0,0 +1,29 @@
package quic
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("Client", func() {
var client *Client
BeforeEach(func() {
client = &Client{}
})
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))
})
})

View file

@ -1,95 +1,26 @@
package main
import (
"bytes"
"fmt"
"math/rand"
"net"
"time"
quic "github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/crypto"
"github.com/lucas-clemente/quic-go/frames"
"github.com/lucas-clemente/quic-go/handshake"
"github.com/lucas-clemente/quic-go/protocol"
"github.com/lucas-clemente/quic-go/utils"
)
func main() {
addr := "quic.clemente.io:6121"
conn, err := connect(addr)
defer conn.Close()
if err != nil {
panic(err)
}
utils.SetLogLevel(utils.LogLevelDebug)
rand.Seed(time.Now().UTC().UnixNano())
connectionID := protocol.ConnectionID(0x1337 + rand.Int63())
packetNumber := protocol.PacketNumber(1)
version := protocol.Version34
ph := quic.PublicHeader{
ConnectionID: connectionID,
PacketNumber: packetNumber,
PacketNumberLen: protocol.PacketNumberLen6,
VersionFlag: true,
VersionNumber: version,
}
raw := make([]byte, 0, protocol.MaxPacketSize)
buffer := bytes.NewBuffer(raw)
err = ph.Write(buffer, protocol.Version34, protocol.PerspectiveClient)
if err != nil {
panic(err)
}
payloadStartIndex := buffer.Len()
b := &bytes.Buffer{}
tags := make(map[handshake.Tag][]byte)
tags[handshake.TagSNI] = []byte("quic.clemente.io")
tags[handshake.TagPDMD] = []byte("X509")
tags[handshake.TagPAD] = bytes.Repeat([]byte("F"), 1000)
handshake.WriteHandshakeMessage(b, handshake.TagCHLO, tags)
frame := frames.StreamFrame{
StreamID: 1,
DataLenPresent: true,
Data: b.Bytes(),
}
frame.Write(buffer, version)
raw = raw[0:buffer.Len()]
aead := crypto.NullAEAD{}
aead.Seal(raw[payloadStartIndex:payloadStartIndex], raw[payloadStartIndex:], packetNumber, raw[:payloadStartIndex])
raw = raw[0 : buffer.Len()+12]
conn.Write(raw)
for {
data := make([]byte, 1500)
n, _, err := conn.ReadFromUDP(data)
if err != nil {
panic(err)
}
data = data[:n]
fmt.Printf("Response length: %d\n", n)
fmt.Println(data)
}
}
func connect(addr string) (*net.UDPConn, error) {
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
panic(err)
}
conn, err := net.DialUDP("udp", nil, udpAddr)
client, err := quic.NewClient(udpAddr)
if err != nil {
return nil, err
panic(err)
}
return conn, nil
client.Listen()
}