<commit_before>package libp2pquic

import (
	"context"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net"
	"sync"
	"time"

	"golang.org/x/crypto/hkdf"

	"github.com/libp2p/go-libp2p-core/connmgr"
	ic "github.com/libp2p/go-libp2p-core/crypto"
	"github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/pnet"
	tpt "github.com/libp2p/go-libp2p-core/transport"

	p2ptls "github.com/libp2p/go-libp2p-tls"

	ma "github.com/multiformats/go-multiaddr"
	mafmt "github.com/multiformats/go-multiaddr-fmt"
	manet "github.com/multiformats/go-multiaddr/net"

	logging "github.com/ipfs/go-log/v2"
	"github.com/lucas-clemente/quic-go"
	"github.com/minio/sha256-simd"
)

var log = logging.Logger("quic-transport")

var ErrHolePunching = errors.New("hole punching attempted; no active dial")

var quicDialContext = quic.DialContext // so we can mock it in tests

var HolePunchTimeout = 5 * time.Second

var quicConfig = &quic.Config{
	MaxIncomingStreams:         256,
	MaxIncomingUniStreams:      -1,             // disable unidirectional streams
	MaxStreamReceiveWindow:     10 * (1 << 20), // 10 MB
	MaxConnectionReceiveWindow: 15 * (1 << 20), // 15 MB
	AcceptToken: func(clientAddr net.Addr, _ *quic.Token) bool {
		// TODO(#6): require source address validation when under load
		return true
	},
	KeepAlive: true,
	Versions:  []quic.VersionNumber{quic.VersionDraft29, quic.Version1},
}

const statelessResetKeyInfo = "libp2p quic stateless reset key"
const errorCodeConnectionGating = 0x47415445 // GATE in ASCII

type connManager struct {
	reuseUDP4 *reuse
	reuseUDP6 *reuse
}

func newConnManager() (*connManager, error) {
	reuseUDP4 := newReuse()
	reuseUDP6 := newReuse()

	return &connManager{
		reuseUDP4: reuseUDP4,
		reuseUDP6: reuseUDP6,
	}, nil
}

func (c *connManager) getReuse(network string) (*reuse, error) {
	switch network {
	case "udp4":
		return c.reuseUDP4, nil
	case "udp6":
		return c.reuseUDP6, nil
	default:
		return nil, errors.New("invalid network: must be either udp4 or udp6")
	}
}

func (c *connManager) Listen(network string, laddr *net.UDPAddr) (*reuseConn, error) {
	reuse, err := c.getReuse(network)
	if err != nil {
		return nil, err
	}
	return reuse.Listen(network, laddr)
}

func (c *connManager) Dial(network string, raddr *net.UDPAddr) (*reuseConn, error) {
	reuse, err := c.getReuse(network)
	if err != nil {
		return nil, err
	}
	return reuse.Dial(network, raddr)
}

func (c *connManager) Close() error {
	if err := c.reuseUDP6.Close(); err != nil {
		return err
	}
	return c.reuseUDP4.Close()
}

// The Transport implements the tpt.Transport interface for QUIC connections.
type transport struct {
	privKey      ic.PrivKey
	localPeer    peer.ID
	identity     *p2ptls.Identity
	connManager  *connManager
	serverConfig *quic.Config
	clientConfig *quic.Config
	gater        connmgr.ConnectionGater
	rcmgr        network.ResourceManager

	holePunchingMx sync.Mutex
	holePunching   map[holePunchKey]*activeHolePunch

	connMx sync.Mutex
	conns  map[quic.Session]*conn
}

var _ tpt.Transport = &transport{}

type holePunchKey struct {
	addr string
	peer peer.ID
}

type activeHolePunch struct {
	connCh    chan tpt.CapableConn
	fulfilled bool
}

// NewTransport creates a new QUIC transport
func NewTransport(key ic.PrivKey, psk pnet.PSK, gater connmgr.ConnectionGater, rcmgr network.ResourceManager) (tpt.Transport, error) {
	if len(psk) > 0 {
		log.Error("QUIC doesn't support private networks yet.")
		return nil, errors.New("QUIC doesn't support private networks yet")
	}
	localPeer, err := peer.IDFromPrivateKey(key)
	if err != nil {
		return nil, err
	}
	identity, err := p2ptls.NewIdentity(key)
	if err != nil {
		return nil, err
	}
	connManager, err := newConnManager()
	if err != nil {
		return nil, err
	}
	if rcmgr == nil {
		rcmgr = network.NullResourceManager
	}
	config := quicConfig.Clone()
	keyBytes, err := key.Raw()
	if err != nil {
		return nil, err
	}
	keyReader := hkdf.New(sha256.New, keyBytes, nil, []byte(statelessResetKeyInfo))
	config.StatelessResetKey = make([]byte, 32)
	if _, err := io.ReadFull(keyReader, config.StatelessResetKey); err != nil {
		return nil, err
	}
	config.Tracer = tracer

	tr := &transport{
		privKey:      key,
		localPeer:    localPeer,
		identity:     identity,
		connManager:  connManager,
		gater:        gater,
		rcmgr:        rcmgr,
		conns:        make(map[quic.Session]*conn),
		holePunching: make(map[holePunchKey]*activeHolePunch),
	}
	config.AllowConnectionWindowIncrease = tr.allowWindowIncrease
	tr.serverConfig = config
	tr.clientConfig = config.Clone()
	return tr, nil
}

// Dial dials a new QUIC connection
func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tpt.CapableConn, error) {
	netw, host, err := manet.DialArgs(raddr)
	if err != nil {
		return nil, err
	}
	addr, err := net.ResolveUDPAddr(netw, host)
	if err != nil {
		return nil, err
	}
	remoteMultiaddr, err := toQuicMultiaddr(addr)
	if err != nil {
		return nil, err
	}
	tlsConf, keyCh := t.identity.ConfigForPeer(p)
	if ok, isClient, _ := network.GetSimultaneousConnect(ctx); ok && !isClient {
		return t.holePunch(ctx, netw, addr, p)
	}

	scope, err := t.rcmgr.OpenConnection(network.DirOutbound, false)
	if err != nil {
		log.Debugw("resource manager blocked outgoing connection", "peer", p, "addr", raddr, "error", err)
		return nil, err
	}
	if err := scope.SetPeer(p); err != nil {
		log.Debugw("resource manager blocked outgoing connection for peer", "peer", p, "addr", raddr, "error", err)
		scope.Done()
		return nil, err
	}
	pconn, err := t.connManager.Dial(netw, addr)
	if err != nil {
		return nil, err
	}
	sess, err := quicDialContext(ctx, pconn, addr, host, tlsConf, t.clientConfig)
	if err != nil {
		scope.Done()
		pconn.DecreaseCount()
		return nil, err
	}
	// Should be ready by this point, don't block.
	var remotePubKey ic.PubKey
	select {
	case remotePubKey = <-keyCh:
	default:
	}
	if remotePubKey == nil {
		pconn.DecreaseCount()
		scope.Done()
		return nil, errors.New("go-libp2p-quic-transport BUG: expected remote pub key to be set")
	}

	localMultiaddr, err := toQuicMultiaddr(pconn.LocalAddr())
	if err != nil {
		sess.CloseWithError(0, "")
		return nil, err
	}
	c := &conn{
		sess:            sess,
		pconn:           pconn,
		transport:       t,
		scope:           scope,
		privKey:         t.privKey,
		localPeer:       t.localPeer,
		localMultiaddr:  localMultiaddr,
		remotePubKey:    remotePubKey,
		remotePeerID:    p,
		remoteMultiaddr: remoteMultiaddr,
	}
	if t.gater != nil && !t.gater.InterceptSecured(network.DirOutbound, p, c) {
		sess.CloseWithError(errorCodeConnectionGating, "connection gated")
		return nil, fmt.Errorf("secured connection gated")
	}
	t.addConn(sess, c)
	return c, nil
}

func (t *transport) addConn(sess quic.Session, c *conn) {
	t.connMx.Lock()
	t.conns[sess] = c
	t.connMx.Unlock()
}

func (t *transport) removeConn(sess quic.Session) {
	t.connMx.Lock()
	delete(t.conns, sess)
	t.connMx.Unlock()
}

func (t *transport) holePunch(ctx context.Context, network string, addr *net.UDPAddr, p peer.ID) (tpt.CapableConn, error) {
	pconn, err := t.connManager.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	defer pconn.DecreaseCount()

	ctx, cancel := context.WithTimeout(ctx, HolePunchTimeout)
	defer cancel()

	key := holePunchKey{addr: addr.String(), peer: p}
	t.holePunchingMx.Lock()
	if _, ok := t.holePunching[key]; ok {
		t.holePunchingMx.Unlock()
		return nil, fmt.Errorf("already punching hole for %s", addr)
	}
	connCh := make(chan tpt.CapableConn, 1)
	t.holePunching[key] = &activeHolePunch{connCh: connCh}
	t.holePunchingMx.Unlock()

	var timer *time.Timer
	defer func() {
		if timer != nil {
			timer.Stop()
		}
	}()

	payload := make([]byte, 64)
	var punchErr error
loop:
	for i := 0; ; i++ {
		if _, err := rand.Read(payload); err != nil {
			punchErr = err
			break
		}
		if _, err := pconn.UDPConn.WriteToUDP(payload, addr); err != nil {
			punchErr = err
			break
		}

		maxSleep := 10 * (i + 1) * (i + 1) // in ms
		if maxSleep > 200 {
			maxSleep = 200
		}
		d := 10*time.Millisecond + time.Duration(rand.Intn(maxSleep))*time.Millisecond
		if timer == nil {
			timer = time.NewTimer(d)
		} else {
			timer.Reset(d)
		}
		select {
		case c := <-connCh:
			t.holePunchingMx.Lock()
			delete(t.holePunching, key)
			t.holePunchingMx.Unlock()
			return c, nil
		case <-timer.C:
		case <-ctx.Done():
			punchErr = ErrHolePunching
			break loop
		}
	}
	// we only arrive here if punchErr != nil
	t.holePunchingMx.Lock()
	defer func() {
		delete(t.holePunching, key)
		t.holePunchingMx.Unlock()
	}()
	select {
	case c := <-t.holePunching[key].connCh:
		return c, nil
	default:
		return nil, punchErr
	}
}

// Don't use mafmt.QUIC as we don't want to dial DNS addresses. Just /ip{4,6}/udp/quic
var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Base(ma.P_QUIC))

// CanDial determines if we can dial to an address
func (t *transport) CanDial(addr ma.Multiaddr) bool {
	return dialMatcher.Matches(addr)
}

// Listen listens for new QUIC connections on the passed multiaddr.
func (t *transport) Listen(addr ma.Multiaddr) (tpt.Listener, error) {
	lnet, host, err := manet.DialArgs(addr)
	if err != nil {
		return nil, err
	}
	laddr, err := net.ResolveUDPAddr(lnet, host)
	if err != nil {
		return nil, err
	}
	conn, err := t.connManager.Listen(lnet, laddr)
	if err != nil {
		return nil, err
	}
	ln, err := newListener(conn, t, t.localPeer, t.privKey, t.identity, t.rcmgr)
	if err != nil {
		conn.DecreaseCount()
		return nil, err
	}
	return ln, nil
}

func (t *transport) allowWindowIncrease(sess quic.Session, size uint64) bool {
	// If the QUIC session tries to increase the window before we've inserted it
	// into our connections map (which we do right after dialing / accepting it),
	// we have no way to account for that memory. This should be very rare.
	// Block this attempt. The session can request more memory later.
	c, ok := t.conns[sess]
	if !ok {
		return false
	}
	return c.allowWindowIncrease(size)
}

