Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+16
@@ -0,0 +1,16 @@
|
||||
version: "2"
|
||||
linters:
|
||||
enable:
|
||||
- misspell
|
||||
- modernize
|
||||
- revive
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
formatters:
|
||||
exclusions:
|
||||
generated: lax
|
||||
+40
@@ -1,5 +1,45 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v0.7.0
|
||||
|
||||
**This is the first release of package socket that only supports Go 1.26+.**
|
||||
|
||||
- [New API] [PR](https://github.com/mdlayher/socket/pull/18):
|
||||
add `Conn.SendmsgBuffers` to wrap `sendmsg(2)` with scatter-gather I/O
|
||||
support for multiple input buffers.
|
||||
- [New API]: add `Conn.SetsockoptBytes` and `Conn.GetsockoptBytes` for
|
||||
binary-safe byte-slice socket options on Linux.
|
||||
- [Bug Fix]: context cancelation is now honored immediately for blocking
|
||||
operations when the context also carries a deadline; previously cancelation
|
||||
was not noticed until the deadline expired.
|
||||
- [Improvement]: bump build to Go 1.26.0. Package socket now supports Go 1.26
|
||||
and Go 1.27, mirroring Go's own release policy.
|
||||
|
||||
## v0.6.1
|
||||
|
||||
- [Improvement]: update dependencies.
|
||||
|
||||
## v0.6.0
|
||||
|
||||
**This is the first release of package socket that only supports Go 1.25+.**
|
||||
|
||||
- [Bug Fix] [PR](https://github.com/mdlayher/socket/pull/16):
|
||||
`socket.Socket` with `Config.NetNS` set now returns the error from socket
|
||||
creation inside the target network namespace, rather than silently discarding
|
||||
it and returning a nil `Conn`.
|
||||
- [Improvement]: bump build to Go 1.25.0.
|
||||
- [Improvement]: enable `golangci-lint` in CI.
|
||||
|
||||
## v0.5.2
|
||||
|
||||
- [Improvement]: Bump build to Go 1.23.0. Note this is required for the latest
|
||||
Go extended library versions.
|
||||
|
||||
## v0.5.1
|
||||
|
||||
- [Improvement]: revert `go.mod` to Go 1.20 to [resolve an issue around Go
|
||||
module version upgrades](https://github.com/mdlayher/socket/issues/13).
|
||||
|
||||
## v0.5.0
|
||||
|
||||
**This is the first release of package socket that only supports Go 1.21+.
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# socket [](https://github.com/mdlayher/socket/actions) [](https://pkg.go.dev/github.com/mdlayher/socket) [](https://goreportcard.com/report/github.com/mdlayher/socket)
|
||||
# socket [](https://github.com/mdlayher/socket/actions) [](https://pkg.go.dev/github.com/mdlayher/socket)
|
||||
|
||||
Package `socket` provides a low-level network connection type which integrates
|
||||
with Go's runtime network poller to provide asynchronous I/O and deadline
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
//go:build dragonfly || freebsd || illumos || linux
|
||||
// +build dragonfly freebsd illumos linux
|
||||
|
||||
package socket
|
||||
|
||||
|
||||
+40
-32
@@ -120,7 +120,7 @@ func (c *Conn) ReadContext(ctx context.Context, b []byte) (int, error) {
|
||||
b = b[:maxRW]
|
||||
}
|
||||
|
||||
n, err := readT(c, ctx, "read", func(fd int) (int, error) {
|
||||
n, err := readT(ctx, c, "read", func(fd int) (int, error) {
|
||||
return unix.Read(fd, b)
|
||||
})
|
||||
if n == 0 && err == nil && c.facts.zeroReadIsEOF {
|
||||
@@ -142,12 +142,12 @@ func (c *Conn) WriteContext(ctx context.Context, b []byte) (int, error) {
|
||||
)
|
||||
|
||||
doErr := c.write(ctx, "write", func(fd int) error {
|
||||
max := len(b)
|
||||
if c.facts.isStream && max-nn > maxRW {
|
||||
max = nn + maxRW
|
||||
lenb := len(b)
|
||||
if c.facts.isStream && lenb-nn > maxRW {
|
||||
lenb = nn + maxRW
|
||||
}
|
||||
|
||||
n, err = unix.Write(fd, b[nn:max])
|
||||
n, err = unix.Write(fd, b[nn:lenb])
|
||||
if n > 0 {
|
||||
nn += n
|
||||
}
|
||||
@@ -418,7 +418,7 @@ func (c *Conn) Accept(ctx context.Context, flags int) (*Conn, unix.Sockaddr, err
|
||||
sa unix.Sockaddr
|
||||
}
|
||||
|
||||
r, err := readT(c, ctx, sysAccept, func(fd int) (ret, error) {
|
||||
r, err := readT(ctx, c, sysAccept, func(fd int) (ret, error) {
|
||||
// Either accept(2) or accept4(2) depending on the OS.
|
||||
nfd, sa, err := accept(fd, flags|socketFlags)
|
||||
return ret{nfd, sa}, err
|
||||
@@ -464,7 +464,7 @@ func (c *Conn) Connect(ctx context.Context, sa unix.Sockaddr) (unix.Sockaddr, er
|
||||
// have an explicit WaitWrite call like internal/poll does, so we have
|
||||
// to wait until the runtime calls the closure again to indicate we can
|
||||
// write.
|
||||
progress uint32
|
||||
progress atomic.Uint32
|
||||
|
||||
// Capture closure sockaddr and error.
|
||||
rsa unix.Sockaddr
|
||||
@@ -472,7 +472,7 @@ func (c *Conn) Connect(ctx context.Context, sa unix.Sockaddr) (unix.Sockaddr, er
|
||||
)
|
||||
|
||||
doErr := c.write(ctx, op, func(fd int) error {
|
||||
if atomic.AddUint32(&progress, 1) == 1 {
|
||||
if progress.Add(1) == 1 {
|
||||
// First call: initiate connect.
|
||||
return unix.Connect(fd, sa)
|
||||
}
|
||||
@@ -569,7 +569,7 @@ func (c *Conn) Recvmsg(ctx context.Context, p, oob []byte, flags int) (int, int,
|
||||
from unix.Sockaddr
|
||||
}
|
||||
|
||||
r, err := readT(c, ctx, "recvmsg", func(fd int) (ret, error) {
|
||||
r, err := readT(ctx, c, "recvmsg", func(fd int) (ret, error) {
|
||||
n, oobn, recvflags, from, err := unix.Recvmsg(fd, p, oob, flags)
|
||||
return ret{n, oobn, recvflags, from}, err
|
||||
})
|
||||
@@ -587,7 +587,7 @@ func (c *Conn) Recvfrom(ctx context.Context, p []byte, flags int) (int, unix.Soc
|
||||
addr unix.Sockaddr
|
||||
}
|
||||
|
||||
out, err := readT(c, ctx, "recvfrom", func(fd int) (ret, error) {
|
||||
out, err := readT(ctx, c, "recvfrom", func(fd int) (ret, error) {
|
||||
n, addr, err := unix.Recvfrom(fd, p, flags)
|
||||
return ret{n, addr}, err
|
||||
})
|
||||
@@ -600,11 +600,18 @@ func (c *Conn) Recvfrom(ctx context.Context, p []byte, flags int) (int, unix.Soc
|
||||
|
||||
// Sendmsg wraps sendmsg(2).
|
||||
func (c *Conn) Sendmsg(ctx context.Context, p, oob []byte, to unix.Sockaddr, flags int) (int, error) {
|
||||
return writeT(c, ctx, "sendmsg", func(fd int) (int, error) {
|
||||
return writeT(ctx, c, "sendmsg", func(fd int) (int, error) {
|
||||
return unix.SendmsgN(fd, p, oob, to, flags)
|
||||
})
|
||||
}
|
||||
|
||||
// SendmsgBuffers wraps sendmsg(2) with scatter-gather I/O support.
|
||||
func (c *Conn) SendmsgBuffers(ctx context.Context, buffers [][]byte, oob []byte, to unix.Sockaddr, flags int) (int, error) {
|
||||
return writeT(ctx, c, "sendmsg", func(fd int) (int, error) {
|
||||
return unix.SendmsgBuffers(fd, buffers, oob, to, flags)
|
||||
})
|
||||
}
|
||||
|
||||
// Sendto wraps sendto(2).
|
||||
func (c *Conn) Sendto(ctx context.Context, p []byte, flags int, to unix.Sockaddr) error {
|
||||
return c.write(ctx, "sendto", func(fd int) error {
|
||||
@@ -645,7 +652,7 @@ func (c *Conn) Shutdown(how int) error {
|
||||
// read wraps readT to execute a function and capture its error result. This is
|
||||
// a convenience wrapper for functions which don't return any extra values.
|
||||
func (c *Conn) read(ctx context.Context, op string, f func(fd int) error) error {
|
||||
_, err := readT(c, ctx, op, func(fd int) (struct{}, error) {
|
||||
_, err := readT(ctx, c, op, func(fd int) (struct{}, error) {
|
||||
return struct{}{}, f(fd)
|
||||
})
|
||||
return err
|
||||
@@ -654,7 +661,7 @@ func (c *Conn) read(ctx context.Context, op string, f func(fd int) error) error
|
||||
// write executes f, a write function, against the associated file descriptor.
|
||||
// op is used to create an *os.SyscallError if the file descriptor is closed.
|
||||
func (c *Conn) write(ctx context.Context, op string, f func(fd int) error) error {
|
||||
_, err := writeT(c, ctx, op, func(fd int) (struct{}, error) {
|
||||
_, err := writeT(ctx, c, op, func(fd int) (struct{}, error) {
|
||||
return struct{}{}, f(fd)
|
||||
})
|
||||
return err
|
||||
@@ -662,7 +669,7 @@ func (c *Conn) write(ctx context.Context, op string, f func(fd int) error) error
|
||||
|
||||
// readT executes c.rc.Read for op using the input function, returning a newly
|
||||
// allocated result T.
|
||||
func readT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
|
||||
func readT[T any](ctx context.Context, c *Conn, op string, f func(fd int) (T, error)) (T, error) {
|
||||
return rwT(c, rwContext[T]{
|
||||
Context: ctx,
|
||||
Type: read,
|
||||
@@ -673,7 +680,7 @@ func readT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, er
|
||||
|
||||
// writeT executes c.rc.Write for op using the input function, returning a newly
|
||||
// allocated result T.
|
||||
func writeT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
|
||||
func writeT[T any](ctx context.Context, c *Conn, op string, f func(fd int) (T, error)) (T, error) {
|
||||
return rwT(c, rwContext[T]{
|
||||
Context: ctx,
|
||||
Type: write,
|
||||
@@ -750,12 +757,6 @@ func rwT[T any](c *Conn, rw rwContext[T]) (T, error) {
|
||||
needDisarm atomic.Bool
|
||||
)
|
||||
|
||||
// On cancel, clean up the watcher.
|
||||
defer func() {
|
||||
close(doneC)
|
||||
wg.Wait()
|
||||
}()
|
||||
|
||||
if d, ok := rw.Context.Deadline(); ok {
|
||||
// The context has an explicit deadline. We will use it for cancelation
|
||||
// but disarm it after poll for the next call.
|
||||
@@ -764,17 +765,17 @@ func rwT[T any](c *Conn, rw rwContext[T]) (T, error) {
|
||||
}
|
||||
setDeadline = true
|
||||
needDisarm.Store(true)
|
||||
} else {
|
||||
// The context does not have an explicit deadline. We have to watch for
|
||||
// cancelation so we can propagate that signal to immediately unblock
|
||||
// the runtime network poller.
|
||||
//
|
||||
// TODO(mdlayher): is it possible to detect a background context vs a
|
||||
// context with possible future cancel?
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
}
|
||||
|
||||
if rw.Context.Done() != nil {
|
||||
// The context can be canceled, regardless of whether or not it also
|
||||
// carries a deadline. We have to watch for cancelation so we can
|
||||
// propagate that signal to immediately unblock the runtime network
|
||||
// poller rather than waiting for a possibly distant deadline to expire.
|
||||
//
|
||||
// A nil Done channel means the context can never be canceled (such as
|
||||
// context.Background), so no watcher is necessary.
|
||||
wg.Go(func() {
|
||||
select {
|
||||
case <-rw.Context.Done():
|
||||
// Cancel the operation. Make the caller disarm after poll
|
||||
@@ -784,7 +785,7 @@ func rwT[T any](c *Conn, rw rwContext[T]) (T, error) {
|
||||
case <-doneC:
|
||||
// Nothing to do.
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -797,6 +798,13 @@ func rwT[T any](c *Conn, rw rwContext[T]) (T, error) {
|
||||
return ready(err)
|
||||
})
|
||||
|
||||
// Stop the watcher and wait for it to exit before checking whether we
|
||||
// must disarm the deadline. Otherwise the watcher could observe
|
||||
// cancelation and arm a deadline in the past after we have already
|
||||
// disarmed, leaving the next call to fail immediately.
|
||||
close(doneC)
|
||||
wg.Wait()
|
||||
|
||||
if needDisarm.Load() {
|
||||
_ = deadline(time.Time{})
|
||||
}
|
||||
|
||||
+62
-1
@@ -1,5 +1,4 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
@@ -96,6 +95,68 @@ func (c *Conn) SetsockoptSockFprog(level, opt int, fprog *unix.SockFprog) error
|
||||
})
|
||||
}
|
||||
|
||||
// GetsockoptBytes wraps getsockopt(2) for raw byte slice values on Linux. The
|
||||
// existing contents of b are passed to the kernel as the input buffer with
|
||||
// optlen set to len(b), and the kernel overwrites b in place. The returned int
|
||||
// is the optlen value reported by the kernel on return, which may be smaller
|
||||
// than len(b). These in/out semantics are required for socket options which
|
||||
// both read from and write to the same buffer.
|
||||
//
|
||||
// No allocation or copying is performed and b is passed to the kernel
|
||||
// verbatim. If len(b) is zero, a nil buffer with optlen 0 is passed.
|
||||
func (c *Conn) GetsockoptBytes(level, opt int, b []byte) (int, error) {
|
||||
return controlT(c, "getsockopt", func(fd int) (int, error) {
|
||||
var p unsafe.Pointer
|
||||
if len(b) > 0 {
|
||||
p = unsafe.Pointer(&b[0])
|
||||
}
|
||||
|
||||
optlen := uint32(len(b))
|
||||
_, _, errno := unix.Syscall6(
|
||||
unix.SYS_GETSOCKOPT,
|
||||
uintptr(fd),
|
||||
uintptr(level),
|
||||
uintptr(opt),
|
||||
uintptr(p),
|
||||
uintptr(unsafe.Pointer(&optlen)),
|
||||
0,
|
||||
)
|
||||
if errno != 0 {
|
||||
return 0, errno
|
||||
}
|
||||
|
||||
return int(optlen), nil
|
||||
})
|
||||
}
|
||||
|
||||
// SetsockoptBytes wraps setsockopt(2) for raw byte slice values on Linux. The
|
||||
// bytes of b are passed to the kernel verbatim with optlen set to len(b) and no
|
||||
// NUL handling of any kind is performed. If len(b) is zero, a nil buffer with
|
||||
// optlen 0 is passed.
|
||||
func (c *Conn) SetsockoptBytes(level, opt int, b []byte) error {
|
||||
return c.control("setsockopt", func(fd int) error {
|
||||
var p unsafe.Pointer
|
||||
if len(b) > 0 {
|
||||
p = unsafe.Pointer(&b[0])
|
||||
}
|
||||
|
||||
_, _, errno := unix.Syscall6(
|
||||
unix.SYS_SETSOCKOPT,
|
||||
uintptr(fd),
|
||||
uintptr(level),
|
||||
uintptr(opt),
|
||||
uintptr(p),
|
||||
uintptr(len(b)),
|
||||
0,
|
||||
)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetsockoptTpacketStats wraps getsockopt(2) for unix.TpacketStats values.
|
||||
func (c *Conn) GetsockoptTpacketStats(level, name int) (*unix.TpacketStats, error) {
|
||||
return controlT(c, "getsockopt", func(fd int) (*unix.TpacketStats, error) {
|
||||
|
||||
+1
-2
@@ -1,5 +1,4 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
@@ -65,7 +64,7 @@ func withNetNS(fd int, fn func() (*Conn, error)) (*Conn, error) {
|
||||
// No more thread-local state manipulation; return the new Conn.
|
||||
runtime.UnlockOSThread()
|
||||
conn = c
|
||||
return nil
|
||||
return err
|
||||
})
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
//go:build !darwin
|
||||
// +build !darwin
|
||||
|
||||
package socket
|
||||
|
||||
|
||||
Reference in new issue
Block a user