mirror of
https://github.com/tinygo-org/net.git
synced 2026-08-08 21:53:38 +00:00
more complete PR for adding more net support (#64)
This commit is contained in:
@@ -14,6 +14,7 @@ package http
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -117,6 +118,11 @@ type Response struct {
|
||||
// Request's Body is nil (having already been consumed).
|
||||
// This is only populated for Client requests.
|
||||
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.
|
||||
|
||||
@@ -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.
|
||||
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
|
||||
|
||||
disableKeepAlives int32 // accessed atomically.
|
||||
|
||||
+107
-1
@@ -12,8 +12,14 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type readTrackingBody struct {
|
||||
@@ -22,6 +28,106 @@ type readTrackingBody struct {
|
||||
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{}
|
||||
|
||||
// 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) {}
|
||||
|
||||
@@ -109,3 +109,8 @@ func InterfaceAddrs() ([]Addr, error) {
|
||||
func InterfaceByIndex(index int) (*Interface, error) {
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -124,5 +124,28 @@ func (r *Resolver) LookupPort(ctx context.Context, network, service string) (por
|
||||
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")
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"context"
|
||||
"internal/itoa"
|
||||
"io"
|
||||
"net/netip"
|
||||
@@ -139,3 +140,10 @@ func (c *TLSConn) Handshake() error {
|
||||
panic("TLSConn.Handshake() not implemented")
|
||||
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()
|
||||
}
|
||||
|
||||
+32
@@ -6,6 +6,11 @@
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BUG(mikio): On JS, WASIP1 and Plan 9, methods and functions related
|
||||
// to UnixConn and UnixListener are not implemented.
|
||||
|
||||
@@ -44,6 +49,33 @@ func (a *UnixAddr) opAddr() Addr {
|
||||
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.
|
||||
//
|
||||
// The network must be a Unix network name.
|
||||
|
||||
Reference in New Issue
Block a user