// Proxy returns true if this transport proxies.
func (t *transport) Proxy() bool {
	return false
}

// Protocols returns the set of protocols handled by this transport.
func (t *transport) Protocols() []int {
	return []int{ma.P_QUIC}
}

func (t *transport) String() string {
	return "QUIC"
}

func (t *transport) Close() error {
	return t.connManager.Close()
}
<commit_msg>Prevent data race in allowWindowIncrease (#259)<commit_after>package libp2pquic

import (
	"context"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"net"
	"sync"
	"time"

	"golang.org/x/crypto/hkdf"

	"github.com/libp2p/go-libp2p-core/connmgr"
	ic "github.com/libp2p/go-libp2p-core/crypto"
	"github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/pnet"
	tpt "github.com/libp2p/go-libp2p-core/transport"

	p2ptls "github.com/libp2p/go-libp2p-tls"

	ma "github.com/multiformats/go-multiaddr"
	mafmt "github.com/multiformats/go-multiaddr-fmt"
	manet "github.com/multiformats/go-multiaddr/net"

	logging "github.com/ipfs/go-log/v2"
	"github.com/lucas-clemente/quic-go"
	"github.com/minio/sha256-simd"
)

var log = logging.Logger("quic-transport")

var ErrHolePunching = errors.New("hole punching attempted; no active dial")

var quicDialContext = quic.DialContext // so we can mock it in tests

var HolePunchTimeout = 5 * time.Second

var quicConfig = &quic.Config{
	MaxIncomingStreams:         256,
	MaxIncomingUniStreams:      -1,             // disable unidirectional streams
	MaxStreamReceiveWindow:     10 * (1 << 20), // 10 MB
	MaxConnectionReceiveWindow: 15 * (1 << 20), // 15 MB
	AcceptToken: func(clientAddr net.Addr, _ *quic.Token) bool {
		// TODO(#6): require source address validation when under load
		return true
	},
	KeepAlive: true,
	Versions:  []quic.VersionNumber{quic.VersionDraft29, quic.Version1},
}

const statelessResetKeyInfo = "libp2p quic stateless reset key"
const errorCodeConnectionGating = 0x47415445 // GATE in ASCII

type connManager struct {
	reuseUDP4 *reuse
	reuseUDP6 *reuse
}

func newConnManager() (*connManager, error) {
	reuseUDP4 := newReuse()
	reuseUDP6 := newReuse()

	return &connManager{
		reuseUDP4: reuseUDP4,
		reuseUDP6: reuseUDP6,
	}, nil
}

func (c *connManager) getReuse(network string) (*reuse, error) {
	switch network {
	case "udp4":
		return c.reuseUDP4, nil
	case "udp6":
		return c.reuseUDP6, nil
	default:
		return nil, errors.New("invalid network: must be either udp4 or udp6")
	}
}

func (c *connManager) Listen(network string, laddr *net.UDPAddr) (*reuseConn, error) {
	reuse, err := c.getReuse(network)
	if err != nil {
		return nil, err
	}
	return reuse.Listen(network, laddr)
}

func (c *connManager) Dial(network string, raddr *net.UDPAddr) (*reuseConn, error) {
	reuse, err := c.getReuse(network)
	if err != nil {
		return nil, err
	}
	return reuse.Dial(network, raddr)
}

func (c *connManager) Close() error {
	if err := c.reuseUDP6.Close(); err != nil {
		return err
	}
	return c.reuseUDP4.Close()
}

// The Transport implements the tpt.Transport interface for QUIC connections.
type transport struct {
	privKey      ic.PrivKey
	localPeer    peer.ID
	identity     *p2ptls.Identity
	connManager  *connManager
	serverConfig *quic.Config
	clientConfig *quic.Config
	gater        connmgr.ConnectionGater
	rcmgr        network.ResourceManager

	holePunchingMx sync.Mutex
	holePunching   map[holePunchKey]*activeHolePunch

	connMx sync.Mutex
	conns  map[quic.Session]*conn
}

var _ tpt.Transport = &transport{}

type holePunchKey struct {
	addr string
	peer peer.ID
}

type activeHolePunch struct {
	connCh    chan tpt.CapableConn
	fulfilled bool
}

// NewTransport creates a new QUIC transport
func NewTransport(key ic.PrivKey, psk pnet.PSK, gater connmgr.ConnectionGater, rcmgr network.ResourceManager) (tpt.Transport, error) {
	if len(psk) > 0 {
		log.Error("QUIC doesn't support private networks yet.")
		return nil, errors.New("QUIC doesn't support private networks yet")
	}
	localPeer, err := peer.IDFromPrivateKey(key)
	if err != nil {
		return nil, err
	}
	identity, err := p2ptls.NewIdentity(key)
	if err != nil {
		return nil, err
	}
	connManager, err := newConnManager()
	if err != nil {
		return nil, err
	}
	if rcmgr == nil {
		rcmgr = network.NullResourceManager
	}
	config := quicConfig.Clone()
	keyBytes, err := key.Raw()
	if err != nil {
		return nil, err
	}
	keyReader := hkdf.New(sha256.New, keyBytes, nil, []byte(statelessResetKeyInfo))
	config.StatelessResetKey = make([]byte, 32)
	if _, err := io.ReadFull(keyReader, config.StatelessResetKey); err != nil {
		return nil, err
	}
	config.Tracer = tracer

	tr := &transport{
		privKey:      key,
		localPeer:    localPeer,
		identity:     identity,
		connManager:  connManager,
		gater:        gater,
		rcmgr:        rcmgr,
		conns:        make(map[quic.Session]*conn),
		holePunching: make(map[holePunchKey]*activeHolePunch),
	}
	config.AllowConnectionWindowIncrease = tr.allowWindowIncrease
	tr.serverConfig = config
	tr.clientConfig = config.Clone()
	return tr, nil
}

// Dial dials a new QUIC connection
func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tpt.CapableConn, error) {
	netw, host, err := manet.DialArgs(raddr)
	if err != nil {
		return nil, err
	}
	addr, err := net.ResolveUDPAddr(netw, host)
	if err != nil {
		return nil, err
	}
	remoteMultiaddr, err := toQuicMultiaddr(addr)
	if err != nil {
		return nil, err
	}
	tlsConf, keyCh := t.identity.ConfigForPeer(p)
	if ok, isClient, _ := network.GetSimultaneousConnect(ctx); ok && !isClient {
		return t.holePunch(ctx, netw, addr, p)
	}

	scope, err := t.rcmgr.OpenConnection(network.DirOutbound, false)
	if err != nil {
		log.Debugw("resource manager blocked outgoing connection", "peer", p, "addr", raddr, "error", err)
		return nil, err
	}
	if err := scope.SetPeer(p); err != nil {
		log.Debugw("resource manager blocked outgoing connection for peer", "peer", p, "addr", raddr, "error", err)
		scope.Done()
		return nil, err
	}
	pconn, err := t.connManager.Dial(netw, addr)
	if err != nil {
		return nil, err
	}
	sess, err := quicDialContext(ctx, pconn, addr, host, tlsConf, t.clientConfig)
	if err != nil {
		scope.Done()
		pconn.DecreaseCount()
		return nil, err
	}
	// Should be ready by this point, don't block.
	var remotePubKey ic.PubKey
	select {
	case remotePubKey = <-keyCh:
	default:
	}
	if remotePubKey == nil {
		pconn.DecreaseCount()
		scope.Done()
		return nil, errors.New("go-libp2p-quic-transport BUG: expected remote pub key to be set")
	}

	localMultiaddr, err := toQuicMultiaddr(pconn.LocalAddr())
	if err != nil {
		sess.CloseWithError(0, "")
		return nil, err
	}
	c := &conn{
		sess:            sess,
		pconn:           pconn,
		transport:       t,
		scope:           scope,
		privKey:         t.privKey,
		localPeer:       t.localPeer,
		localMultiaddr:  localMultiaddr,
		remotePubKey:    remotePubKey,
		remotePeerID:    p,
		remoteMultiaddr: remoteMultiaddr,
	}
	if t.gater != nil && !t.gater.InterceptSecured(network.DirOutbound, p, c) {
		sess.CloseWithError(errorCodeConnectionGating, "connection gated")
		return nil, fmt.Errorf("secured connection gated")
	}
	t.addConn(sess, c)
	return c, nil
}

func (t *transport) addConn(sess quic.Session, c *conn) {
	t.connMx.Lock()
	t.conns[sess] = c
	t.connMx.Unlock()
}

func (t *transport) removeConn(sess quic.Session) {
	t.connMx.Lock()
	delete(t.conns, sess)
	t.connMx.Unlock()
}

func (t *transport) holePunch(ctx context.Context, network string, addr *net.UDPAddr, p peer.ID) (tpt.CapableConn, error) {
	pconn, err := t.connManager.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	defer pconn.DecreaseCount()

	ctx, cancel := context.WithTimeout(ctx, HolePunchTimeout)
	defer cancel()

	key := holePunchKey{addr: addr.String(), peer: p}
	t.holePunchingMx.Lock()
	if _, ok := t.holePunching[key]; ok {
		t.holePunchingMx.Unlock()
		return nil, fmt.Errorf("already punching hole for %s", addr)
	}
	connCh := make(chan tpt.CapableConn, 1)
	t.holePunching[key] = &activeHolePunch{connCh: connCh}
	t.holePunchingMx.Unlock()

	var timer *time.Timer
	defer func() {
		if timer != nil {
			timer.Stop()
		}
	}()

	payload := make([]byte, 64)
	var punchErr error
loop:
	for i := 0; ; i++ {
		if _, err := rand.Read(payload); err != nil {
			punchErr = err
			break
		}
		if _, err := pconn.UDPConn.WriteToUDP(payload, addr); err != nil {
			punchErr = err
			break
		}

		maxSleep := 10 * (i + 1) * (i + 1) // in ms
		if maxSleep > 200 {
			maxSleep = 200
		}
		d := 10*time.Millisecond + time.Duration(rand.Intn(maxSleep))*time.Millisecond
		if timer == nil {
			timer = time.NewTimer(d)
		} else {
			timer.Reset(d)
		}
		select {
		case c := <-connCh:
			t.holePunchingMx.Lock()
			delete(t.holePunching, key)
			t.holePunchingMx.Unlock()
			return c, nil
		case <-timer.C:
		case <-ctx.Done():
			punchErr = ErrHolePunching
			break loop
		}
	}
	// we only arrive here if punchErr != nil
	t.holePunchingMx.Lock()
	defer func() {
		delete(t.holePunching, key)
		t.holePunchingMx.Unlock()
	}()
	select {
	case c := <-t.holePunching[key].connCh:
		return c, nil
	default:
		return nil, punchErr
	}
}

// Don't use mafmt.QUIC as we don't want to dial DNS addresses. Just /ip{4,6}/udp/quic
var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Base(ma.P_QUIC))

// CanDial determines if we can dial to an address
func (t *transport) CanDial(addr ma.Multiaddr) bool {
	return dialMatcher.Matches(addr)
}

// Listen listens for new QUIC connections on the passed multiaddr.
func (t *transport) Listen(addr ma.Multiaddr) (tpt.Listener, error) {
	lnet, host, err := manet.DialArgs(addr)
	if err != nil {
		return nil, err
	}
	laddr, err := net.ResolveUDPAddr(lnet, host)
	if err != nil {
		return nil, err
	}
	conn, err := t.connManager.Listen(lnet, laddr)
	if err != nil {
		return nil, err
	}
	ln, err := newListener(conn, t, t.localPeer, t.privKey, t.identity, t.rcmgr)
	if err != nil {
		conn.DecreaseCount()
		return nil, err
	}
	return ln, nil
}

