mirror of
https://github.com/valyala/fasthttp.git
synced 2026-06-14 15:56:44 +03:00
switch reuseport to github.com/valyala/tcplisten
This commit is contained in:
+13
-99
@@ -9,11 +9,10 @@
|
||||
package reuseport
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/valyala/tcplisten"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrNoReusePort is returned if the OS doesn't support SO_REUSEPORT.
|
||||
@@ -36,107 +35,22 @@ func (e *ErrNoReusePort) Error() string {
|
||||
//
|
||||
// - TCP_FASTOPEN. See https://lwn.net/Articles/508865/ for details.
|
||||
//
|
||||
// Use https://github.com/valyala/tcplisten if you want customizing
|
||||
// these options.
|
||||
//
|
||||
// Only tcp4 and tcp6 networks are supported.
|
||||
//
|
||||
// ErrNoReusePort error is returned if the system doesn't support SO_REUSEPORT.
|
||||
func Listen(network, addr string) (net.Listener, error) {
|
||||
sa, soType, err := getSockaddr(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
ln, err := cfg.NewListener(network, addr)
|
||||
if err != nil && strings.Contains(err.Error(), "SO_REUSEPORT") {
|
||||
return nil, &ErrNoReusePort{err}
|
||||
}
|
||||
|
||||
syscall.ForkLock.RLock()
|
||||
fd, err := syscall.Socket(soType, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
|
||||
if err == nil {
|
||||
syscall.CloseOnExec(fd)
|
||||
}
|
||||
syscall.ForkLock.RUnlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = fdSetup(fd, sa, addr); err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("reuseport.%d.%s.%s", os.Getpid(), network, addr)
|
||||
file := os.NewFile(uintptr(fd), name)
|
||||
ln, err := net.FileListener(file)
|
||||
if err != nil {
|
||||
file.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = file.Close(); err != nil {
|
||||
ln.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ln, nil
|
||||
return ln, err
|
||||
}
|
||||
|
||||
func fdSetup(fd int, sa syscall.Sockaddr, addr string) error {
|
||||
var err error
|
||||
|
||||
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
|
||||
return fmt.Errorf("cannot enable SO_REUSEADDR: %s", err)
|
||||
}
|
||||
|
||||
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, soReusePort, 1); err != nil {
|
||||
return &ErrNoReusePort{err}
|
||||
}
|
||||
|
||||
if err = enableDeferAccept(fd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = enableFastOpen(fd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = syscall.Bind(fd, sa); err != nil {
|
||||
return fmt.Errorf("cannot bind to %q: %s", addr, err)
|
||||
}
|
||||
|
||||
if err = syscall.Listen(fd, syscall.SOMAXCONN); err != nil {
|
||||
return fmt.Errorf("cannot listen on %q: %s", addr, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSockaddr(network, addr string) (sa syscall.Sockaddr, soType int, err error) {
|
||||
// TODO: add support for tcp networks.
|
||||
|
||||
if network != "tcp4" && network != "tcp6" {
|
||||
return nil, -1, errors.New("only tcp4 and tcp6 network is supported")
|
||||
}
|
||||
|
||||
tcpAddr, err := net.ResolveTCPAddr(network, addr)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
|
||||
switch network {
|
||||
case "tcp4":
|
||||
var sa4 syscall.SockaddrInet4
|
||||
sa4.Port = tcpAddr.Port
|
||||
copy(sa4.Addr[:], tcpAddr.IP.To4())
|
||||
return &sa4, syscall.AF_INET, nil
|
||||
case "tcp6":
|
||||
var sa6 syscall.SockaddrInet6
|
||||
sa6.Port = tcpAddr.Port
|
||||
copy(sa6.Addr[:], tcpAddr.IP.To16())
|
||||
if tcpAddr.Zone != "" {
|
||||
ifi, err := net.InterfaceByName(tcpAddr.Zone)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
sa6.ZoneId = uint32(ifi.Index)
|
||||
}
|
||||
return &sa6, syscall.AF_INET6, nil
|
||||
default:
|
||||
return nil, -1, errors.New("Unknown network type " + network)
|
||||
}
|
||||
var cfg = &tcplisten.Config{
|
||||
ReusePort: true,
|
||||
DeferAccept: true,
|
||||
FastOpen: true,
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
// +build darwin dragonfly freebsd netbsd openbsd rumprun
|
||||
|
||||
package reuseport
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const soReusePort = syscall.SO_REUSEPORT
|
||||
|
||||
func enableDeferAccept(fd int) error {
|
||||
// TODO: implement SO_ACCEPTFILTER:dataready here
|
||||
return nil
|
||||
}
|
||||
|
||||
func enableFastOpen(fd int) error {
|
||||
// TODO: implement TCP_FASTOPEN when it will be ready
|
||||
return nil
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package reuseport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
soReusePort = 0x0F
|
||||
tcpFastOpen = 0x17
|
||||
)
|
||||
|
||||
func enableDeferAccept(fd int) error {
|
||||
if err := syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_DEFER_ACCEPT, 1); err != nil {
|
||||
return fmt.Errorf("cannot enable TCP_DEFER_ACCEPT: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func enableFastOpen(fd int) error {
|
||||
if err := syscall.SetsockoptInt(fd, syscall.SOL_TCP, tcpFastOpen, fastOpenQlen); err != nil {
|
||||
return fmt.Errorf("cannot enable TCP_FASTOPEN(qlen=%d): %s", fastOpenQlen, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const fastOpenQlen = 16 * 1024
|
||||
Reference in New Issue
Block a user