github.com/yggdrasil-network/yggdrasil-go@v0.5.6/src/core/link_quic.go (about)

     1  package core
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"fmt"
     7  	"net"
     8  	"net/url"
     9  	"time"
    10  
    11  	"github.com/Arceliar/phony"
    12  	"github.com/quic-go/quic-go"
    13  )
    14  
    15  type linkQUIC struct {
    16  	phony.Inbox
    17  	*links
    18  	tlsconfig  *tls.Config
    19  	quicconfig *quic.Config
    20  }
    21  
    22  type linkQUICStream struct {
    23  	quic.Connection
    24  	quic.Stream
    25  }
    26  
    27  type linkQUICListener struct {
    28  	*quic.Listener
    29  	ch <-chan *linkQUICStream
    30  }
    31  
    32  func (l *linkQUICListener) Accept() (net.Conn, error) {
    33  	qs := <-l.ch
    34  	if qs == nil {
    35  		return nil, context.Canceled
    36  	}
    37  	return qs, nil
    38  }
    39  
    40  func (l *links) newLinkQUIC() *linkQUIC {
    41  	lt := &linkQUIC{
    42  		links:     l,
    43  		tlsconfig: l.core.config.tls.Clone(),
    44  		quicconfig: &quic.Config{
    45  			MaxIdleTimeout:  time.Minute,
    46  			KeepAlivePeriod: time.Second * 20,
    47  			TokenStore:      quic.NewLRUTokenStore(255, 255),
    48  		},
    49  	}
    50  	return lt
    51  }
    52  
    53  func (l *linkQUIC) dial(ctx context.Context, url *url.URL, info linkInfo, options linkOptions) (net.Conn, error) {
    54  	qc, err := quic.DialAddr(ctx, url.Host, l.tlsconfig, l.quicconfig)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	qs, err := qc.OpenStreamSync(ctx)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	return &linkQUICStream{
    63  		Connection: qc,
    64  		Stream:     qs,
    65  	}, nil
    66  }
    67  
    68  func (l *linkQUIC) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
    69  	ql, err := quic.ListenAddr(url.Host, l.tlsconfig, l.quicconfig)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	ch := make(chan *linkQUICStream)
    74  	lql := &linkQUICListener{
    75  		Listener: ql,
    76  		ch:       ch,
    77  	}
    78  	go func() {
    79  		for {
    80  			qc, err := ql.Accept(ctx)
    81  			switch err {
    82  			case context.Canceled, context.DeadlineExceeded:
    83  				ql.Close()
    84  				fallthrough
    85  			case quic.ErrServerClosed:
    86  				return
    87  			case nil:
    88  				qs, err := qc.AcceptStream(ctx)
    89  				if err != nil {
    90  					_ = qc.CloseWithError(1, fmt.Sprintf("stream error: %s", err))
    91  					continue
    92  				}
    93  				ch <- &linkQUICStream{
    94  					Connection: qc,
    95  					Stream:     qs,
    96  				}
    97  			}
    98  		}
    99  	}()
   100  	return lql, nil
   101  }