func (t *transport) allowWindowIncrease(sess quic.Session, size uint64) bool {
	// If the QUIC session tries to increase the window before we've inserted it
	// into our connections map (which we do right after dialing / accepting it),
	// we have no way to account for that memory. This should be very rare.
	// Block this attempt. The session can request more memory later.
	t.connMx.Lock()
	c, ok := t.conns[sess]
	t.connMx.Unlock()
	if !ok {
		return false
	}
	return c.allowWindowIncrease(size)
}

// Proxy returns true if this transport proxies.
func (t *transport) Proxy() bool {
	return false
}

// Protocols returns the set of protocols handled by this transport.
func (t *transport) Protocols() []int {
	return []int{ma.P_QUIC}
}

func (t *transport) String() string {
	return "QUIC"
}

func (t *transport) Close() error {
	return t.connManager.Close()
}
<|endoftext|>


<commit_before>package log_test

import (
	"context"
	"errors"
	"sync"
	"testing"

	"github.com/jasonsoft/log"
	"github.com/jasonsoft/log/handlers/memory"
	"github.com/stretchr/testify/assert"
)

func TestNoHandler(t *testing.T) {
	log.Info("no handler 1")

	logger := log.FromContext(context.Background())
	logger.Info("no handler 2")
}

func TestPrintf(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	log.Infof("logged in %s", "Tobi")

	e := h.Entries[0]
	assert.Equal(t, "logged in Tobi", e.Message)
	assert.Equal(t, log.InfoLevel, e.Level)
}

func TestFlush(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.GetLevelsFromMinLevel("debug")...)

	log.Debugf("logged in")

	assert.Equal(t, 1, len(h.Entries))

	log.Flush()
	assert.Equal(t, 0, len(h.Entries))
}

func TestLevels(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.DebugLevel, log.InfoLevel, log.WarnLevel, log.ErrorLevel)

	log.Debug("debug1")
	log.Info("info1")
	log.Warn("warn1")
	log.Error("error1")

	assert.Equal(t, 4, len(h.Entries))

	e := h.Entries[0]
	assert.Equal(t, "debug1", e.Message)
	assert.Equal(t, log.DebugLevel, e.Level)

	e = h.Entries[1]
	assert.Equal(t, "info1", e.Message)
	assert.Equal(t, log.InfoLevel, e.Level)

	e = h.Entries[2]
	assert.Equal(t, "warn1", e.Message)
	assert.Equal(t, log.WarnLevel, e.Level)

	e = h.Entries[3]
	assert.Equal(t, "error1", e.Message)
	assert.Equal(t, log.ErrorLevel, e.Level)
}

func TestContext(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	ctx := context.Background()
	ctx = log.NewContext(ctx, log.WithFields(log.Fields{"request_id": 123}))

	logger := log.FromContext(ctx)
	logger.Warnf("request test")

	assert.Equal(t, 1, len(h.Entries))

	e := h.Entries[0]
	assert.Equal(t, "request test", e.Message)
	assert.Equal(t, log.WarnLevel, e.Level)
	assert.Equal(t, log.Fields{"request_id": 123}, e.Fields)
}

func TestWithFields(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	logger := log.WithFields(log.Fields{"file": "sloth.png"})
	logger.Info("upload complete")
	logger1 := logger.WithField("source", "machine1")
	logger1.Debug("uploading")
	logger.Warn("warning la")

	assert.Equal(t, 3, len(h.Entries))

	// info
	e := h.Entries[0]
	assert.Equal(t, "upload complete", e.Message)
	assert.Equal(t, log.InfoLevel, e.Level)
	assert.Equal(t, log.Fields{"file": "sloth.png"}, e.Fields)

	// debug
	e = h.Entries[1]
	assert.Equal(t, "uploading", e.Message)
	assert.Equal(t, log.DebugLevel, e.Level)
	assert.Equal(t, log.Fields{"file": "sloth.png", "source": "machine1"}, e.Fields)

	// warn
	e = h.Entries[2]
	assert.Equal(t, "warning la", e.Message)
	assert.Equal(t, log.WarnLevel, e.Level)
	assert.Equal(t, log.Fields{"file": "sloth.png"}, e.Fields)
}

func TestWithField(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	logger := log.WithField("file", "sloth.png").WithField("user", "Tobi")
	logger.Debug("uploading")
	logger.Info("upload complete")

	assert.Equal(t, 2, len(h.Entries))

	e := h.Entries[0]
	assert.Equal(t, "uploading", e.Message)
	assert.Equal(t, log.DebugLevel, e.Level)
	assert.Equal(t, log.Fields{"file": "sloth.png", "user": "Tobi"}, e.Fields)
}

func TestWithError(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	err := errors.New("something bad happened")
	log.WithError(err).Errorf("too bad %s", err.Error())

	err = nil
	log.WithError(err).Error("err is nil")

	assert.Equal(t, 2, len(h.Entries))

	e := h.Entries[0]
	assert.Equal(t, "too bad something bad happened", e.Message)
	assert.Equal(t, log.ErrorLevel, e.Level)
	assert.Equal(t, log.Fields{"error": "something bad happened"}, e.Fields)

	e = h.Entries[1]
	assert.Equal(t, "err is nil", e.Message)
	assert.Equal(t, log.ErrorLevel, e.Level)
	assert.Equal(t, log.Fields{}, e.Fields)
}

func TestTrace(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	func() (err error) {
		defer log.Trace("upload").Stop()
		return nil
	}()

	assert.Equal(t, 1, len(h.Entries))

	e := h.Entries[0]
	assert.Equal(t, "upload", e.Message)
	assert.Equal(t, log.InfoLevel, e.Level)
	assert.IsType(t, e.Fields["duration"], "0")

}

func TestWithDefaultFields(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	log.WithDefaultFields(log.Fields{
		"app_id": "santa",
		"env":    "dev",
	})
	log.Info("upload complete")

	logger := log.WithFields(log.Fields{"file": "sloth.png"})
	logger.Debugf("debug test")

	assert.Equal(t, 2, len(h.Entries))

	e := h.Entries[0]
	assert.Equal(t, "upload complete", e.Message)
	assert.Equal(t, log.InfoLevel, e.Level)
	assert.Equal(t, e.Fields, log.Fields{"app_id": "santa", "env": "dev"})

	e = h.Entries[1]
	assert.Equal(t, "debug test", e.Message)
	assert.Equal(t, log.DebugLevel, e.Level)
	assert.Equal(t, e.Fields, log.Fields{"app_id": "santa", "env": "dev", "file": "sloth.png"})

}

func TestGoroutineSafe(t *testing.T) {
	log.WithDefaultFields(log.Fields{
		"app_id": "santa",
		"env":    "dev",
	})

	logger := log.WithFields(log.Fields{
		"request_id": "abc",
	})

	// logger1 := logger.WithFields(log.Fields{
	// 	"request_id_1": "abc",
	// })

	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		defer wg.Done()
		logger.WithFields(log.Fields{
			"name": "xyz",
		})
	}()
	go func() {
		defer wg.Done()
		logger.WithFields(log.Fields{
			"name": "abc",
		})
	}()
	wg.Wait()

}
<commit_msg>update unit test<commit_after>package log_test

import (
	"context"
	"errors"
	"sync"
	"testing"

	"github.com/jasonsoft/log"
	"github.com/jasonsoft/log/handlers/memory"
	"github.com/stretchr/testify/assert"
)

func TestNoHandler(t *testing.T) {
	log.Info("no handler 1")
	log.Warnf("no handler 2")

	logger := log.FromContext(context.Background())
	logger.Infof("no handler 3")
}

func TestPrintf(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	log.Infof("logged in %s", "Tobi")

	e := h.Entries[0]
	assert.Equal(t, "logged in Tobi", e.Message)
	assert.Equal(t, log.InfoLevel, e.Level)
}

func TestFlush(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.GetLevelsFromMinLevel("debug")...)

	log.Debugf("logged in")

	assert.Equal(t, 1, len(h.Entries))

	log.Flush()
	assert.Equal(t, 0, len(h.Entries))
}

func TestLevels(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.DebugLevel, log.InfoLevel, log.WarnLevel, log.ErrorLevel)

	log.Debug("debug1")
	log.Info("info1")
	log.Warn("warn1")
	log.Error("error1")

	assert.Equal(t, 4, len(h.Entries))

	e := h.Entries[0]
	assert.Equal(t, "debug1", e.Message)
	assert.Equal(t, log.DebugLevel, e.Level)

	e = h.Entries[1]
	assert.Equal(t, "info1", e.Message)
	assert.Equal(t, log.InfoLevel, e.Level)

	e = h.Entries[2]
	assert.Equal(t, "warn1", e.Message)
	assert.Equal(t, log.WarnLevel, e.Level)

	e = h.Entries[3]
	assert.Equal(t, "error1", e.Message)
	assert.Equal(t, log.ErrorLevel, e.Level)
}

func TestContext(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	ctx := context.Background()
	ctx = log.NewContext(ctx, log.WithFields(log.Fields{"request_id": 123}))

	logger := log.FromContext(ctx)
	logger.Warnf("request test")

	assert.Equal(t, 1, len(h.Entries))

	e := h.Entries[0]
	assert.Equal(t, "request test", e.Message)
	assert.Equal(t, log.WarnLevel, e.Level)
	assert.Equal(t, log.Fields{"request_id": 123}, e.Fields)
}

func TestWithFields(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	logger := log.WithFields(log.Fields{"file": "sloth.png"})
	logger.Info("upload complete")
	logger1 := logger.WithField("source", "machine1")
	logger1.Debug("uploading")
	logger.Warn("warning la")

	assert.Equal(t, 3, len(h.Entries))

	// info
	e := h.Entries[0]
	assert.Equal(t, "upload complete", e.Message)
	assert.Equal(t, log.InfoLevel, e.Level)
	assert.Equal(t, log.Fields{"file": "sloth.png"}, e.Fields)

	// debug
	e = h.Entries[1]
	assert.Equal(t, "uploading", e.Message)
	assert.Equal(t, log.DebugLevel, e.Level)
	assert.Equal(t, log.Fields{"file": "sloth.png", "source": "machine1"}, e.Fields)

	// warn
	e = h.Entries[2]
	assert.Equal(t, "warning la", e.Message)
	assert.Equal(t, log.WarnLevel, e.Level)
	assert.Equal(t, log.Fields{"file": "sloth.png"}, e.Fields)
}

func TestWithField(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	logger := log.WithField("file", "sloth.png").WithField("user", "Tobi")
	logger.Debug("uploading")
	logger.Info("upload complete")

	assert.Equal(t, 2, len(h.Entries))

	e := h.Entries[0]
	assert.Equal(t, "uploading", e.Message)
	assert.Equal(t, log.DebugLevel, e.Level)
	assert.Equal(t, log.Fields{"file": "sloth.png", "user": "Tobi"}, e.Fields)
}

func TestWithError(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	err := errors.New("something bad happened")
	log.WithError(err).Errorf("too bad %s", err.Error())

	err = nil
	log.WithError(err).Error("err is nil")

	assert.Equal(t, 2, len(h.Entries))

	e := h.Entries[0]
	assert.Equal(t, "too bad something bad happened", e.Message)
	assert.Equal(t, log.ErrorLevel, e.Level)
	assert.Equal(t, log.Fields{"error": "something bad happened"}, e.Fields)

	e = h.Entries[1]
	assert.Equal(t, "err is nil", e.Message)
	assert.Equal(t, log.ErrorLevel, e.Level)
	assert.Equal(t, log.Fields{}, e.Fields)
}

