mirror of
https://github.com/tinygo-org/net.git
synced 2026-08-15 00:43:39 +00:00
net: add UDP listen, resolver, DNSError, and TCP listener APIs for wasm (#63)
* net: add UDP listen, resolver, DNSError, and TCP listener APIs for wasm TinyGo's net package was missing several symbols that upstream Go's js/wasm net declares, blocking builds that pull pion/transport, pion/dtls, and similar (via netbird's WASM client). Add them, backed by the existing netdev abstraction so they compile for all targets and no-op cleanly under nopNetdev (matching Go's js runtime behavior): - DNSError type (dnserror.go), copied from Go 1.26.2. - Resolver + DefaultResolver with LookupHost/LookupIP/LookupIPAddr/ LookupNetIP/LookupPort; LookupHost/LookupIP package funcs; Dialer.Resolver field for API compatibility. - UDPConn: ListenUDP, ReadFromUDP(AddrPort), WriteToUDP(AddrPort), SetReadBuffer, SetWriteBuffer. - TCPConn: CloseRead, SetNoDelay, SetReadBuffer, SetWriteBuffer, ReadFrom(io.Reader). - TCPListener: ListenTCP, AcceptTCP, SetDeadline; ListenPacket package func. - Interface: Addrs, MulticastAddrs stubs. * more complete PR for adding more net support (#64) * format unixsock
This commit is contained in:
@@ -87,6 +87,13 @@ type Dialer struct {
|
|||||||
// If KeepAliveConfig.Enable is false and KeepAlive is negative,
|
// If KeepAliveConfig.Enable is false and KeepAlive is negative,
|
||||||
// keep-alive probes are disabled.
|
// keep-alive probes are disabled.
|
||||||
KeepAliveConfig KeepAliveConfig
|
KeepAliveConfig KeepAliveConfig
|
||||||
|
|
||||||
|
// Resolver optionally specifies an alternate resolver to use.
|
||||||
|
//
|
||||||
|
// TINYGO: present for API compatibility; DialContext resolves via the
|
||||||
|
// netdev-backed ResolveTCPAddr/ResolveUDPAddr and does not consult this
|
||||||
|
// field.
|
||||||
|
Resolver *Resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dial connects to the address on the named network.
|
// Dial connects to the address on the named network.
|
||||||
@@ -281,3 +288,22 @@ func Listen(network, address string) (Listener, error) {
|
|||||||
|
|
||||||
return listenTCP(laddr)
|
return listenTCP(laddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListenPacket announces on the local network address.
|
||||||
|
//
|
||||||
|
// TINYGO: only UDP networks are supported, backed by ListenUDP; the
|
||||||
|
// returned PacketConn is a *UDPConn.
|
||||||
|
func ListenPacket(network, address string) (PacketConn, error) {
|
||||||
|
switch network {
|
||||||
|
case "udp", "udp4":
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Network %s not supported", network)
|
||||||
|
}
|
||||||
|
|
||||||
|
laddr, err := ResolveUDPAddr(network, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListenUDP(network, laddr)
|
||||||
|
}
|
||||||
|
|||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
// TINYGO: The following is copied and modified from Go 1.26.2 official implementation.
|
||||||
|
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package net
|
||||||
|
|
||||||
|
// DNSError represents a DNS lookup error.
|
||||||
|
type DNSError struct {
|
||||||
|
UnwrapErr error // error returned by the [DNSError.Unwrap] method, might be nil
|
||||||
|
Err string // description of the error
|
||||||
|
Name string // name looked for
|
||||||
|
Server string // server used
|
||||||
|
IsTimeout bool // if true, timed out; not all timeouts set this
|
||||||
|
IsTemporary bool // if true, error is temporary; not all errors set this
|
||||||
|
|
||||||
|
// IsNotFound is set to true when the requested name does not
|
||||||
|
// contain any records of the requested type (data not found),
|
||||||
|
// or the name itself was not found (NXDOMAIN).
|
||||||
|
IsNotFound bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap returns e.UnwrapErr.
|
||||||
|
func (e *DNSError) Unwrap() error { return e.UnwrapErr }
|
||||||
|
|
||||||
|
func (e *DNSError) Error() string {
|
||||||
|
if e == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
s := "lookup " + e.Name
|
||||||
|
if e.Server != "" {
|
||||||
|
s += " on " + e.Server
|
||||||
|
}
|
||||||
|
s += ": " + e.Err
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout reports whether the DNS lookup is known to have timed out.
|
||||||
|
// This is not always known; a DNS lookup may fail due to a timeout
|
||||||
|
// and return a [DNSError] for which Timeout returns false.
|
||||||
|
func (e *DNSError) Timeout() bool { return e.IsTimeout }
|
||||||
|
|
||||||
|
// Temporary reports whether the DNS error is known to be temporary.
|
||||||
|
// This is not always known; a DNS lookup may fail due to a temporary
|
||||||
|
// error and return a [DNSError] for which Temporary returns false.
|
||||||
|
func (e *DNSError) Temporary() bool { return e.IsTimeout || e.IsTemporary }
|
||||||
@@ -14,6 +14,7 @@ package http
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -117,6 +118,11 @@ type Response struct {
|
|||||||
// Request's Body is nil (having already been consumed).
|
// Request's Body is nil (having already been consumed).
|
||||||
// This is only populated for Client requests.
|
// This is only populated for Client requests.
|
||||||
Request *Request
|
Request *Request
|
||||||
|
|
||||||
|
// TLS contains information about the TLS connection on which the response
|
||||||
|
// was received. It is nil for unencrypted responses. TINYGO: populated only
|
||||||
|
// if a caller sets it; the wasm fetch path leaves it nil.
|
||||||
|
TLS *tls.ConnectionState
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cookies parses and returns the cookies set in the Set-Cookie headers.
|
// Cookies parses and returns the cookies set in the Set-Cookie headers.
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
// TINYGO: Copied verbatim from the Go 1.26.2 official implementation to provide
|
||||||
|
// the ResponseController API used by net/http/httputil (reverse proxy). The
|
||||||
|
// underlying methods degrade to ErrNotSupported when the ResponseWriter does
|
||||||
|
// not implement them, which is the standard behavior.
|
||||||
|
|
||||||
|
// Copyright 2022 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A ResponseController is used by an HTTP handler to control the response.
|
||||||
|
//
|
||||||
|
// A ResponseController may not be used after the [Handler.ServeHTTP] method has returned.
|
||||||
|
type ResponseController struct {
|
||||||
|
rw ResponseWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewResponseController creates a [ResponseController] for a request.
|
||||||
|
//
|
||||||
|
// The ResponseWriter should be the original value passed to the [Handler.ServeHTTP] method,
|
||||||
|
// or have an Unwrap method returning the original ResponseWriter.
|
||||||
|
//
|
||||||
|
// If the ResponseWriter implements any of the following methods, the ResponseController
|
||||||
|
// will call them as appropriate:
|
||||||
|
//
|
||||||
|
// Flush()
|
||||||
|
// FlushError() error // alternative Flush returning an error
|
||||||
|
// Hijack() (net.Conn, *bufio.ReadWriter, error)
|
||||||
|
// SetReadDeadline(deadline time.Time) error
|
||||||
|
// SetWriteDeadline(deadline time.Time) error
|
||||||
|
// EnableFullDuplex() error
|
||||||
|
//
|
||||||
|
// If the ResponseWriter does not support a method, ResponseController returns
|
||||||
|
// an error matching [ErrNotSupported].
|
||||||
|
func NewResponseController(rw ResponseWriter) *ResponseController {
|
||||||
|
return &ResponseController{rw}
|
||||||
|
}
|
||||||
|
|
||||||
|
type rwUnwrapper interface {
|
||||||
|
Unwrap() ResponseWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush flushes buffered data to the client.
|
||||||
|
func (c *ResponseController) Flush() error {
|
||||||
|
rw := c.rw
|
||||||
|
for {
|
||||||
|
switch t := rw.(type) {
|
||||||
|
case interface{ FlushError() error }:
|
||||||
|
return t.FlushError()
|
||||||
|
case Flusher:
|
||||||
|
t.Flush()
|
||||||
|
return nil
|
||||||
|
case rwUnwrapper:
|
||||||
|
rw = t.Unwrap()
|
||||||
|
default:
|
||||||
|
return errNotSupported()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hijack lets the caller take over the connection.
|
||||||
|
// See the [Hijacker] interface for details.
|
||||||
|
func (c *ResponseController) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
rw := c.rw
|
||||||
|
for {
|
||||||
|
switch t := rw.(type) {
|
||||||
|
case Hijacker:
|
||||||
|
return t.Hijack()
|
||||||
|
case rwUnwrapper:
|
||||||
|
rw = t.Unwrap()
|
||||||
|
default:
|
||||||
|
return nil, nil, errNotSupported()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadDeadline sets the deadline for reading the entire request, including the body.
|
||||||
|
// Reads from the request body after the deadline has been exceeded will return an error.
|
||||||
|
// A zero value means no deadline.
|
||||||
|
//
|
||||||
|
// Setting the read deadline after it has been exceeded will not extend it.
|
||||||
|
func (c *ResponseController) SetReadDeadline(deadline time.Time) error {
|
||||||
|
rw := c.rw
|
||||||
|
for {
|
||||||
|
switch t := rw.(type) {
|
||||||
|
case interface{ SetReadDeadline(time.Time) error }:
|
||||||
|
return t.SetReadDeadline(deadline)
|
||||||
|
case rwUnwrapper:
|
||||||
|
rw = t.Unwrap()
|
||||||
|
default:
|
||||||
|
return errNotSupported()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteDeadline sets the deadline for writing the response.
|
||||||
|
// Writes to the response body after the deadline has been exceeded will not block,
|
||||||
|
// but may succeed if the data has been buffered.
|
||||||
|
// A zero value means no deadline.
|
||||||
|
//
|
||||||
|
// Setting the write deadline after it has been exceeded will not extend it.
|
||||||
|
func (c *ResponseController) SetWriteDeadline(deadline time.Time) error {
|
||||||
|
rw := c.rw
|
||||||
|
for {
|
||||||
|
switch t := rw.(type) {
|
||||||
|
case interface{ SetWriteDeadline(time.Time) error }:
|
||||||
|
return t.SetWriteDeadline(deadline)
|
||||||
|
case rwUnwrapper:
|
||||||
|
rw = t.Unwrap()
|
||||||
|
default:
|
||||||
|
return errNotSupported()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableFullDuplex indicates that the request handler will interleave reads from [Request.Body]
|
||||||
|
// with writes to the [ResponseWriter].
|
||||||
|
//
|
||||||
|
// For HTTP/1 requests, the Go HTTP server by default consumes any unread portion of
|
||||||
|
// the request body before beginning to write the response, preventing handlers from
|
||||||
|
// concurrently reading from the request and writing the response.
|
||||||
|
// Calling EnableFullDuplex disables this behavior and permits handlers to continue to read
|
||||||
|
// from the request while concurrently writing the response.
|
||||||
|
//
|
||||||
|
// For HTTP/2 requests, the Go HTTP server always permits concurrent reads and responses.
|
||||||
|
func (c *ResponseController) EnableFullDuplex() error {
|
||||||
|
rw := c.rw
|
||||||
|
for {
|
||||||
|
switch t := rw.(type) {
|
||||||
|
case interface{ EnableFullDuplex() error }:
|
||||||
|
return t.EnableFullDuplex()
|
||||||
|
case rwUnwrapper:
|
||||||
|
rw = t.Unwrap()
|
||||||
|
default:
|
||||||
|
return errNotSupported()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// errNotSupported returns an error that Is ErrNotSupported,
|
||||||
|
// but is not == to it.
|
||||||
|
func errNotSupported() error {
|
||||||
|
return fmt.Errorf("%w", ErrNotSupported)
|
||||||
|
}
|
||||||
@@ -2779,6 +2779,23 @@ type Server struct {
|
|||||||
// value.
|
// value.
|
||||||
ConnContext func(ctx context.Context, c net.Conn) context.Context
|
ConnContext func(ctx context.Context, c net.Conn) context.Context
|
||||||
|
|
||||||
|
// TLSNextProto optionally specifies a function to take over
|
||||||
|
// ownership of the provided TLS connection when an ALPN
|
||||||
|
// protocol upgrade has occurred. The map key is the protocol
|
||||||
|
// name negotiated. The Handler argument should be used to
|
||||||
|
// handle HTTP requests and will initialize the Request's TLS
|
||||||
|
// and RemoteAddr if not already set. The connection is
|
||||||
|
// automatically closed when the function returns.
|
||||||
|
// If TLSNextProto is not nil, HTTP/2 support is not enabled
|
||||||
|
// automatically.
|
||||||
|
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
|
||||||
|
|
||||||
|
// HTTP2 configures HTTP/2 connections.
|
||||||
|
//
|
||||||
|
// This field does not yet have any effect.
|
||||||
|
// See https://go.dev/issue/67813.
|
||||||
|
HTTP2 *HTTP2Config
|
||||||
|
|
||||||
inShutdown atomicBool // true when server is in shutdown
|
inShutdown atomicBool // true when server is in shutdown
|
||||||
|
|
||||||
disableKeepAlives int32 // accessed atomically.
|
disableKeepAlives int32 // accessed atomically.
|
||||||
|
|||||||
+107
-1
@@ -12,8 +12,14 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type readTrackingBody struct {
|
type readTrackingBody struct {
|
||||||
@@ -22,6 +28,106 @@ type readTrackingBody struct {
|
|||||||
didClose atomic.Bool
|
didClose atomic.Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Transport struct{}
|
// Transport is an HTTP/1.x RoundTripper. TINYGO: in the wasm/browser build the
|
||||||
|
// actual round trips are performed by the host fetch API (see roundtrip_js.go);
|
||||||
|
// these fields exist only to satisfy configuration by callers such as
|
||||||
|
// golang.org/x/net/http2 and google.golang.org/grpc. They are stored but, aside
|
||||||
|
// from fetch, have no effect at runtime.
|
||||||
|
type Transport struct {
|
||||||
|
// TLSClientConfig specifies the TLS configuration to use with tls.Client.
|
||||||
|
TLSClientConfig *tls.Config
|
||||||
|
|
||||||
|
// TLSNextProto specifies how the Transport switches to an alternate
|
||||||
|
// protocol (such as HTTP/2) after a TLS ALPN protocol negotiation.
|
||||||
|
TLSNextProto map[string]func(authority string, c *tls.Conn) RoundTripper
|
||||||
|
|
||||||
|
// DisableKeepAlives, if true, disables HTTP keep-alives.
|
||||||
|
DisableKeepAlives bool
|
||||||
|
|
||||||
|
// DisableCompression, if true, prevents the Transport from requesting
|
||||||
|
// compression with an "Accept-Encoding: gzip" request header.
|
||||||
|
DisableCompression bool
|
||||||
|
|
||||||
|
// IdleConnTimeout is the maximum amount of time an idle connection will
|
||||||
|
// remain idle before closing itself. Zero means no limit.
|
||||||
|
IdleConnTimeout time.Duration
|
||||||
|
|
||||||
|
// ResponseHeaderTimeout, if non-zero, specifies the amount of time to wait
|
||||||
|
// for a server's response headers after fully writing the request.
|
||||||
|
ResponseHeaderTimeout time.Duration
|
||||||
|
|
||||||
|
// ExpectContinueTimeout, if non-zero, specifies the amount of time to wait
|
||||||
|
// for a server's first response headers after fully writing the request
|
||||||
|
// headers if the request has an "Expect: 100-continue" header.
|
||||||
|
ExpectContinueTimeout time.Duration
|
||||||
|
|
||||||
|
// MaxResponseHeaderBytes specifies a limit on how many response bytes are
|
||||||
|
// allowed in the server's response header. Zero means to use a default limit.
|
||||||
|
MaxResponseHeaderBytes int64
|
||||||
|
|
||||||
|
// MaxIdleConns controls the maximum number of idle (keep-alive) connections
|
||||||
|
// across all hosts. Zero means no limit.
|
||||||
|
MaxIdleConns int
|
||||||
|
|
||||||
|
// MaxIdleConnsPerHost, if non-zero, controls the maximum idle (keep-alive)
|
||||||
|
// connections to keep per-host.
|
||||||
|
MaxIdleConnsPerHost int
|
||||||
|
|
||||||
|
// MaxConnsPerHost optionally limits the total number of connections per host.
|
||||||
|
MaxConnsPerHost int
|
||||||
|
|
||||||
|
// HTTP2 configures HTTP/2 connections. This field does not yet have any effect.
|
||||||
|
HTTP2 *HTTP2Config
|
||||||
|
|
||||||
|
// Dial specifies the dial function for creating unencrypted TCP connections.
|
||||||
|
//
|
||||||
|
// Deprecated: Use DialContext instead. TINYGO: stored only; unused by the
|
||||||
|
// fetch-based round tripper.
|
||||||
|
Dial func(network, addr string) (net.Conn, error)
|
||||||
|
|
||||||
|
// DialContext specifies the dial function for creating unencrypted TCP
|
||||||
|
// connections. TINYGO: honored by callers that dial through the Transport
|
||||||
|
// directly (e.g. the relay WebSocket transport), which is how netbird routes
|
||||||
|
// connections through the netstack in the browser.
|
||||||
|
DialContext func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||||
|
}
|
||||||
|
|
||||||
var DefaultTransport RoundTripper = &Transport{}
|
var DefaultTransport RoundTripper = &Transport{}
|
||||||
|
|
||||||
|
// CloseIdleConnections closes any connections which were previously connected
|
||||||
|
// from previous requests but are now sitting idle. TINYGO: no-op.
|
||||||
|
func (t *Transport) CloseIdleConnections() {}
|
||||||
|
|
||||||
|
// Clone returns a copy of t with its exported fields shared. TINYGO: the
|
||||||
|
// tinygo Transport carries only configuration (round trips go through the host
|
||||||
|
// fetch API), so a shallow struct copy with duplicated maps is sufficient.
|
||||||
|
func (t *Transport) Clone() *Transport {
|
||||||
|
t2 := *t
|
||||||
|
if t.TLSClientConfig != nil {
|
||||||
|
t2.TLSClientConfig = t.TLSClientConfig.Clone()
|
||||||
|
}
|
||||||
|
if t.TLSNextProto != nil {
|
||||||
|
npm := make(map[string]func(authority string, c *tls.Conn) RoundTripper, len(t.TLSNextProto))
|
||||||
|
for k, v := range t.TLSNextProto {
|
||||||
|
npm[k] = v
|
||||||
|
}
|
||||||
|
t2.TLSNextProto = npm
|
||||||
|
}
|
||||||
|
return &t2
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProxyFromEnvironment returns the URL of the proxy to use for a given request.
|
||||||
|
// TINYGO: the wasm build has no environment proxy configuration, so this always
|
||||||
|
// reports "no proxy" (nil URL, nil error), which callers treat as a direct
|
||||||
|
// connection.
|
||||||
|
func ProxyFromEnvironment(req *Request) (*url.URL, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrSkipAltProtocol is a sentinel error value defined by Transport.RegisterProtocol.
|
||||||
|
var ErrSkipAltProtocol = errors.New("net/http: skip alternate protocol")
|
||||||
|
|
||||||
|
// RegisterProtocol registers a new protocol with scheme. TINYGO: no-op; the
|
||||||
|
// wasm build performs round trips via the host fetch API and does not dispatch
|
||||||
|
// on registered alternate protocols.
|
||||||
|
func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) {}
|
||||||
|
|||||||
@@ -35,6 +35,22 @@ type Interface struct {
|
|||||||
Flags Flags // e.g., FlagUp, FlagLoopback, FlagMulticast
|
Flags Flags // e.g., FlagUp, FlagLoopback, FlagMulticast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Addrs returns a list of unicast interface addresses for a specific
|
||||||
|
// interface.
|
||||||
|
//
|
||||||
|
// TINYGO: not implemented; netdev exposes no per-interface address list.
|
||||||
|
func (ifi *Interface) Addrs() ([]Addr, error) {
|
||||||
|
return nil, errors.New("Interface.Addrs not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MulticastAddrs returns a list of multicast, joined group addresses
|
||||||
|
// for a specific interface.
|
||||||
|
//
|
||||||
|
// TINYGO: not implemented; netdev exposes no per-interface address list.
|
||||||
|
func (ifi *Interface) MulticastAddrs() ([]Addr, error) {
|
||||||
|
return nil, errors.New("Interface.MulticastAddrs not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
type Flags uint
|
type Flags uint
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -93,3 +109,8 @@ func InterfaceAddrs() ([]Addr, error) {
|
|||||||
func InterfaceByIndex(index int) (*Interface, error) {
|
func InterfaceByIndex(index int) (*Interface, error) {
|
||||||
return nil, errors.New("InterfaceByIndex not implemented")
|
return nil, errors.New("InterfaceByIndex not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InterfaceByName returns the interface specified by name.
|
||||||
|
func InterfaceByName(name string) (*Interface, error) {
|
||||||
|
return nil, errors.New("InterfaceByName not implemented")
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,13 +7,145 @@
|
|||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/netip"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// A Resolver looks up names and numbers.
|
||||||
|
//
|
||||||
|
// A nil *Resolver is equivalent to a zero Resolver.
|
||||||
|
//
|
||||||
|
// TINYGO: name resolution is backed by the netdev GetHostByName hook;
|
||||||
|
// the PreferGo/StrictErrors/Dial fields exist for API compatibility with
|
||||||
|
// upstream Go and are ignored.
|
||||||
|
type Resolver struct {
|
||||||
|
// PreferGo controls whether Go's built-in DNS resolver is preferred
|
||||||
|
// on platforms where it's available. It is equivalent to setting
|
||||||
|
// GODEBUG=netdns=go, but scoped to just this resolver.
|
||||||
|
PreferGo bool
|
||||||
|
|
||||||
|
// StrictErrors controls the behavior of temporary errors
|
||||||
|
// (including timeout, socket errors, and SERVFAIL) when using
|
||||||
|
// Go's built-in resolver.
|
||||||
|
StrictErrors bool
|
||||||
|
|
||||||
|
// Dial optionally specifies an alternate dialer for use by
|
||||||
|
// Go's built-in DNS resolver to make TCP and UDP connections
|
||||||
|
// to DNS services.
|
||||||
|
Dial func(ctx context.Context, network, address string) (Conn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultResolver is the resolver used by the package-level Lookup
|
||||||
|
// functions and by Dialers without a specified Resolver.
|
||||||
|
var DefaultResolver = &Resolver{}
|
||||||
|
|
||||||
|
// LookupHost looks up the given host using the local resolver.
|
||||||
|
// It returns a slice of that host's addresses.
|
||||||
|
//
|
||||||
|
// LookupHost uses [context.Background] internally; to specify the context, use
|
||||||
|
// [Resolver.LookupHost].
|
||||||
|
func LookupHost(host string) (addrs []string, err error) {
|
||||||
|
return DefaultResolver.LookupHost(context.Background(), host)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupHost looks up the given host using the local resolver.
|
||||||
|
// It returns a slice of that host's addresses.
|
||||||
|
//
|
||||||
|
// TINYGO: resolves via netdev.GetHostByName, returning at most one address.
|
||||||
|
func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {
|
||||||
|
if host == "" {
|
||||||
|
return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
|
||||||
|
}
|
||||||
|
ip, err := netdev.GetHostByName(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &DNSError{Err: err.Error(), Name: host}
|
||||||
|
}
|
||||||
|
return []string{ip.String()}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupIP looks up host using the local resolver.
|
||||||
|
// It returns a slice of that host's IPv4 and IPv6 addresses.
|
||||||
|
func LookupIP(host string) ([]IP, error) {
|
||||||
|
addrs, err := DefaultResolver.LookupIPAddr(context.Background(), host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ips := make([]IP, 0, len(addrs))
|
||||||
|
for _, addr := range addrs {
|
||||||
|
ips = append(ips, addr.IP)
|
||||||
|
}
|
||||||
|
return ips, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupIPAddr looks up host using the local resolver.
|
||||||
|
// It returns a slice of that host's IPv4 and IPv6 addresses.
|
||||||
|
//
|
||||||
|
// TINYGO: resolves via netdev.GetHostByName, returning at most one address.
|
||||||
|
func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) {
|
||||||
|
if host == "" {
|
||||||
|
return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
|
||||||
|
}
|
||||||
|
ip, err := netdev.GetHostByName(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &DNSError{Err: err.Error(), Name: host}
|
||||||
|
}
|
||||||
|
return []IPAddr{{IP: ip.AsSlice(), Zone: ip.Zone()}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupNetIP looks up host using the local resolver.
|
||||||
|
// It returns a slice of that host's IP addresses of the type specified by
|
||||||
|
// network. The network must be one of "ip", "ip4" or "ip6".
|
||||||
|
//
|
||||||
|
// TINYGO: resolves via netdev.GetHostByName, returning at most one address.
|
||||||
|
func (r *Resolver) LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error) {
|
||||||
|
if host == "" {
|
||||||
|
return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
|
||||||
|
}
|
||||||
|
ip, err := netdev.GetHostByName(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &DNSError{Err: err.Error(), Name: host}
|
||||||
|
}
|
||||||
|
return []netip.Addr{ip}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// LookupPort looks up the port for the given network and service.
|
// LookupPort looks up the port for the given network and service.
|
||||||
//
|
//
|
||||||
// LookupPort uses [context.Background] internally; to specify the context, use
|
// LookupPort uses [context.Background] internally; to specify the context, use
|
||||||
// [Resolver.LookupPort].
|
// [Resolver.LookupPort].
|
||||||
func LookupPort(network, service string) (port int, err error) {
|
func LookupPort(network, service string) (port int, err error) {
|
||||||
|
return DefaultResolver.LookupPort(context.Background(), network, service)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupPort looks up the port for the given network and service.
|
||||||
|
//
|
||||||
|
// TINYGO: not implemented; netdev provides no service database.
|
||||||
|
func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {
|
||||||
return 0, errors.New("net:LookupPort not implemented")
|
return 0, errors.New("net:LookupPort not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An SRV represents a single DNS SRV record.
|
||||||
|
type SRV struct {
|
||||||
|
Target string
|
||||||
|
Port uint16
|
||||||
|
Priority uint16
|
||||||
|
Weight uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupSRV tries to resolve an SRV query of the given service, protocol, and
|
||||||
|
// domain name.
|
||||||
|
//
|
||||||
|
// TINYGO: not implemented; netdev provides no SRV record lookup.
|
||||||
|
func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
|
||||||
|
return "", nil, errors.New("net:LookupSRV not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupTXT returns the DNS TXT records for the given domain name.
|
||||||
|
//
|
||||||
|
// TINYGO: not implemented; netdev provides no TXT record lookup.
|
||||||
|
func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) {
|
||||||
|
return nil, errors.New("net:LookupTXT not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// errNoSuchHost is returned when the host lookup finds no matching records.
|
||||||
|
var errNoSuchHost = errors.New("no such host")
|
||||||
|
|||||||
+115
@@ -296,6 +296,61 @@ func (c *TCPConn) CloseWrite() error {
|
|||||||
return fmt.Errorf("CloseWrite not implemented")
|
return fmt.Errorf("CloseWrite not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloseRead shuts down the reading side of the TCP connection.
|
||||||
|
// Most callers should just use Close.
|
||||||
|
//
|
||||||
|
// TINYGO: no-op; netdev has no half-close, reads simply stop when Close
|
||||||
|
// is called.
|
||||||
|
func (c *TCPConn) CloseRead() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNoDelay controls whether the operating system should delay
|
||||||
|
// packet transmission in hopes of sending fewer packets (Nagle's
|
||||||
|
// algorithm). The default is true (no delay), meaning that data is
|
||||||
|
// sent as soon as possible after a Write.
|
||||||
|
//
|
||||||
|
// TINYGO: no-op; Nagle behavior is managed by the netdev driver.
|
||||||
|
func (c *TCPConn) SetNoDelay(noDelay bool) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadBuffer sets the size of the operating system's receive buffer
|
||||||
|
// associated with the connection.
|
||||||
|
//
|
||||||
|
// TINYGO: no-op; buffer sizing is managed by the netdev driver.
|
||||||
|
func (c *TCPConn) SetReadBuffer(bytes int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteBuffer sets the size of the operating system's transmit buffer
|
||||||
|
// associated with the connection.
|
||||||
|
//
|
||||||
|
// TINYGO: no-op; buffer sizing is managed by the netdev driver.
|
||||||
|
func (c *TCPConn) SetWriteBuffer(bytes int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFrom implements the io.ReaderFrom ReadFrom method.
|
||||||
|
//
|
||||||
|
// TINYGO: generic copy loop over Write; no zero-copy sendfile path.
|
||||||
|
func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
|
||||||
|
return genericReadFrom(c, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// genericReadFrom copies from r into w until EOF, returning the number
|
||||||
|
// of bytes copied.
|
||||||
|
func genericReadFrom(w io.Writer, r io.Reader) (int64, error) {
|
||||||
|
// Wrap w to hide its ReadFrom method, so io.Copy uses the generic
|
||||||
|
// read/write loop instead of recursing back into TCPConn.ReadFrom.
|
||||||
|
return io.Copy(onlyWriter{w}, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// onlyWriter hides w's ReadFrom method from io.Copy.
|
||||||
|
type onlyWriter struct {
|
||||||
|
io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
type listener struct {
|
type listener struct {
|
||||||
fd int
|
fd int
|
||||||
laddr *TCPAddr
|
laddr *TCPAddr
|
||||||
@@ -348,3 +403,63 @@ func listenTCP(laddr *TCPAddr) (Listener, error) {
|
|||||||
type TCPListener struct {
|
type TCPListener struct {
|
||||||
listener
|
listener
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListenTCP acts like [Listen] for TCP networks.
|
||||||
|
//
|
||||||
|
// The network must be a TCP network name; see func [Dial] for details.
|
||||||
|
// If the IP field of laddr is nil or an unspecified IP address,
|
||||||
|
// ListenTCP listens on all available unicast and anycast IP addresses
|
||||||
|
// of the local system. If the Port field of laddr is 0, a port number
|
||||||
|
// is automatically chosen.
|
||||||
|
func ListenTCP(network string, laddr *TCPAddr) (*TCPListener, error) {
|
||||||
|
switch network {
|
||||||
|
case "tcp", "tcp4":
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Network '%s' not supported", network)
|
||||||
|
}
|
||||||
|
|
||||||
|
if laddr == nil {
|
||||||
|
laddr = &TCPAddr{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := netdev.Socket(_AF_INET, _SOCK_STREAM, _IPPROTO_TCP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = netdev.Bind(fd, laddr.AddrPort()); err != nil {
|
||||||
|
netdev.Close(fd)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = netdev.Listen(fd, 5); err != nil {
|
||||||
|
netdev.Close(fd)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &TCPListener{listener{fd: fd, laddr: laddr}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcceptTCP accepts the next incoming call and returns the new
|
||||||
|
// connection.
|
||||||
|
func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
|
||||||
|
fd, raddr, err := netdev.Accept(l.fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &TCPConn{
|
||||||
|
fd: fd,
|
||||||
|
net: "tcp",
|
||||||
|
laddr: l.laddr,
|
||||||
|
raddr: TCPAddrFromAddrPort(raddr),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeadline sets the deadline associated with the listener.
|
||||||
|
// A zero time value disables the deadline.
|
||||||
|
//
|
||||||
|
// TINYGO: no-op; netdev.Accept has no deadline support.
|
||||||
|
func (l *TCPListener) SetDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"internal/itoa"
|
"internal/itoa"
|
||||||
"io"
|
"io"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@@ -139,3 +140,10 @@ func (c *TLSConn) Handshake() error {
|
|||||||
panic("TLSConn.Handshake() not implemented")
|
panic("TLSConn.Handshake() not implemented")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandshakeContext runs the client or server handshake protocol if it has not
|
||||||
|
// yet been run. TINYGO: TLS is offloaded to the network device; this exists to
|
||||||
|
// satisfy callers (e.g. pion/ice) that require the context-aware variant.
|
||||||
|
func (c *TLSConn) HandshakeContext(ctx context.Context) error {
|
||||||
|
return c.Handshake()
|
||||||
|
}
|
||||||
|
|||||||
+107
@@ -210,6 +210,53 @@ func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListenUDP acts like [ListenPacket] for UDP networks.
|
||||||
|
//
|
||||||
|
// The network must be a UDP network name; see func [Dial] for details.
|
||||||
|
//
|
||||||
|
// If the IP field of laddr is nil or an unspecified IP address,
|
||||||
|
// ListenUDP listens on all available IP addresses of the local system
|
||||||
|
// except multicast IP addresses.
|
||||||
|
// If the Port field of laddr is 0, a port number is automatically
|
||||||
|
// chosen.
|
||||||
|
func ListenUDP(network string, laddr *UDPAddr) (*UDPConn, error) {
|
||||||
|
switch network {
|
||||||
|
case "udp", "udp4":
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Network '%s' not supported", network)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TINYGO: Use netdev to create UDP socket and bind (no connect)
|
||||||
|
|
||||||
|
if laddr == nil {
|
||||||
|
laddr = &UDPAddr{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no port was given, grab an ephemeral port
|
||||||
|
if laddr.Port == 0 {
|
||||||
|
laddr.Port = ephemeralPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := netdev.Socket(_AF_INET, _SOCK_DGRAM, _IPPROTO_UDP)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lip, _ := netip.AddrFromSlice(laddr.IP)
|
||||||
|
laddrport := netip.AddrPortFrom(lip, uint16(laddr.Port))
|
||||||
|
|
||||||
|
if err = netdev.Bind(fd, laddrport); err != nil {
|
||||||
|
netdev.Close(fd)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &UDPConn{
|
||||||
|
fd: fd,
|
||||||
|
net: network,
|
||||||
|
laddr: laddr,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SyscallConn returns a raw network connection.
|
// SyscallConn returns a raw network connection.
|
||||||
// This implements the syscall.Conn interface.
|
// This implements the syscall.Conn interface.
|
||||||
func (c *UDPConn) SyscallConn() (syscall.RawConn, error) {
|
func (c *UDPConn) SyscallConn() (syscall.RawConn, error) {
|
||||||
@@ -242,6 +289,66 @@ func (c *UDPConn) Write(b []byte) (int, error) {
|
|||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadFromUDP acts like [UDPConn.ReadFrom] but returns a [UDPAddr].
|
||||||
|
//
|
||||||
|
// TINYGO: netdev has no per-datagram source address, so the connected
|
||||||
|
// remote address (c.raddr) is returned as the source.
|
||||||
|
func (c *UDPConn) ReadFromUDP(b []byte) (int, *UDPAddr, error) {
|
||||||
|
n, err := netdev.Recv(c.fd, b, 0, c.readDeadline)
|
||||||
|
if n < 0 {
|
||||||
|
n = 0
|
||||||
|
}
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
err = &OpError{Op: "read", Net: c.net, Source: c.laddr, Addr: c.raddr, Err: err}
|
||||||
|
}
|
||||||
|
return n, c.raddr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFromUDPAddrPort acts like [UDPConn.ReadFromUDP] but returns a [netip.AddrPort].
|
||||||
|
func (c *UDPConn) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) {
|
||||||
|
n, uaddr, err := c.ReadFromUDP(b)
|
||||||
|
if uaddr != nil {
|
||||||
|
addr = uaddr.AddrPort()
|
||||||
|
}
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteToUDP acts like [UDPConn.WriteTo] but takes a [UDPAddr].
|
||||||
|
//
|
||||||
|
// TINYGO: the socket is connected via netdev, so writes go to the
|
||||||
|
// connected remote regardless of addr, mirroring netdev.Send.
|
||||||
|
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
|
||||||
|
n, err := netdev.Send(c.fd, b, 0, c.writeDeadline)
|
||||||
|
if n < 0 {
|
||||||
|
n = 0
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
err = &OpError{Op: "write", Net: c.net, Source: c.laddr, Addr: addr.opAddr(), Err: err}
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteToUDPAddrPort acts like [UDPConn.WriteToUDP] but takes a [netip.AddrPort].
|
||||||
|
func (c *UDPConn) WriteToUDPAddrPort(b []byte, addr netip.AddrPort) (int, error) {
|
||||||
|
return c.WriteToUDP(b, UDPAddrFromAddrPort(addr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadBuffer sets the size of the operating system's receive buffer
|
||||||
|
// associated with the connection.
|
||||||
|
//
|
||||||
|
// TINYGO: no-op; buffer sizing is managed by the netdev driver.
|
||||||
|
func (c *UDPConn) SetReadBuffer(bytes int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteBuffer sets the size of the operating system's transmit buffer
|
||||||
|
// associated with the connection.
|
||||||
|
//
|
||||||
|
// TINYGO: no-op; buffer sizing is managed by the netdev driver.
|
||||||
|
func (c *UDPConn) SetWriteBuffer(bytes int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ReadFrom implements the PacketConn ReadFrom method.
|
// ReadFrom implements the PacketConn ReadFrom method.
|
||||||
func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
|
func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
|
||||||
return 0, nil, errors.New("ReadFrom not implemented")
|
return 0, nil, errors.New("ReadFrom not implemented")
|
||||||
|
|||||||
+32
@@ -6,6 +6,11 @@
|
|||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// BUG(mikio): On JS, WASIP1 and Plan 9, methods and functions related
|
// BUG(mikio): On JS, WASIP1 and Plan 9, methods and functions related
|
||||||
// to UnixConn and UnixListener are not implemented.
|
// to UnixConn and UnixListener are not implemented.
|
||||||
|
|
||||||
@@ -44,6 +49,33 @@ func (a *UnixAddr) opAddr() Addr {
|
|||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnixConn is an implementation of the Conn interface for connections to Unix
|
||||||
|
// domain sockets.
|
||||||
|
//
|
||||||
|
// TINYGO: Unix sockets are not implemented (the browser has no filesystem
|
||||||
|
// sockets). This type exists only to satisfy references and type assertions
|
||||||
|
// such as those in github.com/gliderlabs/ssh agent forwarding; all methods
|
||||||
|
// return an error. The corresponding code paths are never reached at runtime in
|
||||||
|
// the wasm build.
|
||||||
|
type UnixConn struct {
|
||||||
|
fd int
|
||||||
|
laddr *UnixAddr
|
||||||
|
raddr *UnixAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
var errUnixNotImplemented = errors.New("net: Unix sockets not implemented")
|
||||||
|
|
||||||
|
func (c *UnixConn) Read(b []byte) (int, error) { return 0, errUnixNotImplemented }
|
||||||
|
func (c *UnixConn) Write(b []byte) (int, error) { return 0, errUnixNotImplemented }
|
||||||
|
func (c *UnixConn) Close() error { return errUnixNotImplemented }
|
||||||
|
func (c *UnixConn) CloseRead() error { return errUnixNotImplemented }
|
||||||
|
func (c *UnixConn) CloseWrite() error { return errUnixNotImplemented }
|
||||||
|
func (c *UnixConn) LocalAddr() Addr { return c.laddr }
|
||||||
|
func (c *UnixConn) RemoteAddr() Addr { return c.raddr }
|
||||||
|
func (c *UnixConn) SetDeadline(t time.Time) error { return errUnixNotImplemented }
|
||||||
|
func (c *UnixConn) SetReadDeadline(t time.Time) error { return errUnixNotImplemented }
|
||||||
|
func (c *UnixConn) SetWriteDeadline(t time.Time) error { return errUnixNotImplemented }
|
||||||
|
|
||||||
// ResolveUnixAddr returns an address of Unix domain socket end point.
|
// ResolveUnixAddr returns an address of Unix domain socket end point.
|
||||||
//
|
//
|
||||||
// The network must be a Unix network name.
|
// The network must be a Unix network name.
|
||||||
|
|||||||
Reference in New Issue
Block a user