go run example/client/main.go https://quic.clemente.io
## Usage
## Usage
### As a server
See the [example server](example/main.go) or try out [Caddy](https://github.com/mholt/caddy)(from version 0.9, [instructions here](https://github.com/mholt/caddy/wiki/QUIC)). Starting a QUIC server is very similar to the standard lib http in go:
See the [example server](example/main.go) or try out [Caddy](https://github.com/mholt/caddy)(from version 0.9, [instructions here](https://github.com/mholt/caddy/wiki/QUIC)). Starting a QUIC server is very similar to the standard lib http in go:
See the [example client](example/client/main.go). Use a `QuicRoundTripper` as a `Transport` in a `http.Client`.
```go
http.Client{
Transport:&h2quic.QuicRoundTripper{},
}
```
## Building on Windows
## Building on Windows
Due to the low Windows timer resolution (see [StackOverflow question](http://stackoverflow.com/questions/37706834/high-resolution-timers-millisecond-precision-in-go-on-windows)) available with Go 1.6.x, some optimizations might not work when compiled with this version of the compiler. Please use Go 1.7 on Windows.
Due to the low Windows timer resolution (see [StackOverflow question](http://stackoverflow.com/questions/37706834/high-resolution-timers-millisecond-precision-in-go-on-windows)) available with Go 1.6.x, some optimizations might not work when compiled with this version of the compiler. Please use Go 1.7 on Windows.
// ErrMapAccess occurs when a NACK contains invalid NACK ranges
ErrMapAccess=qerr.Error(qerr.InvalidAckData,"Packet does not exist in PacketHistory")
// ErrPacketSmallerThanLastStopWaiting occurs when a packet arrives with a packet number smaller than the largest LeastUnacked of a StopWaitingFrame. If this error occurs, the packet should be ignored
// ErrPacketSmallerThanLastStopWaiting occurs when a packet arrives with a packet number smaller than the largest LeastUnacked of a StopWaitingFrame. If this error occurs, the packet should be ignored
ErrPacketSmallerThanLastStopWaiting=errors.New("ReceivedPacketHandler: Packet number smaller than highest StopWaiting")
ErrPacketSmallerThanLastStopWaiting=errors.New("ReceivedPacketHandler: Packet number smaller than highest StopWaiting")
stateChangedbool// has an ACK for this state already been sent? Will be set to false every time a new packet arrives, and to false every time an ACK is sent
packetHistory*receivedPacketHistory
packetHistory*receivedPacketHistory
receivedTimesmap[protocol.PacketNumber]time.Time
ackSendDelaytime.Duration
lowestInReceivedTimesprotocol.PacketNumber
packetsReceivedSinceLastAckint
retransmittablePacketsReceivedSinceLastAckint
ackQueuedbool
ackAlarmtime.Time
ackAlarmResetCallbackfunc(time.Time)
lastAck*frames.AckFrame
}
}
// NewReceivedPacketHandler creates a new receivedPacketHandler
// NewReceivedPacketHandler creates a new receivedPacketHandler
// the LeastUnacked is the smallest packet number of any packet for which the sender is still awaiting an ack. So the largestInOrderObserved is one less than that
fori:=el.Value.Start;i<leastUnacked;i++{// adjust start value of a range
delete(h.receivedPacketNumbers,i)
}
el.Value.Start=leastUnacked
el.Value.Start=leastUnacked
}
}elseifel.Value.End<leastUnacked{// delete a whole range
ifel.Value.End<leastUnacked{// delete a whole range
fori:=el.Value.Start;i<=el.Value.End;i++{
delete(h.receivedPacketNumbers,i)
}
h.ranges.Remove(el)
h.ranges.Remove(el)
}else{
}else{// no ranges affected. Nothing to do
return
return
}
}
}
}
}
}
// IsDuplicate determines if a packet should be regarded as a duplicate packet
// note that after receiving a StopWaitingFrame, all packets below the LeastUnacked should be regarded as duplicates, even if the packet was just delayed
returnqerr.Error(qerr.FlowControlReceivedTooMuchData,fmt.Sprintf("Received %d bytes for the connection, allowed %d bytes",byteOffset,connectionFlowController.receiveFlowControlWindow))
returnqerr.Error(qerr.FlowControlReceivedTooMuchData,fmt.Sprintf("Received %d bytes for the connection, allowed %d bytes",byteOffset,connectionFlowController.receiveFlowControlWindow))
AckRanges[]AckRange// has to be ordered. The ACK range with the highest FirstPacketNumber goes first, the ACK range with the lowest FirstPacketNumber goes last
AckRanges[]AckRange// has to be ordered. The ACK range with the highest FirstPacketNumber goes first, the ACK range with the lowest FirstPacketNumber goes last
// time when the LargestAcked was receiveid
// this field Will not be set for received ACKs frames
PacketReceivedTimetime.Time
DelayTimetime.Duration
DelayTimetime.Duration
PacketReceivedTimetime.Time// only for received packets. Will not be modified for received ACKs frames
}
}
// ParseAckFrame reads an ACK frame
// ParseAckFrame reads an ACK frame
...
@@ -83,7 +85,7 @@ func ParseAckFrame(r *bytes.Reader, version protocol.VersionNumber) (*AckFrame,
...
@@ -83,7 +85,7 @@ func ParseAckFrame(r *bytes.Reader, version protocol.VersionNumber) (*AckFrame,
iferr!=nil{
iferr!=nil{
returnnil,err
returnnil,err
}
}
ifackBlockLength<1{
ifframe.LargestAcked>0&&ackBlockLength<1{
returnnil,ErrInvalidFirstAckRange
returnnil,ErrInvalidFirstAckRange
}
}
...
@@ -141,7 +143,11 @@ func ParseAckFrame(r *bytes.Reader, version protocol.VersionNumber) (*AckFrame,
...
@@ -141,7 +143,11 @@ func ParseAckFrame(r *bytes.Reader, version protocol.VersionNumber) (*AckFrame,
// see https://groups.google.com/a/chromium.org/forum/#!topic/proto-quic/N-de9j63tCk for a discussion and examples
verSlice,ok:=cryptoData[TagVER]
if!ok{
returnfalse,qerr.Error(qerr.InvalidCryptoMessageParameter,"client hello missing version tag")
}
iflen(verSlice)!=4{
returnfalse,qerr.Error(qerr.InvalidCryptoMessageParameter,"incorrect version tag")
}
verTag:=binary.LittleEndian.Uint32(verSlice)
ver:=protocol.VersionTagToNumber(verTag)
// If the client's preferred version is not the version we are currently speaking, then the client went through a version negotiation. In this case, we need to make sure that we actually do not support this version and that it wasn't a downgrade attack.
// LockForSealing should be called before Seal(). It is needed so that diversification nonces can be obtained before packets are sealed, and the AEADs are not changed in the meantime.
// LockForSealing should be called before Seal(). It is needed so that diversification nonces can be obtained before packets are sealed, and the AEADs are not changed in the meantime.
func(h*CryptoSetup)LockForSealing(){
func(h*cryptoSetupServer)LockForSealing(){
h.mutex.RLock()
h.mutex.RLock()
}
}
// UnlockForSealing should be called after Seal() is complete, see LockForSealing().
// UnlockForSealing should be called after Seal() is complete, see LockForSealing().
func(h*CryptoSetup)UnlockForSealing(){
func(h*cryptoSetupServer)UnlockForSealing(){
h.mutex.RUnlock()
h.mutex.RUnlock()
}
}
// HandshakeComplete returns true after the first forward secure packet was received form the client.
// HandshakeComplete returns true after the first forward secure packet was received form the client.
// the stopWaitingFrame is *guaranteed* to be included in the next packet
// the other controlFrames are sent in the next packet, but might be queued and sent in the next packet if the packet would overflow MaxPacketSize otherwise
// MaxStreamsPerConnection is the maximum value accepted for the number of streams per connection
// MaxStreamsPerConnection is the maximum value accepted for the number of streams per connection
constMaxStreamsPerConnection=100
constMaxStreamsPerConnection=100
// MaxIncomingDynamicStreams is the maximum value accepted for the incoming number of dynamic streams per connection
// MaxIncomingDynamicStreamsPerConnection is the maximum value accepted for the incoming number of dynamic streams per connection
constMaxIncomingDynamicStreams=100
constMaxIncomingDynamicStreamsPerConnection=100
// MaxStreamsMultiplier is the slack the client is allowed for the maximum number of streams per connection, needed e.g. when packets are out of order or dropped. The minimum of this procentual increase and the absolute increment specified by MaxStreamsMinimumIncrement is used.
// MaxStreamsMultiplier is the slack the client is allowed for the maximum number of streams per connection, needed e.g. when packets are out of order or dropped. The minimum of this procentual increase and the absolute increment specified by MaxStreamsMinimumIncrement is used.
utils.Infof("Received a Public Reset for connection %x. An error occurred parsing the packet.")
}else{
utils.Infof("Received a Public Reset for connection %x, rejected packet number: 0x%x.",hdr.ConnectionID,pr.rejectedPacketNumber)
}
}else{
utils.Infof("Received Public Reset for unknown connection %x.",hdr.ConnectionID)
}
returnnil
}
// a session is only created once the client sent a supported version
// if we receive a packet for a connection that already has session, it's probably an old packet that was sent by the client before the version was negotiated
returnnil,qerr.Error(qerr.InvalidStreamID,fmt.Sprintf("attempted to open stream %d, which is a lot smaller than the highest opened stream, %d",id,m.highestStreamOpenedByClient))
returnnil,qerr.Error(qerr.InvalidStreamID,fmt.Sprintf("attempted to open stream %d, which is a lot smaller than the highest opened stream, %d",id,m.highestStreamOpenedByClient))
// NumberOfStreams gets the number of open streams
func(m*streamsMap)NumberOfStreams()int{
m.mutex.RLock()
n:=len(m.openStreams)
m.mutex.RUnlock()
returnn
}
// garbageCollectClosedStreams deletes nil values in the streams if they are smaller than protocol.MaxNewStreamIDDelta than the highest stream opened by the client
// garbageCollectClosedStreams deletes nil values in the streams if they are smaller than protocol.MaxNewStreamIDDelta than the highest stream opened by the client
// note that this garbage collection is relatively expensive, since it iterates over the whole streams map. It should not be called every time a stream is openend or closed
// note that this garbage collection is relatively expensive, since it iterates over the whole streams map. It should not be called every time a stream is openend or closed