func TestTrace(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	func() (err error) {
		defer log.Trace("upload").Stop()
		return nil
	}()

	assert.Equal(t, 1, len(h.Entries))

	e := h.Entries[0]
	assert.Equal(t, "upload", e.Message)
	assert.Equal(t, log.InfoLevel, e.Level)
	assert.IsType(t, e.Fields["duration"], "0")

}

func TestWithDefaultFields(t *testing.T) {
	h := memory.New()
	log.RegisterHandler(h, log.AllLevels...)

	log.WithDefaultFields(log.Fields{
		"app_id": "santa",
		"env":    "dev",
	})
	log.Info("upload complete")

	logger := log.WithFields(log.Fields{"file": "sloth.png"})
	logger.Debugf("debug test")

	assert.Equal(t, 2, len(h.Entries))

	e := h.Entries[0]
	assert.Equal(t, "upload complete", e.Message)
	assert.Equal(t, log.InfoLevel, e.Level)
	assert.Equal(t, e.Fields, log.Fields{"app_id": "santa", "env": "dev"})

	e = h.Entries[1]
	assert.Equal(t, "debug test", e.Message)
	assert.Equal(t, log.DebugLevel, e.Level)
	assert.Equal(t, e.Fields, log.Fields{"app_id": "santa", "env": "dev", "file": "sloth.png"})

}

func TestGoroutineSafe(t *testing.T) {
	log.WithDefaultFields(log.Fields{
		"app_id": "santa",
		"env":    "dev",
	})

	logger := log.WithFields(log.Fields{
		"request_id": "abc",
	})

	// logger1 := logger.WithFields(log.Fields{
	// 	"request_id_1": "abc",
	// })

	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		defer wg.Done()
		logger.WithFields(log.Fields{
			"name": "xyz",
		})
	}()
	go func() {
		defer wg.Done()
		logger.WithFields(log.Fields{
			"name": "abc",
		})
	}()
	wg.Wait()

}
<|endoftext|>


<commit_before>package libp2pquic

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net"

	"github.com/libp2p/go-libp2p-core/connmgr"
	n "github.com/libp2p/go-libp2p-core/network"

	"github.com/minio/sha256-simd"
	"golang.org/x/crypto/hkdf"

	logging "github.com/ipfs/go-log"
	ic "github.com/libp2p/go-libp2p-core/crypto"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/pnet"
	tpt "github.com/libp2p/go-libp2p-core/transport"
	p2ptls "github.com/libp2p/go-libp2p-tls"
	quic "github.com/lucas-clemente/quic-go"
	ma "github.com/multiformats/go-multiaddr"
	mafmt "github.com/multiformats/go-multiaddr-fmt"
	manet "github.com/multiformats/go-multiaddr/net"
)

var log = logging.Logger("quic-transport")

var quicConfig = &quic.Config{
	MaxIncomingStreams:                    1000,
	MaxIncomingUniStreams:                 -1,             // disable unidirectional streams
	MaxReceiveStreamFlowControlWindow:     10 * (1 << 20), // 10 MB
	MaxReceiveConnectionFlowControlWindow: 15 * (1 << 20), // 15 MB
	AcceptToken: func(clientAddr net.Addr, _ *quic.Token) bool {
		// TODO(#6): require source address validation when under load
		return true
	},
	KeepAlive: true,
}

const statelessResetKeyInfo = "libp2p quic stateless reset key"
const errorCodeConnectionGating = 0x47415445 // GATE in ASCII

type connManager struct {
	reuseUDP4 *reuse
	reuseUDP6 *reuse
}

func newConnManager(gater connmgr.ConnectionGater) (*connManager, error) {
	reuseUDP4 := newReuse(gater)
	reuseUDP6 := newReuse(gater)

	return &connManager{
		reuseUDP4: reuseUDP4,
		reuseUDP6: reuseUDP6,
	}, nil
}

func (c *connManager) getReuse(network string) (*reuse, error) {
	switch network {
	case "udp4":
		return c.reuseUDP4, nil
	case "udp6":
		return c.reuseUDP6, nil
	default:
		return nil, errors.New("invalid network: must be either udp4 or udp6")
	}
}

func (c *connManager) Listen(network string, laddr *net.UDPAddr) (*reuseConn, error) {
	reuse, err := c.getReuse(network)
	if err != nil {
		return nil, err
	}
	return reuse.Listen(network, laddr)
}

func (c *connManager) Dial(network string, raddr *net.UDPAddr) (*reuseConn, error) {
	reuse, err := c.getReuse(network)
	if err != nil {
		return nil, err
	}
	return reuse.Dial(network, raddr)
}

// The Transport implements the tpt.Transport interface for QUIC connections.
type transport struct {
	privKey      ic.PrivKey
	localPeer    peer.ID
	identity     *p2ptls.Identity
	connManager  *connManager
	serverConfig *quic.Config
	clientConfig *quic.Config
	gater        connmgr.ConnectionGater
}

var _ tpt.Transport = &transport{}

// NewTransport creates a new QUIC transport
func NewTransport(key ic.PrivKey, psk pnet.PSK, gater connmgr.ConnectionGater) (tpt.Transport, error) {
	if len(psk) > 0 {
		log.Error("QUIC doesn't support private networks yet.")
		return nil, errors.New("QUIC doesn't support private networks yet")
	}
	localPeer, err := peer.IDFromPrivateKey(key)
	if err != nil {
		return nil, err
	}
	identity, err := p2ptls.NewIdentity(key)
	if err != nil {
		return nil, err
	}
	connManager, err := newConnManager(gater)
	if err != nil {
		return nil, err
	}
	config := quicConfig.Clone()
	keyBytes, err := key.Raw()
	if err != nil {
		return nil, err
	}
	keyReader := hkdf.New(sha256.New, keyBytes, nil, []byte(statelessResetKeyInfo))
	config.StatelessResetKey = make([]byte, 32)
	if _, err := io.ReadFull(keyReader, config.StatelessResetKey); err != nil {
		return nil, err
	}
	config.Tracer = tracer

	return &transport{
		privKey:      key,
		localPeer:    localPeer,
		identity:     identity,
		connManager:  connManager,
		serverConfig: config,
		clientConfig: config.Clone(),
		gater:        gater,
	}, nil
}

// Dial dials a new QUIC connection
func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tpt.CapableConn, error) {
	network, host, err := manet.DialArgs(raddr)
	if err != nil {
		return nil, err
	}
	addr, err := net.ResolveUDPAddr(network, host)
	if err != nil {
		return nil, err
	}
	remoteMultiaddr, err := toQuicMultiaddr(addr)
	if err != nil {
		return nil, err
	}
	tlsConf, keyCh := t.identity.ConfigForPeer(p)
	pconn, err := t.connManager.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	sess, err := quic.DialContext(ctx, pconn, addr, host, tlsConf, t.clientConfig)
	if err != nil {
		pconn.DecreaseCount()
		return nil, err
	}
	// Should be ready by this point, don't block.
	var remotePubKey ic.PubKey
	select {
	case remotePubKey = <-keyCh:
	default:
	}
	if remotePubKey == nil {
		pconn.DecreaseCount()
		return nil, errors.New("go-libp2p-quic-transport BUG: expected remote pub key to be set")
	}
	go func() {
		<-sess.Context().Done()
		pconn.DecreaseCount()
	}()

	localMultiaddr, err := toQuicMultiaddr(pconn.LocalAddr())
	if err != nil {
		sess.CloseWithError(0, "")
		return nil, err
	}
	conn := &conn{
		sess:            sess,
		transport:       t,
		privKey:         t.privKey,
		localPeer:       t.localPeer,
		localMultiaddr:  localMultiaddr,
		remotePubKey:    remotePubKey,
		remotePeerID:    p,
		remoteMultiaddr: remoteMultiaddr,
	}
	if t.gater != nil && !(t.gater.InterceptAccept(conn) && t.gater.InterceptSecured(n.DirOutbound, p, conn)) {
		sess.CloseWithError(errorCodeConnectionGating, "connection gated")
		return nil, fmt.Errorf("secured connection gated")
	}
	return conn, nil
}

// Don't use mafmt.QUIC as we don't want to dial DNS addresses. Just /ip{4,6}/udp/quic
var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Base(ma.P_QUIC))

// CanDial determines if we can dial to an address
func (t *transport) CanDial(addr ma.Multiaddr) bool {
	return dialMatcher.Matches(addr)
}

// Listen listens for new QUIC connections on the passed multiaddr.
func (t *transport) Listen(addr ma.Multiaddr) (tpt.Listener, error) {
	lnet, host, err := manet.DialArgs(addr)
	if err != nil {
		return nil, err
	}
	laddr, err := net.ResolveUDPAddr(lnet, host)
	if err != nil {
		return nil, err
	}
	conn, err := t.connManager.Listen(lnet, laddr)
	if err != nil {
		return nil, err
	}
	return newListener(conn, t, t.localPeer, t.privKey, t.identity)
}

// Proxy returns true if this transport proxies.
func (t *transport) Proxy() bool {
	return false
}

// Protocols returns the set of protocols handled by this transport.
func (t *transport) Protocols() []int {
	return []int{ma.P_QUIC}
}

func (t *transport) String() string {
	return "QUIC"
}
<commit_msg>fix reuseConn counting when creating the quic.Listener errors<commit_after>package libp2pquic

import (
	"context"
	"errors"
	"fmt"
	"io"
	"net"

	"github.com/libp2p/go-libp2p-core/connmgr"
	n "github.com/libp2p/go-libp2p-core/network"

	"github.com/minio/sha256-simd"
	"golang.org/x/crypto/hkdf"

	logging "github.com/ipfs/go-log"
	ic "github.com/libp2p/go-libp2p-core/crypto"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/pnet"
	tpt "github.com/libp2p/go-libp2p-core/transport"
	p2ptls "github.com/libp2p/go-libp2p-tls"
	quic "github.com/lucas-clemente/quic-go"
	ma "github.com/multiformats/go-multiaddr"
	mafmt "github.com/multiformats/go-multiaddr-fmt"
	manet "github.com/multiformats/go-multiaddr/net"
)

var log = logging.Logger("quic-transport")

var quicConfig = &quic.Config{
	MaxIncomingStreams:                    1000,
	MaxIncomingUniStreams:                 -1,             // disable unidirectional streams
	MaxReceiveStreamFlowControlWindow:     10 * (1 << 20), // 10 MB
	MaxReceiveConnectionFlowControlWindow: 15 * (1 << 20), // 15 MB
	AcceptToken: func(clientAddr net.Addr, _ *quic.Token) bool {
		// TODO(#6): require source address validation when under load
		return true
	},
	KeepAlive: true,
}

const statelessResetKeyInfo = "libp2p quic stateless reset key"
const errorCodeConnectionGating = 0x47415445 // GATE in ASCII

type connManager struct {
	reuseUDP4 *reuse
	reuseUDP6 *reuse
}

func newConnManager(gater connmgr.ConnectionGater) (*connManager, error) {
	reuseUDP4 := newReuse(gater)
	reuseUDP6 := newReuse(gater)

	return &connManager{
		reuseUDP4: reuseUDP4,
		reuseUDP6: reuseUDP6,
	}, nil
}

func (c *connManager) getReuse(network string) (*reuse, error) {
	switch network {
	case "udp4":
		return c.reuseUDP4, nil
	case "udp6":
		return c.reuseUDP6, nil
	default:
		return nil, errors.New("invalid network: must be either udp4 or udp6")
	}
}

func (c *connManager) Listen(network string, laddr *net.UDPAddr) (*reuseConn, error) {
	reuse, err := c.getReuse(network)
	if err != nil {
		return nil, err
	}
	return reuse.Listen(network, laddr)
}

func (c *connManager) Dial(network string, raddr *net.UDPAddr) (*reuseConn, error) {
	reuse, err := c.getReuse(network)
	if err != nil {
		return nil, err
	}
	return reuse.Dial(network, raddr)
}

