x/listener/http2/listener.go
2022-03-15 15:45:22 +08:00

128 lines
2.4 KiB
Go

package http2
import (
"crypto/tls"
"net"
"net/http"
admission "github.com/go-gost/gost/v3/pkg/admission/wrapper"
"github.com/go-gost/gost/v3/pkg/listener"
"github.com/go-gost/gost/v3/pkg/logger"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
metrics "github.com/go-gost/metrics/wrapper"
"golang.org/x/net/http2"
)
func init() {
registry.ListenerRegistry().Register("http2", NewListener)
}
type http2Listener struct {
server *http.Server
addr net.Addr
cqueue chan net.Conn
errChan chan error
logger logger.Logger
md metadata
options listener.Options
}
func NewListener(opts ...listener.Option) listener.Listener {
options := listener.Options{}
for _, opt := range opts {
opt(&options)
}
return &http2Listener{
logger: options.Logger,
options: options,
}
}
func (l *http2Listener) Init(md md.Metadata) (err error) {
if err = l.parseMetadata(md); err != nil {
return
}
l.server = &http.Server{
Addr: l.options.Addr,
Handler: http.HandlerFunc(l.handleFunc),
TLSConfig: l.options.TLSConfig,
}
if err := http2.ConfigureServer(l.server, nil); err != nil {
return err
}
ln, err := net.Listen("tcp", l.options.Addr)
if err != nil {
return err
}
l.addr = ln.Addr()
ln = metrics.WrapListener(l.options.Service, ln)
ln = admission.WrapListener(l.options.Admission, ln)
ln = tls.NewListener(
ln,
l.options.TLSConfig,
)
l.cqueue = make(chan net.Conn, l.md.backlog)
l.errChan = make(chan error, 1)
go func() {
if err := l.server.Serve(ln); err != nil {
l.logger.Error(err)
}
}()
return
}
func (l *http2Listener) Accept() (conn net.Conn, err error) {
var ok bool
select {
case conn = <-l.cqueue:
case err, ok = <-l.errChan:
if !ok {
err = listener.ErrClosed
}
}
return
}
func (l *http2Listener) Addr() net.Addr {
return l.addr
}
func (l *http2Listener) Close() (err error) {
select {
case <-l.errChan:
default:
err = l.server.Close()
l.errChan <- err
close(l.errChan)
}
return nil
}
func (l *http2Listener) handleFunc(w http.ResponseWriter, r *http.Request) {
raddr, _ := net.ResolveTCPAddr("tcp", r.RemoteAddr)
conn := &conn{
laddr: l.addr,
raddr: raddr,
closed: make(chan struct{}),
md: md.MapMetadata{
"r": r,
"w": w,
},
}
select {
case l.cqueue <- conn:
default:
l.logger.Warnf("connection queue is full, client %s discarded", r.RemoteAddr)
return
}
<-conn.Done()
}