// The Transport implements the tpt.Transport interface for QUIC connections.
type transport struct {
	privKey      ic.PrivKey
	localPeer    peer.ID
	identity     *p2ptls.Identity
	connManager  *connManager
	serverConfig *quic.Config
	clientConfig *quic.Config
	gater        connmgr.ConnectionGater
}

var _ tpt.Transport = &transport{}

// NewTransport creates a new QUIC transport
func NewTransport(key ic.PrivKey, psk pnet.PSK, gater connmgr.ConnectionGater) (tpt.Transport, error) {
	if len(psk) > 0 {
		log.Error("QUIC doesn't support private networks yet.")
		return nil, errors.New("QUIC doesn't support private networks yet")
	}
	localPeer, err := peer.IDFromPrivateKey(key)
	if err != nil {
		return nil, err
	}
	identity, err := p2ptls.NewIdentity(key)
	if err != nil {
		return nil, err
	}
	connManager, err := newConnManager(gater)
	if err != nil {
		return nil, err
	}
	config := quicConfig.Clone()
	keyBytes, err := key.Raw()
	if err != nil {
		return nil, err
	}
	keyReader := hkdf.New(sha256.New, keyBytes, nil, []byte(statelessResetKeyInfo))
	config.StatelessResetKey = make([]byte, 32)
	if _, err := io.ReadFull(keyReader, config.StatelessResetKey); err != nil {
		return nil, err
	}
	config.Tracer = tracer

	return &transport{
		privKey:      key,
		localPeer:    localPeer,
		identity:     identity,
		connManager:  connManager,
		serverConfig: config,
		clientConfig: config.Clone(),
		gater:        gater,
	}, nil
}

// Dial dials a new QUIC connection
func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tpt.CapableConn, error) {
	network, host, err := manet.DialArgs(raddr)
	if err != nil {
		return nil, err
	}
	addr, err := net.ResolveUDPAddr(network, host)
	if err != nil {
		return nil, err
	}
	remoteMultiaddr, err := toQuicMultiaddr(addr)
	if err != nil {
		return nil, err
	}
	tlsConf, keyCh := t.identity.ConfigForPeer(p)
	pconn, err := t.connManager.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	sess, err := quic.DialContext(ctx, pconn, addr, host, tlsConf, t.clientConfig)
	if err != nil {
		pconn.DecreaseCount()
		return nil, err
	}
	// Should be ready by this point, don't block.
	var remotePubKey ic.PubKey
	select {
	case remotePubKey = <-keyCh:
	default:
	}
	if remotePubKey == nil {
		pconn.DecreaseCount()
		return nil, errors.New("go-libp2p-quic-transport BUG: expected remote pub key to be set")
	}
	go func() {
		<-sess.Context().Done()
		pconn.DecreaseCount()
	}()

	localMultiaddr, err := toQuicMultiaddr(pconn.LocalAddr())
	if err != nil {
		sess.CloseWithError(0, "")
		return nil, err
	}
	conn := &conn{
		sess:            sess,
		transport:       t,
		privKey:         t.privKey,
		localPeer:       t.localPeer,
		localMultiaddr:  localMultiaddr,
		remotePubKey:    remotePubKey,
		remotePeerID:    p,
		remoteMultiaddr: remoteMultiaddr,
	}
	if t.gater != nil && !(t.gater.InterceptAccept(conn) && t.gater.InterceptSecured(n.DirOutbound, p, conn)) {
		sess.CloseWithError(errorCodeConnectionGating, "connection gated")
		return nil, fmt.Errorf("secured connection gated")
	}
	return conn, nil
}

// Don't use mafmt.QUIC as we don't want to dial DNS addresses. Just /ip{4,6}/udp/quic
var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Base(ma.P_QUIC))

// CanDial determines if we can dial to an address
func (t *transport) CanDial(addr ma.Multiaddr) bool {
	return dialMatcher.Matches(addr)
}

// Listen listens for new QUIC connections on the passed multiaddr.
func (t *transport) Listen(addr ma.Multiaddr) (tpt.Listener, error) {
	lnet, host, err := manet.DialArgs(addr)
	if err != nil {
		return nil, err
	}
	laddr, err := net.ResolveUDPAddr(lnet, host)
	if err != nil {
		return nil, err
	}
	conn, err := t.connManager.Listen(lnet, laddr)
	if err != nil {
		return nil, err
	}
	ln, err := newListener(conn, t, t.localPeer, t.privKey, t.identity)
	if err != nil {
		conn.DecreaseCount()
		return nil, err
	}
	return ln, nil
}

// Proxy returns true if this transport proxies.
func (t *transport) Proxy() bool {
	return false
}

// Protocols returns the set of protocols handled by this transport.
func (t *transport) Protocols() []int {
	return []int{ma.P_QUIC}
}

func (t *transport) String() string {
	return "QUIC"
}
<|endoftext|>


<commit_before>//+build mage

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/shabbyrobe/gocovmerge"
	"golang.org/x/tools/cover"
)

func Cover() {
	pkgs := goList()

	var files []string

	for idx, pkg := range pkgs {
		covFile := fmt.Sprintf("cover-%d.out", idx)
		files = append(files, covFile)
		cmd := exec.Command("go", "test",
			fmt.Sprintf("-coverprofile=%s", covFile),
			fmt.Sprintf("-coverpkg=%s", strings.Join(pkgs, ",")),
			pkg,
		)
		if err := cmd.Run(); err != nil {
			panic(err)
		}
	}

	var merged []*cover.Profile
	for _, file := range files {
		profiles, err := cover.ParseProfiles(file)
		if err != nil {
			panic(fmt.Errorf("failed to parse profiles: %v", err))
		}
		for _, p := range profiles {
			merged = gocovmerge.AddProfile(merged, p)
		}
		os.Remove(file)
	}

	gocovmerge.DumpProfiles(merged, os.Stdout)
}

func goList() (pkgs []string) {
	cmd := exec.Command("go", "list", "./...")

	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	if err := cmd.Run(); err != nil {
		panic(err)
	}

	scanner := bufio.NewScanner(&stdout)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if len(line) == 0 {
			continue
		}
		pkgs = append(pkgs, line)
	}
	return pkgs
}
<commit_msg>Add some explanatory comments to coverage script<commit_after>//+build mage

// Run using mage: https://github.com/magefile/mage
//
// Example invocation:
//   mage cover
//
package main

import (
	"bufio"
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/shabbyrobe/gocovmerge"
	"golang.org/x/tools/cover"
)

// Cover collects true code coverage for all packages in gofakes3.
// It does so by running 'go test' for each child package (enumerated by
// 'go list ./...') with the '-coverpkg' flag, populated with the same
// 'go list'.
func Cover() {
	pkgs := goList()

	var files []string

	for idx, pkg := range pkgs {
		// FIXME: temp files
		covFile := fmt.Sprintf("cover-%d.out", idx)
		files = append(files, covFile)
		cmd := exec.Command("go", "test",
			fmt.Sprintf("-coverprofile=%s", covFile),
			fmt.Sprintf("-coverpkg=%s", strings.Join(pkgs, ",")),
			pkg,
		)
		if err := cmd.Run(); err != nil {
			panic(err)
		}
	}

	var merged []*cover.Profile
	for _, file := range files {
		profiles, err := cover.ParseProfiles(file)
		if err != nil {
			panic(fmt.Errorf("failed to parse profiles: %v", err))
		}
		for _, p := range profiles {
			merged = gocovmerge.AddProfile(merged, p)
		}
		os.Remove(file)
	}

	gocovmerge.DumpProfiles(merged, os.Stdout)
}

func goList() (pkgs []string) {
	cmd := exec.Command("go", "list", "./...")

	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	if err := cmd.Run(); err != nil {
		panic(err)
	}

	scanner := bufio.NewScanner(&stdout)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if len(line) == 0 {
			continue
		}
		pkgs = append(pkgs, line)
	}
	return pkgs
}
<|endoftext|>


<commit_before>package config

import (
	"io/ioutil"
	"path/filepath"
	"strconv"

	"log"

	"time"

	"github.com/BurntSushi/toml"
)

// Config holds all system-wide settings
type Config struct {
	secret                        string
	serverAddr                    string
	serverContentPath             string
	serverImportPath              string
	importQueueDir                string
	indexDir                      string
	indexFile                     string
	indexRefreshIntervalInMinutes int
	providerRegistryDir           string
	clientCacheMaxAgeInSeconds    int
}

// UnmarshalTOML provides a custom "unmarshaller" so we can keep our fields
// private/immutable from outside this package
func (c *Config) UnmarshalTOML(data interface{}) error {
	d := data.(map[string]interface{})
	// EEEK: https://github.com/golang/go/issues/19752
	c.maybeUpdateConfig(d, "Secret", func(val interface{}) { c.secret = val.(string) })
	c.maybeUpdateConfig(d, "ServerAddr", func(val interface{}) { c.serverAddr = val.(string) })
	c.maybeUpdateConfig(d, "ServerContentPath", func(val interface{}) { c.serverContentPath = val.(string) })
	c.maybeUpdateConfig(d, "ServerImportPath", func(val interface{}) { c.serverImportPath = val.(string) })
	c.maybeUpdateConfig(d, "IndexQueueDir", func(val interface{}) { c.importQueueDir = val.(string) })
	c.maybeUpdateConfig(d, "IndexDir", func(val interface{}) { c.indexDir = val.(string) })
	c.maybeUpdateConfig(d, "IndexFile", func(val interface{}) { c.indexFile = val.(string) })
	c.maybeUpdateConfig(d, "IndexRefreshIntervalInMinutes", func(val interface{}) { c.indexRefreshIntervalInMinutes = val.(int) })
	c.maybeUpdateConfig(d, "ProviderRegistryDir", func(val interface{}) { c.providerRegistryDir = val.(string) })
	c.maybeUpdateConfig(d, "ClientCacheMaxAgeInSeconds", func(val interface{}) { c.clientCacheMaxAgeInSeconds = val.(int) })
	return nil
}

func (c *Config) maybeUpdateConfig(data map[string]interface{}, name string, updater func(interface{})) {
	if val, ok := data[name]; ok {
		updater(val)
	}
}

// Get returns the configuration based on config.toml, if present.
// Default values are provided for all keys not present, except Secret.
func Get() *Config {
	c := &Config{
		serverAddr:                    ":8080",
		serverContentPath:             "/crec/content",
		serverImportPath:              "/crec/import",
		importQueueDir:                "import",
		indexDir:                      "index",
		indexFile:                     "crec.bleve",
		indexRefreshIntervalInMinutes: 5,
		clientCacheMaxAgeInSeconds:    120,
		providerRegistryDir:           "crec-registry"}

	bytes, err := ioutil.ReadFile(filepath.FromSlash("config.toml"))
	if err != nil {
		return c
	}

	_, err = toml.Decode(string(bytes), &c)
	if err != nil {
		log.Println("Failed to read provided config (using default settings): ", err)
	}

	return c
}

// GetAddr returns the address and port for starting up the server .e.g :8080
func (c *Config) GetAddr() string {
	return c.serverAddr
}

// GetContentPath returns the URL path to handle content requests e.g. /crec/content
func (c *Config) GetContentPath() string {
	return c.serverContentPath
}

// GetImportPath returns the URL path to handle import requests e.g. /crec/import
func (c *Config) GetImportPath() string {
	return c.serverImportPath
}

// GetImportQueueDir returns the directory path to store imported content e.g. import
func (c *Config) GetImportQueueDir() string {
	return c.importQueueDir
}

// GetIndexDir returns the directory path to store indexes e.g. index
func (c *Config) GetIndexDir() string {
	return c.indexDir
}

// GetIndexFile returns the path to store the index e.g. crec.bleve
func (c *Config) GetIndexFile() string {
	return c.indexDir
}

// GetProviderRegistryDir returns the directy path containing provider configurations e.g. crec-registry
func (c *Config) GetProviderRegistryDir() string {
	return c.providerRegistryDir
}

// GetSecret returns the configured secret to generate API keys
func (c *Config) GetSecret() string {
	return c.secret
}

// GetIndexRefreshInterval returns the configured refresh interval for the content index
func (c *Config) GetIndexRefreshInterval() time.Duration {
	return time.Minute * time.Duration(int64(c.indexRefreshIntervalInMinutes))
}

// GetClientCacheMaxAge returns the configured cache control max age
func (c *Config) GetClientCacheMaxAge() string {
	return strconv.Itoa(c.clientCacheMaxAgeInSeconds)
}

// Create a config instance from the provided parameters
func Create(secret string) *Config {
	return &Config{secret: secret}
}
<commit_msg>Read server port from env, if available<commit_after>package config

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"

	"log"

	"time"

	"github.com/BurntSushi/toml"
)

// Config holds all system-wide settings
type Config struct {
	secret                        string
	serverAddr                    string
	serverContentPath             string
	serverImportPath              string
	importQueueDir                string
	indexDir                      string
	indexFile                     string
	indexRefreshIntervalInMinutes int
	providerRegistryDir           string
	clientCacheMaxAgeInSeconds    int
}

// UnmarshalTOML provides a custom "unmarshaller" so we can keep our fields
// private/immutable from outside this package
func (c *Config) UnmarshalTOML(data interface{}) error {
	d := data.(map[string]interface{})
	// EEEK: https://github.com/golang/go/issues/19752
	c.maybeUpdateConfig(d, "Secret", func(val interface{}) { c.secret = val.(string) })
	c.maybeUpdateConfig(d, "ServerAddr", func(val interface{}) { c.serverAddr = val.(string) })
	c.maybeUpdateConfig(d, "ServerContentPath", func(val interface{}) { c.serverContentPath = val.(string) })
	c.maybeUpdateConfig(d, "ServerImportPath", func(val interface{}) { c.serverImportPath = val.(string) })
	c.maybeUpdateConfig(d, "IndexQueueDir", func(val interface{}) { c.importQueueDir = val.(string) })
	c.maybeUpdateConfig(d, "IndexDir", func(val interface{}) { c.indexDir = val.(string) })
	c.maybeUpdateConfig(d, "IndexFile", func(val interface{}) { c.indexFile = val.(string) })
	c.maybeUpdateConfig(d, "IndexRefreshIntervalInMinutes", func(val interface{}) { c.indexRefreshIntervalInMinutes = val.(int) })
	c.maybeUpdateConfig(d, "ProviderRegistryDir", func(val interface{}) { c.providerRegistryDir = val.(string) })
	c.maybeUpdateConfig(d, "ClientCacheMaxAgeInSeconds", func(val interface{}) { c.clientCacheMaxAgeInSeconds = val.(int) })
	return nil
}

func (c *Config) maybeUpdateConfig(data map[string]interface{}, name string, updater func(interface{})) {
	if val, ok := data[name]; ok {
		updater(val)
	}
}

// Get returns the configuration based on config.toml, if present.
// Default values are provided for all keys not present, except Secret.
func Get() *Config {
	c := &Config{
		serverAddr:                    ":8080",
		serverContentPath:             "/crec/content",
		serverImportPath:              "/crec/import",
		importQueueDir:                "import",
		indexDir:                      "index",
		indexFile:                     "crec.bleve",
		indexRefreshIntervalInMinutes: 5,
		clientCacheMaxAgeInSeconds:    120,
		providerRegistryDir:           "crec-registry"}

	port := os.Getenv("PORT")
	if port != "" {
		c.serverAddr = ":" + port
	}

	bytes, err := ioutil.ReadFile(filepath.FromSlash("config.toml"))
	if err != nil {
		return c
	}

	_, err = toml.Decode(string(bytes), &c)
	if err != nil {
		log.Println("Failed to read provided config (using default settings): ", err)
	}

	return c
}

// GetAddr returns the address and port for starting up the server .e.g :8080
func (c *Config) GetAddr() string {
	return c.serverAddr
}

// GetContentPath returns the URL path to handle content requests e.g. /crec/content
func (c *Config) GetContentPath() string {
	return c.serverContentPath
}

// GetImportPath returns the URL path to handle import requests e.g. /crec/import
func (c *Config) GetImportPath() string {
	return c.serverImportPath
}

// GetImportQueueDir returns the directory path to store imported content e.g. import
func (c *Config) GetImportQueueDir() string {
	return c.importQueueDir
}

// GetIndexDir returns the directory path to store indexes e.g. index
func (c *Config) GetIndexDir() string {
	return c.indexDir
}

// GetIndexFile returns the path to store the index e.g. crec.bleve
func (c *Config) GetIndexFile() string {
	return c.indexDir
}

// GetProviderRegistryDir returns the directy path containing provider configurations e.g. crec-registry
func (c *Config) GetProviderRegistryDir() string {
	return c.providerRegistryDir
}

// GetSecret returns the configured secret to generate API keys
func (c *Config) GetSecret() string {
	return c.secret
}

// GetIndexRefreshInterval returns the configured refresh interval for the content index
func (c *Config) GetIndexRefreshInterval() time.Duration {
	return time.Minute * time.Duration(int64(c.indexRefreshIntervalInMinutes))
}

// GetClientCacheMaxAge returns the configured cache control max age
func (c *Config) GetClientCacheMaxAge() string {
	return strconv.Itoa(c.clientCacheMaxAgeInSeconds)
}

// Create a config instance from the provided parameters
func Create(secret string) *Config {
	return &Config{secret: secret}
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

// Config is the parent configuration struct and includes fields for single
// configurations of a database, cookie, and SMTP connection.
type Config struct {
	HTTPS       bool           `json:"https"`
	Domain      string         `json:"domain"`
	ProxyDomain string         `json:"proxy_domain"`
	Port        int            `json:"port"`
	ProxyPort   int            `json:"proxy_port"`
	TemplateDir string         `json:"templates"`
	StaticDir   string         `json:"static"`
	StaticURL   string         `json:"static_url"`
	SecretKey   string         `json:"secret_key"`
	Database    DatabaseConfig `json:"database"`
	Cookie      CookieConfig   `json:"cookie"`
	SMTP        SMTPConfig     `json:"smtp"`
}

// Address returns the domain:port pair.
func (c Config) Address() string {
	return fmt.Sprintf("%s:%d", c.Domain, c.Port)
}

func (c Config) FullAddress() string {
	protocol := "http"
	if c.HTTPS {
		protocol = "https"
	}

	domain := c.ProxyDomain
	if domain == "" {
		domain = c.Domain
	}

	port := c.ProxyPort
	if port == 0 {
		port = c.Port
	}
	if port == 80 {
		return fmt.Sprintf("%s://%s", protocol, domain)
	}
	return fmt.Sprintf("%s://%s:%d", protocol, domain, port)
}

// Parse will create a Config using the file settings.json in the
// current directory.
func Parse() (Config, error) {
	return ParseFile("./settings.json")
}

// ParseFile will create a Config using the file at the given path.
func ParseFile(filename string) (Config, error) {
	f, err := os.Open(filename)
	if err != nil {
		return Config{}, err
	}
	return parse(f)
}

// TODO What about default values other than the cookie? Leave to user?
func parse(f io.Reader) (Config, error) {
	c := Config{
		Cookie: DefaultCookie,
	}
	contents, err := ioutil.ReadAll(f)
	if err != nil {
		return c, err
	}
	if err = json.Unmarshal(contents, &c); err != nil {
		return c, err
	}
	return c, nil
}

// Default is a basic configuration with insecure values. It will return the
// Address localhost:8080
var Default = Config{
	Cookie:    DefaultCookie,
	Port:      8080,
	StaticURL: "/static/",
}

// DefaultConfig will return a basic configuration with insecure values. It
// allows the specification of a secret key.
func DefaultConfig(key string) Config {
	return Config{
		Cookie:    DefaultCookie,
		Port:      8080,
		SecretKey: key,
		StaticURL: "/static/",
	}
}
<commit_msg>Added media directory and URL to config<commit_after>package config

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

// Config is the parent configuration struct and includes fields for single
// configurations of a database, cookie, and SMTP connection.
type Config struct {
	HTTPS       bool           `json:"https"`
	Domain      string         `json:"domain"`
	ProxyDomain string         `json:"proxy_domain"`
	Port        int            `json:"port"`
	ProxyPort   int            `json:"proxy_port"`
	TemplateDir string         `json:"templates"`
	MediaDir    string         `json:"media"`
	MediaURL    string         `json:"media_url"`
	StaticDir   string         `json:"static"`
	StaticURL   string         `json:"static_url"`
	SecretKey   string         `json:"secret_key"`
	Database    DatabaseConfig `json:"database"`
	Cookie      CookieConfig   `json:"cookie"`
	SMTP        SMTPConfig     `json:"smtp"`
}

// Address returns the domain:port pair.
func (c Config) Address() string {
	return fmt.Sprintf("%s:%d", c.Domain, c.Port)
}

func (c Config) FullAddress() string {
	protocol := "http"
	if c.HTTPS {
		protocol = "https"
	}

	domain := c.ProxyDomain
	if domain == "" {
		domain = c.Domain
	}

	port := c.ProxyPort
	if port == 0 {
		port = c.Port
	}
	if port == 80 {
		return fmt.Sprintf("%s://%s", protocol, domain)
	}
	return fmt.Sprintf("%s://%s:%d", protocol, domain, port)
}

// Parse will create a Config using the file settings.json in the
// current directory.
func Parse() (Config, error) {
	return ParseFile("./settings.json")
}

// ParseFile will create a Config using the file at the given path.
func ParseFile(filename string) (Config, error) {
	f, err := os.Open(filename)
	if err != nil {
		return Config{}, err
	}
	return parse(f)
}

// TODO What about default values other than the cookie? Leave to user?
func parse(f io.Reader) (Config, error) {
	c := Config{
		Cookie: DefaultCookie,
	}
	contents, err := ioutil.ReadAll(f)
	if err != nil {
		return c, err
	}
	if err = json.Unmarshal(contents, &c); err != nil {
		return c, err
	}
	return c, nil
}

// Default is a basic configuration with insecure values. It will return the
// Address localhost:8080
var Default = Config{
	Cookie:    DefaultCookie,
	Port:      8080,
	StaticURL: "/static/",
}

// DefaultConfig will return a basic configuration with insecure values. It
// allows the specification of a secret key.
func DefaultConfig(key string) Config {
	return Config{
		Cookie:    DefaultCookie,
		Port:      8080,
		SecretKey: key,
		StaticURL: "/static/",
	}
}
<|endoftext|>


<commit_before>// Package config collects together all configuration settings
// NOTE: Subject to change, do not rely on this package from outside git-lfs source
package config

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"sync"

	"github.com/ThomsonReutersEikon/go-ntlm/ntlm"
	"github.com/bgentry/go-netrc/netrc"
	"github.com/git-lfs/git-lfs/lfsapi"
	"github.com/git-lfs/git-lfs/tools"
)

var (
	Config                 = New()
	ShowConfigWarnings     = false
	defaultRemote          = "origin"
	gitConfigWarningPrefix = "lfs."
)

// FetchPruneConfig collects together the config options that control fetching and pruning
type FetchPruneConfig struct {
	// The number of days prior to current date for which (local) refs other than HEAD
	// will be fetched with --recent (default 7, 0 = only fetch HEAD)
	FetchRecentRefsDays int `git:"lfs.fetchrecentrefsdays"`
	// Makes the FetchRecentRefsDays option apply to remote refs from fetch source as well (default true)
	FetchRecentRefsIncludeRemotes bool `git:"lfs.fetchrecentremoterefs"`
	// number of days prior to latest commit on a ref that we'll fetch previous
	// LFS changes too (default 0 = only fetch at ref)
	FetchRecentCommitsDays int `git:"lfs.fetchrecentcommitsdays"`
	// Whether to always fetch recent even without --recent
	FetchRecentAlways bool `git:"lfs.fetchrecentalways"`
	// Number of days added to FetchRecent*; data outside combined window will be
	// deleted when prune is run. (default 3)
	PruneOffsetDays int `git:"lfs.pruneoffsetdays"`
	// Always verify with remote before pruning
	PruneVerifyRemoteAlways bool `git:"lfs.pruneverifyremotealways"`
	// Name of remote to check for unpushed and verify checks
	PruneRemoteName string `git:"lfs.pruneremotetocheck"`
}

type Configuration struct {
	// Os provides a `*Environment` used to access to the system's
	// environment through os.Getenv. It is the point of entry for all
	// system environment configuration.
	Os Environment

	// Git provides a `*Environment` used to access to the various levels of
	// `.gitconfig`'s. It is the point of entry for all Git environment
	// configuration.
	Git Environment

	CurrentRemote   string
	NtlmSession     ntlm.ClientSession
	envVars         map[string]string
	envVarsMutex    sync.Mutex
	IsTracingHttp   bool
	IsDebuggingHttp bool
	IsLoggingStats  bool

	loading        sync.Mutex // guards initialization of gitConfig and remotes
	remotes        []string
	extensions     map[string]Extension
	manualEndpoint *lfsapi.Endpoint
	parsedNetrc    netrcfinder
	endpointFinder lfsapi.EndpointFinder
	endpointMu     sync.Mutex
}

func New() *Configuration {
	c := &Configuration{Os: EnvironmentOf(NewOsFetcher())}
	c.Git = &gitEnvironment{config: c}
	initConfig(c)
	return c
}

// Values is a convenience type used to call the NewFromValues function. It
// specifies `Git` and `Env` maps to use as mock values, instead of calling out
// to real `.gitconfig`s and the `os.Getenv` function.
type Values struct {
	// Git and Os are the stand-in maps used to provide values for their
	// respective environments.
	Git, Os map[string]string
}

// NewFrom returns a new `*config.Configuration` that reads both its Git
// and Enviornment-level values from the ones provided instead of the actual
// `.gitconfig` file or `os.Getenv`, respectively.
//
// This method should only be used during testing.
func NewFrom(v Values) *Configuration {
	c := &Configuration{
		Os:  EnvironmentOf(mapFetcher(v.Os)),
		Git: EnvironmentOf(mapFetcher(v.Git)),
	}
	initConfig(c)
	return c
}

func initConfig(c *Configuration) {
	c.CurrentRemote = defaultRemote
	c.envVars = make(map[string]string)
	c.IsTracingHttp = c.Os.Bool("GIT_CURL_VERBOSE", false)
	c.IsDebuggingHttp = c.Os.Bool("LFS_DEBUG_HTTP", false)
	c.IsLoggingStats = c.Os.Bool("GIT_LOG_STATS", false)
}

// Unmarshal unmarshals the *Configuration in context into all of `v`'s fields,
// according to the following rules:
//
// Values are marshaled according to the given key and environment, as follows:
//	type T struct {
//		Field string `git:"key"`
//		Other string `os:"key"`
//	}
//
// If an unknown environment is given, an error will be returned. If there is no
// method supporting conversion into a field's type, an error will be returned.
// If no value is associated with the given key and environment, the field will
// // only be modified if there is a config value present matching the given
// key. If the field is already set to a non-zero value of that field's type,
// then it will be left alone.
//
// Otherwise, the field will be set to the value of calling the
// appropriately-typed method on the specified environment.
func (c *Configuration) Unmarshal(v interface{}) error {
	into := reflect.ValueOf(v)
	if into.Kind() != reflect.Ptr {
		return fmt.Errorf("lfs/config: unable to parse non-pointer type of %T", v)
	}
	into = into.Elem()

	for i := 0; i < into.Type().NumField(); i++ {
		field := into.Field(i)
		sfield := into.Type().Field(i)

		key, env, err := c.parseTag(sfield.Tag)
		if err != nil {
			return err
		}

		if env == nil {
			continue
		}

		var val interface{}
		switch sfield.Type.Kind() {
		case reflect.String:
			var ok bool

			val, ok = env.Get(key)
			if !ok {
				val = field.String()
			}
		case reflect.Int:
			val = env.Int(key, int(field.Int()))
		case reflect.Bool:
			val = env.Bool(key, field.Bool())
		default:
			return fmt.Errorf(
				"lfs/config: unsupported target type for field %q: %v",
				sfield.Name, sfield.Type.String())
		}

		if val != nil {
			into.Field(i).Set(reflect.ValueOf(val))
		}
	}

	return nil
}

// parseTag returns the key, environment, and optional error assosciated with a
// given tag. It will return the XOR of either the `git` or `os` tag. That is to
// say, a field tagged with EITHER `git` OR `os` is valid, but pone tagged with
// both is not.
//
// If neither field was found, then a nil environment will be returned.
func (c *Configuration) parseTag(tag reflect.StructTag) (key string, env Environment, err error) {
	git, os := tag.Get("git"), tag.Get("os")

	if len(git) != 0 && len(os) != 0 {
		return "", nil, errors.New("lfs/config: ambiguous tags")
	}

	if len(git) != 0 {
		return git, c.Git, nil
	}
	if len(os) != 0 {
		return os, c.Os, nil
	}

	return
}

// GitRemoteUrl returns the git clone/push url for a given remote (blank if not found)
// the forpush argument is to cater for separate remote.name.pushurl settings
func (c *Configuration) GitRemoteUrl(remote string, forpush bool) string {
	return c.endpointConfig().GitRemoteURL(remote, forpush)
}

// Manually set an Endpoint to use instead of deriving from Git config
func (c *Configuration) SetManualEndpoint(e lfsapi.Endpoint) {
	c.manualEndpoint = &e
}

func (c *Configuration) Endpoint(operation string) lfsapi.Endpoint {
	if c.manualEndpoint != nil {
		return *c.manualEndpoint
	}
	return c.endpointConfig().Endpoint(operation, c.CurrentRemote)
}

func (c *Configuration) ConcurrentTransfers() int {
	if c.NtlmAccess("download") {
		return 1
	}

	uploads := 3

	if v, ok := c.Git.Get("lfs.concurrenttransfers"); ok {
		n, err := strconv.Atoi(v)
		if err == nil && n > 0 {
			uploads = n
		}
	}

	return uploads
}

// BasicTransfersOnly returns whether to only allow "basic" HTTP transfers.
// Default is false, including if the lfs.basictransfersonly is invalid
func (c *Configuration) BasicTransfersOnly() bool {
	return c.Git.Bool("lfs.basictransfersonly", false)
}

// TusTransfersAllowed returns whether to only use "tus.io" HTTP transfers.
// Default is false, including if the lfs.tustransfers is invalid
func (c *Configuration) TusTransfersAllowed() bool {
	return c.Git.Bool("lfs.tustransfers", false)
}

func (c *Configuration) BatchTransfer() bool {
	return c.Git.Bool("lfs.batch", true)
}

func (c *Configuration) NtlmAccess(operation string) bool {
	return c.Access(operation) == lfsapi.NTLMAccess
}

// PrivateAccess will retrieve the access value and return true if
// the value is set to private. When a repo is marked as having private
// access, the http requests for the batch api will fetch the credentials
// before running, otherwise the request will run without credentials.
func (c *Configuration) PrivateAccess(operation string) bool {
	return c.Access(operation) != lfsapi.NoneAccess
}

// Access returns the access auth type.
func (c *Configuration) Access(operation string) lfsapi.Access {
	return c.EndpointAccess(c.Endpoint(operation))
}

// SetAccess will set the private access flag in .git/config.
func (c *Configuration) SetAccess(operation string, authType string) {
	c.endpointConfig().SetAccess(c.Endpoint(operation).Url, lfsapi.Access(authType))
}

func (c *Configuration) FindNetrcHost(host string) (*netrc.Machine, error) {
	c.loading.Lock()
	defer c.loading.Unlock()
	if c.parsedNetrc == nil {
		n, err := c.parseNetrc()
		if err != nil {
			return nil, err
		}
		c.parsedNetrc = n
	}

	return c.parsedNetrc.FindMachine(host), nil
}

// Manually override the netrc config
func (c *Configuration) SetNetrc(n netrcfinder) {
	c.parsedNetrc = n
}

func (c *Configuration) EndpointAccess(e lfsapi.Endpoint) lfsapi.Access {
	return c.endpointConfig().AccessFor(e.Url)
}

func (c *Configuration) FetchIncludePaths() []string {
	patterns, _ := c.Git.Get("lfs.fetchinclude")
	return tools.CleanPaths(patterns, ",")
}

func (c *Configuration) FetchExcludePaths() []string {
	patterns, _ := c.Git.Get("lfs.fetchexclude")
	return tools.CleanPaths(patterns, ",")
}

func (c *Configuration) RemoteEndpoint(remote, operation string) lfsapi.Endpoint {
	return c.endpointConfig().RemoteEndpoint(operation, remote)
}

func (c *Configuration) Remotes() []string {
	c.loadGitConfig()

	return c.remotes
}

func (c *Configuration) GitProtocol() string {
	return c.endpointConfig().GitProtocol()
}

func (c *Configuration) endpointConfig() lfsapi.EndpointFinder {
	c.endpointMu.Lock()
	defer c.endpointMu.Unlock()

	if c.endpointFinder == nil {
		c.endpointFinder = lfsapi.NewEndpointFinder(c.Git)
	}

	return c.endpointFinder
}

func (c *Configuration) Extensions() map[string]Extension {
	c.loadGitConfig()
	return c.extensions
}

// SortedExtensions gets the list of extensions ordered by Priority
func (c *Configuration) SortedExtensions() ([]Extension, error) {
	return SortExtensions(c.Extensions())
}

func (c *Configuration) FetchPruneConfig() FetchPruneConfig {
	f := &FetchPruneConfig{
		FetchRecentRefsDays:           7,
		FetchRecentRefsIncludeRemotes: true,
		PruneOffsetDays:               3,
		PruneRemoteName:               "origin",
	}

	if err := c.Unmarshal(f); err != nil {
		panic(err.Error())
	}
	return *f
}

func (c *Configuration) SkipDownloadErrors() bool {
	return c.Os.Bool("GIT_LFS_SKIP_DOWNLOAD_ERRORS", false) || c.Git.Bool("lfs.skipdownloaderrors", false)
}

// loadGitConfig is a temporary measure to support legacy behavior dependent on
// accessing properties set by ReadGitConfig, namely:
//  - `c.extensions`
//  - `c.uniqRemotes`
//  - `c.gitConfig`
//
// Since the *gitEnvironment is responsible for setting these values on the
// (*config.Configuration) instance, we must call that method, if it exists.
//
// loadGitConfig returns a bool returning whether or not `loadGitConfig` was
// called AND the method did not return early.
func (c *Configuration) loadGitConfig() bool {
	if g, ok := c.Git.(*gitEnvironment); ok {
		return g.loadGitConfig()
	}

	return false
}

// CurrentCommitter returns the name/email that would be used to author a commit
// with this configuration. In particular, the "user.name" and "user.email"
// configuration values are used
func (c *Configuration) CurrentCommitter() (name, email string) {
	name, _ = c.Git.Get("user.name")
	email, _ = c.Git.Get("user.email")
	return
}
<commit_msg>config: remove unused netrc, ntlm, env, and debugging properties<commit_after>// Package config collects together all configuration settings
// NOTE: Subject to change, do not rely on this package from outside git-lfs source
package config

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"sync"

	"github.com/git-lfs/git-lfs/lfsapi"
	"github.com/git-lfs/git-lfs/tools"
)

var (
	Config                 = New()
	ShowConfigWarnings     = false
	defaultRemote          = "origin"
	gitConfigWarningPrefix = "lfs."
)

// FetchPruneConfig collects together the config options that control fetching and pruning
type FetchPruneConfig struct {
	// The number of days prior to current date for which (local) refs other than HEAD
	// will be fetched with --recent (default 7, 0 = only fetch HEAD)
	FetchRecentRefsDays int `git:"lfs.fetchrecentrefsdays"`
	// Makes the FetchRecentRefsDays option apply to remote refs from fetch source as well (default true)
	FetchRecentRefsIncludeRemotes bool `git:"lfs.fetchrecentremoterefs"`
	// number of days prior to latest commit on a ref that we'll fetch previous
	// LFS changes too (default 0 = only fetch at ref)
	FetchRecentCommitsDays int `git:"lfs.fetchrecentcommitsdays"`
	// Whether to always fetch recent even without --recent
	FetchRecentAlways bool `git:"lfs.fetchrecentalways"`
	// Number of days added to FetchRecent*; data outside combined window will be
	// deleted when prune is run. (default 3)
	PruneOffsetDays int `git:"lfs.pruneoffsetdays"`
	// Always verify with remote before pruning
	PruneVerifyRemoteAlways bool `git:"lfs.pruneverifyremotealways"`
	// Name of remote to check for unpushed and verify checks
	PruneRemoteName string `git:"lfs.pruneremotetocheck"`
}

type Configuration struct {
	// Os provides a `*Environment` used to access to the system's
	// environment through os.Getenv. It is the point of entry for all
	// system environment configuration.
	Os Environment

	// Git provides a `*Environment` used to access to the various levels of
	// `.gitconfig`'s. It is the point of entry for all Git environment
	// configuration.
	Git Environment

	CurrentRemote string

	loading        sync.Mutex // guards initialization of gitConfig and remotes
	remotes        []string
	extensions     map[string]Extension
	manualEndpoint *lfsapi.Endpoint
	endpointFinder lfsapi.EndpointFinder
	endpointMu     sync.Mutex
}

func New() *Configuration {
	c := &Configuration{Os: EnvironmentOf(NewOsFetcher())}
	c.Git = &gitEnvironment{config: c}
	initConfig(c)
	return c
}

// Values is a convenience type used to call the NewFromValues function. It
// specifies `Git` and `Env` maps to use as mock values, instead of calling out
// to real `.gitconfig`s and the `os.Getenv` function.
type Values struct {
	// Git and Os are the stand-in maps used to provide values for their
	// respective environments.
	Git, Os map[string]string
}

// NewFrom returns a new `*config.Configuration` that reads both its Git
// and Enviornment-level values from the ones provided instead of the actual
// `.gitconfig` file or `os.Getenv`, respectively.
//
// This method should only be used during testing.
func NewFrom(v Values) *Configuration {
	c := &Configuration{
		Os:  EnvironmentOf(mapFetcher(v.Os)),
		Git: EnvironmentOf(mapFetcher(v.Git)),
	}
	initConfig(c)
	return c
}

func initConfig(c *Configuration) {
	c.CurrentRemote = defaultRemote
}

// Unmarshal unmarshals the *Configuration in context into all of `v`'s fields,
// according to the following rules:
//
// Values are marshaled according to the given key and environment, as follows:
//	type T struct {
//		Field string `git:"key"`
//		Other string `os:"key"`
//	}
//
// If an unknown environment is given, an error will be returned. If there is no
// method supporting conversion into a field's type, an error will be returned.
// If no value is associated with the given key and environment, the field will
// // only be modified if there is a config value present matching the given
// key. If the field is already set to a non-zero value of that field's type,
// then it will be left alone.
//
// Otherwise, the field will be set to the value of calling the
// appropriately-typed method on the specified environment.
func (c *Configuration) Unmarshal(v interface{}) error {
	into := reflect.ValueOf(v)
	if into.Kind() != reflect.Ptr {
		return fmt.Errorf("lfs/config: unable to parse non-pointer type of %T", v)
	}
	into = into.Elem()

	for i := 0; i < into.Type().NumField(); i++ {
		field := into.Field(i)
		sfield := into.Type().Field(i)

		key, env, err := c.parseTag(sfield.Tag)
		if err != nil {
			return err
		}

		if env == nil {
			continue
		}

		var val interface{}
		switch sfield.Type.Kind() {
		case reflect.String:
			var ok bool

			val, ok = env.Get(key)
			if !ok {
				val = field.String()
			}
		case reflect.Int:
			val = env.Int(key, int(field.Int()))
		case reflect.Bool:
			val = env.Bool(key, field.Bool())
		default:
			return fmt.Errorf(
				"lfs/config: unsupported target type for field %q: %v",
				sfield.Name, sfield.Type.String())
		}

		if val != nil {
			into.Field(i).Set(reflect.ValueOf(val))
		}
	}

	return nil
}

// parseTag returns the key, environment, and optional error assosciated with a
// given tag. It will return the XOR of either the `git` or `os` tag. That is to
// say, a field tagged with EITHER `git` OR `os` is valid, but pone tagged with
// both is not.
//
// If neither field was found, then a nil environment will be returned.
func (c *Configuration) parseTag(tag reflect.StructTag) (key string, env Environment, err error) {
	git, os := tag.Get("git"), tag.Get("os")

	if len(git) != 0 && len(os) != 0 {
		return "", nil, errors.New("lfs/config: ambiguous tags")
	}

	if len(git) != 0 {
		return git, c.Git, nil
	}
	if len(os) != 0 {
		return os, c.Os, nil
	}

	return
}

// GitRemoteUrl returns the git clone/push url for a given remote (blank if not found)
// the forpush argument is to cater for separate remote.name.pushurl settings
func (c *Configuration) GitRemoteUrl(remote string, forpush bool) string {
	return c.endpointConfig().GitRemoteURL(remote, forpush)
}

// Manually set an Endpoint to use instead of deriving from Git config
func (c *Configuration) SetManualEndpoint(e lfsapi.Endpoint) {
	c.manualEndpoint = &e
}

func (c *Configuration) Endpoint(operation string) lfsapi.Endpoint {
	if c.manualEndpoint != nil {
		return *c.manualEndpoint
	}
	return c.endpointConfig().Endpoint(operation, c.CurrentRemote)
}

func (c *Configuration) ConcurrentTransfers() int {
	if c.NtlmAccess("download") {
		return 1
	}

	uploads := 3

	if v, ok := c.Git.Get("lfs.concurrenttransfers"); ok {
		n, err := strconv.Atoi(v)
		if err == nil && n > 0 {
			uploads = n
		}
	}

	return uploads
}

// BasicTransfersOnly returns whether to only allow "basic" HTTP transfers.
// Default is false, including if the lfs.basictransfersonly is invalid
func (c *Configuration) BasicTransfersOnly() bool {
	return c.Git.Bool("lfs.basictransfersonly", false)
}

// TusTransfersAllowed returns whether to only use "tus.io" HTTP transfers.
// Default is false, including if the lfs.tustransfers is invalid
func (c *Configuration) TusTransfersAllowed() bool {
	return c.Git.Bool("lfs.tustransfers", false)
}

func (c *Configuration) BatchTransfer() bool {
	return c.Git.Bool("lfs.batch", true)
}

func (c *Configuration) NtlmAccess(operation string) bool {
	return c.Access(operation) == lfsapi.NTLMAccess
}

// PrivateAccess will retrieve the access value and return true if
// the value is set to private. When a repo is marked as having private
// access, the http requests for the batch api will fetch the credentials
// before running, otherwise the request will run without credentials.
func (c *Configuration) PrivateAccess(operation string) bool {
	return c.Access(operation) != lfsapi.NoneAccess
}

// Access returns the access auth type.
func (c *Configuration) Access(operation string) lfsapi.Access {
	return c.EndpointAccess(c.Endpoint(operation))
}

// SetAccess will set the private access flag in .git/config.
func (c *Configuration) SetAccess(operation string, authType string) {
	c.endpointConfig().SetAccess(c.Endpoint(operation).Url, lfsapi.Access(authType))
}

func (c *Configuration) EndpointAccess(e lfsapi.Endpoint) lfsapi.Access {
	return c.endpointConfig().AccessFor(e.Url)
}

func (c *Configuration) FetchIncludePaths() []string {
	patterns, _ := c.Git.Get("lfs.fetchinclude")
	return tools.CleanPaths(patterns, ",")
}

func (c *Configuration) FetchExcludePaths() []string {
	patterns, _ := c.Git.Get("lfs.fetchexclude")
	return tools.CleanPaths(patterns, ",")
}

func (c *Configuration) RemoteEndpoint(remote, operation string) lfsapi.Endpoint {
	return c.endpointConfig().RemoteEndpoint(operation, remote)
}

func (c *Configuration) Remotes() []string {
	c.loadGitConfig()

	return c.remotes
}

func (c *Configuration) GitProtocol() string {
	return c.endpointConfig().GitProtocol()
}

func (c *Configuration) endpointConfig() lfsapi.EndpointFinder {
	c.endpointMu.Lock()
	defer c.endpointMu.Unlock()

	if c.endpointFinder == nil {
		c.endpointFinder = lfsapi.NewEndpointFinder(c.Git)
	}

	return c.endpointFinder
}

func (c *Configuration) Extensions() map[string]Extension {
	c.loadGitConfig()
	return c.extensions
}

// SortedExtensions gets the list of extensions ordered by Priority
func (c *Configuration) SortedExtensions() ([]Extension, error) {
	return SortExtensions(c.Extensions())
}

func (c *Configuration) FetchPruneConfig() FetchPruneConfig {
	f := &FetchPruneConfig{
		FetchRecentRefsDays:           7,
		FetchRecentRefsIncludeRemotes: true,
		PruneOffsetDays:               3,
		PruneRemoteName:               "origin",
	}

	if err := c.Unmarshal(f); err != nil {
		panic(err.Error())
	}
	return *f
}

func (c *Configuration) SkipDownloadErrors() bool {
	return c.Os.Bool("GIT_LFS_SKIP_DOWNLOAD_ERRORS", false) || c.Git.Bool("lfs.skipdownloaderrors", false)
}

// loadGitConfig is a temporary measure to support legacy behavior dependent on
// accessing properties set by ReadGitConfig, namely:
//  - `c.extensions`
//  - `c.uniqRemotes`
//  - `c.gitConfig`
//
// Since the *gitEnvironment is responsible for setting these values on the
// (*config.Configuration) instance, we must call that method, if it exists.
//
// loadGitConfig returns a bool returning whether or not `loadGitConfig` was
// called AND the method did not return early.
func (c *Configuration) loadGitConfig() bool {
	if g, ok := c.Git.(*gitEnvironment); ok {
		return g.loadGitConfig()
	}

	return false
}

// CurrentCommitter returns the name/email that would be used to author a commit
// with this configuration. In particular, the "user.name" and "user.email"
// configuration values are used
func (c *Configuration) CurrentCommitter() (name, email string) {
	name, _ = c.Git.Get("user.name")
	email, _ = c.Git.Get("user.email")
	return
}
<|endoftext|>