updated vendor
This commit is contained in:
-11
@@ -1,11 +0,0 @@
|
||||
// Copyright 2025 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.
|
||||
|
||||
//go:build go1.25
|
||||
|
||||
package blake2b
|
||||
|
||||
import "hash"
|
||||
|
||||
var _ hash.XOF = (*xof)(nil)
|
||||
+13
-8
@@ -11,6 +11,7 @@
|
||||
package hkdf
|
||||
|
||||
import (
|
||||
"crypto/hkdf"
|
||||
"crypto/hmac"
|
||||
"errors"
|
||||
"hash"
|
||||
@@ -24,15 +25,19 @@ import (
|
||||
// Expand invocations and different context values. Most common scenarios,
|
||||
// including the generation of multiple keys, should use New instead.
|
||||
func Extract(hash func() hash.Hash, secret, salt []byte) []byte {
|
||||
if salt == nil {
|
||||
salt = make([]byte, hash().Size())
|
||||
// Use the stdlib Extract, which disables FIPS 140 enforcement of the HMAC
|
||||
// key (which in HKDF is the salt). The only possible error is FIPS 140
|
||||
// enforcement of the hash, which had to panic under this API anyway. We
|
||||
// don't use the stdlib Expand, because it switched to returning a []byte
|
||||
// instead of an io.Reader, and Expand uses the HMAC key as a key.
|
||||
out, err := hkdf.Extract(hash, secret, salt)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
extractor := hmac.New(hash, salt)
|
||||
extractor.Write(secret)
|
||||
return extractor.Sum(nil)
|
||||
return out
|
||||
}
|
||||
|
||||
type hkdf struct {
|
||||
type hkdfReader struct {
|
||||
expander hash.Hash
|
||||
size int
|
||||
|
||||
@@ -43,7 +48,7 @@ type hkdf struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (f *hkdf) Read(p []byte) (int, error) {
|
||||
func (f *hkdfReader) Read(p []byte) (int, error) {
|
||||
// Check whether enough data can be generated
|
||||
need := len(p)
|
||||
remains := len(f.buf) + int(255-f.counter+1)*f.size
|
||||
@@ -84,7 +89,7 @@ func (f *hkdf) Read(p []byte) (int, error) {
|
||||
// 3.3. Most common scenarios will want to use New instead.
|
||||
func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader {
|
||||
expander := hmac.New(hash, pseudorandomKey)
|
||||
return &hkdf{expander, expander.Size(), info, 1, nil, nil}
|
||||
return &hkdfReader{expander, expander.Size(), info, 1, nil, nil}
|
||||
}
|
||||
|
||||
// New returns a Reader, from which keys can be read, using the given hash,
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
This package (golang.org/x/net/http2) is the original source of truth
|
||||
of the Go HTTP/2 implementation.
|
||||
|
||||
As of Go 1.27, the source of truth has moved to the standard library
|
||||
package net/http/internal/http2.
|
||||
All new feature development should happen in that package.
|
||||
Only critical bug fixes and security fixes will be backported to x/net.
|
||||
|
||||
The x/net package contains two implementations of the HTTP/2 transport and server:
|
||||
|
||||
The original implementation (no longer the source of truth).
|
||||
|
||||
A reimplementation of the x/net/http2 APIs in terms of net/http.
|
||||
This is called "the wrapping implementation", since it wraps net/http.
|
||||
|
||||
The original implementation is used when the Go version is less than 1.27.
|
||||
|
||||
The wrapping implementation is used when the Go version is at least 1.27.
|
||||
The build tag "http2legacy" may be set to use the original implementation.
|
||||
+2
-12
@@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !(go1.27 && !http2legacy)
|
||||
|
||||
// Transport code's client connection pooling.
|
||||
|
||||
package http2
|
||||
@@ -14,18 +16,6 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ClientConnPool manages a pool of HTTP/2 client connections.
|
||||
type ClientConnPool interface {
|
||||
// GetClientConn returns a specific HTTP/2 connection (usually
|
||||
// a TLS-TCP connection) to an HTTP/2 server. On success, the
|
||||
// returned ClientConn accounts for the upcoming RoundTrip
|
||||
// call, so the caller should not omit it. If the caller needs
|
||||
// to, ClientConn.RoundTrip can be called with a bogus
|
||||
// new(http.Request) to release the stream reservation.
|
||||
GetClientConn(req *http.Request, addr string) (*ClientConn, error)
|
||||
MarkDead(*ClientConn)
|
||||
}
|
||||
|
||||
// clientConnPoolIdleCloser is the interface implemented by ClientConnPool
|
||||
// implementations which can close their idle connections.
|
||||
type clientConnPoolIdleCloser interface {
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
//go:build !go1.27
|
||||
|
||||
package http2
|
||||
|
||||
import "net/http"
|
||||
|
||||
// Support for go.dev/issue/75500 is added in Go 1.27. In case anyone uses
|
||||
// x/net with versions before Go 1.27, we return true here so that their write
|
||||
// scheduler will still be the round-robin write scheduler rather than the RFC
|
||||
// 9218 write scheduler. That way, older users of Go will not see a sudden
|
||||
// change of behavior just from importing x/net.
|
||||
//
|
||||
// TODO(nsh): remove this file after x/net go.mod is at Go 1.27.
|
||||
func clientPriorityDisabled(_ *http.Server) bool {
|
||||
return true
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
//go:build go1.27
|
||||
|
||||
package http2
|
||||
|
||||
import "net/http"
|
||||
|
||||
func clientPriorityDisabled(s *http.Server) bool {
|
||||
return s.DisableClientPriority
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
// Copyright 2026 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 http2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return cc.roundTrip(req)
|
||||
}
|
||||
|
||||
// SetDoNotReuse marks cc as not reusable for future HTTP requests.
|
||||
func (cc *ClientConn) SetDoNotReuse() {
|
||||
cc.setDoNotReuse()
|
||||
}
|
||||
|
||||
// CanTakeNewRequest reports whether the connection can take a new request,
|
||||
// meaning it has not been closed or received or sent a GOAWAY.
|
||||
//
|
||||
// If the caller is going to immediately make a new request on this
|
||||
// connection, use ReserveNewRequest instead.
|
||||
func (cc *ClientConn) CanTakeNewRequest() bool {
|
||||
return cc.canTakeNewRequest()
|
||||
}
|
||||
|
||||
// ReserveNewRequest is like CanTakeNewRequest but also reserves a
|
||||
// concurrent stream in cc. The reservation is decremented on the
|
||||
// next call to RoundTrip.
|
||||
func (cc *ClientConn) ReserveNewRequest() bool {
|
||||
return cc.reserveNewRequest()
|
||||
}
|
||||
|
||||
// State returns a snapshot of cc's state.
|
||||
func (cc *ClientConn) State() ClientConnState {
|
||||
return cc.state()
|
||||
}
|
||||
|
||||
// Shutdown gracefully closes the client connection, waiting for running streams to complete.
|
||||
func (cc *ClientConn) Shutdown(ctx context.Context) error {
|
||||
return cc.shutdown(ctx)
|
||||
}
|
||||
|
||||
// Close closes the client connection immediately.
|
||||
//
|
||||
// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead.
|
||||
func (cc *ClientConn) Close() error {
|
||||
return cc.close()
|
||||
}
|
||||
|
||||
// Ping sends a PING frame to the server and waits for the ack.
|
||||
func (cc *ClientConn) Ping(ctx context.Context) error {
|
||||
return cc.ping(ctx)
|
||||
}
|
||||
+2
@@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !(go1.27 && !http2legacy)
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
|
||||
+153
-35
@@ -11,11 +11,13 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/internal/httpsfv"
|
||||
)
|
||||
|
||||
const frameHeaderLen = 9
|
||||
@@ -23,33 +25,36 @@ const frameHeaderLen = 9
|
||||
var padZeros = make([]byte, 255) // zeros for padding
|
||||
|
||||
// A FrameType is a registered frame type as defined in
|
||||
// https://httpwg.org/specs/rfc7540.html#rfc.section.11.2
|
||||
// https://httpwg.org/specs/rfc7540.html#rfc.section.11.2 and other future
|
||||
// RFCs.
|
||||
type FrameType uint8
|
||||
|
||||
const (
|
||||
FrameData FrameType = 0x0
|
||||
FrameHeaders FrameType = 0x1
|
||||
FramePriority FrameType = 0x2
|
||||
FrameRSTStream FrameType = 0x3
|
||||
FrameSettings FrameType = 0x4
|
||||
FramePushPromise FrameType = 0x5
|
||||
FramePing FrameType = 0x6
|
||||
FrameGoAway FrameType = 0x7
|
||||
FrameWindowUpdate FrameType = 0x8
|
||||
FrameContinuation FrameType = 0x9
|
||||
FrameData FrameType = 0x0
|
||||
FrameHeaders FrameType = 0x1
|
||||
FramePriority FrameType = 0x2
|
||||
FrameRSTStream FrameType = 0x3
|
||||
FrameSettings FrameType = 0x4
|
||||
FramePushPromise FrameType = 0x5
|
||||
FramePing FrameType = 0x6
|
||||
FrameGoAway FrameType = 0x7
|
||||
FrameWindowUpdate FrameType = 0x8
|
||||
FrameContinuation FrameType = 0x9
|
||||
FramePriorityUpdate FrameType = 0x10
|
||||
)
|
||||
|
||||
var frameNames = [...]string{
|
||||
FrameData: "DATA",
|
||||
FrameHeaders: "HEADERS",
|
||||
FramePriority: "PRIORITY",
|
||||
FrameRSTStream: "RST_STREAM",
|
||||
FrameSettings: "SETTINGS",
|
||||
FramePushPromise: "PUSH_PROMISE",
|
||||
FramePing: "PING",
|
||||
FrameGoAway: "GOAWAY",
|
||||
FrameWindowUpdate: "WINDOW_UPDATE",
|
||||
FrameContinuation: "CONTINUATION",
|
||||
FrameData: "DATA",
|
||||
FrameHeaders: "HEADERS",
|
||||
FramePriority: "PRIORITY",
|
||||
FrameRSTStream: "RST_STREAM",
|
||||
FrameSettings: "SETTINGS",
|
||||
FramePushPromise: "PUSH_PROMISE",
|
||||
FramePing: "PING",
|
||||
FrameGoAway: "GOAWAY",
|
||||
FrameWindowUpdate: "WINDOW_UPDATE",
|
||||
FrameContinuation: "CONTINUATION",
|
||||
FramePriorityUpdate: "PRIORITY_UPDATE",
|
||||
}
|
||||
|
||||
func (t FrameType) String() string {
|
||||
@@ -125,21 +130,24 @@ var flagName = map[FrameType]map[Flags]string{
|
||||
type frameParser func(fc *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error)
|
||||
|
||||
var frameParsers = [...]frameParser{
|
||||
FrameData: parseDataFrame,
|
||||
FrameHeaders: parseHeadersFrame,
|
||||
FramePriority: parsePriorityFrame,
|
||||
FrameRSTStream: parseRSTStreamFrame,
|
||||
FrameSettings: parseSettingsFrame,
|
||||
FramePushPromise: parsePushPromise,
|
||||
FramePing: parsePingFrame,
|
||||
FrameGoAway: parseGoAwayFrame,
|
||||
FrameWindowUpdate: parseWindowUpdateFrame,
|
||||
FrameContinuation: parseContinuationFrame,
|
||||
FrameData: parseDataFrame,
|
||||
FrameHeaders: parseHeadersFrame,
|
||||
FramePriority: parsePriorityFrame,
|
||||
FrameRSTStream: parseRSTStreamFrame,
|
||||
FrameSettings: parseSettingsFrame,
|
||||
FramePushPromise: parsePushPromise,
|
||||
FramePing: parsePingFrame,
|
||||
FrameGoAway: parseGoAwayFrame,
|
||||
FrameWindowUpdate: parseWindowUpdateFrame,
|
||||
FrameContinuation: parseContinuationFrame,
|
||||
FramePriorityUpdate: parsePriorityUpdateFrame,
|
||||
}
|
||||
|
||||
func typeFrameParser(t FrameType) frameParser {
|
||||
if int(t) < len(frameParsers) {
|
||||
return frameParsers[t]
|
||||
if f := frameParsers[t]; f != nil {
|
||||
return f
|
||||
}
|
||||
}
|
||||
return parseUnknownFrame
|
||||
}
|
||||
@@ -1180,9 +1188,34 @@ type PriorityFrame struct {
|
||||
PriorityParam
|
||||
}
|
||||
|
||||
var defaultRFC9218Priority = PriorityParam{
|
||||
incremental: 0,
|
||||
urgency: 3,
|
||||
// defaultRFC9218Priority determines what priority we should use as the default
|
||||
// value.
|
||||
//
|
||||
// According to RFC 9218, by default, streams should be given an urgency of 3
|
||||
// and should be non-incremental. However, making streams non-incremental by
|
||||
// default would be a huge change to our historical behavior where we would
|
||||
// round-robin writes across streams. When streams are non-incremental, we
|
||||
// would process streams of the same urgency one-by-one to completion instead.
|
||||
//
|
||||
// To avoid such a sudden change which might break some HTTP/2 users, this
|
||||
// function allows the caller to specify whether they can actually use the
|
||||
// default value as specified in RFC 9218. If not, this function will return a
|
||||
// priority value where streams are incremental by default instead: effectively
|
||||
// a round-robin between stream of the same urgency.
|
||||
//
|
||||
// As an example, a server might not be able to use the RFC 9218 default value
|
||||
// when it's not sure that the client it is serving is aware of RFC 9218.
|
||||
func defaultRFC9218Priority(canUseDefault bool) PriorityParam {
|
||||
if canUseDefault {
|
||||
return PriorityParam{
|
||||
urgency: 3,
|
||||
incremental: 0,
|
||||
}
|
||||
}
|
||||
return PriorityParam{
|
||||
urgency: 3,
|
||||
incremental: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Note that HTTP/2 has had two different prioritization schemes, and
|
||||
@@ -1266,6 +1299,74 @@ func (f *Framer) WritePriority(streamID uint32, p PriorityParam) error {
|
||||
return f.endWrite()
|
||||
}
|
||||
|
||||
// PriorityUpdateFrame is a PRIORITY_UPDATE frame as described in
|
||||
// https://www.rfc-editor.org/rfc/rfc9218.html#name-the-priority_update-frame.
|
||||
type PriorityUpdateFrame struct {
|
||||
FrameHeader
|
||||
Priority string
|
||||
PrioritizedStreamID uint32
|
||||
}
|
||||
|
||||
func parseRFC9218Priority(s string, canUseDefault bool) (p PriorityParam, ok bool) {
|
||||
p = defaultRFC9218Priority(canUseDefault)
|
||||
ok = httpsfv.ParseDictionary(s, func(key, val, _ string) {
|
||||
switch key {
|
||||
case "u":
|
||||
if u, ok := httpsfv.ParseInteger(val); ok && u >= 0 && u <= 7 {
|
||||
p.urgency = uint8(u)
|
||||
}
|
||||
case "i":
|
||||
if i, ok := httpsfv.ParseBoolean(val); ok {
|
||||
if i {
|
||||
p.incremental = 1
|
||||
} else {
|
||||
p.incremental = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if !ok {
|
||||
return defaultRFC9218Priority(canUseDefault), ok
|
||||
}
|
||||
return p, true
|
||||
}
|
||||
|
||||
func parsePriorityUpdateFrame(_ *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error) {
|
||||
if fh.StreamID != 0 {
|
||||
countError("frame_priority_update_non_zero_stream")
|
||||
return nil, connError{ErrCodeProtocol, "PRIORITY_UPDATE frame with non-zero stream ID"}
|
||||
}
|
||||
if len(payload) < 4 {
|
||||
countError("frame_priority_update_bad_length")
|
||||
return nil, connError{ErrCodeFrameSize, fmt.Sprintf("PRIORITY_UPDATE frame payload size was %d; want at least 4", len(payload))}
|
||||
}
|
||||
v := binary.BigEndian.Uint32(payload[:4])
|
||||
streamID := v & 0x7fffffff // mask off high bit
|
||||
if streamID == 0 {
|
||||
countError("frame_priority_update_prioritizing_zero_stream")
|
||||
return nil, connError{ErrCodeProtocol, "PRIORITY_UPDATE frame with prioritized stream ID of zero"}
|
||||
}
|
||||
return &PriorityUpdateFrame{
|
||||
FrameHeader: fh,
|
||||
PrioritizedStreamID: streamID,
|
||||
Priority: string(payload[4:]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WritePriorityUpdate writes a PRIORITY_UPDATE frame.
|
||||
//
|
||||
// It will perform exactly one Write to the underlying Writer.
|
||||
// It is the caller's responsibility to not call other Write methods concurrently.
|
||||
func (f *Framer) WritePriorityUpdate(streamID uint32, priority string) error {
|
||||
if !validStreamID(streamID) && !f.AllowIllegalWrites {
|
||||
return errStreamID
|
||||
}
|
||||
f.startWrite(FramePriorityUpdate, 0, 0)
|
||||
f.writeUint32(streamID)
|
||||
f.writeBytes([]byte(priority))
|
||||
return f.endWrite()
|
||||
}
|
||||
|
||||
// A RSTStreamFrame allows for abnormal termination of a stream.
|
||||
// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.4
|
||||
type RSTStreamFrame struct {
|
||||
@@ -1547,6 +1648,23 @@ func (mh *MetaHeadersFrame) PseudoFields() []hpack.HeaderField {
|
||||
return mh.Fields
|
||||
}
|
||||
|
||||
func (mh *MetaHeadersFrame) rfc9218Priority(priorityAware bool) (p PriorityParam, priorityAwareAfter, hasIntermediary bool) {
|
||||
var s string
|
||||
for _, field := range mh.Fields {
|
||||
if field.Name == "priority" {
|
||||
s = field.Value
|
||||
priorityAware = true
|
||||
}
|
||||
if slices.Contains([]string{"via", "forwarded", "x-forwarded-for"}, field.Name) {
|
||||
hasIntermediary = true
|
||||
}
|
||||
}
|
||||
// No need to check for ok. parseRFC9218Priority will return a default
|
||||
// value if there is no priority field or if the field cannot be parsed.
|
||||
p, _ = parseRFC9218Priority(s, priorityAware && !hasIntermediary)
|
||||
return p, priorityAware, hasIntermediary
|
||||
}
|
||||
|
||||
func (mh *MetaHeadersFrame) checkPseudos() error {
|
||||
var isRequest, isResponse bool
|
||||
pf := mh.PseudoFields()
|
||||
|
||||
+13
-7
@@ -4,13 +4,17 @@
|
||||
|
||||
// Package http2 implements the HTTP/2 protocol.
|
||||
//
|
||||
// This package is low-level and intended to be used directly by very
|
||||
// few people. Most users will use it indirectly through the automatic
|
||||
// use by the net/http package (from Go 1.6 and later).
|
||||
// For use in earlier Go versions see ConfigureServer. (Transport support
|
||||
// requires Go 1.6 or later)
|
||||
// Almost no users should need to import this package directly.
|
||||
// The net/http package supports HTTP/2 natively.
|
||||
//
|
||||
// See https://http2.github.io/ for more information on HTTP/2.
|
||||
// To enable or disable HTTP/2 support in net/http clients and servers, see
|
||||
// [http.Transport.Protocols] and [http.Server.Protocols].
|
||||
//
|
||||
// To configure HTTP/2 parameters, see
|
||||
// [http.Transport.HTTP2] and [http.Server.HTTP2].
|
||||
//
|
||||
// To create HTTP/1 or HTTP/2 connections, see
|
||||
// [http.Transport.NewClientConn].
|
||||
package http2 // import "golang.org/x/net/http2"
|
||||
|
||||
import (
|
||||
@@ -169,6 +173,7 @@ const (
|
||||
SettingMaxFrameSize SettingID = 0x5
|
||||
SettingMaxHeaderListSize SettingID = 0x6
|
||||
SettingEnableConnectProtocol SettingID = 0x8
|
||||
SettingNoRFC7540Priorities SettingID = 0x9
|
||||
)
|
||||
|
||||
var settingName = map[SettingID]string{
|
||||
@@ -179,6 +184,7 @@ var settingName = map[SettingID]string{
|
||||
SettingMaxFrameSize: "MAX_FRAME_SIZE",
|
||||
SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE",
|
||||
SettingEnableConnectProtocol: "ENABLE_CONNECT_PROTOCOL",
|
||||
SettingNoRFC7540Priorities: "NO_RFC7540_PRIORITIES",
|
||||
}
|
||||
|
||||
func (s SettingID) String() string {
|
||||
@@ -189,7 +195,7 @@ func (s SettingID) String() string {
|
||||
}
|
||||
|
||||
// validWireHeaderFieldName reports whether v is a valid header field
|
||||
// name (key). See httpguts.ValidHeaderName for the base rules.
|
||||
// name (key). See httpguts.ValidHeaderFieldName for the base rules.
|
||||
//
|
||||
// Further, http2 says:
|
||||
//
|
||||
|
||||
+87
-211
@@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !(go1.27 && !http2legacy)
|
||||
|
||||
// TODO: turn off the serve goroutine when idle, so
|
||||
// an idle conn only has the readFrames goroutine active. (which could
|
||||
// also be optimized probably to pin less memory in crypto/tls). This
|
||||
@@ -88,96 +90,6 @@ var (
|
||||
testHookOnPanic func(sc *serverConn, panicVal interface{}) (rePanic bool)
|
||||
)
|
||||
|
||||
// Server is an HTTP/2 server.
|
||||
type Server struct {
|
||||
// MaxHandlers limits the number of http.Handler ServeHTTP goroutines
|
||||
// which may run at a time over all connections.
|
||||
// Negative or zero no limit.
|
||||
// TODO: implement
|
||||
MaxHandlers int
|
||||
|
||||
// MaxConcurrentStreams optionally specifies the number of
|
||||
// concurrent streams that each client may have open at a
|
||||
// time. This is unrelated to the number of http.Handler goroutines
|
||||
// which may be active globally, which is MaxHandlers.
|
||||
// If zero, MaxConcurrentStreams defaults to at least 100, per
|
||||
// the HTTP/2 spec's recommendations.
|
||||
MaxConcurrentStreams uint32
|
||||
|
||||
// MaxDecoderHeaderTableSize optionally specifies the http2
|
||||
// SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame. It
|
||||
// informs the remote endpoint of the maximum size of the header compression
|
||||
// table used to decode header blocks, in octets. If zero, the default value
|
||||
// of 4096 is used.
|
||||
MaxDecoderHeaderTableSize uint32
|
||||
|
||||
// MaxEncoderHeaderTableSize optionally specifies an upper limit for the
|
||||
// header compression table used for encoding request headers. Received
|
||||
// SETTINGS_HEADER_TABLE_SIZE settings are capped at this limit. If zero,
|
||||
// the default value of 4096 is used.
|
||||
MaxEncoderHeaderTableSize uint32
|
||||
|
||||
// MaxReadFrameSize optionally specifies the largest frame
|
||||
// this server is willing to read. A valid value is between
|
||||
// 16k and 16M, inclusive. If zero or otherwise invalid, a
|
||||
// default value is used.
|
||||
MaxReadFrameSize uint32
|
||||
|
||||
// PermitProhibitedCipherSuites, if true, permits the use of
|
||||
// cipher suites prohibited by the HTTP/2 spec.
|
||||
PermitProhibitedCipherSuites bool
|
||||
|
||||
// IdleTimeout specifies how long until idle clients should be
|
||||
// closed with a GOAWAY frame. PING frames are not considered
|
||||
// activity for the purposes of IdleTimeout.
|
||||
// If zero or negative, there is no timeout.
|
||||
IdleTimeout time.Duration
|
||||
|
||||
// ReadIdleTimeout is the timeout after which a health check using a ping
|
||||
// frame will be carried out if no frame is received on the connection.
|
||||
// If zero, no health check is performed.
|
||||
ReadIdleTimeout time.Duration
|
||||
|
||||
// PingTimeout is the timeout after which the connection will be closed
|
||||
// if a response to a ping is not received.
|
||||
// If zero, a default of 15 seconds is used.
|
||||
PingTimeout time.Duration
|
||||
|
||||
// WriteByteTimeout is the timeout after which a connection will be
|
||||
// closed if no data can be written to it. The timeout begins when data is
|
||||
// available to write, and is extended whenever any bytes are written.
|
||||
// If zero or negative, there is no timeout.
|
||||
WriteByteTimeout time.Duration
|
||||
|
||||
// MaxUploadBufferPerConnection is the size of the initial flow
|
||||
// control window for each connections. The HTTP/2 spec does not
|
||||
// allow this to be smaller than 65535 or larger than 2^32-1.
|
||||
// If the value is outside this range, a default value will be
|
||||
// used instead.
|
||||
MaxUploadBufferPerConnection int32
|
||||
|
||||
// MaxUploadBufferPerStream is the size of the initial flow control
|
||||
// window for each stream. The HTTP/2 spec does not allow this to
|
||||
// be larger than 2^32-1. If the value is zero or larger than the
|
||||
// maximum, a default value will be used instead.
|
||||
MaxUploadBufferPerStream int32
|
||||
|
||||
// NewWriteScheduler constructs a write scheduler for a connection.
|
||||
// If nil, a default scheduler is chosen.
|
||||
NewWriteScheduler func() WriteScheduler
|
||||
|
||||
// CountError, if non-nil, is called on HTTP/2 server errors.
|
||||
// It's intended to increment a metric for monitoring, such
|
||||
// as an expvar or Prometheus metric.
|
||||
// The errType consists of only ASCII word characters.
|
||||
CountError func(errType string)
|
||||
|
||||
// Internal state. This is a pointer (rather than embedded directly)
|
||||
// so that we don't embed a Mutex in this struct, which will make the
|
||||
// struct non-copyable, which might break some callers.
|
||||
state *serverInternalState
|
||||
}
|
||||
|
||||
type serverInternalState struct {
|
||||
mu sync.Mutex
|
||||
activeConns map[*serverConn]struct{}
|
||||
@@ -185,6 +97,9 @@ type serverInternalState struct {
|
||||
// Pool of error channels. This is per-Server rather than global
|
||||
// because channels can't be reused across synctest bubbles.
|
||||
errChanPool sync.Pool
|
||||
|
||||
// Used in tests.
|
||||
testNewConn func(*serverConn)
|
||||
}
|
||||
|
||||
func (s *serverInternalState) registerConn(sc *serverConn) {
|
||||
@@ -237,12 +152,7 @@ func (s *serverInternalState) putErrChan(ch chan error) {
|
||||
s.errChanPool.Put(ch)
|
||||
}
|
||||
|
||||
// ConfigureServer adds HTTP/2 support to a net/http Server.
|
||||
//
|
||||
// The configuration conf may be nil.
|
||||
//
|
||||
// ConfigureServer must be called before s begins serving.
|
||||
func ConfigureServer(s *http.Server, conf *Server) error {
|
||||
func configureServer(s *http.Server, conf *Server) error {
|
||||
if s == nil {
|
||||
panic("nil *http.Server")
|
||||
}
|
||||
@@ -347,83 +257,6 @@ func ConfigureServer(s *http.Server, conf *Server) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServeConnOpts are options for the Server.ServeConn method.
|
||||
type ServeConnOpts struct {
|
||||
// Context is the base context to use.
|
||||
// If nil, context.Background is used.
|
||||
Context context.Context
|
||||
|
||||
// BaseConfig optionally sets the base configuration
|
||||
// for values. If nil, defaults are used.
|
||||
BaseConfig *http.Server
|
||||
|
||||
// Handler specifies which handler to use for processing
|
||||
// requests. If nil, BaseConfig.Handler is used. If BaseConfig
|
||||
// or BaseConfig.Handler is nil, http.DefaultServeMux is used.
|
||||
Handler http.Handler
|
||||
|
||||
// UpgradeRequest is an initial request received on a connection
|
||||
// undergoing an h2c upgrade. The request body must have been
|
||||
// completely read from the connection before calling ServeConn,
|
||||
// and the 101 Switching Protocols response written.
|
||||
UpgradeRequest *http.Request
|
||||
|
||||
// Settings is the decoded contents of the HTTP2-Settings header
|
||||
// in an h2c upgrade request.
|
||||
Settings []byte
|
||||
|
||||
// SawClientPreface is set if the HTTP/2 connection preface
|
||||
// has already been read from the connection.
|
||||
SawClientPreface bool
|
||||
}
|
||||
|
||||
func (o *ServeConnOpts) context() context.Context {
|
||||
if o != nil && o.Context != nil {
|
||||
return o.Context
|
||||
}
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
func (o *ServeConnOpts) baseConfig() *http.Server {
|
||||
if o != nil && o.BaseConfig != nil {
|
||||
return o.BaseConfig
|
||||
}
|
||||
return new(http.Server)
|
||||
}
|
||||
|
||||
func (o *ServeConnOpts) handler() http.Handler {
|
||||
if o != nil {
|
||||
if o.Handler != nil {
|
||||
return o.Handler
|
||||
}
|
||||
if o.BaseConfig != nil && o.BaseConfig.Handler != nil {
|
||||
return o.BaseConfig.Handler
|
||||
}
|
||||
}
|
||||
return http.DefaultServeMux
|
||||
}
|
||||
|
||||
// ServeConn serves HTTP/2 requests on the provided connection and
|
||||
// blocks until the connection is no longer readable.
|
||||
//
|
||||
// ServeConn starts speaking HTTP/2 assuming that c has not had any
|
||||
// reads or writes. It writes its initial settings frame and expects
|
||||
// to be able to read the preface and settings frame from the
|
||||
// client. If c has a ConnectionState method like a *tls.Conn, the
|
||||
// ConnectionState is used to verify the TLS ciphersuite and to set
|
||||
// the Request.TLS field in Handlers.
|
||||
//
|
||||
// ServeConn does not support h2c by itself. Any h2c support must be
|
||||
// implemented in terms of providing a suitably-behaving net.Conn.
|
||||
//
|
||||
// The opts parameter is optional. If nil, default values are used.
|
||||
func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||
if opts == nil {
|
||||
opts = &ServeConnOpts{}
|
||||
}
|
||||
s.serveConn(c, opts, nil)
|
||||
}
|
||||
|
||||
func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverConn)) {
|
||||
baseCtx, cancel := serverConnBaseContext(c, opts)
|
||||
defer cancel()
|
||||
@@ -459,6 +292,9 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon
|
||||
if newf != nil {
|
||||
newf(sc)
|
||||
}
|
||||
if s.state != nil && s.state.testNewConn != nil {
|
||||
s.state.testNewConn(sc)
|
||||
}
|
||||
|
||||
s.state.registerConn(sc)
|
||||
defer s.state.unregisterConn(sc)
|
||||
@@ -472,10 +308,13 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon
|
||||
sc.conn.SetWriteDeadline(time.Time{})
|
||||
}
|
||||
|
||||
if s.NewWriteScheduler != nil {
|
||||
switch {
|
||||
case s.NewWriteScheduler != nil:
|
||||
sc.writeSched = s.NewWriteScheduler()
|
||||
} else {
|
||||
case clientPriorityDisabled(http1srv):
|
||||
sc.writeSched = newRoundRobinWriteScheduler()
|
||||
default:
|
||||
sc.writeSched = newPriorityWriteSchedulerRFC9218()
|
||||
}
|
||||
|
||||
// These start at the RFC-specified defaults. If there is a higher
|
||||
@@ -565,15 +404,6 @@ func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, newf func(*serverCon
|
||||
sc.serve(conf)
|
||||
}
|
||||
|
||||
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx context.Context, cancel func()) {
|
||||
ctx, cancel = context.WithCancel(opts.context())
|
||||
ctx = context.WithValue(ctx, http.LocalAddrContextKey, c.LocalAddr())
|
||||
if hs := opts.baseConfig(); hs != nil {
|
||||
ctx = context.WithValue(ctx, http.ServerContextKey, hs)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (sc *serverConn) rejectConn(err ErrCode, debug string) {
|
||||
sc.vlogf("http2: server rejecting conn: %v, %s", err, debug)
|
||||
// ignoring errors. hanging up anyway.
|
||||
@@ -648,6 +478,23 @@ type serverConn struct {
|
||||
|
||||
// Used by startGracefulShutdown.
|
||||
shutdownOnce sync.Once
|
||||
|
||||
// Used for RFC 9218 prioritization.
|
||||
hasIntermediary bool // connection is done via an intermediary / proxy
|
||||
priorityAware bool // the client has sent priority signal, meaning that it is aware of it.
|
||||
}
|
||||
|
||||
func (sc *serverConn) writeSchedIgnoresRFC7540() bool {
|
||||
switch sc.writeSched.(type) {
|
||||
case *priorityWriteSchedulerRFC9218:
|
||||
return true
|
||||
case *randomWriteScheduler:
|
||||
return true
|
||||
case *roundRobinWriteScheduler:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *serverConn) maxHeaderListSize() uint32 {
|
||||
@@ -938,6 +785,9 @@ func (sc *serverConn) serve(conf http2Config) {
|
||||
if !disableExtendedConnectProtocol {
|
||||
settings = append(settings, Setting{SettingEnableConnectProtocol, 1})
|
||||
}
|
||||
if sc.writeSchedIgnoresRFC7540() {
|
||||
settings = append(settings, Setting{SettingNoRFC7540Priorities, 1})
|
||||
}
|
||||
sc.writeFrame(FrameWriteRequest{
|
||||
write: settings,
|
||||
})
|
||||
@@ -1623,6 +1473,8 @@ func (sc *serverConn) processFrame(f Frame) error {
|
||||
// A client cannot push. Thus, servers MUST treat the receipt of a PUSH_PROMISE
|
||||
// frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||
return sc.countError("push_promise", ConnectionError(ErrCodeProtocol))
|
||||
case *PriorityUpdateFrame:
|
||||
return sc.processPriorityUpdate(f)
|
||||
default:
|
||||
sc.vlogf("http2: server ignoring frame: %v", f.Header())
|
||||
return nil
|
||||
@@ -1803,6 +1655,10 @@ func (sc *serverConn) processSetting(s Setting) error {
|
||||
case SettingEnableConnectProtocol:
|
||||
// Receipt of this parameter by a server does not
|
||||
// have any impact
|
||||
case SettingNoRFC7540Priorities:
|
||||
if s.Val > 1 {
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
default:
|
||||
// Unknown setting: "An endpoint that receives a SETTINGS
|
||||
// frame with any unknown or unsupported identifier MUST
|
||||
@@ -2073,13 +1929,33 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
||||
if f.StreamEnded() {
|
||||
initialState = stateHalfClosedRemote
|
||||
}
|
||||
st := sc.newStream(id, 0, initialState)
|
||||
|
||||
// We are handling two special cases here:
|
||||
// 1. When a request is sent via an intermediary, we force priority to be
|
||||
// u=3,i. This is essentially a round-robin behavior, and is done to ensure
|
||||
// fairness between, for example, multiple clients using the same proxy.
|
||||
// 2. Until a client has shown that it is aware of RFC 9218, we make its
|
||||
// streams non-incremental by default. This is done to preserve the
|
||||
// historical behavior of handling streams in a round-robin manner, rather
|
||||
// than one-by-one to completion.
|
||||
initialPriority := defaultRFC9218Priority(sc.priorityAware && !sc.hasIntermediary)
|
||||
if _, ok := sc.writeSched.(*priorityWriteSchedulerRFC9218); ok && !sc.hasIntermediary {
|
||||
headerPriority, priorityAware, hasIntermediary := f.rfc9218Priority(sc.priorityAware)
|
||||
initialPriority = headerPriority
|
||||
sc.hasIntermediary = hasIntermediary
|
||||
if priorityAware {
|
||||
sc.priorityAware = true
|
||||
}
|
||||
}
|
||||
st := sc.newStream(id, 0, initialState, initialPriority)
|
||||
|
||||
if f.HasPriority() {
|
||||
if err := sc.checkPriority(f.StreamID, f.Priority); err != nil {
|
||||
return err
|
||||
}
|
||||
sc.writeSched.AdjustStream(st.id, f.Priority)
|
||||
if !sc.writeSchedIgnoresRFC7540() {
|
||||
sc.writeSched.AdjustStream(st.id, f.Priority)
|
||||
}
|
||||
}
|
||||
|
||||
rw, req, err := sc.newWriterAndRequest(st, f)
|
||||
@@ -2120,7 +1996,7 @@ func (sc *serverConn) upgradeRequest(req *http.Request) {
|
||||
sc.serveG.check()
|
||||
id := uint32(1)
|
||||
sc.maxClientStreamID = id
|
||||
st := sc.newStream(id, 0, stateHalfClosedRemote)
|
||||
st := sc.newStream(id, 0, stateHalfClosedRemote, defaultRFC9218Priority(sc.priorityAware && !sc.hasIntermediary))
|
||||
st.reqTrailer = req.Trailer
|
||||
if st.reqTrailer != nil {
|
||||
st.trailer = make(http.Header)
|
||||
@@ -2185,11 +2061,32 @@ func (sc *serverConn) processPriority(f *PriorityFrame) error {
|
||||
if err := sc.checkPriority(f.StreamID, f.PriorityParam); err != nil {
|
||||
return err
|
||||
}
|
||||
// We need to avoid calling AdjustStream when using the RFC 9218 write
|
||||
// scheduler. Otherwise, incremental's zero value in PriorityParam will
|
||||
// unexpectedly make all streams non-incremental. This causes us to process
|
||||
// streams one-by-one to completion rather than doing it in a round-robin
|
||||
// manner (the historical behavior), which might be unexpected to users.
|
||||
if sc.writeSchedIgnoresRFC7540() {
|
||||
return nil
|
||||
}
|
||||
sc.writeSched.AdjustStream(f.StreamID, f.PriorityParam)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream {
|
||||
func (sc *serverConn) processPriorityUpdate(f *PriorityUpdateFrame) error {
|
||||
sc.priorityAware = true
|
||||
if _, ok := sc.writeSched.(*priorityWriteSchedulerRFC9218); !ok {
|
||||
return nil
|
||||
}
|
||||
p, ok := parseRFC9218Priority(f.Priority, sc.priorityAware)
|
||||
if !ok {
|
||||
return sc.countError("unparsable_priority_update", streamError(f.PrioritizedStreamID, ErrCodeProtocol))
|
||||
}
|
||||
sc.writeSched.AdjustStream(f.PrioritizedStreamID, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sc *serverConn) newStream(id, pusherID uint32, state streamState, priority PriorityParam) *stream {
|
||||
sc.serveG.check()
|
||||
if id == 0 {
|
||||
panic("internal error: cannot create stream with id 0")
|
||||
@@ -2212,7 +2109,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream
|
||||
}
|
||||
|
||||
sc.streams[id] = st
|
||||
sc.writeSched.OpenStream(st.id, OpenStreamOptions{PusherID: pusherID})
|
||||
sc.writeSched.OpenStream(st.id, OpenStreamOptions{PusherID: pusherID, priority: priority})
|
||||
if st.isPushed() {
|
||||
sc.curPushedStreams++
|
||||
} else {
|
||||
@@ -2760,21 +2657,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// TrailerPrefix is a magic prefix for ResponseWriter.Header map keys
|
||||
// that, if present, signals that the map entry is actually for
|
||||
// the response trailers, and not the response headers. The prefix
|
||||
// is stripped after the ServeHTTP call finishes and the values are
|
||||
// sent in the trailers.
|
||||
//
|
||||
// This mechanism is intended only for trailers that are not known
|
||||
// prior to the headers being written. If the set of trailers is fixed
|
||||
// or known before the header is written, the normal Go trailers mechanism
|
||||
// is preferred:
|
||||
//
|
||||
// https://golang.org/pkg/net/http/#ResponseWriter
|
||||
// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
|
||||
const TrailerPrefix = "Trailer:"
|
||||
|
||||
// promoteUndeclaredTrailers permits http.Handlers to set trailers
|
||||
// after the header has already been flushed. Because the Go
|
||||
// ResponseWriter interface has no way to set Trailers (only the
|
||||
@@ -3051,12 +2933,6 @@ func (w *responseWriter) handlerDone() {
|
||||
responseWriterStatePool.Put(rws)
|
||||
}
|
||||
|
||||
// Push errors.
|
||||
var (
|
||||
ErrRecursivePush = errors.New("http2: recursive push not allowed")
|
||||
ErrPushLimitReached = errors.New("http2: push would exceed peer's SETTINGS_MAX_CONCURRENT_STREAMS")
|
||||
)
|
||||
|
||||
var _ http.Pusher = (*responseWriter)(nil)
|
||||
|
||||
func (w *responseWriter) Push(target string, opts *http.PushOptions) error {
|
||||
@@ -3218,7 +3094,7 @@ func (sc *serverConn) startPush(msg *startPushRequest) {
|
||||
// transition to "half closed (remote)" after sending the initial HEADERS, but
|
||||
// we start in "half closed (remote)" for simplicity.
|
||||
// See further comments at the definition of stateHalfClosedRemote.
|
||||
promised := sc.newStream(promisedID, msg.parent.id, stateHalfClosedRemote)
|
||||
promised := sc.newStream(promisedID, msg.parent.id, stateHalfClosedRemote, defaultRFC9218Priority(sc.priorityAware && !sc.hasIntermediary))
|
||||
rw, req, err := sc.newWriterAndRequestNoBody(promised, httpcommon.ServerRequestParam{
|
||||
Method: msg.method,
|
||||
Scheme: msg.url.Scheme,
|
||||
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
// Copyright 2026 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 http2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TrailerPrefix is a magic prefix for ResponseWriter.Header map keys
|
||||
// that, if present, signals that the map entry is actually for
|
||||
// the response trailers, and not the response headers. The prefix
|
||||
// is stripped after the ServeHTTP call finishes and the values are
|
||||
// sent in the trailers.
|
||||
//
|
||||
// This mechanism is intended only for trailers that are not known
|
||||
// prior to the headers being written. If the set of trailers is fixed
|
||||
// or known before the header is written, the normal Go trailers mechanism
|
||||
// is preferred:
|
||||
//
|
||||
// https://golang.org/pkg/net/http/#ResponseWriter
|
||||
// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
|
||||
const TrailerPrefix = "Trailer:"
|
||||
|
||||
// Push errors.
|
||||
var (
|
||||
ErrRecursivePush = errors.New("http2: recursive push not allowed")
|
||||
ErrPushLimitReached = errors.New("http2: push would exceed peer's SETTINGS_MAX_CONCURRENT_STREAMS")
|
||||
)
|
||||
|
||||
// ConfigureServer adds HTTP/2 support to a net/http Server.
|
||||
//
|
||||
// The configuration conf may be nil.
|
||||
//
|
||||
// ConfigureServer must be called before s begins serving.
|
||||
func ConfigureServer(s *http.Server, conf *Server) error {
|
||||
return configureServer(s, conf)
|
||||
}
|
||||
|
||||
// Server is an HTTP/2 server.
|
||||
type Server struct {
|
||||
// MaxHandlers limits the number of http.Handler ServeHTTP goroutines
|
||||
// which may run at a time over all connections.
|
||||
// Negative or zero no limit.
|
||||
// TODO: implement
|
||||
MaxHandlers int
|
||||
|
||||
// MaxConcurrentStreams optionally specifies the number of
|
||||
// concurrent streams that each client may have open at a
|
||||
// time. This is unrelated to the number of http.Handler goroutines
|
||||
// which may be active globally, which is MaxHandlers.
|
||||
// If zero, MaxConcurrentStreams defaults to at least 100, per
|
||||
// the HTTP/2 spec's recommendations.
|
||||
MaxConcurrentStreams uint32
|
||||
|
||||
// MaxDecoderHeaderTableSize optionally specifies the http2
|
||||
// SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame. It
|
||||
// informs the remote endpoint of the maximum size of the header compression
|
||||
// table used to decode header blocks, in octets. If zero, the default value
|
||||
// of 4096 is used.
|
||||
MaxDecoderHeaderTableSize uint32
|
||||
|
||||
// MaxEncoderHeaderTableSize optionally specifies an upper limit for the
|
||||
// header compression table used for encoding request headers. Received
|
||||
// SETTINGS_HEADER_TABLE_SIZE settings are capped at this limit. If zero,
|
||||
// the default value of 4096 is used.
|
||||
MaxEncoderHeaderTableSize uint32
|
||||
|
||||
// MaxReadFrameSize optionally specifies the largest frame
|
||||
// this server is willing to read. A valid value is between
|
||||
// 16k and 16M, inclusive. If zero or otherwise invalid, a
|
||||
// default value is used.
|
||||
MaxReadFrameSize uint32
|
||||
|
||||
// PermitProhibitedCipherSuites, if true, permits the use of
|
||||
// cipher suites prohibited by the HTTP/2 spec.
|
||||
PermitProhibitedCipherSuites bool
|
||||
|
||||
// IdleTimeout specifies how long until idle clients should be
|
||||
// closed with a GOAWAY frame. PING frames are not considered
|
||||
// activity for the purposes of IdleTimeout.
|
||||
// If zero or negative, there is no timeout.
|
||||
IdleTimeout time.Duration
|
||||
|
||||
// ReadIdleTimeout is the timeout after which a health check using a ping
|
||||
// frame will be carried out if no frame is received on the connection.
|
||||
// If zero, no health check is performed.
|
||||
ReadIdleTimeout time.Duration
|
||||
|
||||
// PingTimeout is the timeout after which the connection will be closed
|
||||
// if a response to a ping is not received.
|
||||
// If zero, a default of 15 seconds is used.
|
||||
PingTimeout time.Duration
|
||||
|
||||
// WriteByteTimeout is the timeout after which a connection will be
|
||||
// closed if no data can be written to it. The timeout begins when data is
|
||||
// available to write, and is extended whenever any bytes are written.
|
||||
// If zero or negative, there is no timeout.
|
||||
WriteByteTimeout time.Duration
|
||||
|
||||
// MaxUploadBufferPerConnection is the size of the initial flow
|
||||
// control window for each connections. The HTTP/2 spec does not
|
||||
// allow this to be smaller than 65535 or larger than 2^32-1.
|
||||
// If the value is outside this range, a default value will be
|
||||
// used instead.
|
||||
MaxUploadBufferPerConnection int32
|
||||
|
||||
// MaxUploadBufferPerStream is the size of the initial flow control
|
||||
// window for each stream. The HTTP/2 spec does not allow this to
|
||||
// be larger than 2^32-1. If the value is zero or larger than the
|
||||
// maximum, a default value will be used instead.
|
||||
MaxUploadBufferPerStream int32
|
||||
|
||||
// NewWriteScheduler constructs a write scheduler for a connection.
|
||||
// If nil, a default scheduler is chosen.
|
||||
//
|
||||
// Deprecated: User-provided write schedulers are deprecated.
|
||||
NewWriteScheduler func() WriteScheduler
|
||||
|
||||
// CountError, if non-nil, is called on HTTP/2 server errors.
|
||||
// It's intended to increment a metric for monitoring, such
|
||||
// as an expvar or Prometheus metric.
|
||||
// The errType consists of only ASCII word characters.
|
||||
CountError func(errType string)
|
||||
|
||||
// Internal state. This is a pointer (rather than embedded directly)
|
||||
// so that we don't embed a Mutex in this struct, which will make the
|
||||
// struct non-copyable, which might break some callers.
|
||||
state *serverInternalState
|
||||
}
|
||||
|
||||
// ServeConnOpts are options for the Server.ServeConn method.
|
||||
type ServeConnOpts struct {
|
||||
// Context is the base context to use.
|
||||
// If nil, context.Background is used.
|
||||
Context context.Context
|
||||
|
||||
// BaseConfig optionally sets the base configuration
|
||||
// for values. If nil, defaults are used.
|
||||
BaseConfig *http.Server
|
||||
|
||||
// Handler specifies which handler to use for processing
|
||||
// requests. If nil, BaseConfig.Handler is used. If BaseConfig
|
||||
// or BaseConfig.Handler is nil, http.DefaultServeMux is used.
|
||||
Handler http.Handler
|
||||
|
||||
// UpgradeRequest is an initial request received on a connection
|
||||
// undergoing an h2c upgrade. The request body must have been
|
||||
// completely read from the connection before calling ServeConn,
|
||||
// and the 101 Switching Protocols response written.
|
||||
UpgradeRequest *http.Request
|
||||
|
||||
// Settings is the decoded contents of the HTTP2-Settings header
|
||||
// in an h2c upgrade request.
|
||||
Settings []byte
|
||||
|
||||
// SawClientPreface is set if the HTTP/2 connection preface
|
||||
// has already been read from the connection.
|
||||
SawClientPreface bool
|
||||
}
|
||||
|
||||
// ServeConn serves HTTP/2 requests on the provided connection and
|
||||
// blocks until the connection is no longer readable.
|
||||
//
|
||||
// ServeConn starts speaking HTTP/2 assuming that c has not had any
|
||||
// reads or writes. It writes its initial settings frame and expects
|
||||
// to be able to read the preface and settings frame from the
|
||||
// client. If c has a ConnectionState method like a *tls.Conn, the
|
||||
// ConnectionState is used to verify the TLS ciphersuite and to set
|
||||
// the Request.TLS field in Handlers.
|
||||
//
|
||||
// ServeConn does not support h2c by itself. Any h2c support must be
|
||||
// implemented in terms of providing a suitably-behaving net.Conn.
|
||||
//
|
||||
// The opts parameter is optional. If nil, default values are used.
|
||||
func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||
if opts == nil {
|
||||
opts = &ServeConnOpts{}
|
||||
}
|
||||
s.serveConn(c, opts, nil)
|
||||
}
|
||||
|
||||
func (o *ServeConnOpts) context() context.Context {
|
||||
if o != nil && o.Context != nil {
|
||||
return o.Context
|
||||
}
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
func (o *ServeConnOpts) baseConfig() *http.Server {
|
||||
if o != nil && o.BaseConfig != nil {
|
||||
return o.BaseConfig
|
||||
}
|
||||
return new(http.Server)
|
||||
}
|
||||
|
||||
func (o *ServeConnOpts) handler() http.Handler {
|
||||
if o != nil {
|
||||
if o.Handler != nil {
|
||||
return o.Handler
|
||||
}
|
||||
if o.BaseConfig != nil && o.BaseConfig.Handler != nil {
|
||||
return o.BaseConfig.Handler
|
||||
}
|
||||
}
|
||||
return http.DefaultServeMux
|
||||
}
|
||||
|
||||
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx context.Context, cancel func()) {
|
||||
ctx, cancel = context.WithCancel(opts.context())
|
||||
ctx = context.WithValue(ctx, http.LocalAddrContextKey, c.LocalAddr())
|
||||
if hs := opts.baseConfig(); hs != nil {
|
||||
ctx = context.WithValue(ctx, http.ServerContextKey, hs)
|
||||
}
|
||||
return
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
//go:build go1.27 && !http2legacy
|
||||
|
||||
// Server wrapping a net/http.Server.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type serverInternalState struct {
|
||||
s1 *http.Server
|
||||
initOnce sync.Once
|
||||
serveConnFunc func(context.Context, net.Conn, http.Handler, bool, *http.Request, []byte)
|
||||
}
|
||||
|
||||
func configureServer(s *http.Server, conf *Server) error {
|
||||
if s == nil {
|
||||
panic("nil *http.Server")
|
||||
}
|
||||
if conf == nil {
|
||||
conf = new(Server)
|
||||
}
|
||||
if conf.state != nil {
|
||||
// This isn't a panic in the pre-wrapping implementation,
|
||||
// but calling ConfigureServer twice with the same http2.Server
|
||||
// overwrites internal state on the server.
|
||||
// Make the error explicit and early here.
|
||||
panic("ConfigureServer may be called only once per Server")
|
||||
}
|
||||
if h1, h2 := s, conf; h2.IdleTimeout == 0 {
|
||||
if h1.IdleTimeout != 0 {
|
||||
h2.IdleTimeout = h1.IdleTimeout
|
||||
} else {
|
||||
h2.IdleTimeout = h1.ReadTimeout
|
||||
}
|
||||
}
|
||||
conf.state = &serverInternalState{
|
||||
s1: s,
|
||||
}
|
||||
sconfig := &serverConfig{s: conf}
|
||||
if err := s.Serve(sconfig); err != nil || sconfig.serveConnFunc == nil {
|
||||
panic("http2: net/http does not support this version of x/net/http2")
|
||||
}
|
||||
conf.state.serveConnFunc = sconfig.serveConnFunc
|
||||
return nil
|
||||
}
|
||||
|
||||
type serverConfig struct {
|
||||
s *Server
|
||||
serveConnFunc func(context.Context, net.Conn, http.Handler, bool, *http.Request, []byte)
|
||||
}
|
||||
|
||||
func (*serverConfig) Accept() (net.Conn, error) {
|
||||
return nil, errors.New("unexpected call to Accept")
|
||||
}
|
||||
func (*serverConfig) Close() error {
|
||||
return nil
|
||||
}
|
||||
func (*serverConfig) Addr() net.Addr {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *serverConfig) ServeConnFunc(f func(context.Context, net.Conn, http.Handler, bool, *http.Request, []byte)) {
|
||||
s.serveConnFunc = f
|
||||
}
|
||||
|
||||
func (s *serverConfig) HTTP2Config() http.HTTP2Config {
|
||||
return http.HTTP2Config{
|
||||
MaxConcurrentStreams: int(s.s.MaxConcurrentStreams),
|
||||
MaxDecoderHeaderTableSize: int(s.s.MaxDecoderHeaderTableSize),
|
||||
MaxEncoderHeaderTableSize: int(s.s.MaxEncoderHeaderTableSize),
|
||||
MaxReadFrameSize: int(s.s.MaxReadFrameSize),
|
||||
PermitProhibitedCipherSuites: s.s.PermitProhibitedCipherSuites,
|
||||
MaxReceiveBufferPerConnection: int(s.s.MaxUploadBufferPerConnection),
|
||||
MaxReceiveBufferPerStream: int(s.s.MaxUploadBufferPerStream),
|
||||
SendPingTimeout: s.s.ReadIdleTimeout,
|
||||
PingTimeout: s.s.PingTimeout,
|
||||
WriteByteTimeout: s.s.WriteByteTimeout,
|
||||
CountError: s.s.CountError,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serverConfig) IdleTimeout() time.Duration {
|
||||
return s.s.IdleTimeout
|
||||
}
|
||||
|
||||
type serverConn struct{}
|
||||
|
||||
func (s *Server) serveConn(c net.Conn, opts *ServeConnOpts, _ func(*serverConn)) {
|
||||
var serveConnFunc func(context.Context, net.Conn, http.Handler, bool, *http.Request, []byte)
|
||||
switch {
|
||||
case opts.BaseConfig != nil:
|
||||
// The user has provided us with an http.Server to take configuration from.
|
||||
//
|
||||
// We can't send our request to opts.BaseConfig, because an http.Server can
|
||||
// only be associated with a single http2.Server and the user might
|
||||
// use this one with several http.Servers.
|
||||
//
|
||||
// We can't send our request to s.state.s1, because it doesn't contain
|
||||
// the right configuration.
|
||||
//
|
||||
// So create a one-off copy of opts.BaseConfig and use it.
|
||||
h1 := &http.Server{
|
||||
TLSConfig: opts.BaseConfig.TLSConfig,
|
||||
ReadTimeout: opts.BaseConfig.ReadTimeout,
|
||||
ReadHeaderTimeout: opts.BaseConfig.ReadHeaderTimeout,
|
||||
WriteTimeout: opts.BaseConfig.WriteTimeout,
|
||||
IdleTimeout: opts.BaseConfig.IdleTimeout,
|
||||
MaxHeaderBytes: opts.BaseConfig.MaxHeaderBytes,
|
||||
ConnState: opts.BaseConfig.ConnState,
|
||||
ErrorLog: opts.BaseConfig.ErrorLog,
|
||||
BaseContext: opts.BaseConfig.BaseContext,
|
||||
ConnContext: opts.BaseConfig.ConnContext,
|
||||
HTTP2: opts.BaseConfig.HTTP2,
|
||||
}
|
||||
sconfig := &serverConfig{s: s}
|
||||
if err := h1.Serve(sconfig); err != nil || sconfig.serveConnFunc == nil {
|
||||
panic("http2: net/http does not support this version of x/net/http2")
|
||||
}
|
||||
serveConnFunc = sconfig.serveConnFunc
|
||||
case s.state != nil:
|
||||
serveConnFunc = s.state.serveConnFunc
|
||||
default:
|
||||
// Strange-but-true: Server has no concurrency-safe way to initialize
|
||||
// its internal state, so historically ServeConn just doesn't use any
|
||||
// persistent state if you don't call ConfigureServer first.
|
||||
//
|
||||
// If ConfigureServer hasn't been called, create a one-off http.Server
|
||||
// for the connection, since we don't have any way to keep one around for reuse.
|
||||
h1 := &http.Server{}
|
||||
sconfig := &serverConfig{s: s}
|
||||
if err := h1.Serve(sconfig); err != nil || sconfig.serveConnFunc == nil {
|
||||
panic("http2: net/http does not support this version of x/net/http2")
|
||||
}
|
||||
serveConnFunc = sconfig.serveConnFunc
|
||||
}
|
||||
|
||||
ctx, cancel := serverConnBaseContext(c, opts)
|
||||
defer cancel()
|
||||
serveConnFunc(ctx, c, opts.handler(), opts.SawClientPreface, opts.UpgradeRequest, opts.Settings)
|
||||
|
||||
}
|
||||
|
||||
// FrameWriteRequest is a request to write a frame.
|
||||
//
|
||||
// Deprecated: User-provided write schedulers are deprecated.
|
||||
type FrameWriteRequest struct {
|
||||
// Ideally we'd define this in writesched_common.go,
|
||||
// to avoid duplicating an exported symbol across two files,
|
||||
// but the changes required to make this work are fairly large.
|
||||
}
|
||||
|
||||
func (wr FrameWriteRequest) StreamID() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (wr FrameWriteRequest) DataSize() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (wr FrameWriteRequest) Consume(n int32) (FrameWriteRequest, FrameWriteRequest, int) {
|
||||
return FrameWriteRequest{}, FrameWriteRequest{}, 0
|
||||
}
|
||||
|
||||
func (wr FrameWriteRequest) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// NewPriorityWriteScheduler is deprecated.
|
||||
//
|
||||
// Deprecated: User-provided write schedulers are deprecated.
|
||||
func NewPriorityWriteScheduler(cfg *PriorityWriteSchedulerConfig) WriteScheduler {
|
||||
return unsupportedWriteScheduler{}
|
||||
}
|
||||
|
||||
// NewRandomWriteScheduler is deprecated.
|
||||
//
|
||||
// Deprecated: User-provided write schedulers are deprecated.
|
||||
func NewRandomWriteScheduler() WriteScheduler {
|
||||
return unsupportedWriteScheduler{}
|
||||
}
|
||||
|
||||
type unsupportedWriteScheduler struct{}
|
||||
|
||||
func (unsupportedWriteScheduler) OpenStream(streamID uint32, options OpenStreamOptions) {}
|
||||
func (unsupportedWriteScheduler) CloseStream(streamID uint32) {}
|
||||
func (unsupportedWriteScheduler) AdjustStream(streamID uint32, priority PriorityParam) {}
|
||||
func (unsupportedWriteScheduler) Push(wr FrameWriteRequest) {}
|
||||
func (unsupportedWriteScheduler) Pop() (wr FrameWriteRequest, ok bool) {
|
||||
return FrameWriteRequest{}, false
|
||||
}
|
||||
+37
-440
@@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !(go1.27 && !http2legacy)
|
||||
|
||||
// Transport code.
|
||||
|
||||
package http2
|
||||
@@ -21,20 +23,17 @@ import (
|
||||
"log"
|
||||
"math"
|
||||
"math/bits"
|
||||
mathrand "math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"net/textproto"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/idna"
|
||||
"golang.org/x/net/internal/httpcommon"
|
||||
)
|
||||
|
||||
@@ -60,123 +59,7 @@ const (
|
||||
defaultMaxConcurrentStreams = 1000
|
||||
)
|
||||
|
||||
// Transport is an HTTP/2 Transport.
|
||||
//
|
||||
// A Transport internally caches connections to servers. It is safe
|
||||
// for concurrent use by multiple goroutines.
|
||||
type Transport struct {
|
||||
// DialTLSContext specifies an optional dial function with context for
|
||||
// creating TLS connections for requests.
|
||||
//
|
||||
// If DialTLSContext and DialTLS is nil, tls.Dial is used.
|
||||
//
|
||||
// If the returned net.Conn has a ConnectionState method like tls.Conn,
|
||||
// it will be used to set http.Response.TLS.
|
||||
DialTLSContext func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error)
|
||||
|
||||
// DialTLS specifies an optional dial function for creating
|
||||
// TLS connections for requests.
|
||||
//
|
||||
// If DialTLSContext and DialTLS is nil, tls.Dial is used.
|
||||
//
|
||||
// Deprecated: Use DialTLSContext instead, which allows the transport
|
||||
// to cancel dials as soon as they are no longer needed.
|
||||
// If both are set, DialTLSContext takes priority.
|
||||
DialTLS func(network, addr string, cfg *tls.Config) (net.Conn, error)
|
||||
|
||||
// TLSClientConfig specifies the TLS configuration to use with
|
||||
// tls.Client. If nil, the default configuration is used.
|
||||
TLSClientConfig *tls.Config
|
||||
|
||||
// ConnPool optionally specifies an alternate connection pool to use.
|
||||
// If nil, the default is used.
|
||||
ConnPool ClientConnPool
|
||||
|
||||
// DisableCompression, if true, prevents the Transport from
|
||||
// requesting compression with an "Accept-Encoding: gzip"
|
||||
// request header when the Request contains no existing
|
||||
// Accept-Encoding value. If the Transport requests gzip on
|
||||
// its own and gets a gzipped response, it's transparently
|
||||
// decoded in the Response.Body. However, if the user
|
||||
// explicitly requested gzip it is not automatically
|
||||
// uncompressed.
|
||||
DisableCompression bool
|
||||
|
||||
// AllowHTTP, if true, permits HTTP/2 requests using the insecure,
|
||||
// plain-text "http" scheme. Note that this does not enable h2c support.
|
||||
AllowHTTP bool
|
||||
|
||||
// MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to
|
||||
// send in the initial settings frame. It is how many bytes
|
||||
// of response headers are allowed. Unlike the http2 spec, zero here
|
||||
// means to use a default limit (currently 10MB). If you actually
|
||||
// want to advertise an unlimited value to the peer, Transport
|
||||
// interprets the highest possible value here (0xffffffff or 1<<32-1)
|
||||
// to mean no limit.
|
||||
MaxHeaderListSize uint32
|
||||
|
||||
// MaxReadFrameSize is the http2 SETTINGS_MAX_FRAME_SIZE to send in the
|
||||
// initial settings frame. It is the size in bytes of the largest frame
|
||||
// payload that the sender is willing to receive. If 0, no setting is
|
||||
// sent, and the value is provided by the peer, which should be 16384
|
||||
// according to the spec:
|
||||
// https://datatracker.ietf.org/doc/html/rfc7540#section-6.5.2.
|
||||
// Values are bounded in the range 16k to 16M.
|
||||
MaxReadFrameSize uint32
|
||||
|
||||
// MaxDecoderHeaderTableSize optionally specifies the http2
|
||||
// SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame. It
|
||||
// informs the remote endpoint of the maximum size of the header compression
|
||||
// table used to decode header blocks, in octets. If zero, the default value
|
||||
// of 4096 is used.
|
||||
MaxDecoderHeaderTableSize uint32
|
||||
|
||||
// MaxEncoderHeaderTableSize optionally specifies an upper limit for the
|
||||
// header compression table used for encoding request headers. Received
|
||||
// SETTINGS_HEADER_TABLE_SIZE settings are capped at this limit. If zero,
|
||||
// the default value of 4096 is used.
|
||||
MaxEncoderHeaderTableSize uint32
|
||||
|
||||
// StrictMaxConcurrentStreams controls whether the server's
|
||||
// SETTINGS_MAX_CONCURRENT_STREAMS should be respected
|
||||
// globally. If false, new TCP connections are created to the
|
||||
// server as needed to keep each under the per-connection
|
||||
// SETTINGS_MAX_CONCURRENT_STREAMS limit. If true, the
|
||||
// server's SETTINGS_MAX_CONCURRENT_STREAMS is interpreted as
|
||||
// a global limit and callers of RoundTrip block when needed,
|
||||
// waiting for their turn.
|
||||
StrictMaxConcurrentStreams bool
|
||||
|
||||
// IdleConnTimeout is the maximum amount of time an idle
|
||||
// (keep-alive) connection will remain idle before closing
|
||||
// itself.
|
||||
// Zero means no limit.
|
||||
IdleConnTimeout time.Duration
|
||||
|
||||
// ReadIdleTimeout is the timeout after which a health check using ping
|
||||
// frame will be carried out if no frame is received on the connection.
|
||||
// Note that a ping response will is considered a received frame, so if
|
||||
// there is no other traffic on the connection, the health check will
|
||||
// be performed every ReadIdleTimeout interval.
|
||||
// If zero, no health check is performed.
|
||||
ReadIdleTimeout time.Duration
|
||||
|
||||
// PingTimeout is the timeout after which the connection will be closed
|
||||
// if a response to Ping is not received.
|
||||
// Defaults to 15s.
|
||||
PingTimeout time.Duration
|
||||
|
||||
// WriteByteTimeout is the timeout after which the connection will be
|
||||
// closed no data can be written to it. The timeout begins when data is
|
||||
// available to write, and is extended whenever any bytes are written.
|
||||
WriteByteTimeout time.Duration
|
||||
|
||||
// CountError, if non-nil, is called on HTTP/2 transport errors.
|
||||
// It's intended to increment a metric for monitoring, such
|
||||
// as an expvar or Prometheus metric.
|
||||
// The errType consists of only ASCII word characters.
|
||||
CountError func(errType string)
|
||||
|
||||
type transportInternal struct {
|
||||
// t1, if non-nil, is the standard library Transport using
|
||||
// this transport. Its settings are used (but not its
|
||||
// RoundTrip method, etc).
|
||||
@@ -217,27 +100,18 @@ func (t *Transport) disableCompression() bool {
|
||||
return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression)
|
||||
}
|
||||
|
||||
// ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2.
|
||||
// It returns an error if t1 has already been HTTP/2-enabled.
|
||||
//
|
||||
// Use ConfigureTransports instead to configure the HTTP/2 Transport.
|
||||
func ConfigureTransport(t1 *http.Transport) error {
|
||||
_, err := ConfigureTransports(t1)
|
||||
func configureTransport(t1 *http.Transport) error {
|
||||
_, err := configureTransports(t1)
|
||||
return err
|
||||
}
|
||||
|
||||
// ConfigureTransports configures a net/http HTTP/1 Transport to use HTTP/2.
|
||||
// It returns a new HTTP/2 Transport for further configuration.
|
||||
// It returns an error if t1 has already been HTTP/2-enabled.
|
||||
func ConfigureTransports(t1 *http.Transport) (*Transport, error) {
|
||||
return configureTransports(t1)
|
||||
}
|
||||
|
||||
func configureTransports(t1 *http.Transport) (*Transport, error) {
|
||||
connPool := new(clientConnPool)
|
||||
t2 := &Transport{
|
||||
ConnPool: noDialClientConnPool{connPool},
|
||||
t1: t1,
|
||||
transportInternal: transportInternal{
|
||||
t1: t1,
|
||||
},
|
||||
}
|
||||
connPool.t = t2
|
||||
if err := registerHTTPSProtocol(t1, noDialH2RoundTripper{t2}); err != nil {
|
||||
@@ -525,68 +399,7 @@ func (sew stickyErrWriter) Write(p []byte) (n int, err error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// noCachedConnError is the concrete type of ErrNoCachedConn, which
|
||||
// needs to be detected by net/http regardless of whether it's its
|
||||
// bundled version (in h2_bundle.go with a rewritten type name) or
|
||||
// from a user's x/net/http2. As such, as it has a unique method name
|
||||
// (IsHTTP2NoCachedConnError) that net/http sniffs for via func
|
||||
// isNoCachedConnError.
|
||||
type noCachedConnError struct{}
|
||||
|
||||
func (noCachedConnError) IsHTTP2NoCachedConnError() {}
|
||||
func (noCachedConnError) Error() string { return "http2: no cached connection was available" }
|
||||
|
||||
// isNoCachedConnError reports whether err is of type noCachedConnError
|
||||
// or its equivalent renamed type in net/http2's h2_bundle.go. Both types
|
||||
// may coexist in the same running program.
|
||||
func isNoCachedConnError(err error) bool {
|
||||
_, ok := err.(interface{ IsHTTP2NoCachedConnError() })
|
||||
return ok
|
||||
}
|
||||
|
||||
var ErrNoCachedConn error = noCachedConnError{}
|
||||
|
||||
// RoundTripOpt are options for the Transport.RoundTripOpt method.
|
||||
type RoundTripOpt struct {
|
||||
// OnlyCachedConn controls whether RoundTripOpt may
|
||||
// create a new TCP connection. If set true and
|
||||
// no cached connection is available, RoundTripOpt
|
||||
// will return ErrNoCachedConn.
|
||||
OnlyCachedConn bool
|
||||
|
||||
allowHTTP bool // allow http:// URLs
|
||||
}
|
||||
|
||||
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return t.RoundTripOpt(req, RoundTripOpt{})
|
||||
}
|
||||
|
||||
// authorityAddr returns a given authority (a host/IP, or host:port / ip:port)
|
||||
// and returns a host:port. The port 443 is added if needed.
|
||||
func authorityAddr(scheme string, authority string) (addr string) {
|
||||
host, port, err := net.SplitHostPort(authority)
|
||||
if err != nil { // authority didn't have a port
|
||||
host = authority
|
||||
port = ""
|
||||
}
|
||||
if port == "" { // authority's port was empty
|
||||
port = "443"
|
||||
if scheme == "http" {
|
||||
port = "80"
|
||||
}
|
||||
}
|
||||
if a, err := idna.ToASCII(host); err == nil {
|
||||
host = a
|
||||
}
|
||||
// IPv6 address literal, without a port:
|
||||
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||
return host + ":" + port
|
||||
}
|
||||
return net.JoinHostPort(host, port)
|
||||
}
|
||||
|
||||
// RoundTripOpt is like RoundTrip, but takes options.
|
||||
func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
|
||||
func (t *Transport) roundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
|
||||
switch req.URL.Scheme {
|
||||
case "https":
|
||||
// Always okay.
|
||||
@@ -597,134 +410,16 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
|
||||
default:
|
||||
return nil, errors.New("http2: unsupported scheme")
|
||||
}
|
||||
|
||||
addr := authorityAddr(req.URL.Scheme, req.URL.Host)
|
||||
for retry := 0; ; retry++ {
|
||||
cc, err := t.connPool().GetClientConn(req, addr)
|
||||
if err != nil {
|
||||
t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err)
|
||||
return nil, err
|
||||
}
|
||||
reused := !atomic.CompareAndSwapUint32(&cc.atomicReused, 0, 1)
|
||||
traceGotConn(req, cc, reused)
|
||||
res, err := cc.RoundTrip(req)
|
||||
if err != nil && retry <= 6 {
|
||||
roundTripErr := err
|
||||
if req, err = shouldRetryRequest(req, err); err == nil {
|
||||
// After the first retry, do exponential backoff with 10% jitter.
|
||||
if retry == 0 {
|
||||
t.vlogf("RoundTrip retrying after failure: %v", roundTripErr)
|
||||
continue
|
||||
}
|
||||
backoff := float64(uint(1) << (uint(retry) - 1))
|
||||
backoff += backoff * (0.1 * mathrand.Float64())
|
||||
d := time.Second * time.Duration(backoff)
|
||||
tm := time.NewTimer(d)
|
||||
select {
|
||||
case <-tm.C:
|
||||
t.vlogf("RoundTrip retrying after failure: %v", roundTripErr)
|
||||
continue
|
||||
case <-req.Context().Done():
|
||||
tm.Stop()
|
||||
err = req.Context().Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == errClientConnNotEstablished {
|
||||
// This ClientConn was created recently,
|
||||
// this is the first request to use it,
|
||||
// and the connection is closed and not usable.
|
||||
//
|
||||
// In this state, cc.idleTimer will remove the conn from the pool
|
||||
// when it fires. Stop the timer and remove it here so future requests
|
||||
// won't try to use this connection.
|
||||
//
|
||||
// If the timer has already fired and we're racing it, the redundant
|
||||
// call to MarkDead is harmless.
|
||||
if cc.idleTimer != nil {
|
||||
cc.idleTimer.Stop()
|
||||
}
|
||||
t.connPool().MarkDead(cc)
|
||||
}
|
||||
if err != nil {
|
||||
t.vlogf("RoundTrip failure: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
return t.roundTripViaPool(req, opt, t.connPool())
|
||||
}
|
||||
|
||||
// CloseIdleConnections closes any connections which were previously
|
||||
// connected from previous requests but are now sitting idle.
|
||||
// It does not interrupt any connections currently in use.
|
||||
func (t *Transport) CloseIdleConnections() {
|
||||
func (t *Transport) closeIdleConnections() {
|
||||
if cp, ok := t.connPool().(clientConnPoolIdleCloser); ok {
|
||||
cp.closeIdleConnections()
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
errClientConnClosed = errors.New("http2: client conn is closed")
|
||||
errClientConnUnusable = errors.New("http2: client conn not usable")
|
||||
errClientConnNotEstablished = errors.New("http2: client conn could not be established")
|
||||
errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY")
|
||||
errClientConnForceClosed = errors.New("http2: client connection force closed via ClientConn.Close")
|
||||
)
|
||||
|
||||
// shouldRetryRequest is called by RoundTrip when a request fails to get
|
||||
// response headers. It is always called with a non-nil error.
|
||||
// It returns either a request to retry (either the same request, or a
|
||||
// modified clone), or an error if the request can't be replayed.
|
||||
func shouldRetryRequest(req *http.Request, err error) (*http.Request, error) {
|
||||
if !canRetryError(err) {
|
||||
return nil, err
|
||||
}
|
||||
// If the Body is nil (or http.NoBody), it's safe to reuse
|
||||
// this request and its Body.
|
||||
if req.Body == nil || req.Body == http.NoBody {
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// If the request body can be reset back to its original
|
||||
// state via the optional req.GetBody, do that.
|
||||
if req.GetBody != nil {
|
||||
body, err := req.GetBody()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newReq := *req
|
||||
newReq.Body = body
|
||||
return &newReq, nil
|
||||
}
|
||||
|
||||
// The Request.Body can't reset back to the beginning, but we
|
||||
// don't seem to have started to read from it yet, so reuse
|
||||
// the request directly.
|
||||
if err == errClientConnUnusable {
|
||||
return req, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err)
|
||||
}
|
||||
|
||||
func canRetryError(err error) bool {
|
||||
if err == errClientConnUnusable || err == errClientConnGotGoAway {
|
||||
return true
|
||||
}
|
||||
if se, ok := err.(StreamError); ok {
|
||||
if se.Code == ErrCodeProtocol && se.Cause == errFromPeer {
|
||||
// See golang/go#47635, golang/go#42777
|
||||
return true
|
||||
}
|
||||
return se.Code == ErrCodeRefusedStream
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) {
|
||||
if t.transportTestHooks != nil {
|
||||
return t.newClientConn(nil, singleUse, nil)
|
||||
}
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -750,27 +445,6 @@ func (t *Transport) newTLSConfig(host string) *tls.Config {
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (t *Transport) dialTLS(ctx context.Context, network, addr string, tlsCfg *tls.Config) (net.Conn, error) {
|
||||
if t.DialTLSContext != nil {
|
||||
return t.DialTLSContext(ctx, network, addr, tlsCfg)
|
||||
} else if t.DialTLS != nil {
|
||||
return t.DialTLS(network, addr, tlsCfg)
|
||||
}
|
||||
|
||||
tlsCn, err := t.dialTLSWithContext(ctx, network, addr, tlsCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state := tlsCn.ConnectionState()
|
||||
if p := state.NegotiatedProtocol; p != NextProtoTLS {
|
||||
return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, NextProtoTLS)
|
||||
}
|
||||
if !state.NegotiatedProtocolIsMutual {
|
||||
return nil, errors.New("http2: could not negotiate protocol mutually")
|
||||
}
|
||||
return tlsCn, nil
|
||||
}
|
||||
|
||||
// disableKeepAlives reports whether connections should be closed as
|
||||
// soon as possible after handling the first request.
|
||||
func (t *Transport) disableKeepAlives() bool {
|
||||
@@ -784,7 +458,7 @@ func (t *Transport) expectContinueTimeout() time.Duration {
|
||||
return t.t1.ExpectContinueTimeout
|
||||
}
|
||||
|
||||
func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
|
||||
func (t *Transport) newUserClientConn(c net.Conn) (*ClientConn, error) {
|
||||
return t.newClientConn(c, t.disableKeepAlives(), nil)
|
||||
}
|
||||
|
||||
@@ -897,8 +571,7 @@ func (cc *ClientConn) healthCheck() {
|
||||
}
|
||||
}
|
||||
|
||||
// SetDoNotReuse marks cc as not reusable for future HTTP requests.
|
||||
func (cc *ClientConn) SetDoNotReuse() {
|
||||
func (cc *ClientConn) setDoNotReuse() {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
cc.doNotReuse = true
|
||||
@@ -939,21 +612,13 @@ func (cc *ClientConn) setGoAway(f *GoAwayFrame) {
|
||||
}
|
||||
}
|
||||
|
||||
// CanTakeNewRequest reports whether the connection can take a new request,
|
||||
// meaning it has not been closed or received or sent a GOAWAY.
|
||||
//
|
||||
// If the caller is going to immediately make a new request on this
|
||||
// connection, use ReserveNewRequest instead.
|
||||
func (cc *ClientConn) CanTakeNewRequest() bool {
|
||||
func (cc *ClientConn) canTakeNewRequest() bool {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
return cc.canTakeNewRequestLocked()
|
||||
}
|
||||
|
||||
// ReserveNewRequest is like CanTakeNewRequest but also reserves a
|
||||
// concurrent stream in cc. The reservation is decremented on the
|
||||
// next call to RoundTrip.
|
||||
func (cc *ClientConn) ReserveNewRequest() bool {
|
||||
func (cc *ClientConn) reserveNewRequest() bool {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
if st := cc.idleStateLocked(); !st.canTakeNewRequest {
|
||||
@@ -963,41 +628,7 @@ func (cc *ClientConn) ReserveNewRequest() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// ClientConnState describes the state of a ClientConn.
|
||||
type ClientConnState struct {
|
||||
// Closed is whether the connection is closed.
|
||||
Closed bool
|
||||
|
||||
// Closing is whether the connection is in the process of
|
||||
// closing. It may be closing due to shutdown, being a
|
||||
// single-use connection, being marked as DoNotReuse, or
|
||||
// having received a GOAWAY frame.
|
||||
Closing bool
|
||||
|
||||
// StreamsActive is how many streams are active.
|
||||
StreamsActive int
|
||||
|
||||
// StreamsReserved is how many streams have been reserved via
|
||||
// ClientConn.ReserveNewRequest.
|
||||
StreamsReserved int
|
||||
|
||||
// StreamsPending is how many requests have been sent in excess
|
||||
// of the peer's advertised MaxConcurrentStreams setting and
|
||||
// are waiting for other streams to complete.
|
||||
StreamsPending int
|
||||
|
||||
// MaxConcurrentStreams is how many concurrent streams the
|
||||
// peer advertised as acceptable. Zero means no SETTINGS
|
||||
// frame has been received yet.
|
||||
MaxConcurrentStreams uint32
|
||||
|
||||
// LastIdle, if non-zero, is when the connection last
|
||||
// transitioned to idle state.
|
||||
LastIdle time.Time
|
||||
}
|
||||
|
||||
// State returns a snapshot of cc's state.
|
||||
func (cc *ClientConn) State() ClientConnState {
|
||||
func (cc *ClientConn) state() ClientConnState {
|
||||
cc.wmu.Lock()
|
||||
maxConcurrent := cc.maxConcurrentStreams
|
||||
if !cc.seenSettings {
|
||||
@@ -1168,6 +799,12 @@ func (cc *ClientConn) closeIfIdle() {
|
||||
cc.closeConn()
|
||||
}
|
||||
|
||||
func (cc *ClientConn) stopIdleTimer() {
|
||||
if cc.idleTimer != nil {
|
||||
cc.idleTimer.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func (cc *ClientConn) isDoNotReuseAndIdle() bool {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
@@ -1176,8 +813,7 @@ func (cc *ClientConn) isDoNotReuseAndIdle() bool {
|
||||
|
||||
var shutdownEnterWaitStateHook = func() {}
|
||||
|
||||
// Shutdown gracefully closes the client connection, waiting for running streams to complete.
|
||||
func (cc *ClientConn) Shutdown(ctx context.Context) error {
|
||||
func (cc *ClientConn) shutdown(ctx context.Context) error {
|
||||
if err := cc.sendGoAway(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1251,10 +887,7 @@ func (cc *ClientConn) closeForError(err error) {
|
||||
cc.closeConn()
|
||||
}
|
||||
|
||||
// Close closes the client connection immediately.
|
||||
//
|
||||
// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead.
|
||||
func (cc *ClientConn) Close() error {
|
||||
func (cc *ClientConn) close() error {
|
||||
cc.closeForError(errClientConnForceClosed)
|
||||
return nil
|
||||
}
|
||||
@@ -1308,11 +941,11 @@ func (cc *ClientConn) decrStreamReservationsLocked() {
|
||||
}
|
||||
}
|
||||
|
||||
func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return cc.roundTrip(req, nil)
|
||||
func (cc *ClientConn) roundTrip(req *http.Request) (*http.Response, error) {
|
||||
return cc.internalRoundTrip(req, nil)
|
||||
}
|
||||
|
||||
func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) (*http.Response, error) {
|
||||
func (cc *ClientConn) internalRoundTrip(req *http.Request, streamf func(*clientStream)) (*http.Response, error) {
|
||||
ctx := req.Context()
|
||||
cs := &clientStream{
|
||||
cc: cc,
|
||||
@@ -2132,19 +1765,6 @@ func (cc *ClientConn) readLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
// GoAwayError is returned by the Transport when the server closes the
|
||||
// TCP connection after sending a GOAWAY frame.
|
||||
type GoAwayError struct {
|
||||
LastStreamID uint32
|
||||
ErrCode ErrCode
|
||||
DebugData string
|
||||
}
|
||||
|
||||
func (e GoAwayError) Error() string {
|
||||
return fmt.Sprintf("http2: server sent GOAWAY and closed the connection; LastStreamID=%v, ErrCode=%v, debug=%q",
|
||||
e.LastStreamID, e.ErrCode, e.DebugData)
|
||||
}
|
||||
|
||||
func isEOFOrNetReadError(err error) bool {
|
||||
if err == io.EOF {
|
||||
return true
|
||||
@@ -2779,6 +2399,11 @@ func (rl *clientConnReadLoop) endStreamError(cs *clientStream, err error) {
|
||||
cs.abortStream(err)
|
||||
}
|
||||
|
||||
func (rl *clientConnReadLoop) endStreamErrorLocked(cs *clientStream, err error) {
|
||||
cs.readAborted = true
|
||||
cs.abortStreamLocked(err)
|
||||
}
|
||||
|
||||
// Constants passed to streamByID for documentation purposes.
|
||||
const (
|
||||
headerOrDataFrame = true
|
||||
@@ -2860,6 +2485,9 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
|
||||
|
||||
var seenMaxConcurrentStreams bool
|
||||
err := f.ForeachSetting(func(s Setting) error {
|
||||
if err := s.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
switch s.ID {
|
||||
case SettingMaxFrameSize:
|
||||
cc.maxFrameSize = s.Val
|
||||
@@ -2891,9 +2519,6 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
|
||||
cc.henc.SetMaxDynamicTableSize(s.Val)
|
||||
cc.peerMaxHeaderTableSize = s.Val
|
||||
case SettingEnableConnectProtocol:
|
||||
if err := s.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
// If the peer wants to send us SETTINGS_ENABLE_CONNECT_PROTOCOL,
|
||||
// we require that it do so in the first SETTINGS frame.
|
||||
//
|
||||
@@ -2946,7 +2571,7 @@ func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error {
|
||||
if !fl.add(int32(f.Increment)) {
|
||||
// For stream, the sender sends RST_STREAM with an error code of FLOW_CONTROL_ERROR
|
||||
if cs != nil {
|
||||
rl.endStreamError(cs, StreamError{
|
||||
rl.endStreamErrorLocked(cs, StreamError{
|
||||
StreamID: f.StreamID,
|
||||
Code: ErrCodeFlowControl,
|
||||
})
|
||||
@@ -2980,7 +2605,7 @@ func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error {
|
||||
}
|
||||
|
||||
// Ping sends a PING frame to the server and waits for the ack.
|
||||
func (cc *ClientConn) Ping(ctx context.Context) error {
|
||||
func (cc *ClientConn) ping(ctx context.Context) error {
|
||||
c := make(chan struct{})
|
||||
// Generate a random payload
|
||||
var p [8]byte
|
||||
@@ -3094,16 +2719,6 @@ func (cc *ClientConn) vlogf(format string, args ...interface{}) {
|
||||
cc.t.vlogf(format, args...)
|
||||
}
|
||||
|
||||
func (t *Transport) vlogf(format string, args ...interface{}) {
|
||||
if VerboseLogs {
|
||||
t.logf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transport) logf(format string, args ...interface{}) {
|
||||
log.Printf(format, args...)
|
||||
}
|
||||
|
||||
var noBody io.ReadCloser = noBodyReader{}
|
||||
|
||||
type noBodyReader struct{}
|
||||
@@ -3228,10 +2843,6 @@ func (gz *gzipReader) Close() error {
|
||||
return gz.body.Close()
|
||||
}
|
||||
|
||||
type errorReader struct{ err error }
|
||||
|
||||
func (r errorReader) Read(p []byte) (int, error) { return 0, r.err }
|
||||
|
||||
// isConnectionCloseRequest reports whether req should use its own
|
||||
// connection for a single request and then close the connection.
|
||||
func isConnectionCloseRequest(req *http.Request) bool {
|
||||
@@ -3423,17 +3034,3 @@ func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.M
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS
|
||||
// connection.
|
||||
func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) {
|
||||
dialer := &tls.Dialer{
|
||||
Config: cfg,
|
||||
}
|
||||
cn, err := dialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed
|
||||
return tlsCn, nil
|
||||
}
|
||||
|
||||
+447
@@ -0,0 +1,447 @@
|
||||
// Copyright 2026 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 http2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
mathrand "math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
// ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2.
|
||||
// It returns an error if t1 has already been HTTP/2-enabled.
|
||||
//
|
||||
// Use ConfigureTransports instead to configure the HTTP/2 Transport.
|
||||
func ConfigureTransport(t1 *http.Transport) error {
|
||||
return configureTransport(t1)
|
||||
}
|
||||
|
||||
// ConfigureTransports configures a net/http HTTP/1 Transport to use HTTP/2.
|
||||
// It returns a new HTTP/2 Transport for further configuration.
|
||||
// It returns an error if t1 has already been HTTP/2-enabled.
|
||||
func ConfigureTransports(t1 *http.Transport) (*Transport, error) {
|
||||
return configureTransports(t1)
|
||||
}
|
||||
|
||||
// Transport is an HTTP/2 Transport.
|
||||
//
|
||||
// A Transport internally caches connections to servers. It is safe
|
||||
// for concurrent use by multiple goroutines.
|
||||
type Transport struct {
|
||||
// DialTLSContext specifies an optional dial function with context for
|
||||
// creating TLS connections for requests.
|
||||
//
|
||||
// If DialTLSContext and DialTLS is nil, tls.Dial is used.
|
||||
//
|
||||
// If the returned net.Conn has a ConnectionState method like tls.Conn,
|
||||
// it will be used to set http.Response.TLS.
|
||||
DialTLSContext func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error)
|
||||
|
||||
// DialTLS specifies an optional dial function for creating
|
||||
// TLS connections for requests.
|
||||
//
|
||||
// If DialTLSContext and DialTLS is nil, tls.Dial is used.
|
||||
//
|
||||
// Deprecated: Use DialTLSContext instead, which allows the transport
|
||||
// to cancel dials as soon as they are no longer needed.
|
||||
// If both are set, DialTLSContext takes priority.
|
||||
DialTLS func(network, addr string, cfg *tls.Config) (net.Conn, error)
|
||||
|
||||
// TLSClientConfig specifies the TLS configuration to use with
|
||||
// tls.Client. If nil, the default configuration is used.
|
||||
TLSClientConfig *tls.Config
|
||||
|
||||
// ConnPool optionally specifies an alternate connection pool to use.
|
||||
// If nil, the default is used.
|
||||
ConnPool ClientConnPool
|
||||
|
||||
// DisableCompression, if true, prevents the Transport from
|
||||
// requesting compression with an "Accept-Encoding: gzip"
|
||||
// request header when the Request contains no existing
|
||||
// Accept-Encoding value. If the Transport requests gzip on
|
||||
// its own and gets a gzipped response, it's transparently
|
||||
// decoded in the Response.Body. However, if the user
|
||||
// explicitly requested gzip it is not automatically
|
||||
// uncompressed.
|
||||
DisableCompression bool
|
||||
|
||||
// AllowHTTP, if true, permits HTTP/2 requests using the insecure,
|
||||
// plain-text "http" scheme. Note that this does not enable h2c support.
|
||||
AllowHTTP bool
|
||||
|
||||
// MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to
|
||||
// send in the initial settings frame. It is how many bytes
|
||||
// of response headers are allowed. Unlike the http2 spec, zero here
|
||||
// means to use a default limit (currently 10MB). If you actually
|
||||
// want to advertise an unlimited value to the peer, Transport
|
||||
// interprets the highest possible value here (0xffffffff or 1<<32-1)
|
||||
// to mean no limit.
|
||||
MaxHeaderListSize uint32
|
||||
|
||||
// MaxReadFrameSize is the http2 SETTINGS_MAX_FRAME_SIZE to send in the
|
||||
// initial settings frame. It is the size in bytes of the largest frame
|
||||
// payload that the sender is willing to receive. If 0, no setting is
|
||||
// sent, and the value is provided by the peer, which should be 16384
|
||||
// according to the spec:
|
||||
// https://datatracker.ietf.org/doc/html/rfc7540#section-6.5.2.
|
||||
// Values are bounded in the range 16k to 16M.
|
||||
MaxReadFrameSize uint32
|
||||
|
||||
// MaxDecoderHeaderTableSize optionally specifies the http2
|
||||
// SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame. It
|
||||
// informs the remote endpoint of the maximum size of the header compression
|
||||
// table used to decode header blocks, in octets. If zero, the default value
|
||||
// of 4096 is used.
|
||||
MaxDecoderHeaderTableSize uint32
|
||||
|
||||
// MaxEncoderHeaderTableSize optionally specifies an upper limit for the
|
||||
// header compression table used for encoding request headers. Received
|
||||
// SETTINGS_HEADER_TABLE_SIZE settings are capped at this limit. If zero,
|
||||
// the default value of 4096 is used.
|
||||
MaxEncoderHeaderTableSize uint32
|
||||
|
||||
// StrictMaxConcurrentStreams controls whether the server's
|
||||
// SETTINGS_MAX_CONCURRENT_STREAMS should be respected
|
||||
// globally. If false, new TCP connections are created to the
|
||||
// server as needed to keep each under the per-connection
|
||||
// SETTINGS_MAX_CONCURRENT_STREAMS limit. If true, the
|
||||
// server's SETTINGS_MAX_CONCURRENT_STREAMS is interpreted as
|
||||
// a global limit and callers of RoundTrip block when needed,
|
||||
// waiting for their turn.
|
||||
StrictMaxConcurrentStreams bool
|
||||
|
||||
// IdleConnTimeout is the maximum amount of time an idle
|
||||
// (keep-alive) connection will remain idle before closing
|
||||
// itself.
|
||||
// Zero means no limit.
|
||||
IdleConnTimeout time.Duration
|
||||
|
||||
// ReadIdleTimeout is the timeout after which a health check using ping
|
||||
// frame will be carried out if no frame is received on the connection.
|
||||
// Note that a ping response will is considered a received frame, so if
|
||||
// there is no other traffic on the connection, the health check will
|
||||
// be performed every ReadIdleTimeout interval.
|
||||
// If zero, no health check is performed.
|
||||
ReadIdleTimeout time.Duration
|
||||
|
||||
// PingTimeout is the timeout after which the connection will be closed
|
||||
// if a response to Ping is not received.
|
||||
// Defaults to 15s.
|
||||
PingTimeout time.Duration
|
||||
|
||||
// WriteByteTimeout is the timeout after which the connection will be
|
||||
// closed no data can be written to it. The timeout begins when data is
|
||||
// available to write, and is extended whenever any bytes are written.
|
||||
WriteByteTimeout time.Duration
|
||||
|
||||
// CountError, if non-nil, is called on HTTP/2 transport errors.
|
||||
// It's intended to increment a metric for monitoring, such
|
||||
// as an expvar or Prometheus metric.
|
||||
// The errType consists of only ASCII word characters.
|
||||
CountError func(errType string)
|
||||
|
||||
// Internal state, differs between wrapped and non-wrapped implementations.
|
||||
transportInternal
|
||||
}
|
||||
|
||||
var (
|
||||
errClientConnClosed = errors.New("http2: client conn is closed")
|
||||
errClientConnNotEstablished = errors.New("http2: client conn could not be established")
|
||||
errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY")
|
||||
errClientConnForceClosed = errors.New("http2: client connection force closed via ClientConn.Close")
|
||||
errClientConnUnusable = errors.New("http2: client conn not usable")
|
||||
)
|
||||
|
||||
// ClientConnPool manages a pool of HTTP/2 client connections.
|
||||
type ClientConnPool interface {
|
||||
// GetClientConn returns a specific HTTP/2 connection (usually
|
||||
// a TLS-TCP connection) to an HTTP/2 server. On success, the
|
||||
// returned ClientConn accounts for the upcoming RoundTrip
|
||||
// call, so the caller should not omit it. If the caller needs
|
||||
// to, ClientConn.RoundTrip can be called with a bogus
|
||||
// new(http.Request) to release the stream reservation.
|
||||
GetClientConn(req *http.Request, addr string) (*ClientConn, error)
|
||||
MarkDead(*ClientConn)
|
||||
}
|
||||
|
||||
// ClientConnState describes the state of a ClientConn.
|
||||
type ClientConnState struct {
|
||||
// Closed is whether the connection is closed.
|
||||
Closed bool
|
||||
|
||||
// Closing is whether the connection is in the process of
|
||||
// closing. It may be closing due to shutdown, being a
|
||||
// single-use connection, being marked as DoNotReuse, or
|
||||
// having received a GOAWAY frame.
|
||||
Closing bool
|
||||
|
||||
// StreamsActive is how many streams are active.
|
||||
StreamsActive int
|
||||
|
||||
// StreamsReserved is how many streams have been reserved via
|
||||
// ClientConn.ReserveNewRequest.
|
||||
StreamsReserved int
|
||||
|
||||
// StreamsPending is how many requests have been sent in excess
|
||||
// of the peer's advertised MaxConcurrentStreams setting and
|
||||
// are waiting for other streams to complete.
|
||||
StreamsPending int
|
||||
|
||||
// MaxConcurrentStreams is how many concurrent streams the
|
||||
// peer advertised as acceptable. Zero means no SETTINGS
|
||||
// frame has been received yet.
|
||||
MaxConcurrentStreams uint32
|
||||
|
||||
// LastIdle, if non-zero, is when the connection last
|
||||
// transitioned to idle state.
|
||||
LastIdle time.Time
|
||||
}
|
||||
|
||||
// RoundTripOpt are options for the Transport.RoundTripOpt method.
|
||||
type RoundTripOpt struct {
|
||||
// OnlyCachedConn controls whether RoundTripOpt may
|
||||
// create a new TCP connection. If set true and
|
||||
// no cached connection is available, RoundTripOpt
|
||||
// will return ErrNoCachedConn.
|
||||
|
||||
// OnlyCachedConn was broken in https://go.dev/cl/16699.
|
||||
OnlyCachedConn bool
|
||||
|
||||
allowHTTP bool // allow http:// URLs
|
||||
}
|
||||
|
||||
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return t.RoundTripOpt(req, RoundTripOpt{})
|
||||
}
|
||||
|
||||
// RoundTripOpt is like RoundTrip, but takes options.
|
||||
func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
|
||||
return t.roundTripOpt(req, opt)
|
||||
}
|
||||
|
||||
// CloseIdleConnections closes any connections which were previously
|
||||
// connected from previous requests but are now sitting idle.
|
||||
// It does not interrupt any connections currently in use.
|
||||
func (t *Transport) CloseIdleConnections() {
|
||||
t.closeIdleConnections()
|
||||
}
|
||||
|
||||
func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
|
||||
return t.newUserClientConn(c)
|
||||
}
|
||||
|
||||
// authorityAddr returns a given authority (a host/IP, or host:port / ip:port)
|
||||
// and returns a host:port. The port 443 is added if needed.
|
||||
func authorityAddr(scheme string, authority string) (addr string) {
|
||||
host, port, err := net.SplitHostPort(authority)
|
||||
if err != nil { // authority didn't have a port
|
||||
host = authority
|
||||
port = ""
|
||||
}
|
||||
if port == "" { // authority's port was empty
|
||||
port = "443"
|
||||
if scheme == "http" {
|
||||
port = "80"
|
||||
}
|
||||
}
|
||||
if a, err := idna.ToASCII(host); err == nil {
|
||||
host = a
|
||||
}
|
||||
// IPv6 address literal, without a port:
|
||||
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||
return host + ":" + port
|
||||
}
|
||||
return net.JoinHostPort(host, port)
|
||||
}
|
||||
|
||||
func (t *Transport) roundTripViaPool(req *http.Request, opt RoundTripOpt, pool ClientConnPool) (*http.Response, error) {
|
||||
addr := authorityAddr(req.URL.Scheme, req.URL.Host)
|
||||
for retry := 0; ; retry++ {
|
||||
cc, err := pool.GetClientConn(req, addr)
|
||||
if err != nil {
|
||||
t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err)
|
||||
return nil, err
|
||||
}
|
||||
reused := !atomic.CompareAndSwapUint32(&cc.atomicReused, 0, 1)
|
||||
traceGotConn(req, cc, reused)
|
||||
res, err := cc.RoundTrip(req)
|
||||
if err != nil && retry <= 6 {
|
||||
roundTripErr := err
|
||||
if req, err = shouldRetryRequest(req, err); err == nil {
|
||||
// After the first retry, do exponential backoff with 10% jitter.
|
||||
if retry == 0 {
|
||||
t.vlogf("RoundTrip retrying after failure: %v", roundTripErr)
|
||||
continue
|
||||
}
|
||||
backoff := float64(uint(1) << (uint(retry) - 1))
|
||||
backoff += backoff * (0.1 * mathrand.Float64())
|
||||
d := time.Second * time.Duration(backoff)
|
||||
tm := time.NewTimer(d)
|
||||
select {
|
||||
case <-tm.C:
|
||||
t.vlogf("RoundTrip retrying after failure: %v", roundTripErr)
|
||||
continue
|
||||
case <-req.Context().Done():
|
||||
tm.Stop()
|
||||
err = req.Context().Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == errClientConnNotEstablished {
|
||||
// This ClientConn was created recently,
|
||||
// this is the first request to use it,
|
||||
// and the connection is closed and not usable.
|
||||
//
|
||||
// In this state, cc.idleTimer will remove the conn from the pool
|
||||
// when it fires. Stop the timer and remove it here so future requests
|
||||
// won't try to use this connection.
|
||||
//
|
||||
// If the timer has already fired and we're racing it, the redundant
|
||||
// call to MarkDead is harmless.
|
||||
cc.stopIdleTimer()
|
||||
pool.MarkDead(cc)
|
||||
}
|
||||
if err != nil {
|
||||
t.vlogf("RoundTrip failure: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
// shouldRetryRequest is called by RoundTrip when a request fails to get
|
||||
// response headers. It is always called with a non-nil error.
|
||||
// It returns either a request to retry (either the same request, or a
|
||||
// modified clone), or an error if the request can't be replayed.
|
||||
func shouldRetryRequest(req *http.Request, err error) (*http.Request, error) {
|
||||
if !canRetryError(err) {
|
||||
return nil, err
|
||||
}
|
||||
// If the Body is nil (or http.NoBody), it's safe to reuse
|
||||
// this request and its Body.
|
||||
if req.Body == nil || req.Body == http.NoBody {
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// If the request body can be reset back to its original
|
||||
// state via the optional req.GetBody, do that.
|
||||
if req.GetBody != nil {
|
||||
body, err := req.GetBody()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newReq := *req
|
||||
newReq.Body = body
|
||||
return &newReq, nil
|
||||
}
|
||||
|
||||
// The Request.Body can't reset back to the beginning, but we
|
||||
// don't seem to have started to read from it yet, so reuse
|
||||
// the request directly.
|
||||
if err == errClientConnUnusable {
|
||||
return req, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err)
|
||||
}
|
||||
|
||||
func canRetryError(err error) bool {
|
||||
if err == errClientConnUnusable || err == errClientConnGotGoAway {
|
||||
return true
|
||||
}
|
||||
if se, ok := err.(StreamError); ok {
|
||||
return se.Code == ErrCodeRefusedStream
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Transport) vlogf(format string, args ...interface{}) {
|
||||
if VerboseLogs {
|
||||
t.logf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transport) logf(format string, args ...interface{}) {
|
||||
log.Printf(format, args...)
|
||||
}
|
||||
|
||||
func (t *Transport) dialTLS(ctx context.Context, network, addr string, tlsCfg *tls.Config) (net.Conn, error) {
|
||||
if t.DialTLSContext != nil {
|
||||
return t.DialTLSContext(ctx, network, addr, tlsCfg)
|
||||
} else if t.DialTLS != nil {
|
||||
return t.DialTLS(network, addr, tlsCfg)
|
||||
}
|
||||
|
||||
tlsCn, err := t.dialTLSWithContext(ctx, network, addr, tlsCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state := tlsCn.ConnectionState()
|
||||
if p := state.NegotiatedProtocol; p != NextProtoTLS {
|
||||
return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, NextProtoTLS)
|
||||
}
|
||||
if !state.NegotiatedProtocolIsMutual {
|
||||
return nil, errors.New("http2: could not negotiate protocol mutually")
|
||||
}
|
||||
return tlsCn, nil
|
||||
}
|
||||
|
||||
// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS
|
||||
// connection.
|
||||
func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) {
|
||||
dialer := &tls.Dialer{
|
||||
Config: cfg,
|
||||
}
|
||||
cn, err := dialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed
|
||||
return tlsCn, nil
|
||||
}
|
||||
|
||||
// GoAwayError is returned by the Transport when the server closes the
|
||||
// TCP connection after sending a GOAWAY frame.
|
||||
type GoAwayError struct {
|
||||
LastStreamID uint32
|
||||
ErrCode ErrCode
|
||||
DebugData string
|
||||
}
|
||||
|
||||
func (e GoAwayError) Error() string {
|
||||
return fmt.Sprintf("http2: server sent GOAWAY and closed the connection; LastStreamID=%v, ErrCode=%v, debug=%q",
|
||||
e.LastStreamID, e.ErrCode, e.DebugData)
|
||||
}
|
||||
|
||||
// noCachedConnError is the concrete type of ErrNoCachedConn, which
|
||||
// needs to be detected by net/http regardless of whether it's its
|
||||
// bundled version (in h2_bundle.go with a rewritten type name) or
|
||||
// from a user's x/net/http2. As such, as it has a unique method name
|
||||
// (IsHTTP2NoCachedConnError) that net/http sniffs for via func
|
||||
// isNoCachedConnError.
|
||||
type noCachedConnError struct{}
|
||||
|
||||
func (noCachedConnError) IsHTTP2NoCachedConnError() {}
|
||||
func (noCachedConnError) Error() string { return "http2: no cached connection was available" }
|
||||
|
||||
// isNoCachedConnError reports whether err is of type noCachedConnError
|
||||
// or its equivalent renamed type in net/http2's h2_bundle.go. Both types
|
||||
// may coexist in the same running program.
|
||||
func isNoCachedConnError(err error) bool {
|
||||
_, ok := err.(interface{ IsHTTP2NoCachedConnError() })
|
||||
return ok
|
||||
}
|
||||
|
||||
var ErrNoCachedConn error = noCachedConnError{}
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
//go:build go1.27 && !http2legacy
|
||||
|
||||
// Transport wrapping a net/http.Transport.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func configureTransport(t1 *http.Transport) error {
|
||||
// ConfigureTransport is a no-op: The http.Transport already supports HTTP/2.
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureTransports(t1 *http.Transport) (*Transport, error) {
|
||||
// ConfigureTransport returns an http2.Transport with a configuration
|
||||
// linked to the http.Transport's.
|
||||
tr2 := &Transport{}
|
||||
tr2.configure(t1)
|
||||
return tr2, nil
|
||||
}
|
||||
|
||||
// transportConfig is passed to net/http.Transport.RegisterProtocol("http/2", config).
|
||||
// It provides the net/http.Transport with access to the configuration in the
|
||||
// x/net/http2.Transport.
|
||||
type transportConfig struct {
|
||||
t *Transport
|
||||
}
|
||||
|
||||
// Registered is called by net/http.Transport.RegisterProtocol,
|
||||
// to let us know that it understands the registration mechanism we're using.
|
||||
func (t transportConfig) Registered(t1 *http.Transport) {
|
||||
t.t.t1 = t1
|
||||
}
|
||||
|
||||
func (t transportConfig) DisableCompression() bool {
|
||||
return t.t.DisableCompression
|
||||
}
|
||||
|
||||
func (t transportConfig) MaxHeaderListSize() int64 {
|
||||
return int64(t.t.MaxHeaderListSize)
|
||||
}
|
||||
|
||||
func (t transportConfig) IdleConnTimeout() time.Duration {
|
||||
return t.t.IdleConnTimeout
|
||||
}
|
||||
|
||||
func (t transportConfig) HTTP2Config() http.HTTP2Config {
|
||||
return http.HTTP2Config{
|
||||
StrictMaxConcurrentRequests: t.t.StrictMaxConcurrentStreams,
|
||||
MaxDecoderHeaderTableSize: int(t.t.MaxDecoderHeaderTableSize),
|
||||
MaxEncoderHeaderTableSize: int(t.t.MaxEncoderHeaderTableSize),
|
||||
MaxReadFrameSize: int(t.t.MaxReadFrameSize),
|
||||
SendPingTimeout: t.t.ReadIdleTimeout,
|
||||
PingTimeout: t.t.PingTimeout,
|
||||
WriteByteTimeout: t.t.WriteByteTimeout,
|
||||
CountError: t.t.CountError,
|
||||
}
|
||||
}
|
||||
|
||||
// ExternalRoundTrip reports whether the Transport wants to take control of the RoundTrip call.
|
||||
// If the user hasn't configured a custom connection pool, we leave the RoundTrip up to net/http.
|
||||
func (t transportConfig) ExternalRoundTrip() bool {
|
||||
return t.t.ConnPool != nil
|
||||
}
|
||||
|
||||
// RoundTrip is used when the http.Transport is passing control of the full
|
||||
// RoundTrip to us--connection pooling, retries, etc.
|
||||
//
|
||||
// This is only used when the http2.Transport has a user-provided ConnPool.
|
||||
// Any other time, net/http handles everything.
|
||||
func (t transportConfig) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if t.t.ConnPool == nil {
|
||||
return nil, http.ErrSkipAltProtocol
|
||||
}
|
||||
return t.t.RoundTrip(req)
|
||||
}
|
||||
|
||||
// netConnContextKey passes a net.Conn to http.Transport.NewClientConn.
|
||||
// See http2.Transport.NewClientConn.
|
||||
type netConnContextKey struct{}
|
||||
|
||||
// ConnFromContext lets the http.Transport fetch a net.Conn out of a context
|
||||
// passed to NewClientConn. See http2.Transport.NewClientConn.
|
||||
func (t transportConfig) ConnFromContext(ctx context.Context) net.Conn {
|
||||
nc, _ := ctx.Value(netConnContextKey{}).(net.Conn)
|
||||
return nc
|
||||
}
|
||||
|
||||
// http2TransportContextKey marks a RoundTrip as needing its dial handled by the http2.Transport.
|
||||
// We set this for http2.RoundTrip calls, where the historical behavior is to use the
|
||||
// http2.Transport's dialer.
|
||||
type http2TransportContextKey struct{}
|
||||
|
||||
// DialFromContext dials a new connection using the http2.Transport's DialTLS/DialTLSContext.
|
||||
func (t transportConfig) DialFromContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if ctx.Value(http2TransportContextKey{}) == nil {
|
||||
// We're being called from a RoundTrip that did not start with an http2.Transport.
|
||||
// Use the http.Transport's dialer.
|
||||
return nil, errors.ErrUnsupported
|
||||
}
|
||||
|
||||
tlsConf := t.t.TLSClientConfig
|
||||
if tlsConf == nil {
|
||||
tlsConf = &tls.Config{}
|
||||
} else {
|
||||
tlsConf = tlsConf.Clone()
|
||||
}
|
||||
if !slices.Contains(tlsConf.NextProtos, "h2") {
|
||||
tlsConf.NextProtos = append([]string{"h2"}, tlsConf.NextProtos...)
|
||||
}
|
||||
if tlsConf.ServerName == "" {
|
||||
host, _, err := net.SplitHostPort(address)
|
||||
if err == nil {
|
||||
tlsConf.ServerName = host
|
||||
}
|
||||
}
|
||||
return t.t.dialTLS(ctx, network, address, tlsConf)
|
||||
}
|
||||
|
||||
type transportInternal struct {
|
||||
initOnce sync.Once
|
||||
t1 *http.Transport
|
||||
}
|
||||
|
||||
func (t *Transport) init() {
|
||||
t.initOnce.Do(func() {
|
||||
if t.t1 != nil {
|
||||
return
|
||||
}
|
||||
t1 := &http.Transport{}
|
||||
t.configure(t1)
|
||||
})
|
||||
}
|
||||
|
||||
func (t *Transport) configure(t1 *http.Transport) {
|
||||
t1.RegisterProtocol("http/2", transportConfig{t})
|
||||
// tr2.t1 is set by transportConfig.Registered.
|
||||
if t.t1 != t1 {
|
||||
panic("http2: net/http does not support this version of x/net/http2")
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transport) roundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
|
||||
t.init()
|
||||
|
||||
if req.URL.Scheme == "http" && !t.AllowHTTP {
|
||||
return nil, errors.New("http2: unencrypted HTTP/2 not enabled")
|
||||
}
|
||||
|
||||
// When the Transport has a user-provided connection pool (unusual, deprecated),
|
||||
// we need to handle picking a connection, retrys, etc.
|
||||
if t.ConnPool != nil {
|
||||
return t.roundTripViaPool(req, opt, t.ConnPool)
|
||||
}
|
||||
|
||||
// Setting this context key lets net/http know that if it is necessary to dial
|
||||
// a new connection, we should handle the net.Dial.
|
||||
//
|
||||
// Both http.Transport and http2.Transport allow the user to provide a custom
|
||||
// dial function, and historically you only get the dial function from the
|
||||
// Transport you're calling RoundTrip on.
|
||||
ctx := context.WithValue(req.Context(), http2TransportContextKey{}, t)
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
return t.t1.RoundTrip(req)
|
||||
}
|
||||
|
||||
func (t *Transport) closeIdleConnections() {
|
||||
t.init()
|
||||
t.t1.CloseIdleConnections()
|
||||
}
|
||||
|
||||
func (t *Transport) newUserClientConn(c net.Conn) (*ClientConn, error) {
|
||||
// http.Transport's NewClientConn doesn't provide a supported way to create
|
||||
// a connection from a net.Conn. (This might be useful to add in the future?)
|
||||
// We're going to craftily sneak one in via the context key, with the
|
||||
// scheme of "http/2" telling NewClientConn to look for it.
|
||||
ctx := context.WithValue(context.Background(), netConnContextKey{}, c)
|
||||
|
||||
nhcc, err := t.t1.NewClientConn(ctx, "http/2", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc := &ClientConn{cc: nhcc, tr: t, tconn: c}
|
||||
nhcc.SetStateHook(cc.stateHook)
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
// ClientConn is the state of a single HTTP/2 client connection to an
|
||||
// HTTP/2 server.
|
||||
type ClientConn struct {
|
||||
cc *http.ClientConn
|
||||
tconn net.Conn
|
||||
tr *Transport
|
||||
doNotReuse bool
|
||||
|
||||
mu sync.Mutex
|
||||
closing bool
|
||||
closed bool
|
||||
roundTrips int
|
||||
reserved int
|
||||
starting int
|
||||
pending int
|
||||
maxConcurrent int
|
||||
lastIdle time.Time
|
||||
shutdownc chan struct{}
|
||||
|
||||
atomicReused uint32 // whether conn is being reused; atomic
|
||||
}
|
||||
|
||||
func (cc *ClientConn) roundTrip(req *http.Request) (*http.Response, error) {
|
||||
err := func() error {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
if cc.doNotReuse {
|
||||
return errClientConnUnusable
|
||||
}
|
||||
cc.roundTrips++
|
||||
if cc.reserved > 0 {
|
||||
// We've already reserved a concurrency slot for this request.
|
||||
cc.reserved--
|
||||
} else if cc.cc.Reserve() != nil {
|
||||
// We don't seem to have an available concurrency slot,
|
||||
// so bump the pending count (requests waiting for a slot).
|
||||
cc.pending++
|
||||
}
|
||||
// ClientConn.Shutdown will not shut down the conn while
|
||||
// cc.starting > 0 or cc.cc.InFlight() > 0.
|
||||
//
|
||||
// The starting state covers the gap between us deciding to
|
||||
// start sending the request, and actually sending it.
|
||||
cc.starting++
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := cc.cc.RoundTrip(req)
|
||||
cc.mu.Lock()
|
||||
cc.starting--
|
||||
if cc.pending > 0 {
|
||||
// A request completing frees up a concurrency slot for
|
||||
// a pending request to start.
|
||||
cc.pending--
|
||||
}
|
||||
cc.updateStateLocked()
|
||||
cc.mu.Unlock()
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (cc *ClientConn) canTakeNewRequest() bool {
|
||||
return cc.cc.Available() > 0 && !cc.doNotReuse
|
||||
}
|
||||
|
||||
func (cc *ClientConn) close() error {
|
||||
return cc.cc.Close()
|
||||
}
|
||||
|
||||
func (cc *ClientConn) ping(ctx context.Context) error {
|
||||
// Ask net/http to ping its connection by sending a request with a method of ":ping".
|
||||
_, err := cc.cc.RoundTrip((&http.Request{
|
||||
Method: ":ping",
|
||||
}).WithContext(ctx))
|
||||
return err
|
||||
}
|
||||
|
||||
func (cc *ClientConn) reserveNewRequest() bool {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
if cc.doNotReuse {
|
||||
return false
|
||||
}
|
||||
if err := cc.cc.Reserve(); err != nil {
|
||||
return false
|
||||
}
|
||||
cc.reserved++
|
||||
return true
|
||||
}
|
||||
|
||||
func (cc *ClientConn) setDoNotReuse() {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
cc.doNotReuse = true
|
||||
cc.closing = true
|
||||
}
|
||||
|
||||
func (cc *ClientConn) shutdown(ctx context.Context) error {
|
||||
cc.mu.Lock()
|
||||
inFlight := cc.cc.InFlight() + cc.starting
|
||||
if inFlight > 0 && cc.shutdownc == nil {
|
||||
cc.shutdownc = make(chan struct{})
|
||||
}
|
||||
shutdownc := cc.shutdownc
|
||||
cc.mu.Unlock()
|
||||
if shutdownc != nil {
|
||||
// Wait for in-flight requests to finish.
|
||||
select {
|
||||
case <-shutdownc:
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
cc.cc.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cc *ClientConn) state() ClientConnState {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
cc.updateStateLocked()
|
||||
return ClientConnState{
|
||||
Closed: cc.closed,
|
||||
Closing: cc.closing,
|
||||
StreamsActive: cc.cc.InFlight() - cc.reserved,
|
||||
StreamsReserved: cc.reserved,
|
||||
StreamsPending: cc.pending,
|
||||
MaxConcurrentStreams: uint32(min(int64(cc.maxConcurrent), math.MaxUint32)),
|
||||
LastIdle: cc.lastIdle,
|
||||
}
|
||||
}
|
||||
|
||||
// stateHook is the http.ClientConn's state hook.
|
||||
func (cc *ClientConn) stateHook(*http.ClientConn) {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
cc.updateStateLocked()
|
||||
}
|
||||
|
||||
func (cc *ClientConn) updateStateLocked() {
|
||||
if cc.cc.Err() != nil && !cc.closed {
|
||||
cc.closing = true
|
||||
cc.closed = true
|
||||
if cc.tr.ConnPool != nil {
|
||||
// Do the ConnPool update in another goroutine,
|
||||
// to avoid holding the conn mutex while it runs.
|
||||
go cc.tr.ConnPool.MarkDead(cc)
|
||||
}
|
||||
}
|
||||
if cc.cc.InFlight() == 0 && cc.roundTrips > 0 && cc.starting == 0 {
|
||||
cc.lastIdle = time.Now()
|
||||
}
|
||||
if !cc.closed {
|
||||
// This is slightly racy (a request could start or finish in between
|
||||
// the Available and InFlight calls), but the best we can do given that
|
||||
// the net/http ClientConn API doesn't expose the conn's max concurrency.
|
||||
cc.maxConcurrent = cc.cc.Available() + cc.cc.InFlight()
|
||||
}
|
||||
if cc.shutdownc != nil && cc.cc.InFlight()+cc.starting == 0 {
|
||||
close(cc.shutdownc)
|
||||
cc.shutdownc = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (cc *ClientConn) stopIdleTimer() {}
|
||||
|
||||
// traceGotConn is (when http2legacy is not enabled) only used for tracing
|
||||
// connections acquired while using a user-provided ClientConnPool.
|
||||
func traceGotConn(req *http.Request, cc *ClientConn, reused bool) {
|
||||
trace := httptrace.ContextClientTrace(req.Context())
|
||||
if trace == nil || trace.GotConn == nil {
|
||||
return
|
||||
}
|
||||
ci := httptrace.GotConnInfo{Conn: cc.tconn}
|
||||
ci.Reused = reused
|
||||
trace.GotConn(ci)
|
||||
}
|
||||
+4
-40
@@ -2,51 +2,15 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !(go1.27 && !http2legacy)
|
||||
|
||||
package http2
|
||||
|
||||
import "fmt"
|
||||
|
||||
// WriteScheduler is the interface implemented by HTTP/2 write schedulers.
|
||||
// Methods are never called concurrently.
|
||||
type WriteScheduler interface {
|
||||
// OpenStream opens a new stream in the write scheduler.
|
||||
// It is illegal to call this with streamID=0 or with a streamID that is
|
||||
// already open -- the call may panic.
|
||||
OpenStream(streamID uint32, options OpenStreamOptions)
|
||||
|
||||
// CloseStream closes a stream in the write scheduler. Any frames queued on
|
||||
// this stream should be discarded. It is illegal to call this on a stream
|
||||
// that is not open -- the call may panic.
|
||||
CloseStream(streamID uint32)
|
||||
|
||||
// AdjustStream adjusts the priority of the given stream. This may be called
|
||||
// on a stream that has not yet been opened or has been closed. Note that
|
||||
// RFC 7540 allows PRIORITY frames to be sent on streams in any state. See:
|
||||
// https://tools.ietf.org/html/rfc7540#section-5.1
|
||||
AdjustStream(streamID uint32, priority PriorityParam)
|
||||
|
||||
// Push queues a frame in the scheduler. In most cases, this will not be
|
||||
// called with wr.StreamID()!=0 unless that stream is currently open. The one
|
||||
// exception is RST_STREAM frames, which may be sent on idle or closed streams.
|
||||
Push(wr FrameWriteRequest)
|
||||
|
||||
// Pop dequeues the next frame to write. Returns false if no frames can
|
||||
// be written. Frames with a given wr.StreamID() are Pop'd in the same
|
||||
// order they are Push'd, except RST_STREAM frames. No frames should be
|
||||
// discarded except by CloseStream.
|
||||
Pop() (wr FrameWriteRequest, ok bool)
|
||||
}
|
||||
|
||||
// OpenStreamOptions specifies extra options for WriteScheduler.OpenStream.
|
||||
type OpenStreamOptions struct {
|
||||
// PusherID is zero if the stream was initiated by the client. Otherwise,
|
||||
// PusherID names the stream that pushed the newly opened stream.
|
||||
PusherID uint32
|
||||
// priority is used to set the priority of the newly opened stream.
|
||||
priority PriorityParam
|
||||
}
|
||||
|
||||
// FrameWriteRequest is a request to write a frame.
|
||||
//
|
||||
// Deprecated: User-provided write schedulers are deprecated.
|
||||
type FrameWriteRequest struct {
|
||||
// write is the interface value that does the writing, once the
|
||||
// WriteScheduler has selected this frame to write. The write
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
// Copyright 2026 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 http2
|
||||
|
||||
// WriteScheduler is the interface implemented by HTTP/2 write schedulers.
|
||||
// Methods are never called concurrently.
|
||||
//
|
||||
// Deprecated: User-provided write schedulers are deprecated.
|
||||
type WriteScheduler interface {
|
||||
// OpenStream opens a new stream in the write scheduler.
|
||||
// It is illegal to call this with streamID=0 or with a streamID that is
|
||||
// already open -- the call may panic.
|
||||
OpenStream(streamID uint32, options OpenStreamOptions)
|
||||
|
||||
// CloseStream closes a stream in the write scheduler. Any frames queued on
|
||||
// this stream should be discarded. It is illegal to call this on a stream
|
||||
// that is not open -- the call may panic.
|
||||
CloseStream(streamID uint32)
|
||||
|
||||
// AdjustStream adjusts the priority of the given stream. This may be called
|
||||
// on a stream that has not yet been opened or has been closed. Note that
|
||||
// RFC 7540 allows PRIORITY frames to be sent on streams in any state. See:
|
||||
// https://tools.ietf.org/html/rfc7540#section-5.1
|
||||
AdjustStream(streamID uint32, priority PriorityParam)
|
||||
|
||||
// Push queues a frame in the scheduler. In most cases, this will not be
|
||||
// called with wr.StreamID()!=0 unless that stream is currently open. The one
|
||||
// exception is RST_STREAM frames, which may be sent on idle or closed streams.
|
||||
Push(wr FrameWriteRequest)
|
||||
|
||||
// Pop dequeues the next frame to write. Returns false if no frames can
|
||||
// be written. Frames with a given wr.StreamID() are Pop'd in the same
|
||||
// order they are Push'd, except RST_STREAM frames. No frames should be
|
||||
// discarded except by CloseStream.
|
||||
Pop() (wr FrameWriteRequest, ok bool)
|
||||
}
|
||||
|
||||
// OpenStreamOptions specifies extra options for WriteScheduler.OpenStream.
|
||||
//
|
||||
// Deprecated: User-provided write schedulers are deprecated.
|
||||
type OpenStreamOptions struct {
|
||||
// PusherID is zero if the stream was initiated by the client. Otherwise,
|
||||
// PusherID names the stream that pushed the newly opened stream.
|
||||
PusherID uint32
|
||||
// priority is used to set the priority of the newly opened stream.
|
||||
priority PriorityParam
|
||||
}
|
||||
|
||||
// PriorityWriteSchedulerConfig configures a priorityWriteScheduler.
|
||||
//
|
||||
// Deprecated: User-provided write schedulers are deprecated.
|
||||
type PriorityWriteSchedulerConfig struct {
|
||||
// MaxClosedNodesInTree controls the maximum number of closed streams to
|
||||
// retain in the priority tree. Setting this to zero saves a small amount
|
||||
// of memory at the cost of performance.
|
||||
//
|
||||
// See RFC 7540, Section 5.3.4:
|
||||
// "It is possible for a stream to become closed while prioritization
|
||||
// information ... is in transit. ... This potentially creates suboptimal
|
||||
// prioritization, since the stream could be given a priority that is
|
||||
// different from what is intended. To avoid these problems, an endpoint
|
||||
// SHOULD retain stream prioritization state for a period after streams
|
||||
// become closed. The longer state is retained, the lower the chance that
|
||||
// streams are assigned incorrect or default priority values."
|
||||
MaxClosedNodesInTree int
|
||||
|
||||
// MaxIdleNodesInTree controls the maximum number of idle streams to
|
||||
// retain in the priority tree. Setting this to zero saves a small amount
|
||||
// of memory at the cost of performance.
|
||||
//
|
||||
// See RFC 7540, Section 5.3.4:
|
||||
// Similarly, streams that are in the "idle" state can be assigned
|
||||
// priority or become a parent of other streams. This allows for the
|
||||
// creation of a grouping node in the dependency tree, which enables
|
||||
// more flexible expressions of priority. Idle streams begin with a
|
||||
// default priority (Section 5.3.5).
|
||||
MaxIdleNodesInTree int
|
||||
|
||||
// ThrottleOutOfOrderWrites enables write throttling to help ensure that
|
||||
// data is delivered in priority order. This works around a race where
|
||||
// stream B depends on stream A and both streams are about to call Write
|
||||
// to queue DATA frames. If B wins the race, a naive scheduler would eagerly
|
||||
// write as much data from B as possible, but this is suboptimal because A
|
||||
// is a higher-priority stream. With throttling enabled, we write a small
|
||||
// amount of data from B to minimize the amount of bandwidth that B can
|
||||
// steal from A.
|
||||
ThrottleOutOfOrderWrites bool
|
||||
}
|
||||
+9
-39
@@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !(go1.27 && !http2legacy)
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
@@ -13,49 +15,17 @@ import (
|
||||
// RFC 7540, Section 5.3.5: the default weight is 16.
|
||||
const priorityDefaultWeightRFC7540 = 15 // 16 = 15 + 1
|
||||
|
||||
// PriorityWriteSchedulerConfig configures a priorityWriteScheduler.
|
||||
type PriorityWriteSchedulerConfig struct {
|
||||
// MaxClosedNodesInTree controls the maximum number of closed streams to
|
||||
// retain in the priority tree. Setting this to zero saves a small amount
|
||||
// of memory at the cost of performance.
|
||||
//
|
||||
// See RFC 7540, Section 5.3.4:
|
||||
// "It is possible for a stream to become closed while prioritization
|
||||
// information ... is in transit. ... This potentially creates suboptimal
|
||||
// prioritization, since the stream could be given a priority that is
|
||||
// different from what is intended. To avoid these problems, an endpoint
|
||||
// SHOULD retain stream prioritization state for a period after streams
|
||||
// become closed. The longer state is retained, the lower the chance that
|
||||
// streams are assigned incorrect or default priority values."
|
||||
MaxClosedNodesInTree int
|
||||
|
||||
// MaxIdleNodesInTree controls the maximum number of idle streams to
|
||||
// retain in the priority tree. Setting this to zero saves a small amount
|
||||
// of memory at the cost of performance.
|
||||
//
|
||||
// See RFC 7540, Section 5.3.4:
|
||||
// Similarly, streams that are in the "idle" state can be assigned
|
||||
// priority or become a parent of other streams. This allows for the
|
||||
// creation of a grouping node in the dependency tree, which enables
|
||||
// more flexible expressions of priority. Idle streams begin with a
|
||||
// default priority (Section 5.3.5).
|
||||
MaxIdleNodesInTree int
|
||||
|
||||
// ThrottleOutOfOrderWrites enables write throttling to help ensure that
|
||||
// data is delivered in priority order. This works around a race where
|
||||
// stream B depends on stream A and both streams are about to call Write
|
||||
// to queue DATA frames. If B wins the race, a naive scheduler would eagerly
|
||||
// write as much data from B as possible, but this is suboptimal because A
|
||||
// is a higher-priority stream. With throttling enabled, we write a small
|
||||
// amount of data from B to minimize the amount of bandwidth that B can
|
||||
// steal from A.
|
||||
ThrottleOutOfOrderWrites bool
|
||||
}
|
||||
|
||||
// NewPriorityWriteScheduler constructs a WriteScheduler that schedules
|
||||
// frames by following HTTP/2 priorities as described in RFC 7540 Section 5.3.
|
||||
// If cfg is nil, default options are used.
|
||||
//
|
||||
// Deprecated: The RFC 7540 write scheduler has known bugs and performance issues,
|
||||
// and RFC 7540 prioritization was deprecated in RFC 9113.
|
||||
func NewPriorityWriteScheduler(cfg *PriorityWriteSchedulerConfig) WriteScheduler {
|
||||
return newPriorityWriteSchedulerRFC7540(cfg)
|
||||
}
|
||||
|
||||
func newPriorityWriteSchedulerRFC7540(cfg *PriorityWriteSchedulerConfig) WriteScheduler {
|
||||
if cfg == nil {
|
||||
// For justification of these defaults, see:
|
||||
// https://docs.google.com/document/d/1oLhNg1skaWD4_DtaoCxdSRN5erEXrH-KnLrMwEpOtFY
|
||||
|
||||
+2
@@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !(go1.27 && !http2legacy)
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
|
||||
+4
@@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !(go1.27 && !http2legacy)
|
||||
|
||||
package http2
|
||||
|
||||
import "math"
|
||||
@@ -10,6 +12,8 @@ import "math"
|
||||
// priorities. Control frames like SETTINGS and PING are written before DATA
|
||||
// frames, but if no control frames are queued and multiple streams have queued
|
||||
// HEADERS or DATA frames, Pop selects a ready stream arbitrarily.
|
||||
//
|
||||
// Deprecated: User-provided write schedulers are deprecated.
|
||||
func NewRandomWriteScheduler() WriteScheduler {
|
||||
return &randomWriteScheduler{sq: make(map[uint32]*writeQueue)}
|
||||
}
|
||||
|
||||
+2
@@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !(go1.27 && !http2legacy)
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2021 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.
|
||||
|
||||
//go:build go1.18
|
||||
|
||||
package idna
|
||||
|
||||
// Transitional processing is disabled by default in Go 1.18.
|
||||
// https://golang.org/issue/47510
|
||||
const transitionalLookup = false
|
||||
Generated
Vendored
+148
-37
@@ -4,8 +4,6 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.10
|
||||
|
||||
// Package idna implements IDNA2008 using the compatibility processing
|
||||
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to
|
||||
// deal with the transition from IDNA2003.
|
||||
@@ -20,6 +18,7 @@ package idna // import "golang.org/x/net/idna"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/secure/bidirule"
|
||||
@@ -27,6 +26,8 @@ import (
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
const unicode16 = unicode.Version >= "16.0.0"
|
||||
|
||||
// NOTE: Unlike common practice in Go APIs, the functions will return a
|
||||
// sanitized domain name in case of errors. Browsers sometimes use a partially
|
||||
// evaluated string as lookup.
|
||||
@@ -101,6 +102,11 @@ func ValidateLabels(enable bool) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// validateLabels reports whether the ValidateLabels option is enabled.
|
||||
func (p *Profile) validateLabels() bool {
|
||||
return p.fromPuny != nil
|
||||
}
|
||||
|
||||
// CheckHyphens sets whether to check for correct use of hyphens ('-') in
|
||||
// labels. Most web browsers do not have this option set, since labels such as
|
||||
// "r3---sn-apo3qvuoxuxbt-j5pe" are in common use.
|
||||
@@ -263,6 +269,10 @@ func (p *Profile) String() string {
|
||||
return s
|
||||
}
|
||||
|
||||
// Transitional processing is disabled by default as of Go 1.18.
|
||||
// https://golang.org/issue/47510
|
||||
const transitionalLookup = false
|
||||
|
||||
var (
|
||||
// Punycode is a Profile that does raw punycode processing with a minimum
|
||||
// of validation.
|
||||
@@ -324,15 +334,30 @@ func (e labelError) Error() string {
|
||||
return fmt.Sprintf("idna: invalid label %q", e.label)
|
||||
}
|
||||
|
||||
type runeError rune
|
||||
|
||||
func (e runeError) code() string { return "P1" }
|
||||
func (e runeError) Error() string {
|
||||
return fmt.Sprintf("idna: disallowed rune %U", e)
|
||||
type runeError struct {
|
||||
r rune
|
||||
code_ string
|
||||
}
|
||||
|
||||
// process implements the algorithm described in section 4 of UTS #46,
|
||||
// see https://www.unicode.org/reports/tr46.
|
||||
func (e runeError) code() string { return e.code_ }
|
||||
func (e runeError) Error() string {
|
||||
return fmt.Sprintf("idna: disallowed rune %U", e.r)
|
||||
}
|
||||
|
||||
// code16 returns old for Unicode < 16, new for Unicode >= 16.
|
||||
func code16(old, new string) string {
|
||||
if unicode16 {
|
||||
return new
|
||||
}
|
||||
return old
|
||||
}
|
||||
|
||||
// process10 implements the algorithm described in section 4 of UTS #46.
|
||||
// It implements both the Unicode 10 algorithm
|
||||
// (https://www.unicode.org/reports/tr46/tr46-19.html)
|
||||
// and the Unicode 16 algorithm
|
||||
// (https://www.unicode.org/reports/tr46/tr46-35.html)
|
||||
// depending on unicode16, which in turn depends on unicode.Version.
|
||||
func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||
var err error
|
||||
var isBidi bool
|
||||
@@ -347,8 +372,12 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||
// TODO: allow for a quick check of the tables data.
|
||||
// It seems like we should only create this error on ToASCII, but the
|
||||
// UTS 46 conformance tests suggests we should always check this.
|
||||
labelCode := "X4_2"
|
||||
if !unicode16 || toASCII {
|
||||
labelCode = "A4"
|
||||
}
|
||||
if err == nil && p.verifyDNSLength && s == "" {
|
||||
err = &labelError{s, "A4"}
|
||||
err = labelError{s, labelCode}
|
||||
}
|
||||
labels := labelIter{orig: s}
|
||||
for ; !labels.done(); labels.next() {
|
||||
@@ -357,12 +386,13 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||
// Empty labels are not okay. The label iterator skips the last
|
||||
// label if it is empty.
|
||||
if err == nil && p.verifyDNSLength {
|
||||
err = &labelError{s, "A4"}
|
||||
err = labelError{s, labelCode}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(label, acePrefix) {
|
||||
u, err2 := decode(label[len(acePrefix):])
|
||||
enc := label[len(acePrefix):]
|
||||
u, err2 := decode(enc)
|
||||
if err2 != nil {
|
||||
if err == nil {
|
||||
err = err2
|
||||
@@ -370,6 +400,9 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||
// Spec says keep the old label.
|
||||
continue
|
||||
}
|
||||
if unicode16 && err == nil && len(u) > 0 && isASCII(u) {
|
||||
err = punyError(enc)
|
||||
}
|
||||
isBidi = isBidi || bidirule.DirectionString(u) != bidi.LeftToRight
|
||||
labels.set(u)
|
||||
if err == nil && p.fromPuny != nil {
|
||||
@@ -379,16 +412,16 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||
// This should be called on NonTransitional, according to the
|
||||
// spec, but that currently does not have any effect. Use the
|
||||
// original profile to preserve options.
|
||||
err = p.validateLabel(u)
|
||||
err = p.validateLabel(u, labelCode)
|
||||
}
|
||||
} else if err == nil {
|
||||
err = p.validateLabel(label)
|
||||
err = p.validateLabel(label, labelCode)
|
||||
}
|
||||
}
|
||||
if isBidi && p.bidirule != nil && err == nil {
|
||||
for labels.reset(); !labels.done(); labels.next() {
|
||||
if !p.bidirule(labels.label()) {
|
||||
err = &labelError{s, "B"}
|
||||
err = labelError{s, "B"}
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -406,24 +439,36 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||
}
|
||||
n := len(label)
|
||||
if p.verifyDNSLength && err == nil && (n == 0 || n > 63) {
|
||||
err = &labelError{label, "A4"}
|
||||
err = labelError{label, labelCode}
|
||||
}
|
||||
}
|
||||
}
|
||||
s = labels.result()
|
||||
if toASCII && p.verifyDNSLength && err == nil {
|
||||
if unicode16 && strings.HasSuffix(s, ".") {
|
||||
err = labelError{s, labelCode}
|
||||
}
|
||||
// Compute the length of the domain name minus the root label and its dot.
|
||||
n := len(s)
|
||||
if n > 0 && s[n-1] == '.' {
|
||||
n--
|
||||
}
|
||||
if len(s) < 1 || n > 253 {
|
||||
err = &labelError{s, "A4"}
|
||||
err = labelError{s, labelCode}
|
||||
}
|
||||
}
|
||||
return s, err
|
||||
}
|
||||
|
||||
func isASCII(s string) bool {
|
||||
for _, c := range []byte(s) {
|
||||
if c >= 0x80 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func normalize(p *Profile, s string) (mapped string, isBidi bool, err error) {
|
||||
// TODO: consider first doing a quick check to see if any of these checks
|
||||
// need to be done. This will make it slower in the general case, but
|
||||
@@ -436,12 +481,12 @@ func normalize(p *Profile, s string) (mapped string, isBidi bool, err error) {
|
||||
func validateRegistration(p *Profile, s string) (idem string, bidi bool, err error) {
|
||||
// TODO: filter need for normalization in loop below.
|
||||
if !norm.NFC.IsNormalString(s) {
|
||||
return s, false, &labelError{s, "V1"}
|
||||
return s, false, labelError{s, "V1"}
|
||||
}
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
if sz == 0 {
|
||||
return s, bidi, runeError(utf8.RuneError)
|
||||
return s, bidi, runeError{utf8.RuneError, "P1"}
|
||||
}
|
||||
bidi = bidi || info(v).isBidi(s[i:])
|
||||
// Copy bytes not copied so far.
|
||||
@@ -449,9 +494,12 @@ func validateRegistration(p *Profile, s string) (idem string, bidi bool, err err
|
||||
// TODO: handle the NV8 defined in the Unicode idna data set to allow
|
||||
// for strict conformance to IDNA2008.
|
||||
case valid, deviation:
|
||||
if sz == 1 && p.useSTD3Rules && !allowedSTD3(rune(s[i])) {
|
||||
return s, bidi, runeError{rune(s[i]), "P1"}
|
||||
}
|
||||
case disallowed, mapped, unknown, ignored:
|
||||
r, _ := utf8.DecodeRuneInString(s[i:])
|
||||
return s, bidi, runeError(r)
|
||||
return s, bidi, runeError{r, "P1"}
|
||||
}
|
||||
i += sz
|
||||
}
|
||||
@@ -489,7 +537,7 @@ func validateAndMap(p *Profile, s string) (vm string, bidi bool, err error) {
|
||||
b = append(b, "\ufffd"...)
|
||||
k = len(s)
|
||||
if err == nil {
|
||||
err = runeError(utf8.RuneError)
|
||||
err = runeError{utf8.RuneError, "P1"}
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -502,14 +550,26 @@ func validateAndMap(p *Profile, s string) (vm string, bidi bool, err error) {
|
||||
case valid:
|
||||
continue
|
||||
case disallowed:
|
||||
if err == nil {
|
||||
// Unicode 16 delays the error until validateLabels.
|
||||
// Unicode 10 gave an error now.
|
||||
if !unicode16 && err == nil {
|
||||
r, _ := utf8.DecodeRuneInString(s[start:])
|
||||
err = runeError(r)
|
||||
err = runeError{r, "P1"}
|
||||
}
|
||||
continue
|
||||
case mapped, deviation:
|
||||
case deviation:
|
||||
if unicode16 && !p.transitional {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case mapped:
|
||||
b = append(b, s[k:start]...)
|
||||
b = info(v).appendMapping(b, s[start:i])
|
||||
// Unicode 16 requires a special case to handle ẞ -> ss in transitional mode.
|
||||
if unicode16 && p.transitional && s[start:start+sz] == "ẞ" {
|
||||
b = append(b, "ss"...)
|
||||
} else {
|
||||
b = info(v).appendMapping(b, s[start:i])
|
||||
}
|
||||
case ignored:
|
||||
b = append(b, s[k:start]...)
|
||||
// drop the rune
|
||||
@@ -600,13 +660,13 @@ const acePrefix = "xn--"
|
||||
|
||||
func (p *Profile) simplify(cat category) category {
|
||||
switch cat {
|
||||
case disallowedSTD3Mapped:
|
||||
case disallowedSTD3Mapped: // only happens for pre-Unicode 16
|
||||
if p.useSTD3Rules {
|
||||
cat = disallowed
|
||||
} else {
|
||||
cat = mapped
|
||||
}
|
||||
case disallowedSTD3Valid:
|
||||
case disallowedSTD3Valid: // only happens for pre-Unicode 16
|
||||
if p.useSTD3Rules {
|
||||
cat = disallowed
|
||||
} else {
|
||||
@@ -625,17 +685,18 @@ func (p *Profile) simplify(cat category) category {
|
||||
|
||||
func validateFromPunycode(p *Profile, s string) error {
|
||||
if !norm.NFC.IsNormalString(s) {
|
||||
return &labelError{s, "V1"}
|
||||
return labelError{s, "V1"}
|
||||
}
|
||||
// TODO: detect whether string may have to be normalized in the following
|
||||
// loop.
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
if sz == 0 {
|
||||
return runeError(utf8.RuneError)
|
||||
return runeError{utf8.RuneError, "P1"}
|
||||
}
|
||||
if c := p.simplify(info(v).category()); c != valid && c != deviation {
|
||||
return &labelError{s, "V6"}
|
||||
cat := info(v).category()
|
||||
if c := p.simplify(cat); c != valid && c != deviation {
|
||||
return labelError{s, code16("V6", "V7")}
|
||||
}
|
||||
i += sz
|
||||
}
|
||||
@@ -704,23 +765,51 @@ var joinStates = [][numJoinTypes]joinState{
|
||||
},
|
||||
}
|
||||
|
||||
// allowedSTD3 reports whether r is a rune that can appear in a domain name
|
||||
// according to STD3. We allow all non-ASCII runes and then letters, digits, hyphens.
|
||||
// We also add dot so that this can be run against the whole name and not just
|
||||
// a single name element (label). The surrounding code checks dots well enough.
|
||||
func allowedSTD3(r rune) bool {
|
||||
return r >= 0x80 || 'a' <= r && r <= 'z' || '0' <= r && r <= '9' || r == '-' || r == '.'
|
||||
}
|
||||
|
||||
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
|
||||
// already implicitly satisfied by the overall implementation.
|
||||
func (p *Profile) validateLabel(s string) (err error) {
|
||||
func (p *Profile) validateLabel(s string, labelCode string) (err error) {
|
||||
if s == "" {
|
||||
if p.verifyDNSLength {
|
||||
return &labelError{s, "A4"}
|
||||
return labelError{s, labelCode}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if p.checkHyphens {
|
||||
if len(s) > 4 && s[2] == '-' && s[3] == '-' {
|
||||
return &labelError{s, "V2"}
|
||||
return labelError{s, "V2"}
|
||||
}
|
||||
if s[0] == '-' || s[len(s)-1] == '-' {
|
||||
return &labelError{s, "V3"}
|
||||
return labelError{s, "V3"}
|
||||
}
|
||||
}
|
||||
|
||||
// Unicode 16's TR 46 delays the rune validity checks until after the label is decoded.
|
||||
// (validateAndMap did not reject them earlier.)
|
||||
if unicode16 && p.validateLabels() {
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
if sz == 0 {
|
||||
return runeError{utf8.RuneError, "P1"}
|
||||
}
|
||||
cat := info(v).category()
|
||||
if c := p.simplify(cat); c != valid && (!p.transitional || c != deviation) {
|
||||
return labelError{s, "V7"}
|
||||
}
|
||||
if sz == 1 && p.useSTD3Rules && !allowedSTD3(rune(s[i])) {
|
||||
return runeError{rune(s[i]), "U1"}
|
||||
}
|
||||
i += sz
|
||||
}
|
||||
}
|
||||
|
||||
if !p.checkJoiners {
|
||||
return nil
|
||||
}
|
||||
@@ -729,7 +818,7 @@ func (p *Profile) validateLabel(s string) (err error) {
|
||||
v, sz := trie.lookupString(s)
|
||||
x := info(v)
|
||||
if x.isModifier() {
|
||||
return &labelError{s, "V5"}
|
||||
return labelError{s, code16("V5", "V6")}
|
||||
}
|
||||
// Quickly return in the absence of zero-width (non) joiners.
|
||||
if strings.Index(s, zwj) == -1 && strings.Index(s, zwnj) == -1 {
|
||||
@@ -754,8 +843,9 @@ func (p *Profile) validateLabel(s string) (err error) {
|
||||
x = info(v)
|
||||
}
|
||||
if st == stateFAIL || st == stateAfter {
|
||||
return &labelError{s, "C"}
|
||||
return labelError{s, "C"}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -767,3 +857,24 @@ func ascii(s string) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// appendMapping appends the mapping for the respective rune. isMapped must be
|
||||
// true. A mapping is a categorization of a rune as defined in UTS #46.
|
||||
func (c info) appendMapping(b []byte, s string) []byte {
|
||||
index := int(c >> indexShift)
|
||||
if c&xorBit == 0 {
|
||||
p := index
|
||||
return append(b, mappings[mappingIndex[p]:mappingIndex[p+1]]...)
|
||||
}
|
||||
b = append(b, s...)
|
||||
if c&inlineXOR == inlineXOR {
|
||||
// TODO: support and handle two-byte inline masks
|
||||
b[len(b)-1] ^= byte(index)
|
||||
} else {
|
||||
for p := len(b) - int(xorData[index]); p < len(b); p++ {
|
||||
index++
|
||||
b[p] ^= xorData[index]
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
-717
@@ -1,717 +0,0 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2016 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.
|
||||
|
||||
//go:build !go1.10
|
||||
|
||||
// Package idna implements IDNA2008 using the compatibility processing
|
||||
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to
|
||||
// deal with the transition from IDNA2003.
|
||||
//
|
||||
// IDNA2008 (Internationalized Domain Names for Applications), is defined in RFC
|
||||
// 5890, RFC 5891, RFC 5892, RFC 5893 and RFC 5894.
|
||||
// UTS #46 is defined in https://www.unicode.org/reports/tr46.
|
||||
// See https://unicode.org/cldr/utility/idna.jsp for a visualization of the
|
||||
// differences between these two standards.
|
||||
package idna // import "golang.org/x/net/idna"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/secure/bidirule"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
// NOTE: Unlike common practice in Go APIs, the functions will return a
|
||||
// sanitized domain name in case of errors. Browsers sometimes use a partially
|
||||
// evaluated string as lookup.
|
||||
// TODO: the current error handling is, in my opinion, the least opinionated.
|
||||
// Other strategies are also viable, though:
|
||||
// Option 1) Return an empty string in case of error, but allow the user to
|
||||
// specify explicitly which errors to ignore.
|
||||
// Option 2) Return the partially evaluated string if it is itself a valid
|
||||
// string, otherwise return the empty string in case of error.
|
||||
// Option 3) Option 1 and 2.
|
||||
// Option 4) Always return an empty string for now and implement Option 1 as
|
||||
// needed, and document that the return string may not be empty in case of
|
||||
// error in the future.
|
||||
// I think Option 1 is best, but it is quite opinionated.
|
||||
|
||||
// ToASCII is a wrapper for Punycode.ToASCII.
|
||||
func ToASCII(s string) (string, error) {
|
||||
return Punycode.process(s, true)
|
||||
}
|
||||
|
||||
// ToUnicode is a wrapper for Punycode.ToUnicode.
|
||||
func ToUnicode(s string) (string, error) {
|
||||
return Punycode.process(s, false)
|
||||
}
|
||||
|
||||
// An Option configures a Profile at creation time.
|
||||
type Option func(*options)
|
||||
|
||||
// Transitional sets a Profile to use the Transitional mapping as defined in UTS
|
||||
// #46. This will cause, for example, "ß" to be mapped to "ss". Using the
|
||||
// transitional mapping provides a compromise between IDNA2003 and IDNA2008
|
||||
// compatibility. It is used by some browsers when resolving domain names. This
|
||||
// option is only meaningful if combined with MapForLookup.
|
||||
func Transitional(transitional bool) Option {
|
||||
return func(o *options) { o.transitional = transitional }
|
||||
}
|
||||
|
||||
// VerifyDNSLength sets whether a Profile should fail if any of the IDN parts
|
||||
// are longer than allowed by the RFC.
|
||||
//
|
||||
// This option corresponds to the VerifyDnsLength flag in UTS #46.
|
||||
func VerifyDNSLength(verify bool) Option {
|
||||
return func(o *options) { o.verifyDNSLength = verify }
|
||||
}
|
||||
|
||||
// RemoveLeadingDots removes leading label separators. Leading runes that map to
|
||||
// dots, such as U+3002 IDEOGRAPHIC FULL STOP, are removed as well.
|
||||
func RemoveLeadingDots(remove bool) Option {
|
||||
return func(o *options) { o.removeLeadingDots = remove }
|
||||
}
|
||||
|
||||
// ValidateLabels sets whether to check the mandatory label validation criteria
|
||||
// as defined in Section 5.4 of RFC 5891. This includes testing for correct use
|
||||
// of hyphens ('-'), normalization, validity of runes, and the context rules.
|
||||
// In particular, ValidateLabels also sets the CheckHyphens and CheckJoiners flags
|
||||
// in UTS #46.
|
||||
func ValidateLabels(enable bool) Option {
|
||||
return func(o *options) {
|
||||
// Don't override existing mappings, but set one that at least checks
|
||||
// normalization if it is not set.
|
||||
if o.mapping == nil && enable {
|
||||
o.mapping = normalize
|
||||
}
|
||||
o.trie = trie
|
||||
o.checkJoiners = enable
|
||||
o.checkHyphens = enable
|
||||
if enable {
|
||||
o.fromPuny = validateFromPunycode
|
||||
} else {
|
||||
o.fromPuny = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CheckHyphens sets whether to check for correct use of hyphens ('-') in
|
||||
// labels. Most web browsers do not have this option set, since labels such as
|
||||
// "r3---sn-apo3qvuoxuxbt-j5pe" are in common use.
|
||||
//
|
||||
// This option corresponds to the CheckHyphens flag in UTS #46.
|
||||
func CheckHyphens(enable bool) Option {
|
||||
return func(o *options) { o.checkHyphens = enable }
|
||||
}
|
||||
|
||||
// CheckJoiners sets whether to check the ContextJ rules as defined in Appendix
|
||||
// A of RFC 5892, concerning the use of joiner runes.
|
||||
//
|
||||
// This option corresponds to the CheckJoiners flag in UTS #46.
|
||||
func CheckJoiners(enable bool) Option {
|
||||
return func(o *options) {
|
||||
o.trie = trie
|
||||
o.checkJoiners = enable
|
||||
}
|
||||
}
|
||||
|
||||
// StrictDomainName limits the set of permissible ASCII characters to those
|
||||
// allowed in domain names as defined in RFC 1034 (A-Z, a-z, 0-9 and the
|
||||
// hyphen). This is set by default for MapForLookup and ValidateForRegistration,
|
||||
// but is only useful if ValidateLabels is set.
|
||||
//
|
||||
// This option is useful, for instance, for browsers that allow characters
|
||||
// outside this range, for example a '_' (U+005F LOW LINE). See
|
||||
// http://www.rfc-editor.org/std/std3.txt for more details.
|
||||
//
|
||||
// This option corresponds to the UseSTD3ASCIIRules flag in UTS #46.
|
||||
func StrictDomainName(use bool) Option {
|
||||
return func(o *options) { o.useSTD3Rules = use }
|
||||
}
|
||||
|
||||
// NOTE: the following options pull in tables. The tables should not be linked
|
||||
// in as long as the options are not used.
|
||||
|
||||
// BidiRule enables the Bidi rule as defined in RFC 5893. Any application
|
||||
// that relies on proper validation of labels should include this rule.
|
||||
//
|
||||
// This option corresponds to the CheckBidi flag in UTS #46.
|
||||
func BidiRule() Option {
|
||||
return func(o *options) { o.bidirule = bidirule.ValidString }
|
||||
}
|
||||
|
||||
// ValidateForRegistration sets validation options to verify that a given IDN is
|
||||
// properly formatted for registration as defined by Section 4 of RFC 5891.
|
||||
func ValidateForRegistration() Option {
|
||||
return func(o *options) {
|
||||
o.mapping = validateRegistration
|
||||
StrictDomainName(true)(o)
|
||||
ValidateLabels(true)(o)
|
||||
VerifyDNSLength(true)(o)
|
||||
BidiRule()(o)
|
||||
}
|
||||
}
|
||||
|
||||
// MapForLookup sets validation and mapping options such that a given IDN is
|
||||
// transformed for domain name lookup according to the requirements set out in
|
||||
// Section 5 of RFC 5891. The mappings follow the recommendations of RFC 5894,
|
||||
// RFC 5895 and UTS 46. It does not add the Bidi Rule. Use the BidiRule option
|
||||
// to add this check.
|
||||
//
|
||||
// The mappings include normalization and mapping case, width and other
|
||||
// compatibility mappings.
|
||||
func MapForLookup() Option {
|
||||
return func(o *options) {
|
||||
o.mapping = validateAndMap
|
||||
StrictDomainName(true)(o)
|
||||
ValidateLabels(true)(o)
|
||||
RemoveLeadingDots(true)(o)
|
||||
}
|
||||
}
|
||||
|
||||
type options struct {
|
||||
transitional bool
|
||||
useSTD3Rules bool
|
||||
checkHyphens bool
|
||||
checkJoiners bool
|
||||
verifyDNSLength bool
|
||||
removeLeadingDots bool
|
||||
|
||||
trie *idnaTrie
|
||||
|
||||
// fromPuny calls validation rules when converting A-labels to U-labels.
|
||||
fromPuny func(p *Profile, s string) error
|
||||
|
||||
// mapping implements a validation and mapping step as defined in RFC 5895
|
||||
// or UTS 46, tailored to, for example, domain registration or lookup.
|
||||
mapping func(p *Profile, s string) (string, error)
|
||||
|
||||
// bidirule, if specified, checks whether s conforms to the Bidi Rule
|
||||
// defined in RFC 5893.
|
||||
bidirule func(s string) bool
|
||||
}
|
||||
|
||||
// A Profile defines the configuration of a IDNA mapper.
|
||||
type Profile struct {
|
||||
options
|
||||
}
|
||||
|
||||
func apply(o *options, opts []Option) {
|
||||
for _, f := range opts {
|
||||
f(o)
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new Profile.
|
||||
//
|
||||
// With no options, the returned Profile is the most permissive and equals the
|
||||
// Punycode Profile. Options can be passed to further restrict the Profile. The
|
||||
// MapForLookup and ValidateForRegistration options set a collection of options,
|
||||
// for lookup and registration purposes respectively, which can be tailored by
|
||||
// adding more fine-grained options, where later options override earlier
|
||||
// options.
|
||||
func New(o ...Option) *Profile {
|
||||
p := &Profile{}
|
||||
apply(&p.options, o)
|
||||
return p
|
||||
}
|
||||
|
||||
// ToASCII converts a domain or domain label to its ASCII form. For example,
|
||||
// ToASCII("bücher.example.com") is "xn--bcher-kva.example.com", and
|
||||
// ToASCII("golang") is "golang". If an error is encountered it will return
|
||||
// an error and a (partially) processed result.
|
||||
func (p *Profile) ToASCII(s string) (string, error) {
|
||||
return p.process(s, true)
|
||||
}
|
||||
|
||||
// ToUnicode converts a domain or domain label to its Unicode form. For example,
|
||||
// ToUnicode("xn--bcher-kva.example.com") is "bücher.example.com", and
|
||||
// ToUnicode("golang") is "golang". If an error is encountered it will return
|
||||
// an error and a (partially) processed result.
|
||||
func (p *Profile) ToUnicode(s string) (string, error) {
|
||||
pp := *p
|
||||
pp.transitional = false
|
||||
return pp.process(s, false)
|
||||
}
|
||||
|
||||
// String reports a string with a description of the profile for debugging
|
||||
// purposes. The string format may change with different versions.
|
||||
func (p *Profile) String() string {
|
||||
s := ""
|
||||
if p.transitional {
|
||||
s = "Transitional"
|
||||
} else {
|
||||
s = "NonTransitional"
|
||||
}
|
||||
if p.useSTD3Rules {
|
||||
s += ":UseSTD3Rules"
|
||||
}
|
||||
if p.checkHyphens {
|
||||
s += ":CheckHyphens"
|
||||
}
|
||||
if p.checkJoiners {
|
||||
s += ":CheckJoiners"
|
||||
}
|
||||
if p.verifyDNSLength {
|
||||
s += ":VerifyDNSLength"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
var (
|
||||
// Punycode is a Profile that does raw punycode processing with a minimum
|
||||
// of validation.
|
||||
Punycode *Profile = punycode
|
||||
|
||||
// Lookup is the recommended profile for looking up domain names, according
|
||||
// to Section 5 of RFC 5891. The exact configuration of this profile may
|
||||
// change over time.
|
||||
Lookup *Profile = lookup
|
||||
|
||||
// Display is the recommended profile for displaying domain names.
|
||||
// The configuration of this profile may change over time.
|
||||
Display *Profile = display
|
||||
|
||||
// Registration is the recommended profile for checking whether a given
|
||||
// IDN is valid for registration, according to Section 4 of RFC 5891.
|
||||
Registration *Profile = registration
|
||||
|
||||
punycode = &Profile{}
|
||||
lookup = &Profile{options{
|
||||
transitional: true,
|
||||
removeLeadingDots: true,
|
||||
useSTD3Rules: true,
|
||||
checkHyphens: true,
|
||||
checkJoiners: true,
|
||||
trie: trie,
|
||||
fromPuny: validateFromPunycode,
|
||||
mapping: validateAndMap,
|
||||
bidirule: bidirule.ValidString,
|
||||
}}
|
||||
display = &Profile{options{
|
||||
useSTD3Rules: true,
|
||||
removeLeadingDots: true,
|
||||
checkHyphens: true,
|
||||
checkJoiners: true,
|
||||
trie: trie,
|
||||
fromPuny: validateFromPunycode,
|
||||
mapping: validateAndMap,
|
||||
bidirule: bidirule.ValidString,
|
||||
}}
|
||||
registration = &Profile{options{
|
||||
useSTD3Rules: true,
|
||||
verifyDNSLength: true,
|
||||
checkHyphens: true,
|
||||
checkJoiners: true,
|
||||
trie: trie,
|
||||
fromPuny: validateFromPunycode,
|
||||
mapping: validateRegistration,
|
||||
bidirule: bidirule.ValidString,
|
||||
}}
|
||||
|
||||
// TODO: profiles
|
||||
// Register: recommended for approving domain names: don't do any mappings
|
||||
// but rather reject on invalid input. Bundle or block deviation characters.
|
||||
)
|
||||
|
||||
type labelError struct{ label, code_ string }
|
||||
|
||||
func (e labelError) code() string { return e.code_ }
|
||||
func (e labelError) Error() string {
|
||||
return fmt.Sprintf("idna: invalid label %q", e.label)
|
||||
}
|
||||
|
||||
type runeError rune
|
||||
|
||||
func (e runeError) code() string { return "P1" }
|
||||
func (e runeError) Error() string {
|
||||
return fmt.Sprintf("idna: disallowed rune %U", e)
|
||||
}
|
||||
|
||||
// process implements the algorithm described in section 4 of UTS #46,
|
||||
// see https://www.unicode.org/reports/tr46.
|
||||
func (p *Profile) process(s string, toASCII bool) (string, error) {
|
||||
var err error
|
||||
if p.mapping != nil {
|
||||
s, err = p.mapping(p, s)
|
||||
}
|
||||
// Remove leading empty labels.
|
||||
if p.removeLeadingDots {
|
||||
for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
|
||||
}
|
||||
}
|
||||
// It seems like we should only create this error on ToASCII, but the
|
||||
// UTS 46 conformance tests suggests we should always check this.
|
||||
if err == nil && p.verifyDNSLength && s == "" {
|
||||
err = &labelError{s, "A4"}
|
||||
}
|
||||
labels := labelIter{orig: s}
|
||||
for ; !labels.done(); labels.next() {
|
||||
label := labels.label()
|
||||
if label == "" {
|
||||
// Empty labels are not okay. The label iterator skips the last
|
||||
// label if it is empty.
|
||||
if err == nil && p.verifyDNSLength {
|
||||
err = &labelError{s, "A4"}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(label, acePrefix) {
|
||||
u, err2 := decode(label[len(acePrefix):])
|
||||
if err2 != nil {
|
||||
if err == nil {
|
||||
err = err2
|
||||
}
|
||||
// Spec says keep the old label.
|
||||
continue
|
||||
}
|
||||
labels.set(u)
|
||||
if err == nil && p.fromPuny != nil {
|
||||
err = p.fromPuny(p, u)
|
||||
}
|
||||
if err == nil {
|
||||
// This should be called on NonTransitional, according to the
|
||||
// spec, but that currently does not have any effect. Use the
|
||||
// original profile to preserve options.
|
||||
err = p.validateLabel(u)
|
||||
}
|
||||
} else if err == nil {
|
||||
err = p.validateLabel(label)
|
||||
}
|
||||
}
|
||||
if toASCII {
|
||||
for labels.reset(); !labels.done(); labels.next() {
|
||||
label := labels.label()
|
||||
if !ascii(label) {
|
||||
a, err2 := encode(acePrefix, label)
|
||||
if err == nil {
|
||||
err = err2
|
||||
}
|
||||
label = a
|
||||
labels.set(a)
|
||||
}
|
||||
n := len(label)
|
||||
if p.verifyDNSLength && err == nil && (n == 0 || n > 63) {
|
||||
err = &labelError{label, "A4"}
|
||||
}
|
||||
}
|
||||
}
|
||||
s = labels.result()
|
||||
if toASCII && p.verifyDNSLength && err == nil {
|
||||
// Compute the length of the domain name minus the root label and its dot.
|
||||
n := len(s)
|
||||
if n > 0 && s[n-1] == '.' {
|
||||
n--
|
||||
}
|
||||
if len(s) < 1 || n > 253 {
|
||||
err = &labelError{s, "A4"}
|
||||
}
|
||||
}
|
||||
return s, err
|
||||
}
|
||||
|
||||
func normalize(p *Profile, s string) (string, error) {
|
||||
return norm.NFC.String(s), nil
|
||||
}
|
||||
|
||||
func validateRegistration(p *Profile, s string) (string, error) {
|
||||
if !norm.NFC.IsNormalString(s) {
|
||||
return s, &labelError{s, "V1"}
|
||||
}
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
// Copy bytes not copied so far.
|
||||
switch p.simplify(info(v).category()) {
|
||||
// TODO: handle the NV8 defined in the Unicode idna data set to allow
|
||||
// for strict conformance to IDNA2008.
|
||||
case valid, deviation:
|
||||
case disallowed, mapped, unknown, ignored:
|
||||
r, _ := utf8.DecodeRuneInString(s[i:])
|
||||
return s, runeError(r)
|
||||
}
|
||||
i += sz
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func validateAndMap(p *Profile, s string) (string, error) {
|
||||
var (
|
||||
err error
|
||||
b []byte
|
||||
k int
|
||||
)
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
start := i
|
||||
i += sz
|
||||
// Copy bytes not copied so far.
|
||||
switch p.simplify(info(v).category()) {
|
||||
case valid:
|
||||
continue
|
||||
case disallowed:
|
||||
if err == nil {
|
||||
r, _ := utf8.DecodeRuneInString(s[start:])
|
||||
err = runeError(r)
|
||||
}
|
||||
continue
|
||||
case mapped, deviation:
|
||||
b = append(b, s[k:start]...)
|
||||
b = info(v).appendMapping(b, s[start:i])
|
||||
case ignored:
|
||||
b = append(b, s[k:start]...)
|
||||
// drop the rune
|
||||
case unknown:
|
||||
b = append(b, s[k:start]...)
|
||||
b = append(b, "\ufffd"...)
|
||||
}
|
||||
k = i
|
||||
}
|
||||
if k == 0 {
|
||||
// No changes so far.
|
||||
s = norm.NFC.String(s)
|
||||
} else {
|
||||
b = append(b, s[k:]...)
|
||||
if norm.NFC.QuickSpan(b) != len(b) {
|
||||
b = norm.NFC.Bytes(b)
|
||||
}
|
||||
// TODO: the punycode converters require strings as input.
|
||||
s = string(b)
|
||||
}
|
||||
return s, err
|
||||
}
|
||||
|
||||
// A labelIter allows iterating over domain name labels.
|
||||
type labelIter struct {
|
||||
orig string
|
||||
slice []string
|
||||
curStart int
|
||||
curEnd int
|
||||
i int
|
||||
}
|
||||
|
||||
func (l *labelIter) reset() {
|
||||
l.curStart = 0
|
||||
l.curEnd = 0
|
||||
l.i = 0
|
||||
}
|
||||
|
||||
func (l *labelIter) done() bool {
|
||||
return l.curStart >= len(l.orig)
|
||||
}
|
||||
|
||||
func (l *labelIter) result() string {
|
||||
if l.slice != nil {
|
||||
return strings.Join(l.slice, ".")
|
||||
}
|
||||
return l.orig
|
||||
}
|
||||
|
||||
func (l *labelIter) label() string {
|
||||
if l.slice != nil {
|
||||
return l.slice[l.i]
|
||||
}
|
||||
p := strings.IndexByte(l.orig[l.curStart:], '.')
|
||||
l.curEnd = l.curStart + p
|
||||
if p == -1 {
|
||||
l.curEnd = len(l.orig)
|
||||
}
|
||||
return l.orig[l.curStart:l.curEnd]
|
||||
}
|
||||
|
||||
// next sets the value to the next label. It skips the last label if it is empty.
|
||||
func (l *labelIter) next() {
|
||||
l.i++
|
||||
if l.slice != nil {
|
||||
if l.i >= len(l.slice) || l.i == len(l.slice)-1 && l.slice[l.i] == "" {
|
||||
l.curStart = len(l.orig)
|
||||
}
|
||||
} else {
|
||||
l.curStart = l.curEnd + 1
|
||||
if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' {
|
||||
l.curStart = len(l.orig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *labelIter) set(s string) {
|
||||
if l.slice == nil {
|
||||
l.slice = strings.Split(l.orig, ".")
|
||||
}
|
||||
l.slice[l.i] = s
|
||||
}
|
||||
|
||||
// acePrefix is the ASCII Compatible Encoding prefix.
|
||||
const acePrefix = "xn--"
|
||||
|
||||
func (p *Profile) simplify(cat category) category {
|
||||
switch cat {
|
||||
case disallowedSTD3Mapped:
|
||||
if p.useSTD3Rules {
|
||||
cat = disallowed
|
||||
} else {
|
||||
cat = mapped
|
||||
}
|
||||
case disallowedSTD3Valid:
|
||||
if p.useSTD3Rules {
|
||||
cat = disallowed
|
||||
} else {
|
||||
cat = valid
|
||||
}
|
||||
case deviation:
|
||||
if !p.transitional {
|
||||
cat = valid
|
||||
}
|
||||
case validNV8, validXV8:
|
||||
// TODO: handle V2008
|
||||
cat = valid
|
||||
}
|
||||
return cat
|
||||
}
|
||||
|
||||
func validateFromPunycode(p *Profile, s string) error {
|
||||
if !norm.NFC.IsNormalString(s) {
|
||||
return &labelError{s, "V1"}
|
||||
}
|
||||
for i := 0; i < len(s); {
|
||||
v, sz := trie.lookupString(s[i:])
|
||||
if c := p.simplify(info(v).category()); c != valid && c != deviation {
|
||||
return &labelError{s, "V6"}
|
||||
}
|
||||
i += sz
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
zwnj = "\u200c"
|
||||
zwj = "\u200d"
|
||||
)
|
||||
|
||||
type joinState int8
|
||||
|
||||
const (
|
||||
stateStart joinState = iota
|
||||
stateVirama
|
||||
stateBefore
|
||||
stateBeforeVirama
|
||||
stateAfter
|
||||
stateFAIL
|
||||
)
|
||||
|
||||
var joinStates = [][numJoinTypes]joinState{
|
||||
stateStart: {
|
||||
joiningL: stateBefore,
|
||||
joiningD: stateBefore,
|
||||
joinZWNJ: stateFAIL,
|
||||
joinZWJ: stateFAIL,
|
||||
joinVirama: stateVirama,
|
||||
},
|
||||
stateVirama: {
|
||||
joiningL: stateBefore,
|
||||
joiningD: stateBefore,
|
||||
},
|
||||
stateBefore: {
|
||||
joiningL: stateBefore,
|
||||
joiningD: stateBefore,
|
||||
joiningT: stateBefore,
|
||||
joinZWNJ: stateAfter,
|
||||
joinZWJ: stateFAIL,
|
||||
joinVirama: stateBeforeVirama,
|
||||
},
|
||||
stateBeforeVirama: {
|
||||
joiningL: stateBefore,
|
||||
joiningD: stateBefore,
|
||||
joiningT: stateBefore,
|
||||
},
|
||||
stateAfter: {
|
||||
joiningL: stateFAIL,
|
||||
joiningD: stateBefore,
|
||||
joiningT: stateAfter,
|
||||
joiningR: stateStart,
|
||||
joinZWNJ: stateFAIL,
|
||||
joinZWJ: stateFAIL,
|
||||
joinVirama: stateAfter, // no-op as we can't accept joiners here
|
||||
},
|
||||
stateFAIL: {
|
||||
0: stateFAIL,
|
||||
joiningL: stateFAIL,
|
||||
joiningD: stateFAIL,
|
||||
joiningT: stateFAIL,
|
||||
joiningR: stateFAIL,
|
||||
joinZWNJ: stateFAIL,
|
||||
joinZWJ: stateFAIL,
|
||||
joinVirama: stateFAIL,
|
||||
},
|
||||
}
|
||||
|
||||
// validateLabel validates the criteria from Section 4.1. Item 1, 4, and 6 are
|
||||
// already implicitly satisfied by the overall implementation.
|
||||
func (p *Profile) validateLabel(s string) error {
|
||||
if s == "" {
|
||||
if p.verifyDNSLength {
|
||||
return &labelError{s, "A4"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if p.bidirule != nil && !p.bidirule(s) {
|
||||
return &labelError{s, "B"}
|
||||
}
|
||||
if p.checkHyphens {
|
||||
if len(s) > 4 && s[2] == '-' && s[3] == '-' {
|
||||
return &labelError{s, "V2"}
|
||||
}
|
||||
if s[0] == '-' || s[len(s)-1] == '-' {
|
||||
return &labelError{s, "V3"}
|
||||
}
|
||||
}
|
||||
if !p.checkJoiners {
|
||||
return nil
|
||||
}
|
||||
trie := p.trie // p.checkJoiners is only set if trie is set.
|
||||
// TODO: merge the use of this in the trie.
|
||||
v, sz := trie.lookupString(s)
|
||||
x := info(v)
|
||||
if x.isModifier() {
|
||||
return &labelError{s, "V5"}
|
||||
}
|
||||
// Quickly return in the absence of zero-width (non) joiners.
|
||||
if strings.Index(s, zwj) == -1 && strings.Index(s, zwnj) == -1 {
|
||||
return nil
|
||||
}
|
||||
st := stateStart
|
||||
for i := 0; ; {
|
||||
jt := x.joinType()
|
||||
if s[i:i+sz] == zwj {
|
||||
jt = joinZWJ
|
||||
} else if s[i:i+sz] == zwnj {
|
||||
jt = joinZWNJ
|
||||
}
|
||||
st = joinStates[st][jt]
|
||||
if x.isViramaModifier() {
|
||||
st = joinStates[st][joinVirama]
|
||||
}
|
||||
if i += sz; i == len(s) {
|
||||
break
|
||||
}
|
||||
v, sz = trie.lookupString(s[i:])
|
||||
x = info(v)
|
||||
}
|
||||
if st == stateFAIL || st == stateAfter {
|
||||
return &labelError{s, "C"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ascii(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] >= utf8.RuneSelf {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2021 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.
|
||||
|
||||
//go:build !go1.18
|
||||
|
||||
package idna
|
||||
|
||||
const transitionalLookup = true
|
||||
+4
-1
@@ -28,7 +28,7 @@ const (
|
||||
tmin int32 = 1
|
||||
)
|
||||
|
||||
func punyError(s string) error { return &labelError{s, "A3"} }
|
||||
func punyError(s string) error { return &labelError{s, code16("A3", "P4")} }
|
||||
|
||||
// decode decodes a string as specified in section 6.2.
|
||||
func decode(encoded string) (string, error) {
|
||||
@@ -108,6 +108,9 @@ func encode(prefix, s string) (string, error) {
|
||||
delta, n, bias := int32(0), initialN, initialBias
|
||||
b, remaining := int32(0), int32(0)
|
||||
for _, r := range s {
|
||||
if unicode16 && r == 0xfffd {
|
||||
return s, &labelError{s, "A3"}
|
||||
}
|
||||
if r < 0x80 {
|
||||
b++
|
||||
output = append(output, byte(r))
|
||||
|
||||
-4559
File diff suppressed because it is too large
Load Diff
-4653
File diff suppressed because it is too large
Load Diff
-4733
File diff suppressed because it is too large
Load Diff
-4959
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,6 +1,6 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
//go:build go1.21
|
||||
//go:build !go1.27
|
||||
|
||||
package idna
|
||||
|
||||
|
||||
+5302
File diff suppressed because it is too large
Load Diff
-4486
File diff suppressed because it is too large
Load Diff
-30
@@ -1,30 +0,0 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2016 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.
|
||||
|
||||
//go:build !go1.16
|
||||
|
||||
package idna
|
||||
|
||||
// appendMapping appends the mapping for the respective rune. isMapped must be
|
||||
// true. A mapping is a categorization of a rune as defined in UTS #46.
|
||||
func (c info) appendMapping(b []byte, s string) []byte {
|
||||
index := int(c >> indexShift)
|
||||
if c&xorBit == 0 {
|
||||
s := mappings[index:]
|
||||
return append(b, s[1:s[0]+1]...)
|
||||
}
|
||||
b = append(b, s...)
|
||||
if c&inlineXOR == inlineXOR {
|
||||
// TODO: support and handle two-byte inline masks
|
||||
b[len(b)-1] ^= byte(index)
|
||||
} else {
|
||||
for p := len(b) - int(xorData[index]); p < len(b); p++ {
|
||||
index++
|
||||
b[p] ^= xorData[index]
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2016 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.
|
||||
|
||||
//go:build go1.16
|
||||
|
||||
package idna
|
||||
|
||||
// appendMapping appends the mapping for the respective rune. isMapped must be
|
||||
// true. A mapping is a categorization of a rune as defined in UTS #46.
|
||||
func (c info) appendMapping(b []byte, s string) []byte {
|
||||
index := int(c >> indexShift)
|
||||
if c&xorBit == 0 {
|
||||
p := index
|
||||
return append(b, mappings[mappingIndex[p]:mappingIndex[p+1]]...)
|
||||
}
|
||||
b = append(b, s...)
|
||||
if c&inlineXOR == inlineXOR {
|
||||
// TODO: support and handle two-byte inline masks
|
||||
b[len(b)-1] ^= byte(index)
|
||||
} else {
|
||||
for p := len(b) - int(xorData[index]); p < len(b); p++ {
|
||||
index++
|
||||
b[p] ^= xorData[index]
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
+8
@@ -448,6 +448,14 @@ func NewServerRequest(rp ServerRequestParam) ServerRequestResult {
|
||||
url_ = &url.URL{Host: rp.Authority}
|
||||
requestURI = rp.Authority // mimic HTTP/1 server behavior
|
||||
} else {
|
||||
// "[The :path] pseudo-header field MUST NOT be empty [...]"
|
||||
// https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3.1-2.4.2
|
||||
if rp.Path == "" || (rp.Path[0] != '/' && rp.Path != "*") {
|
||||
return ServerRequestResult{
|
||||
InvalidReason: "bad_path",
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
url_, err = url.ParseRequestURI(rp.Path)
|
||||
if err != nil {
|
||||
|
||||
+665
@@ -0,0 +1,665 @@
|
||||
// Copyright 2025 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 httpsfv provides functionality for dealing with HTTP Structured
|
||||
// Field Values.
|
||||
package httpsfv
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func isLCAlpha(b byte) bool {
|
||||
return (b >= 'a' && b <= 'z')
|
||||
}
|
||||
|
||||
func isAlpha(b byte) bool {
|
||||
return isLCAlpha(b) || (b >= 'A' && b <= 'Z')
|
||||
}
|
||||
|
||||
func isDigit(b byte) bool {
|
||||
return b >= '0' && b <= '9'
|
||||
}
|
||||
|
||||
func isVChar(b byte) bool {
|
||||
return b >= 0x21 && b <= 0x7e
|
||||
}
|
||||
|
||||
func isSP(b byte) bool {
|
||||
return b == 0x20
|
||||
}
|
||||
|
||||
func isTChar(b byte) bool {
|
||||
if isAlpha(b) || isDigit(b) {
|
||||
return true
|
||||
}
|
||||
return slices.Contains([]byte{'!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '_', '`', '|', '~'}, b)
|
||||
}
|
||||
|
||||
func countLeftWhitespace(s string) int {
|
||||
i := 0
|
||||
for _, ch := range []byte(s) {
|
||||
if ch != ' ' && ch != '\t' {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc4648#section-8.
|
||||
func decOctetHex(ch1, ch2 byte) (ch byte, ok bool) {
|
||||
decBase16 := func(in byte) (out byte, ok bool) {
|
||||
if !isDigit(in) && !(in >= 'a' && in <= 'f') {
|
||||
return 0, false
|
||||
}
|
||||
if isDigit(in) {
|
||||
return in - '0', true
|
||||
}
|
||||
return in - 'a' + 10, true
|
||||
}
|
||||
|
||||
if ch1, ok = decBase16(ch1); !ok {
|
||||
return 0, ok
|
||||
}
|
||||
if ch2, ok = decBase16(ch2); !ok {
|
||||
return 0, ok
|
||||
}
|
||||
return ch1<<4 | ch2, true
|
||||
}
|
||||
|
||||
// ParseList parses a list from a given HTTP Structured Field Values.
|
||||
//
|
||||
// Given an HTTP SFV string that represents a list, it will call the given
|
||||
// function using each of the members and parameters contained in the list.
|
||||
// This allows the caller to extract information out of the list.
|
||||
//
|
||||
// This function will return once it encounters the end of the string, or
|
||||
// something that is not a list. If it cannot consume the entire given
|
||||
// string, the ok value returned will be false.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-list.
|
||||
func ParseList(s string, f func(member, param string)) (ok bool) {
|
||||
for len(s) != 0 {
|
||||
var member, param string
|
||||
if len(s) != 0 && s[0] == '(' {
|
||||
if member, s, ok = consumeBareInnerList(s, nil); !ok {
|
||||
return ok
|
||||
}
|
||||
} else {
|
||||
if member, s, ok = consumeBareItem(s); !ok {
|
||||
return ok
|
||||
}
|
||||
}
|
||||
if param, s, ok = consumeParameter(s, nil); !ok {
|
||||
return ok
|
||||
}
|
||||
if f != nil {
|
||||
f(member, param)
|
||||
}
|
||||
|
||||
s = s[countLeftWhitespace(s):]
|
||||
if len(s) == 0 {
|
||||
break
|
||||
}
|
||||
if s[0] != ',' {
|
||||
return false
|
||||
}
|
||||
s = s[1:]
|
||||
s = s[countLeftWhitespace(s):]
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// consumeBareInnerList consumes an inner list
|
||||
// (https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-inner-list),
|
||||
// except for the inner list's top-most parameter.
|
||||
// For example, given `(a;b c;d);e`, it will consume only `(a;b c;d)`.
|
||||
func consumeBareInnerList(s string, f func(bareItem, param string)) (consumed, rest string, ok bool) {
|
||||
if len(s) == 0 || s[0] != '(' {
|
||||
return "", s, false
|
||||
}
|
||||
rest = s[1:]
|
||||
for len(rest) != 0 {
|
||||
var bareItem, param string
|
||||
rest = rest[countLeftWhitespace(rest):]
|
||||
if len(rest) != 0 && rest[0] == ')' {
|
||||
rest = rest[1:]
|
||||
break
|
||||
}
|
||||
if bareItem, rest, ok = consumeBareItem(rest); !ok {
|
||||
return "", s, ok
|
||||
}
|
||||
if param, rest, ok = consumeParameter(rest, nil); !ok {
|
||||
return "", s, ok
|
||||
}
|
||||
if len(rest) == 0 || (rest[0] != ')' && !isSP(rest[0])) {
|
||||
return "", s, false
|
||||
}
|
||||
if f != nil {
|
||||
f(bareItem, param)
|
||||
}
|
||||
}
|
||||
return s[:len(s)-len(rest)], rest, true
|
||||
}
|
||||
|
||||
// ParseBareInnerList parses a bare inner list from a given HTTP Structured
|
||||
// Field Values.
|
||||
//
|
||||
// We define a bare inner list as an inner list
|
||||
// (https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-inner-list),
|
||||
// without the top-most parameter of the inner list. For example, given the
|
||||
// inner list `(a;b c;d);e`, the bare inner list would be `(a;b c;d)`.
|
||||
//
|
||||
// Given an HTTP SFV string that represents a bare inner list, it will call the
|
||||
// given function using each of the bare item and parameter within the bare
|
||||
// inner list. This allows the caller to extract information out of the bare
|
||||
// inner list.
|
||||
//
|
||||
// This function will return once it encounters the end of the bare inner list,
|
||||
// or something that is not a bare inner list. If it cannot consume the entire
|
||||
// given string, the ok value returned will be false.
|
||||
func ParseBareInnerList(s string, f func(bareItem, param string)) (ok bool) {
|
||||
_, rest, ok := consumeBareInnerList(s, f)
|
||||
return rest == "" && ok
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-item.
|
||||
func consumeItem(s string, f func(bareItem, param string)) (consumed, rest string, ok bool) {
|
||||
var bareItem, param string
|
||||
if bareItem, rest, ok = consumeBareItem(s); !ok {
|
||||
return "", s, ok
|
||||
}
|
||||
if param, rest, ok = consumeParameter(rest, nil); !ok {
|
||||
return "", s, ok
|
||||
}
|
||||
if f != nil {
|
||||
f(bareItem, param)
|
||||
}
|
||||
return s[:len(s)-len(rest)], rest, true
|
||||
}
|
||||
|
||||
// ParseItem parses an item from a given HTTP Structured Field Values.
|
||||
//
|
||||
// Given an HTTP SFV string that represents an item, it will call the given
|
||||
// function once, with the bare item and the parameter of the item. This allows
|
||||
// the caller to extract information out of the item.
|
||||
//
|
||||
// This function will return once it encounters the end of the string, or
|
||||
// something that is not an item. If it cannot consume the entire given
|
||||
// string, the ok value returned will be false.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-item.
|
||||
func ParseItem(s string, f func(bareItem, param string)) (ok bool) {
|
||||
_, rest, ok := consumeItem(s, f)
|
||||
return rest == "" && ok
|
||||
}
|
||||
|
||||
// ParseDictionary parses a dictionary from a given HTTP Structured Field
|
||||
// Values.
|
||||
//
|
||||
// Given an HTTP SFV string that represents a dictionary, it will call the
|
||||
// given function using each of the keys, values, and parameters contained in
|
||||
// the dictionary. This allows the caller to extract information out of the
|
||||
// dictionary.
|
||||
//
|
||||
// This function will return once it encounters the end of the string, or
|
||||
// something that is not a dictionary. If it cannot consume the entire given
|
||||
// string, the ok value returned will be false.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-dictionary.
|
||||
func ParseDictionary(s string, f func(key, val, param string)) (ok bool) {
|
||||
for len(s) != 0 {
|
||||
var key, val, param string
|
||||
val = "?1" // Default value for empty val is boolean true.
|
||||
if key, s, ok = consumeKey(s); !ok {
|
||||
return ok
|
||||
}
|
||||
if len(s) != 0 && s[0] == '=' {
|
||||
s = s[1:]
|
||||
if len(s) != 0 && s[0] == '(' {
|
||||
if val, s, ok = consumeBareInnerList(s, nil); !ok {
|
||||
return ok
|
||||
}
|
||||
} else {
|
||||
if val, s, ok = consumeBareItem(s); !ok {
|
||||
return ok
|
||||
}
|
||||
}
|
||||
}
|
||||
if param, s, ok = consumeParameter(s, nil); !ok {
|
||||
return ok
|
||||
}
|
||||
if f != nil {
|
||||
f(key, val, param)
|
||||
}
|
||||
s = s[countLeftWhitespace(s):]
|
||||
if len(s) == 0 {
|
||||
break
|
||||
}
|
||||
if s[0] == ',' {
|
||||
s = s[1:]
|
||||
}
|
||||
s = s[countLeftWhitespace(s):]
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#parse-param.
|
||||
func consumeParameter(s string, f func(key, val string)) (consumed, rest string, ok bool) {
|
||||
rest = s
|
||||
for len(rest) != 0 {
|
||||
var key, val string
|
||||
val = "?1" // Default value for empty val is boolean true.
|
||||
if rest[0] != ';' {
|
||||
break
|
||||
}
|
||||
rest = rest[1:]
|
||||
rest = rest[countLeftWhitespace(rest):]
|
||||
key, rest, ok = consumeKey(rest)
|
||||
if !ok {
|
||||
return "", s, ok
|
||||
}
|
||||
if len(rest) != 0 && rest[0] == '=' {
|
||||
rest = rest[1:]
|
||||
val, rest, ok = consumeBareItem(rest)
|
||||
if !ok {
|
||||
return "", s, ok
|
||||
}
|
||||
}
|
||||
if f != nil {
|
||||
f(key, val)
|
||||
}
|
||||
}
|
||||
return s[:len(s)-len(rest)], rest, true
|
||||
}
|
||||
|
||||
// ParseParameter parses a parameter from a given HTTP Structured Field Values.
|
||||
//
|
||||
// Given an HTTP SFV string that represents a parameter, it will call the given
|
||||
// function using each of the keys and values contained in the parameter. This
|
||||
// allows the caller to extract information out of the parameter.
|
||||
//
|
||||
// This function will return once it encounters the end of the string, or
|
||||
// something that is not a parameter. If it cannot consume the entire given
|
||||
// string, the ok value returned will be false.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#parse-param.
|
||||
func ParseParameter(s string, f func(key, val string)) (ok bool) {
|
||||
_, rest, ok := consumeParameter(s, f)
|
||||
return rest == "" && ok
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-key.
|
||||
func consumeKey(s string) (consumed, rest string, ok bool) {
|
||||
if len(s) == 0 || (!isLCAlpha(s[0]) && s[0] != '*') {
|
||||
return "", s, false
|
||||
}
|
||||
i := 0
|
||||
for _, ch := range []byte(s) {
|
||||
if !isLCAlpha(ch) && !isDigit(ch) && !slices.Contains([]byte("_-.*"), ch) {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
return s[:i], s[i:], true
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-integer-or-decim.
|
||||
func consumeIntegerOrDecimal(s string) (consumed, rest string, ok bool) {
|
||||
var i, signOffset, periodIndex int
|
||||
var isDecimal bool
|
||||
if i < len(s) && s[i] == '-' {
|
||||
i++
|
||||
signOffset++
|
||||
}
|
||||
if i >= len(s) {
|
||||
return "", s, false
|
||||
}
|
||||
if !isDigit(s[i]) {
|
||||
return "", s, false
|
||||
}
|
||||
for i < len(s) {
|
||||
ch := s[i]
|
||||
if isDigit(ch) {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if !isDecimal && ch == '.' {
|
||||
if i-signOffset > 12 {
|
||||
return "", s, false
|
||||
}
|
||||
periodIndex = i
|
||||
isDecimal = true
|
||||
i++
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if !isDecimal && i-signOffset > 15 {
|
||||
return "", s, false
|
||||
}
|
||||
if isDecimal {
|
||||
if i-signOffset > 16 {
|
||||
return "", s, false
|
||||
}
|
||||
if s[i-1] == '.' {
|
||||
return "", s, false
|
||||
}
|
||||
if i-periodIndex-1 > 3 {
|
||||
return "", s, false
|
||||
}
|
||||
}
|
||||
return s[:i], s[i:], true
|
||||
}
|
||||
|
||||
// ParseInteger parses an integer from a given HTTP Structured Field Values.
|
||||
//
|
||||
// The entire HTTP SFV string must consist of a valid integer. It returns the
|
||||
// parsed integer and an ok boolean value, indicating success or not.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-integer-or-decim.
|
||||
func ParseInteger(s string) (parsed int64, ok bool) {
|
||||
if _, rest, ok := consumeIntegerOrDecimal(s); !ok || rest != "" {
|
||||
return 0, false
|
||||
}
|
||||
if n, err := strconv.ParseInt(s, 10, 64); err == nil {
|
||||
return n, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// ParseDecimal parses a decimal from a given HTTP Structured Field Values.
|
||||
//
|
||||
// The entire HTTP SFV string must consist of a valid decimal. It returns the
|
||||
// parsed decimal and an ok boolean value, indicating success or not.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-an-integer-or-decim.
|
||||
func ParseDecimal(s string) (parsed float64, ok bool) {
|
||||
if _, rest, ok := consumeIntegerOrDecimal(s); !ok || rest != "" {
|
||||
return 0, false
|
||||
}
|
||||
if !strings.Contains(s, ".") {
|
||||
return 0, false
|
||||
}
|
||||
if n, err := strconv.ParseFloat(s, 64); err == nil {
|
||||
return n, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-string.
|
||||
func consumeString(s string) (consumed, rest string, ok bool) {
|
||||
if len(s) == 0 || s[0] != '"' {
|
||||
return "", s, false
|
||||
}
|
||||
for i := 1; i < len(s); i++ {
|
||||
switch ch := s[i]; ch {
|
||||
case '\\':
|
||||
if i+1 >= len(s) {
|
||||
return "", s, false
|
||||
}
|
||||
i++
|
||||
if ch = s[i]; ch != '"' && ch != '\\' {
|
||||
return "", s, false
|
||||
}
|
||||
case '"':
|
||||
return s[:i+1], s[i+1:], true
|
||||
default:
|
||||
if !isVChar(ch) && !isSP(ch) {
|
||||
return "", s, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", s, false
|
||||
}
|
||||
|
||||
// ParseString parses a Go string from a given HTTP Structured Field Values.
|
||||
//
|
||||
// The entire HTTP SFV string must consist of a valid string. It returns the
|
||||
// parsed string and an ok boolean value, indicating success or not.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-string.
|
||||
func ParseString(s string) (parsed string, ok bool) {
|
||||
if _, rest, ok := consumeString(s); !ok || rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return s[1 : len(s)-1], true
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-token
|
||||
func consumeToken(s string) (consumed, rest string, ok bool) {
|
||||
if len(s) == 0 || (!isAlpha(s[0]) && s[0] != '*') {
|
||||
return "", s, false
|
||||
}
|
||||
i := 0
|
||||
for _, ch := range []byte(s) {
|
||||
if !isTChar(ch) && !slices.Contains([]byte(":/"), ch) {
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
return s[:i], s[i:], true
|
||||
}
|
||||
|
||||
// ParseToken parses a token from a given HTTP Structured Field Values.
|
||||
//
|
||||
// The entire HTTP SFV string must consist of a valid token. It returns the
|
||||
// parsed token and an ok boolean value, indicating success or not.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-token
|
||||
func ParseToken(s string) (parsed string, ok bool) {
|
||||
if _, rest, ok := consumeToken(s); !ok || rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return s, true
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-byte-sequence.
|
||||
func consumeByteSequence(s string) (consumed, rest string, ok bool) {
|
||||
if len(s) == 0 || s[0] != ':' {
|
||||
return "", s, false
|
||||
}
|
||||
for i := 1; i < len(s); i++ {
|
||||
if ch := s[i]; ch == ':' {
|
||||
return s[:i+1], s[i+1:], true
|
||||
}
|
||||
if ch := s[i]; !isAlpha(ch) && !isDigit(ch) && !slices.Contains([]byte("+/="), ch) {
|
||||
return "", s, false
|
||||
}
|
||||
}
|
||||
return "", s, false
|
||||
}
|
||||
|
||||
// ParseByteSequence parses a byte sequence from a given HTTP Structured Field
|
||||
// Values.
|
||||
//
|
||||
// The entire HTTP SFV string must consist of a valid byte sequence. It returns
|
||||
// the parsed byte sequence and an ok boolean value, indicating success or not.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-byte-sequence.
|
||||
func ParseByteSequence(s string) (parsed []byte, ok bool) {
|
||||
if _, rest, ok := consumeByteSequence(s); !ok || rest != "" {
|
||||
return nil, false
|
||||
}
|
||||
return []byte(s[1 : len(s)-1]), true
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-boolean.
|
||||
func consumeBoolean(s string) (consumed, rest string, ok bool) {
|
||||
if len(s) >= 2 && (s[:2] == "?0" || s[:2] == "?1") {
|
||||
return s[:2], s[2:], true
|
||||
}
|
||||
return "", s, false
|
||||
}
|
||||
|
||||
// ParseBoolean parses a boolean from a given HTTP Structured Field Values.
|
||||
//
|
||||
// The entire HTTP SFV string must consist of a valid boolean. It returns the
|
||||
// parsed boolean and an ok boolean value, indicating success or not.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-boolean.
|
||||
func ParseBoolean(s string) (parsed bool, ok bool) {
|
||||
if _, rest, ok := consumeBoolean(s); !ok || rest != "" {
|
||||
return false, false
|
||||
}
|
||||
return s == "?1", true
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-date.
|
||||
func consumeDate(s string) (consumed, rest string, ok bool) {
|
||||
if len(s) == 0 || s[0] != '@' {
|
||||
return "", s, false
|
||||
}
|
||||
if _, rest, ok = consumeIntegerOrDecimal(s[1:]); !ok {
|
||||
return "", s, ok
|
||||
}
|
||||
consumed = s[:len(s)-len(rest)]
|
||||
if slices.Contains([]byte(consumed), '.') {
|
||||
return "", s, false
|
||||
}
|
||||
return consumed, rest, ok
|
||||
}
|
||||
|
||||
// ParseDate parses a date from a given HTTP Structured Field Values.
|
||||
//
|
||||
// The entire HTTP SFV string must consist of a valid date. It returns the
|
||||
// parsed date and an ok boolean value, indicating success or not.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-date.
|
||||
func ParseDate(s string) (parsed time.Time, ok bool) {
|
||||
if _, rest, ok := consumeDate(s); !ok || rest != "" {
|
||||
return time.Time{}, false
|
||||
}
|
||||
if n, ok := ParseInteger(s[1:]); !ok {
|
||||
return time.Time{}, false
|
||||
} else {
|
||||
return time.Unix(n, 0), true
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-display-string.
|
||||
func consumeDisplayString(s string) (consumed, rest string, ok bool) {
|
||||
// To prevent excessive allocation, especially when input is large, we
|
||||
// maintain a buffer of 4 bytes to keep track of the last rune we
|
||||
// encounter. This way, we can validate that the display string conforms to
|
||||
// UTF-8 without actually building the whole string.
|
||||
var lastRune [4]byte
|
||||
var runeLen int
|
||||
isPartOfValidRune := func(ch byte) bool {
|
||||
lastRune[runeLen] = ch
|
||||
runeLen++
|
||||
if utf8.FullRune(lastRune[:runeLen]) {
|
||||
r, s := utf8.DecodeRune(lastRune[:runeLen])
|
||||
if r == utf8.RuneError {
|
||||
return false
|
||||
}
|
||||
copy(lastRune[:], lastRune[s:runeLen])
|
||||
runeLen -= s
|
||||
return true
|
||||
}
|
||||
return runeLen <= 4
|
||||
}
|
||||
|
||||
if len(s) <= 1 || s[:2] != `%"` {
|
||||
return "", s, false
|
||||
}
|
||||
i := 2
|
||||
for i < len(s) {
|
||||
ch := s[i]
|
||||
if !isVChar(ch) && !isSP(ch) {
|
||||
return "", s, false
|
||||
}
|
||||
switch ch {
|
||||
case '"':
|
||||
if runeLen > 0 {
|
||||
return "", s, false
|
||||
}
|
||||
return s[:i+1], s[i+1:], true
|
||||
case '%':
|
||||
if i+2 >= len(s) {
|
||||
return "", s, false
|
||||
}
|
||||
if ch, ok = decOctetHex(s[i+1], s[i+2]); !ok {
|
||||
return "", s, ok
|
||||
}
|
||||
if ok = isPartOfValidRune(ch); !ok {
|
||||
return "", s, ok
|
||||
}
|
||||
i += 3
|
||||
default:
|
||||
if ok = isPartOfValidRune(ch); !ok {
|
||||
return "", s, ok
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
return "", s, false
|
||||
}
|
||||
|
||||
// ParseDisplayString parses a display string from a given HTTP Structured
|
||||
// Field Values.
|
||||
//
|
||||
// The entire HTTP SFV string must consist of a valid display string. It
|
||||
// returns the parsed display string and an ok boolean value, indicating
|
||||
// success or not.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#name-parsing-a-display-string.
|
||||
func ParseDisplayString(s string) (parsed string, ok bool) {
|
||||
if _, rest, ok := consumeDisplayString(s); !ok || rest != "" {
|
||||
return "", false
|
||||
}
|
||||
// consumeDisplayString() already validates that we have a valid display
|
||||
// string. Therefore, we can just construct the display string, without
|
||||
// validating it again.
|
||||
s = s[2 : len(s)-1]
|
||||
var b strings.Builder
|
||||
for i := 0; i < len(s); {
|
||||
if s[i] == '%' {
|
||||
decoded, _ := decOctetHex(s[i+1], s[i+2])
|
||||
b.WriteByte(decoded)
|
||||
i += 3
|
||||
continue
|
||||
}
|
||||
b.WriteByte(s[i])
|
||||
i++
|
||||
}
|
||||
return b.String(), true
|
||||
}
|
||||
|
||||
// https://www.rfc-editor.org/rfc/rfc9651.html#parse-bare-item.
|
||||
func consumeBareItem(s string) (consumed, rest string, ok bool) {
|
||||
if len(s) == 0 {
|
||||
return "", s, false
|
||||
}
|
||||
ch := s[0]
|
||||
switch {
|
||||
case ch == '-' || isDigit(ch):
|
||||
return consumeIntegerOrDecimal(s)
|
||||
case ch == '"':
|
||||
return consumeString(s)
|
||||
case ch == '*' || isAlpha(ch):
|
||||
return consumeToken(s)
|
||||
case ch == ':':
|
||||
return consumeByteSequence(s)
|
||||
case ch == '?':
|
||||
return consumeBoolean(s)
|
||||
case ch == '@':
|
||||
return consumeDate(s)
|
||||
case ch == '%':
|
||||
return consumeDisplayString(s)
|
||||
default:
|
||||
return "", s, false
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -109,7 +109,7 @@ func (g *Group) TryGo(f func() error) bool {
|
||||
if g.sem != nil {
|
||||
select {
|
||||
case g.sem <- token{}:
|
||||
// Note: this allows barging iff channels in general allow barging.
|
||||
// Note: this allows barging if and only if channels in general allow barging.
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
+5
-5
@@ -83,7 +83,7 @@ func (s *Weighted) Acquire(ctx context.Context, n int64) error {
|
||||
default:
|
||||
isFront := s.waiters.Front() == elem
|
||||
s.waiters.Remove(elem)
|
||||
// If we're at the front and there're extra tokens left, notify other waiters.
|
||||
// If we're at the front and there are extra tokens left, notify other waiters.
|
||||
if isFront && s.size > s.cur {
|
||||
s.notifyWaiters()
|
||||
}
|
||||
@@ -139,15 +139,15 @@ func (s *Weighted) notifyWaiters() {
|
||||
|
||||
w := next.Value.(waiter)
|
||||
if s.size-s.cur < w.n {
|
||||
// Not enough tokens for the next waiter. We could keep going (to try to
|
||||
// Not enough tokens for the next waiter. We could keep going (to try to
|
||||
// find a waiter with a smaller request), but under load that could cause
|
||||
// starvation for large requests; instead, we leave all remaining waiters
|
||||
// blocked.
|
||||
//
|
||||
// Consider a semaphore used as a read-write lock, with N tokens, N
|
||||
// readers, and one writer. Each reader can Acquire(1) to obtain a read
|
||||
// lock. The writer can Acquire(N) to obtain a write lock, excluding all
|
||||
// of the readers. If we allow the readers to jump ahead in the queue,
|
||||
// readers, and one writer. Each reader can Acquire(1) to obtain a read
|
||||
// lock. The writer can Acquire(N) to obtain a write lock, excluding all
|
||||
// of the readers. If we allow the readers to jump ahead in the queue,
|
||||
// the writer will starve — there is always one token available for every
|
||||
// reader.
|
||||
break
|
||||
|
||||
+12
-7
@@ -152,13 +152,17 @@ var ARM struct {
|
||||
// The booleans in Loong64 contain the correspondingly named cpu feature bit.
|
||||
// The struct is padded to avoid false sharing.
|
||||
var Loong64 struct {
|
||||
_ CacheLinePad
|
||||
HasLSX bool // support 128-bit vector extension
|
||||
HasLASX bool // support 256-bit vector extension
|
||||
HasCRC32 bool // support CRC instruction
|
||||
HasLAM_BH bool // support AM{SWAP/ADD}[_DB].{B/H} instruction
|
||||
HasLAMCAS bool // support AMCAS[_DB].{B/H/W/D} instruction
|
||||
_ CacheLinePad
|
||||
_ CacheLinePad
|
||||
HasLSX bool // support 128-bit vector extension
|
||||
HasLASX bool // support 256-bit vector extension
|
||||
HasCRC32 bool // support CRC instruction
|
||||
HasLAMCAS bool // support AMCAS[_DB].{B/H/W/D}
|
||||
HasLAM_BH bool // support AM{SWAP/ADD}[_DB].{B/H} instruction
|
||||
HasLLACQ_SCREL bool // support LLACQ.{W/D}, SCREL.{W/D} instruction
|
||||
HasSCQ bool // support SC.Q instruction
|
||||
HasDBAR_HINTS bool // supports finer-grained DBAR hints
|
||||
|
||||
_ CacheLinePad
|
||||
}
|
||||
|
||||
// MIPS64X contains the supported CPU features of the current mips64/mips64le
|
||||
@@ -232,6 +236,7 @@ var RISCV64 struct {
|
||||
HasZba bool // Address generation instructions extension
|
||||
HasZbb bool // Basic bit-manipulation extension
|
||||
HasZbs bool // Single-bit instructions extension
|
||||
HasZbc bool // Carryless multiplication extension
|
||||
HasZvbb bool // Vector Basic Bit-manipulation
|
||||
HasZvbc bool // Vector Carryless Multiplication
|
||||
HasZvkb bool // Vector Cryptography Bit-manipulation
|
||||
|
||||
+2
@@ -6,6 +6,8 @@
|
||||
|
||||
package cpu
|
||||
|
||||
import "runtime"
|
||||
|
||||
func doinit() {
|
||||
setMinimalFeatures()
|
||||
|
||||
|
||||
+2
@@ -58,6 +58,7 @@ const (
|
||||
riscv_HWPROBE_EXT_ZBA = 0x8
|
||||
riscv_HWPROBE_EXT_ZBB = 0x10
|
||||
riscv_HWPROBE_EXT_ZBS = 0x20
|
||||
riscv_HWPROBE_EXT_ZBC = 0x80
|
||||
riscv_HWPROBE_EXT_ZVBB = 0x20000
|
||||
riscv_HWPROBE_EXT_ZVBC = 0x40000
|
||||
riscv_HWPROBE_EXT_ZVKB = 0x80000
|
||||
@@ -108,6 +109,7 @@ func doinit() {
|
||||
RISCV64.HasZba = isSet(v, riscv_HWPROBE_EXT_ZBA)
|
||||
RISCV64.HasZbb = isSet(v, riscv_HWPROBE_EXT_ZBB)
|
||||
RISCV64.HasZbs = isSet(v, riscv_HWPROBE_EXT_ZBS)
|
||||
RISCV64.HasZbc = isSet(v, riscv_HWPROBE_EXT_ZBC)
|
||||
RISCV64.HasZvbb = isSet(v, riscv_HWPROBE_EXT_ZVBB)
|
||||
RISCV64.HasZvbc = isSet(v, riscv_HWPROBE_EXT_ZVBC)
|
||||
RISCV64.HasZvkb = isSet(v, riscv_HWPROBE_EXT_ZVKB)
|
||||
|
||||
+14
-2
@@ -15,8 +15,13 @@ const (
|
||||
cpucfg1_CRC32 = 1 << 25
|
||||
|
||||
// CPUCFG2 bits
|
||||
cpucfg2_LAM_BH = 1 << 27
|
||||
cpucfg2_LAMCAS = 1 << 28
|
||||
cpucfg2_LAM_BH = 1 << 27
|
||||
cpucfg2_LAMCAS = 1 << 28
|
||||
cpucfg2_LLACQ_SCREL = 1 << 29
|
||||
cpucfg2_SCQ = 1 << 30
|
||||
|
||||
// CPUCFG3 bits
|
||||
cpucfg3_DBAR_HINTS = 1 << 17
|
||||
)
|
||||
|
||||
func initOptions() {
|
||||
@@ -26,6 +31,9 @@ func initOptions() {
|
||||
{Name: "crc32", Feature: &Loong64.HasCRC32},
|
||||
{Name: "lam_bh", Feature: &Loong64.HasLAM_BH},
|
||||
{Name: "lamcas", Feature: &Loong64.HasLAMCAS},
|
||||
{Name: "llacq_screl", Feature: &Loong64.HasLLACQ_SCREL},
|
||||
{Name: "scq", Feature: &Loong64.HasSCQ},
|
||||
{Name: "dbar_hints", Feature: &Loong64.HasDBAR_HINTS},
|
||||
}
|
||||
|
||||
// The CPUCFG data on Loong64 only reflects the hardware capabilities,
|
||||
@@ -37,10 +45,14 @@ func initOptions() {
|
||||
// through CPUCFG
|
||||
cfg1 := get_cpucfg(1)
|
||||
cfg2 := get_cpucfg(2)
|
||||
cfg3 := get_cpucfg(3)
|
||||
|
||||
Loong64.HasCRC32 = cfgIsSet(cfg1, cpucfg1_CRC32)
|
||||
Loong64.HasLAMCAS = cfgIsSet(cfg2, cpucfg2_LAMCAS)
|
||||
Loong64.HasLAM_BH = cfgIsSet(cfg2, cpucfg2_LAM_BH)
|
||||
Loong64.HasLLACQ_SCREL = cfgIsSet(cfg2, cpucfg2_LLACQ_SCREL)
|
||||
Loong64.HasSCQ = cfgIsSet(cfg2, cpucfg2_SCQ)
|
||||
Loong64.HasDBAR_HINTS = cfgIsSet(cfg3, cpucfg3_DBAR_HINTS)
|
||||
}
|
||||
|
||||
func get_cpucfg(reg uint32) uint32
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ func initOptions() {
|
||||
{Name: "zba", Feature: &RISCV64.HasZba},
|
||||
{Name: "zbb", Feature: &RISCV64.HasZbb},
|
||||
{Name: "zbs", Feature: &RISCV64.HasZbs},
|
||||
{Name: "zbc", Feature: &RISCV64.HasZbc},
|
||||
// RISC-V Cryptography Extensions
|
||||
{Name: "zvbb", Feature: &RISCV64.HasZvbb},
|
||||
{Name: "zvbc", Feature: &RISCV64.HasZvbc},
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// Copyright 2026 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 cpu
|
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -systemdll=false -output zcpu_windows.go cpu_windows.go
|
||||
|
||||
//sys isProcessorFeaturePresent(ProcessorFeature uint32) (ret bool) = kernel32.IsProcessorFeaturePresent
|
||||
|
||||
// The processor features to be tested for IsProcessorFeaturePresent, see
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent
|
||||
const (
|
||||
_PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE = 30
|
||||
_PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE = 31
|
||||
_PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE = 34
|
||||
_PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE = 43
|
||||
|
||||
_PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE = 44
|
||||
_PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE = 45
|
||||
_PF_ARM_SVE_INSTRUCTIONS_AVAILABLE = 46
|
||||
_PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE = 47
|
||||
|
||||
_PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE = 64
|
||||
_PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE = 65
|
||||
)
|
||||
+10
-14
@@ -4,10 +4,6 @@
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func doinit() {
|
||||
// set HasASIMD and HasFP to true as per
|
||||
// https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170#base-requirements
|
||||
@@ -18,25 +14,25 @@ func doinit() {
|
||||
ARM64.HasASIMD = true
|
||||
ARM64.HasFP = true
|
||||
|
||||
if windows.IsProcessorFeaturePresent(windows.PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) {
|
||||
if isProcessorFeaturePresent(_PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) {
|
||||
ARM64.HasAES = true
|
||||
ARM64.HasPMULL = true
|
||||
ARM64.HasSHA1 = true
|
||||
ARM64.HasSHA2 = true
|
||||
}
|
||||
ARM64.HasSHA3 = windows.IsProcessorFeaturePresent(windows.PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE)
|
||||
ARM64.HasCRC32 = windows.IsProcessorFeaturePresent(windows.PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)
|
||||
ARM64.HasSHA512 = windows.IsProcessorFeaturePresent(windows.PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE)
|
||||
ARM64.HasATOMICS = windows.IsProcessorFeaturePresent(windows.PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE)
|
||||
if windows.IsProcessorFeaturePresent(windows.PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) {
|
||||
ARM64.HasSHA3 = isProcessorFeaturePresent(_PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE)
|
||||
ARM64.HasCRC32 = isProcessorFeaturePresent(_PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)
|
||||
ARM64.HasSHA512 = isProcessorFeaturePresent(_PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE)
|
||||
ARM64.HasATOMICS = isProcessorFeaturePresent(_PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE)
|
||||
if isProcessorFeaturePresent(_PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) {
|
||||
ARM64.HasASIMDDP = true
|
||||
ARM64.HasASIMDRDM = true
|
||||
}
|
||||
if windows.IsProcessorFeaturePresent(windows.PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE) {
|
||||
if isProcessorFeaturePresent(_PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE) {
|
||||
ARM64.HasLRCPC = true
|
||||
ARM64.HasSM3 = true
|
||||
}
|
||||
ARM64.HasSVE = windows.IsProcessorFeaturePresent(windows.PF_ARM_SVE_INSTRUCTIONS_AVAILABLE)
|
||||
ARM64.HasSVE2 = windows.IsProcessorFeaturePresent(windows.PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE)
|
||||
ARM64.HasJSCVT = windows.IsProcessorFeaturePresent(windows.PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE)
|
||||
ARM64.HasSVE = isProcessorFeaturePresent(_PF_ARM_SVE_INSTRUCTIONS_AVAILABLE)
|
||||
ARM64.HasSVE2 = isProcessorFeaturePresent(_PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE)
|
||||
ARM64.HasJSCVT = isProcessorFeaturePresent(_PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE)
|
||||
}
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
errERROR_EINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return errERROR_EINVAL
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
procIsProcessorFeaturePresent = modkernel32.NewProc("IsProcessorFeaturePresent")
|
||||
)
|
||||
|
||||
func isProcessorFeaturePresent(ProcessorFeature uint32) (ret bool) {
|
||||
r0, _, _ := syscall.SyscallN(procIsProcessorFeaturePresent.Addr(), uintptr(ProcessorFeature))
|
||||
ret = r0 != 0
|
||||
return
|
||||
}
|
||||
+112
-16
@@ -13,11 +13,19 @@ import (
|
||||
|
||||
const cpuSetSize = _CPU_SETSIZE / _NCPUBITS
|
||||
|
||||
// CPUSet represents a CPU affinity mask.
|
||||
// CPUSet represents a bit mask of CPUs, to be used with [SchedGetaffinity], [SchedSetaffinity],
|
||||
// and [SetMemPolicy].
|
||||
//
|
||||
// Note this type can only represent CPU IDs 0 through 1023.
|
||||
// Use [CPUSetDynamic]/[NewCPUSet] instead to avoid this limit.
|
||||
type CPUSet [cpuSetSize]cpuMask
|
||||
|
||||
func schedAffinity(trap uintptr, pid int, set *CPUSet) error {
|
||||
_, _, e := RawSyscall(trap, uintptr(pid), uintptr(unsafe.Sizeof(*set)), uintptr(unsafe.Pointer(set)))
|
||||
// CPUSetDynamic represents a bit mask of CPUs, to be used with [SchedGetaffinityDynamic],
|
||||
// [SchedSetaffinityDynamic], and [SetMemPolicyDynamic]. Use [NewCPUSet] to allocate.
|
||||
type CPUSetDynamic []cpuMask
|
||||
|
||||
func schedAffinity(trap uintptr, pid int, size uintptr, ptr unsafe.Pointer) error {
|
||||
_, _, e := RawSyscall(trap, uintptr(pid), uintptr(size), uintptr(ptr))
|
||||
if e != 0 {
|
||||
return errnoErr(e)
|
||||
}
|
||||
@@ -27,13 +35,13 @@ func schedAffinity(trap uintptr, pid int, set *CPUSet) error {
|
||||
// SchedGetaffinity gets the CPU affinity mask of the thread specified by pid.
|
||||
// If pid is 0 the calling thread is used.
|
||||
func SchedGetaffinity(pid int, set *CPUSet) error {
|
||||
return schedAffinity(SYS_SCHED_GETAFFINITY, pid, set)
|
||||
return schedAffinity(SYS_SCHED_GETAFFINITY, pid, unsafe.Sizeof(*set), unsafe.Pointer(set))
|
||||
}
|
||||
|
||||
// SchedSetaffinity sets the CPU affinity mask of the thread specified by pid.
|
||||
// If pid is 0 the calling thread is used.
|
||||
func SchedSetaffinity(pid int, set *CPUSet) error {
|
||||
return schedAffinity(SYS_SCHED_SETAFFINITY, pid, set)
|
||||
return schedAffinity(SYS_SCHED_SETAFFINITY, pid, unsafe.Sizeof(*set), unsafe.Pointer(set))
|
||||
}
|
||||
|
||||
// Zero clears the set s, so that it contains no CPUs.
|
||||
@@ -45,9 +53,7 @@ func (s *CPUSet) Zero() {
|
||||
// will silently ignore any invalid CPU bits in [CPUSet] so this is an
|
||||
// efficient way of resetting the CPU affinity of a process.
|
||||
func (s *CPUSet) Fill() {
|
||||
for i := range s {
|
||||
s[i] = ^cpuMask(0)
|
||||
}
|
||||
cpuMaskFill(s[:])
|
||||
}
|
||||
|
||||
func cpuBitsIndex(cpu int) int {
|
||||
@@ -58,24 +64,27 @@ func cpuBitsMask(cpu int) cpuMask {
|
||||
return cpuMask(1 << (uint(cpu) % _NCPUBITS))
|
||||
}
|
||||
|
||||
// Set adds cpu to the set s.
|
||||
func (s *CPUSet) Set(cpu int) {
|
||||
func cpuMaskFill(s []cpuMask) {
|
||||
for i := range s {
|
||||
s[i] = ^cpuMask(0)
|
||||
}
|
||||
}
|
||||
|
||||
func cpuMaskSet(s []cpuMask, cpu int) {
|
||||
i := cpuBitsIndex(cpu)
|
||||
if i < len(s) {
|
||||
s[i] |= cpuBitsMask(cpu)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear removes cpu from the set s.
|
||||
func (s *CPUSet) Clear(cpu int) {
|
||||
func cpuMaskClear(s []cpuMask, cpu int) {
|
||||
i := cpuBitsIndex(cpu)
|
||||
if i < len(s) {
|
||||
s[i] &^= cpuBitsMask(cpu)
|
||||
}
|
||||
}
|
||||
|
||||
// IsSet reports whether cpu is in the set s.
|
||||
func (s *CPUSet) IsSet(cpu int) bool {
|
||||
func cpuMaskIsSet(s []cpuMask, cpu int) bool {
|
||||
i := cpuBitsIndex(cpu)
|
||||
if i < len(s) {
|
||||
return s[i]&cpuBitsMask(cpu) != 0
|
||||
@@ -83,11 +92,98 @@ func (s *CPUSet) IsSet(cpu int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Count returns the number of CPUs in the set s.
|
||||
func (s *CPUSet) Count() int {
|
||||
func cpuMaskCount(s []cpuMask) int {
|
||||
c := 0
|
||||
for _, b := range s {
|
||||
c += bits.OnesCount64(uint64(b))
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Set adds cpu to the set s. If cpu is out of bounds for s, no action is taken.
|
||||
func (s *CPUSet) Set(cpu int) {
|
||||
cpuMaskSet(s[:], cpu)
|
||||
}
|
||||
|
||||
// Clear removes cpu from the set s. If cpu is out of bounds for s, no action is taken.
|
||||
func (s *CPUSet) Clear(cpu int) {
|
||||
cpuMaskClear(s[:], cpu)
|
||||
}
|
||||
|
||||
// IsSet reports whether cpu is in the set s.
|
||||
func (s *CPUSet) IsSet(cpu int) bool {
|
||||
return cpuMaskIsSet(s[:], cpu)
|
||||
}
|
||||
|
||||
// Count returns the number of CPUs in the set s.
|
||||
func (s *CPUSet) Count() int {
|
||||
return cpuMaskCount(s[:])
|
||||
}
|
||||
|
||||
// NewCPUSet creates a CPU affinity mask capable of representing CPU IDs
|
||||
// up to maxCPU (exclusive).
|
||||
func NewCPUSet(maxCPU int) CPUSetDynamic {
|
||||
numMasks := (maxCPU + _NCPUBITS - 1) / _NCPUBITS
|
||||
if numMasks == 0 {
|
||||
numMasks = 1
|
||||
}
|
||||
return make(CPUSetDynamic, numMasks)
|
||||
}
|
||||
|
||||
// Zero clears the set s, so that it contains no CPUs.
|
||||
func (s CPUSetDynamic) Zero() {
|
||||
clear(s)
|
||||
}
|
||||
|
||||
// Fill adds all possible CPU bits to the set s. On Linux, [SchedSetaffinityDynamic]
|
||||
// will silently ignore any invalid CPU bits in [CPUSetDynamic] so this is an
|
||||
// efficient way of resetting the CPU affinity of a process.
|
||||
func (s CPUSetDynamic) Fill() {
|
||||
cpuMaskFill(s)
|
||||
}
|
||||
|
||||
// Set adds cpu to the set s. If cpu is out of bounds for s, no action is taken.
|
||||
func (s CPUSetDynamic) Set(cpu int) {
|
||||
cpuMaskSet(s, cpu)
|
||||
}
|
||||
|
||||
// Clear removes cpu from the set s. If cpu is out of bounds for s, no action is taken.
|
||||
func (s CPUSetDynamic) Clear(cpu int) {
|
||||
cpuMaskClear(s, cpu)
|
||||
}
|
||||
|
||||
// IsSet reports whether cpu is in the set s.
|
||||
func (s CPUSetDynamic) IsSet(cpu int) bool {
|
||||
return cpuMaskIsSet(s, cpu)
|
||||
}
|
||||
|
||||
// Count returns the number of CPUs in the set s.
|
||||
func (s CPUSetDynamic) Count() int {
|
||||
return cpuMaskCount(s)
|
||||
}
|
||||
|
||||
func (s CPUSetDynamic) size() uintptr {
|
||||
return uintptr(len(s)) * unsafe.Sizeof(cpuMask(0))
|
||||
}
|
||||
|
||||
func (s CPUSetDynamic) pointer() unsafe.Pointer {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
return unsafe.Pointer(&s[0])
|
||||
}
|
||||
|
||||
// SchedGetaffinityDynamic gets the CPU affinity mask of the thread specified by pid.
|
||||
// If pid is 0 the calling thread is used.
|
||||
//
|
||||
// If the set is smaller than the size of the affinity mask used by the kernel,
|
||||
// [EINVAL] is returned.
|
||||
func SchedGetaffinityDynamic(pid int, set CPUSetDynamic) error {
|
||||
return schedAffinity(SYS_SCHED_GETAFFINITY, pid, set.size(), set.pointer())
|
||||
}
|
||||
|
||||
// SchedSetaffinityDynamic sets the CPU affinity mask of the thread specified by pid.
|
||||
// If pid is 0 the calling thread is used.
|
||||
func SchedSetaffinityDynamic(pid int, set CPUSetDynamic) error {
|
||||
return schedAffinity(SYS_SCHED_SETAFFINITY, pid, set.size(), set.pointer())
|
||||
}
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ if [[ "$GOOS" = "linux" ]]; then
|
||||
# Files generated through docker (use $cmd so you can Ctl-C the build or run)
|
||||
set -e
|
||||
$cmd docker build --tag generate:$GOOS $GOOS
|
||||
$cmd docker run --interactive --tty --volume $(cd -- "$(dirname -- "$0")/.." && pwd):/build generate:$GOOS
|
||||
$cmd docker run --rm --interactive --tty --volume $(cd -- "$(dirname -- "$0")/.." && pwd):/build generate:$GOOS
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
+3
@@ -354,6 +354,9 @@ struct ltchars {
|
||||
// Renamed in v6.16, commit c6d732c38f93 ("net: ethtool: remove duplicate defines for family info")
|
||||
#define ETHTOOL_FAMILY_NAME ETHTOOL_GENL_NAME
|
||||
#define ETHTOOL_FAMILY_VERSION ETHTOOL_GENL_VERSION
|
||||
|
||||
// Removed in v6.17, commit 760e6f7befba ("futex: Remove support for IMMUTABLE")
|
||||
#define PR_FUTEX_HASH_GET_IMMUTABLE 3
|
||||
'
|
||||
|
||||
includes_NetBSD='
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
//go:build darwin || linux || openbsd
|
||||
|
||||
package unix
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// minIovec is the size of the small initial allocation used by
|
||||
// Readv, Writev, etc.
|
||||
//
|
||||
// This small allocation gets stack allocated, which lets the
|
||||
// common use case of len(iovs) <= minIovec avoid more expensive
|
||||
// heap allocations.
|
||||
const minIovec = 8
|
||||
|
||||
// appendBytes converts bs to Iovecs and appends them to vecs.
|
||||
func appendBytes(vecs []Iovec, bs [][]byte) []Iovec {
|
||||
for _, b := range bs {
|
||||
var v Iovec
|
||||
v.SetLen(len(b))
|
||||
if len(b) > 0 {
|
||||
v.Base = &b[0]
|
||||
} else {
|
||||
v.Base = (*byte)(unsafe.Pointer(&_zero))
|
||||
}
|
||||
vecs = append(vecs, v)
|
||||
}
|
||||
return vecs
|
||||
}
|
||||
|
||||
// writevRaceDetect tells the race detector that the program
|
||||
// has read the first n bytes stored in iovecs.
|
||||
func writevRaceDetect(iovecs []Iovec, n int) {
|
||||
if !raceenabled {
|
||||
return
|
||||
}
|
||||
for i := 0; n > 0 && i < len(iovecs); i++ {
|
||||
m := min(int(iovecs[i].Len), n)
|
||||
n -= m
|
||||
if m > 0 {
|
||||
raceReadRange(unsafe.Pointer(iovecs[i].Base), m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// readvRaceDetect tells the race detector that the program
|
||||
// has written to the first n bytes stored in iovecs.
|
||||
func readvRaceDetect(iovecs []Iovec, n int, err error) {
|
||||
if !raceenabled {
|
||||
return
|
||||
}
|
||||
for i := 0; n > 0 && i < len(iovecs); i++ {
|
||||
m := min(int(iovecs[i].Len), n)
|
||||
n -= m
|
||||
if m > 0 {
|
||||
raceWriteRange(unsafe.Pointer(iovecs[i].Base), m)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
raceAcquire(unsafe.Pointer(&ioSync))
|
||||
}
|
||||
}
|
||||
|
||||
func Readv(fd int, iovs [][]byte) (n int, err error) {
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
n, err = readv(fd, iovecs)
|
||||
readvRaceDetect(iovecs, n, err)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) {
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
n, err = preadv(fd, iovecs, offset)
|
||||
readvRaceDetect(iovecs, n, err)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func Writev(fd int, iovs [][]byte) (n int, err error) {
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
if raceenabled {
|
||||
raceReleaseMerge(unsafe.Pointer(&ioSync))
|
||||
}
|
||||
n, err = writev(fd, iovecs)
|
||||
writevRaceDetect(iovecs, n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
if raceenabled {
|
||||
raceReleaseMerge(unsafe.Pointer(&ioSync))
|
||||
}
|
||||
n, err = pwritev(fd, iovecs, offset)
|
||||
writevRaceDetect(iovecs, n)
|
||||
return n, err
|
||||
}
|
||||
-89
@@ -602,95 +602,6 @@ func Connectx(fd int, srcIf uint32, srcAddr, dstAddr Sockaddr, associd SaeAssocI
|
||||
return
|
||||
}
|
||||
|
||||
const minIovec = 8
|
||||
|
||||
func Readv(fd int, iovs [][]byte) (n int, err error) {
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
n, err = readv(fd, iovecs)
|
||||
readvRacedetect(iovecs, n, err)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) {
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
n, err = preadv(fd, iovecs, offset)
|
||||
readvRacedetect(iovecs, n, err)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func Writev(fd int, iovs [][]byte) (n int, err error) {
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
if raceenabled {
|
||||
raceReleaseMerge(unsafe.Pointer(&ioSync))
|
||||
}
|
||||
n, err = writev(fd, iovecs)
|
||||
writevRacedetect(iovecs, n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
if raceenabled {
|
||||
raceReleaseMerge(unsafe.Pointer(&ioSync))
|
||||
}
|
||||
n, err = pwritev(fd, iovecs, offset)
|
||||
writevRacedetect(iovecs, n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func appendBytes(vecs []Iovec, bs [][]byte) []Iovec {
|
||||
for _, b := range bs {
|
||||
var v Iovec
|
||||
v.SetLen(len(b))
|
||||
if len(b) > 0 {
|
||||
v.Base = &b[0]
|
||||
} else {
|
||||
v.Base = (*byte)(unsafe.Pointer(&_zero))
|
||||
}
|
||||
vecs = append(vecs, v)
|
||||
}
|
||||
return vecs
|
||||
}
|
||||
|
||||
func writevRacedetect(iovecs []Iovec, n int) {
|
||||
if !raceenabled {
|
||||
return
|
||||
}
|
||||
for i := 0; n > 0 && i < len(iovecs); i++ {
|
||||
m := int(iovecs[i].Len)
|
||||
if m > n {
|
||||
m = n
|
||||
}
|
||||
n -= m
|
||||
if m > 0 {
|
||||
raceReadRange(unsafe.Pointer(iovecs[i].Base), m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readvRacedetect(iovecs []Iovec, n int, err error) {
|
||||
if !raceenabled {
|
||||
return
|
||||
}
|
||||
for i := 0; n > 0 && i < len(iovecs); i++ {
|
||||
m := int(iovecs[i].Len)
|
||||
if m > n {
|
||||
m = n
|
||||
}
|
||||
n -= m
|
||||
if m > 0 {
|
||||
raceWriteRange(unsafe.Pointer(iovecs[i].Base), m)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
raceAcquire(unsafe.Pointer(&ioSync))
|
||||
}
|
||||
}
|
||||
|
||||
//sys connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error)
|
||||
//sys sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error)
|
||||
|
||||
|
||||
+18
-96
@@ -2150,33 +2150,10 @@ func Signalfd(fd int, sigmask *Sigset_t, flags int) (newfd int, err error) {
|
||||
//sys exitThread(code int) (err error) = SYS_EXIT
|
||||
//sys readv(fd int, iovs []Iovec) (n int, err error) = SYS_READV
|
||||
//sys writev(fd int, iovs []Iovec) (n int, err error) = SYS_WRITEV
|
||||
//sys preadv(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr) (n int, err error) = SYS_PREADV
|
||||
//sys pwritev(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr) (n int, err error) = SYS_PWRITEV
|
||||
//sys preadv2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PREADV2
|
||||
//sys pwritev2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PWRITEV2
|
||||
|
||||
// minIovec is the size of the small initial allocation used by
|
||||
// Readv, Writev, etc.
|
||||
//
|
||||
// This small allocation gets stack allocated, which lets the
|
||||
// common use case of len(iovs) <= minIovs avoid more expensive
|
||||
// heap allocations.
|
||||
const minIovec = 8
|
||||
|
||||
// appendBytes converts bs to Iovecs and appends them to vecs.
|
||||
func appendBytes(vecs []Iovec, bs [][]byte) []Iovec {
|
||||
for _, b := range bs {
|
||||
var v Iovec
|
||||
v.SetLen(len(b))
|
||||
if len(b) > 0 {
|
||||
v.Base = &b[0]
|
||||
} else {
|
||||
v.Base = (*byte)(unsafe.Pointer(&_zero))
|
||||
}
|
||||
vecs = append(vecs, v)
|
||||
}
|
||||
return vecs
|
||||
}
|
||||
//sys preadvSyscall(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr) (n int, err error) = SYS_PREADV
|
||||
//sys pwritevSyscall(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr) (n int, err error) = SYS_PWRITEV
|
||||
//sys preadv2Syscall(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PREADV2
|
||||
//sys pwritev2Syscall(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) = SYS_PWRITEV2
|
||||
|
||||
// offs2lohi splits offs into its low and high order bits.
|
||||
func offs2lohi(offs int64) (lo, hi uintptr) {
|
||||
@@ -2184,69 +2161,23 @@ func offs2lohi(offs int64) (lo, hi uintptr) {
|
||||
return uintptr(offs), uintptr(uint64(offs) >> (longBits - 1) >> 1) // two shifts to avoid false positive in vet
|
||||
}
|
||||
|
||||
func Readv(fd int, iovs [][]byte) (n int, err error) {
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
n, err = readv(fd, iovecs)
|
||||
readvRacedetect(iovecs, n, err)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) {
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
lo, hi := offs2lohi(offset)
|
||||
n, err = preadv(fd, iovecs, lo, hi)
|
||||
readvRacedetect(iovecs, n, err)
|
||||
return n, err
|
||||
return preadvSyscall(fd, iovecs, lo, hi)
|
||||
}
|
||||
|
||||
func Preadv2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) {
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
lo, hi := offs2lohi(offset)
|
||||
n, err = preadv2(fd, iovecs, lo, hi, flags)
|
||||
readvRacedetect(iovecs, n, err)
|
||||
n, err = preadv2Syscall(fd, iovecs, lo, hi, flags)
|
||||
readvRaceDetect(iovecs, n, err)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func readvRacedetect(iovecs []Iovec, n int, err error) {
|
||||
if !raceenabled {
|
||||
return
|
||||
}
|
||||
for i := 0; n > 0 && i < len(iovecs); i++ {
|
||||
m := min(int(iovecs[i].Len), n)
|
||||
n -= m
|
||||
if m > 0 {
|
||||
raceWriteRange(unsafe.Pointer(iovecs[i].Base), m)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
raceAcquire(unsafe.Pointer(&ioSync))
|
||||
}
|
||||
}
|
||||
|
||||
func Writev(fd int, iovs [][]byte) (n int, err error) {
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
if raceenabled {
|
||||
raceReleaseMerge(unsafe.Pointer(&ioSync))
|
||||
}
|
||||
n, err = writev(fd, iovecs)
|
||||
writevRacedetect(iovecs, n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
if raceenabled {
|
||||
raceReleaseMerge(unsafe.Pointer(&ioSync))
|
||||
}
|
||||
func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
lo, hi := offs2lohi(offset)
|
||||
n, err = pwritev(fd, iovecs, lo, hi)
|
||||
writevRacedetect(iovecs, n)
|
||||
return n, err
|
||||
return pwritevSyscall(fd, iovecs, lo, hi)
|
||||
}
|
||||
|
||||
func Pwritev2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) {
|
||||
@@ -2256,24 +2187,11 @@ func Pwritev2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error)
|
||||
raceReleaseMerge(unsafe.Pointer(&ioSync))
|
||||
}
|
||||
lo, hi := offs2lohi(offset)
|
||||
n, err = pwritev2(fd, iovecs, lo, hi, flags)
|
||||
writevRacedetect(iovecs, n)
|
||||
n, err = pwritev2Syscall(fd, iovecs, lo, hi, flags)
|
||||
writevRaceDetect(iovecs, n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func writevRacedetect(iovecs []Iovec, n int) {
|
||||
if !raceenabled {
|
||||
return
|
||||
}
|
||||
for i := 0; n > 0 && i < len(iovecs); i++ {
|
||||
m := min(int(iovecs[i].Len), n)
|
||||
n -= m
|
||||
if m > 0 {
|
||||
raceReadRange(unsafe.Pointer(iovecs[i].Base), m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mmap varies by architecture; see syscall_linux_*.go.
|
||||
//sys munmap(addr uintptr, length uintptr) (err error)
|
||||
//sys mremap(oldaddr uintptr, oldlength uintptr, newlength uintptr, flags int, newaddr uintptr) (xaddr uintptr, err error)
|
||||
@@ -2644,8 +2562,12 @@ func SchedGetAttr(pid int, flags uint) (*SchedAttr, error) {
|
||||
//sys Cachestat(fd uint, crange *CachestatRange, cstat *Cachestat_t, flags uint) (err error)
|
||||
//sys Mseal(b []byte, flags uint) (err error)
|
||||
|
||||
//sys setMemPolicy(mode int, mask *CPUSet, size int) (err error) = SYS_SET_MEMPOLICY
|
||||
//sys setMemPolicy(mode int, mask unsafe.Pointer, size uintptr) (err error) = SYS_SET_MEMPOLICY
|
||||
|
||||
func SetMemPolicy(mode int, mask *CPUSet) error {
|
||||
return setMemPolicy(mode, mask, _CPU_SETSIZE)
|
||||
return setMemPolicy(mode, unsafe.Pointer(mask), _CPU_SETSIZE)
|
||||
}
|
||||
|
||||
func SetMemPolicyDynamic(mode int, mask CPUSetDynamic) error {
|
||||
return setMemPolicy(mode, mask.pointer(), mask.size())
|
||||
}
|
||||
|
||||
+3
@@ -82,6 +82,9 @@ func Time(t *Time_t) (Time_t, error) {
|
||||
}
|
||||
|
||||
func Utime(path string, buf *Utimbuf) error {
|
||||
if buf == nil {
|
||||
return Utimes(path, nil)
|
||||
}
|
||||
tv := []Timeval{
|
||||
{Sec: buf.Actime},
|
||||
{Sec: buf.Modtime},
|
||||
|
||||
+3
@@ -113,6 +113,9 @@ func Time(t *Time_t) (Time_t, error) {
|
||||
}
|
||||
|
||||
func Utime(path string, buf *Utimbuf) error {
|
||||
if buf == nil {
|
||||
return Utimes(path, nil)
|
||||
}
|
||||
tv := []Timeval{
|
||||
{Sec: buf.Actime},
|
||||
{Sec: buf.Modtime},
|
||||
|
||||
+3
@@ -150,6 +150,9 @@ func Time(t *Time_t) (Time_t, error) {
|
||||
}
|
||||
|
||||
func Utime(path string, buf *Utimbuf) error {
|
||||
if buf == nil {
|
||||
return Utimes(path, nil)
|
||||
}
|
||||
tv := []Timeval{
|
||||
{Sec: buf.Actime},
|
||||
{Sec: buf.Modtime},
|
||||
|
||||
+3
@@ -112,6 +112,9 @@ func Time(t *Time_t) (Time_t, error) {
|
||||
}
|
||||
|
||||
func Utime(path string, buf *Utimbuf) error {
|
||||
if buf == nil {
|
||||
return Utimes(path, nil)
|
||||
}
|
||||
tv := []Timeval{
|
||||
{Sec: buf.Actime},
|
||||
{Sec: buf.Modtime},
|
||||
|
||||
+4
@@ -300,6 +300,10 @@ func Uname(uname *Utsname) error {
|
||||
//sys Pathconf(path string, name int) (val int, err error)
|
||||
//sys pread(fd int, p []byte, offset int64) (n int, err error)
|
||||
//sys pwrite(fd int, p []byte, offset int64) (n int, err error)
|
||||
//sys readv(fd int, iovecs []Iovec) (n int, err error)
|
||||
//sys writev(fd int, iovecs []Iovec) (n int, err error)
|
||||
//sys preadv(fd int, iovecs []Iovec, offset int64) (n int, err error)
|
||||
//sys pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error)
|
||||
//sys read(fd int, p []byte) (n int, err error)
|
||||
//sys Readlink(path string, buf []byte) (n int, err error)
|
||||
//sys Readlinkat(dirfd int, path string, buf []byte) (n int, err error)
|
||||
|
||||
+59
-2
@@ -353,8 +353,10 @@ const (
|
||||
AUDIT_MAC_IPSEC_EVENT = 0x587
|
||||
AUDIT_MAC_MAP_ADD = 0x581
|
||||
AUDIT_MAC_MAP_DEL = 0x582
|
||||
AUDIT_MAC_OBJ_CONTEXTS = 0x592
|
||||
AUDIT_MAC_POLICY_LOAD = 0x57b
|
||||
AUDIT_MAC_STATUS = 0x57c
|
||||
AUDIT_MAC_TASK_CONTEXTS = 0x591
|
||||
AUDIT_MAC_UNLBL_ALLOW = 0x57e
|
||||
AUDIT_MAC_UNLBL_STCADD = 0x588
|
||||
AUDIT_MAC_UNLBL_STCDEL = 0x589
|
||||
@@ -591,8 +593,13 @@ const (
|
||||
CAN_CTRLMODE_LOOPBACK = 0x1
|
||||
CAN_CTRLMODE_ONE_SHOT = 0x8
|
||||
CAN_CTRLMODE_PRESUME_ACK = 0x40
|
||||
CAN_CTRLMODE_RESTRICTED = 0x800
|
||||
CAN_CTRLMODE_TDC_AUTO = 0x200
|
||||
CAN_CTRLMODE_TDC_MANUAL = 0x400
|
||||
CAN_CTRLMODE_XL = 0x1000
|
||||
CAN_CTRLMODE_XL_TDC_AUTO = 0x2000
|
||||
CAN_CTRLMODE_XL_TDC_MANUAL = 0x4000
|
||||
CAN_CTRLMODE_XL_TMS = 0x8000
|
||||
CAN_EFF_FLAG = 0x80000000
|
||||
CAN_EFF_ID_BITS = 0x1d
|
||||
CAN_EFF_MASK = 0x1fffffff
|
||||
@@ -800,6 +807,8 @@ const (
|
||||
DEVLINK_PORT_FN_CAP_IPSEC_PACKET = 0x8
|
||||
DEVLINK_PORT_FN_CAP_MIGRATABLE = 0x2
|
||||
DEVLINK_PORT_FN_CAP_ROCE = 0x1
|
||||
DEVLINK_RATE_TCS_MAX = 0x8
|
||||
DEVLINK_RATE_TC_INDEX_MAX = 0x7
|
||||
DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX = 0x14
|
||||
DEVLINK_SUPPORTED_FLASH_OVERWRITE_SECTIONS = 0x3
|
||||
DEVMEM_MAGIC = 0x454d444d
|
||||
@@ -1186,6 +1195,7 @@ const (
|
||||
ETH_P_MPLS_UC = 0x8847
|
||||
ETH_P_MRP = 0x88e3
|
||||
ETH_P_MVRP = 0x88f5
|
||||
ETH_P_MXLGSW = 0x88c3
|
||||
ETH_P_NCSI = 0x88f8
|
||||
ETH_P_NSH = 0x894f
|
||||
ETH_P_PAE = 0x888e
|
||||
@@ -1218,6 +1228,7 @@ const (
|
||||
ETH_P_WCCP = 0x883e
|
||||
ETH_P_X25 = 0x805
|
||||
ETH_P_XDSA = 0xf8
|
||||
ETH_P_YT921X = 0x9988
|
||||
ET_CORE = 0x4
|
||||
ET_DYN = 0x3
|
||||
ET_EXEC = 0x2
|
||||
@@ -1258,6 +1269,7 @@ const (
|
||||
FALLOC_FL_NO_HIDE_STALE = 0x4
|
||||
FALLOC_FL_PUNCH_HOLE = 0x2
|
||||
FALLOC_FL_UNSHARE_RANGE = 0x40
|
||||
FALLOC_FL_WRITE_ZEROES = 0x80
|
||||
FALLOC_FL_ZERO_RANGE = 0x10
|
||||
FANOTIFY_METADATA_VERSION = 0x3
|
||||
FAN_ACCESS = 0x1
|
||||
@@ -1477,6 +1489,7 @@ const (
|
||||
GRND_INSECURE = 0x4
|
||||
GRND_NONBLOCK = 0x1
|
||||
GRND_RANDOM = 0x2
|
||||
GUEST_MEMFD_MAGIC = 0x474d454d
|
||||
HDIO_DRIVE_CMD = 0x31f
|
||||
HDIO_DRIVE_CMD_AEB = 0x31e
|
||||
HDIO_DRIVE_CMD_HDR_SIZE = 0x4
|
||||
@@ -1517,6 +1530,7 @@ const (
|
||||
HDIO_SET_XFER = 0x306
|
||||
HDIO_TRISTATE_HWIF = 0x31b
|
||||
HDIO_UNREGISTER_HWIF = 0x32a
|
||||
HIDIOCTL_LAST = 0xd
|
||||
HID_MAX_DESCRIPTOR_SIZE = 0x1000
|
||||
HOSTFS_SUPER_MAGIC = 0xc0ffee
|
||||
HPFS_SUPER_MAGIC = 0xf995e849
|
||||
@@ -1809,6 +1823,8 @@ const (
|
||||
KEXEC_ARCH_X86_64 = 0x3e0000
|
||||
KEXEC_CRASH_HOTPLUG_SUPPORT = 0x8
|
||||
KEXEC_FILE_DEBUG = 0x8
|
||||
KEXEC_FILE_FORCE_DTB = 0x20
|
||||
KEXEC_FILE_NO_CMA = 0x10
|
||||
KEXEC_FILE_NO_INITRAMFS = 0x4
|
||||
KEXEC_FILE_ON_CRASH = 0x2
|
||||
KEXEC_FILE_UNLOAD = 0x1
|
||||
@@ -1905,6 +1921,7 @@ const (
|
||||
LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON = 0x2
|
||||
LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF = 0x1
|
||||
LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF = 0x4
|
||||
LANDLOCK_RESTRICT_SELF_TSYNC = 0x8
|
||||
LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET = 0x1
|
||||
LANDLOCK_SCOPE_SIGNAL = 0x2
|
||||
LINUX_REBOOT_CMD_CAD_OFF = 0x0
|
||||
@@ -2412,6 +2429,7 @@ const (
|
||||
NN_PRXFPREG = "LINUX"
|
||||
NN_RISCV_CSR = "LINUX"
|
||||
NN_RISCV_TAGGED_ADDR_CTRL = "LINUX"
|
||||
NN_RISCV_USER_CFI = "LINUX"
|
||||
NN_RISCV_VECTOR = "LINUX"
|
||||
NN_S390_CTRS = "LINUX"
|
||||
NN_S390_GS_BC = "LINUX"
|
||||
@@ -2493,6 +2511,7 @@ const (
|
||||
NT_PRXFPREG = 0x46e62b7f
|
||||
NT_RISCV_CSR = 0x900
|
||||
NT_RISCV_TAGGED_ADDR_CTRL = 0x902
|
||||
NT_RISCV_USER_CFI = 0x903
|
||||
NT_RISCV_VECTOR = 0x901
|
||||
NT_S390_CTRS = 0x304
|
||||
NT_S390_GS_BC = 0x30c
|
||||
@@ -2515,6 +2534,7 @@ const (
|
||||
NT_X86_SHSTK = 0x204
|
||||
NT_X86_XSAVE_LAYOUT = 0x205
|
||||
NT_X86_XSTATE = 0x202
|
||||
NULL_FS_MAGIC = 0x4e554c4c
|
||||
OCFS2_SUPER_MAGIC = 0x7461636f
|
||||
OCRNL = 0x8
|
||||
OFDEL = 0x80
|
||||
@@ -2594,6 +2614,7 @@ const (
|
||||
PERF_ATTR_SIZE_VER6 = 0x78
|
||||
PERF_ATTR_SIZE_VER7 = 0x80
|
||||
PERF_ATTR_SIZE_VER8 = 0x88
|
||||
PERF_ATTR_SIZE_VER9 = 0x90
|
||||
PERF_AUX_FLAG_COLLISION = 0x8
|
||||
PERF_AUX_FLAG_CORESIGHT_FORMAT_CORESIGHT = 0x0
|
||||
PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW = 0x100
|
||||
@@ -2629,6 +2650,7 @@ const (
|
||||
PERF_MEM_LVLNUM_ANY_CACHE = 0xb
|
||||
PERF_MEM_LVLNUM_CXL = 0x9
|
||||
PERF_MEM_LVLNUM_IO = 0xa
|
||||
PERF_MEM_LVLNUM_L0 = 0x7
|
||||
PERF_MEM_LVLNUM_L1 = 0x1
|
||||
PERF_MEM_LVLNUM_L2 = 0x2
|
||||
PERF_MEM_LVLNUM_L2_MHB = 0x5
|
||||
@@ -2662,6 +2684,23 @@ const (
|
||||
PERF_MEM_OP_PFETCH = 0x8
|
||||
PERF_MEM_OP_SHIFT = 0x0
|
||||
PERF_MEM_OP_STORE = 0x4
|
||||
PERF_MEM_REGION_L_NON_SHARE = 0x3
|
||||
PERF_MEM_REGION_L_SHARE = 0x2
|
||||
PERF_MEM_REGION_MEM0 = 0x8
|
||||
PERF_MEM_REGION_MEM1 = 0x9
|
||||
PERF_MEM_REGION_MEM2 = 0xa
|
||||
PERF_MEM_REGION_MEM3 = 0xb
|
||||
PERF_MEM_REGION_MEM4 = 0xc
|
||||
PERF_MEM_REGION_MEM5 = 0xd
|
||||
PERF_MEM_REGION_MEM6 = 0xe
|
||||
PERF_MEM_REGION_MEM7 = 0xf
|
||||
PERF_MEM_REGION_MMIO = 0x7
|
||||
PERF_MEM_REGION_NA = 0x0
|
||||
PERF_MEM_REGION_O_IO = 0x4
|
||||
PERF_MEM_REGION_O_NON_SHARE = 0x6
|
||||
PERF_MEM_REGION_O_SHARE = 0x5
|
||||
PERF_MEM_REGION_RSVD = 0x1
|
||||
PERF_MEM_REGION_SHIFT = 0x2e
|
||||
PERF_MEM_REMOTE_REMOTE = 0x1
|
||||
PERF_MEM_REMOTE_SHIFT = 0x25
|
||||
PERF_MEM_SNOOPX_FWD = 0x1
|
||||
@@ -2776,6 +2815,10 @@ const (
|
||||
PR_CAP_AMBIENT_IS_SET = 0x1
|
||||
PR_CAP_AMBIENT_LOWER = 0x3
|
||||
PR_CAP_AMBIENT_RAISE = 0x2
|
||||
PR_CFI_BRANCH_LANDING_PADS = 0x0
|
||||
PR_CFI_DISABLE = 0x2
|
||||
PR_CFI_ENABLE = 0x1
|
||||
PR_CFI_LOCK = 0x4
|
||||
PR_ENDIAN_BIG = 0x0
|
||||
PR_ENDIAN_LITTLE = 0x1
|
||||
PR_ENDIAN_PPC_LITTLE = 0x2
|
||||
@@ -2798,6 +2841,7 @@ const (
|
||||
PR_FUTEX_HASH_GET_SLOTS = 0x2
|
||||
PR_FUTEX_HASH_SET_SLOTS = 0x1
|
||||
PR_GET_AUXV = 0x41555856
|
||||
PR_GET_CFI = 0x50
|
||||
PR_GET_CHILD_SUBREAPER = 0x25
|
||||
PR_GET_DUMPABLE = 0x3
|
||||
PR_GET_ENDIAN = 0x13
|
||||
@@ -2834,6 +2878,7 @@ const (
|
||||
PR_MDWE_REFUSE_EXEC_GAIN = 0x1
|
||||
PR_MPX_DISABLE_MANAGEMENT = 0x2c
|
||||
PR_MPX_ENABLE_MANAGEMENT = 0x2b
|
||||
PR_MTE_STORE_ONLY = 0x80000
|
||||
PR_MTE_TAG_MASK = 0x7fff8
|
||||
PR_MTE_TAG_SHIFT = 0x3
|
||||
PR_MTE_TCF_ASYNC = 0x4
|
||||
@@ -2877,6 +2922,10 @@ const (
|
||||
PR_RISCV_V_VSTATE_CTRL_NEXT_MASK = 0xc
|
||||
PR_RISCV_V_VSTATE_CTRL_OFF = 0x1
|
||||
PR_RISCV_V_VSTATE_CTRL_ON = 0x2
|
||||
PR_RSEQ_SLICE_EXTENSION = 0x4f
|
||||
PR_RSEQ_SLICE_EXTENSION_GET = 0x1
|
||||
PR_RSEQ_SLICE_EXTENSION_SET = 0x2
|
||||
PR_RSEQ_SLICE_EXT_ENABLE = 0x1
|
||||
PR_SCHED_CORE = 0x3e
|
||||
PR_SCHED_CORE_CREATE = 0x1
|
||||
PR_SCHED_CORE_GET = 0x0
|
||||
@@ -2886,6 +2935,7 @@ const (
|
||||
PR_SCHED_CORE_SCOPE_THREAD_GROUP = 0x1
|
||||
PR_SCHED_CORE_SHARE_FROM = 0x3
|
||||
PR_SCHED_CORE_SHARE_TO = 0x2
|
||||
PR_SET_CFI = 0x51
|
||||
PR_SET_CHILD_SUBREAPER = 0x24
|
||||
PR_SET_DUMPABLE = 0x4
|
||||
PR_SET_ENDIAN = 0x14
|
||||
@@ -2951,11 +3001,14 @@ const (
|
||||
PR_SVE_SET_VL_ONEXEC = 0x40000
|
||||
PR_SVE_VL_INHERIT = 0x20000
|
||||
PR_SVE_VL_LEN_MASK = 0xffff
|
||||
PR_SYS_DISPATCH_EXCLUSIVE_ON = 0x1
|
||||
PR_SYS_DISPATCH_INCLUSIVE_ON = 0x2
|
||||
PR_SYS_DISPATCH_OFF = 0x0
|
||||
PR_SYS_DISPATCH_ON = 0x1
|
||||
PR_TAGGED_ADDR_ENABLE = 0x1
|
||||
PR_TASK_PERF_EVENTS_DISABLE = 0x1f
|
||||
PR_TASK_PERF_EVENTS_ENABLE = 0x20
|
||||
PR_THP_DISABLE_EXCEPT_ADVISED = 0x2
|
||||
PR_TIMER_CREATE_RESTORE_IDS = 0x4d
|
||||
PR_TIMER_CREATE_RESTORE_IDS_GET = 0x2
|
||||
PR_TIMER_CREATE_RESTORE_IDS_OFF = 0x0
|
||||
@@ -2987,8 +3040,10 @@ const (
|
||||
PTP_STRICT_FLAGS = 0x8
|
||||
PTP_SYS_OFFSET_EXTENDED = 0xc4c03d09
|
||||
PTP_SYS_OFFSET_EXTENDED2 = 0xc4c03d12
|
||||
PTP_SYS_OFFSET_EXTENDED_CYCLES = 0xc4c03d16
|
||||
PTP_SYS_OFFSET_PRECISE = 0xc0403d08
|
||||
PTP_SYS_OFFSET_PRECISE2 = 0xc0403d11
|
||||
PTP_SYS_OFFSET_PRECISE_CYCLES = 0xc0403d15
|
||||
PTRACE_ATTACH = 0x10
|
||||
PTRACE_CONT = 0x7
|
||||
PTRACE_DETACH = 0x11
|
||||
@@ -3330,8 +3385,9 @@ const (
|
||||
RWF_DSYNC = 0x2
|
||||
RWF_HIPRI = 0x1
|
||||
RWF_NOAPPEND = 0x20
|
||||
RWF_NOSIGNAL = 0x100
|
||||
RWF_NOWAIT = 0x8
|
||||
RWF_SUPPORTED = 0xff
|
||||
RWF_SUPPORTED = 0x1ff
|
||||
RWF_SYNC = 0x4
|
||||
RWF_WRITE_LIFE_NOT_SET = 0x0
|
||||
SCHED_BATCH = 0x3
|
||||
@@ -3714,7 +3770,7 @@ const (
|
||||
TASKSTATS_GENL_NAME = "TASKSTATS"
|
||||
TASKSTATS_GENL_VERSION = 0x1
|
||||
TASKSTATS_TYPE_MAX = 0x6
|
||||
TASKSTATS_VERSION = 0x10
|
||||
TASKSTATS_VERSION = 0x11
|
||||
TCIFLUSH = 0x0
|
||||
TCIOFF = 0x2
|
||||
TCIOFLUSH = 0x2
|
||||
@@ -4052,6 +4108,7 @@ const (
|
||||
XDP_FLAGS_REPLACE = 0x10
|
||||
XDP_FLAGS_SKB_MODE = 0x2
|
||||
XDP_FLAGS_UPDATE_IF_NOEXIST = 0x1
|
||||
XDP_MAX_TX_SKB_BUDGET = 0x9
|
||||
XDP_MMAP_OFFSETS = 0x1
|
||||
XDP_OPTIONS = 0x8
|
||||
XDP_OPTIONS_ZEROCOPY = 0x1
|
||||
|
||||
+6
-1
@@ -159,6 +159,7 @@ const (
|
||||
NFDBITS = 0x20
|
||||
NLDLY = 0x100
|
||||
NOFLSH = 0x80
|
||||
NS_GET_ID = 0x8008b70d
|
||||
NS_GET_MNTNS_ID = 0x8008b705
|
||||
NS_GET_NSTYPE = 0xb703
|
||||
NS_GET_OWNER_UID = 0xb704
|
||||
@@ -305,6 +306,7 @@ const (
|
||||
RTC_WKALM_SET = 0x4028700f
|
||||
SCM_DEVMEM_DMABUF = 0x4f
|
||||
SCM_DEVMEM_LINEAR = 0x4e
|
||||
SCM_INQ = 0x54
|
||||
SCM_TIMESTAMPING = 0x25
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x36
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3a
|
||||
@@ -352,6 +354,7 @@ const (
|
||||
SO_ERROR = 0x4
|
||||
SO_INCOMING_CPU = 0x31
|
||||
SO_INCOMING_NAPI_ID = 0x38
|
||||
SO_INQ = 0x54
|
||||
SO_KEEPALIVE = 0x9
|
||||
SO_LINGER = 0xd
|
||||
SO_LOCK_FILTER = 0x2c
|
||||
@@ -596,6 +599,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x59)
|
||||
EDOTDOT = syscall.Errno(0x49)
|
||||
EDQUOT = syscall.Errno(0x7a)
|
||||
EFSBADCRC = syscall.Errno(0x4a)
|
||||
EFSCORRUPTED = syscall.Errno(0x75)
|
||||
EHOSTDOWN = syscall.Errno(0x70)
|
||||
EHOSTUNREACH = syscall.Errno(0x71)
|
||||
EHWPOISON = syscall.Errno(0x85)
|
||||
@@ -819,7 +824,7 @@ var errorList = [...]struct {
|
||||
{114, "EALREADY", "operation already in progress"},
|
||||
{115, "EINPROGRESS", "operation now in progress"},
|
||||
{116, "ESTALE", "stale file handle"},
|
||||
{117, "EUCLEAN", "structure needs cleaning"},
|
||||
{117, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{118, "ENOTNAM", "not a XENIX named type file"},
|
||||
{119, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{120, "EISNAM", "is a named type file"},
|
||||
|
||||
+6
-1
@@ -159,6 +159,7 @@ const (
|
||||
NFDBITS = 0x40
|
||||
NLDLY = 0x100
|
||||
NOFLSH = 0x80
|
||||
NS_GET_ID = 0x8008b70d
|
||||
NS_GET_MNTNS_ID = 0x8008b705
|
||||
NS_GET_NSTYPE = 0xb703
|
||||
NS_GET_OWNER_UID = 0xb704
|
||||
@@ -306,6 +307,7 @@ const (
|
||||
RTC_WKALM_SET = 0x4028700f
|
||||
SCM_DEVMEM_DMABUF = 0x4f
|
||||
SCM_DEVMEM_LINEAR = 0x4e
|
||||
SCM_INQ = 0x54
|
||||
SCM_TIMESTAMPING = 0x25
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x36
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3a
|
||||
@@ -353,6 +355,7 @@ const (
|
||||
SO_ERROR = 0x4
|
||||
SO_INCOMING_CPU = 0x31
|
||||
SO_INCOMING_NAPI_ID = 0x38
|
||||
SO_INQ = 0x54
|
||||
SO_KEEPALIVE = 0x9
|
||||
SO_LINGER = 0xd
|
||||
SO_LOCK_FILTER = 0x2c
|
||||
@@ -596,6 +599,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x59)
|
||||
EDOTDOT = syscall.Errno(0x49)
|
||||
EDQUOT = syscall.Errno(0x7a)
|
||||
EFSBADCRC = syscall.Errno(0x4a)
|
||||
EFSCORRUPTED = syscall.Errno(0x75)
|
||||
EHOSTDOWN = syscall.Errno(0x70)
|
||||
EHOSTUNREACH = syscall.Errno(0x71)
|
||||
EHWPOISON = syscall.Errno(0x85)
|
||||
@@ -819,7 +824,7 @@ var errorList = [...]struct {
|
||||
{114, "EALREADY", "operation already in progress"},
|
||||
{115, "EINPROGRESS", "operation now in progress"},
|
||||
{116, "ESTALE", "stale file handle"},
|
||||
{117, "EUCLEAN", "structure needs cleaning"},
|
||||
{117, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{118, "ENOTNAM", "not a XENIX named type file"},
|
||||
{119, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{120, "EISNAM", "is a named type file"},
|
||||
|
||||
+6
-1
@@ -156,6 +156,7 @@ const (
|
||||
NFDBITS = 0x20
|
||||
NLDLY = 0x100
|
||||
NOFLSH = 0x80
|
||||
NS_GET_ID = 0x8008b70d
|
||||
NS_GET_MNTNS_ID = 0x8008b705
|
||||
NS_GET_NSTYPE = 0xb703
|
||||
NS_GET_OWNER_UID = 0xb704
|
||||
@@ -311,6 +312,7 @@ const (
|
||||
RTC_WKALM_SET = 0x4028700f
|
||||
SCM_DEVMEM_DMABUF = 0x4f
|
||||
SCM_DEVMEM_LINEAR = 0x4e
|
||||
SCM_INQ = 0x54
|
||||
SCM_TIMESTAMPING = 0x25
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x36
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3a
|
||||
@@ -358,6 +360,7 @@ const (
|
||||
SO_ERROR = 0x4
|
||||
SO_INCOMING_CPU = 0x31
|
||||
SO_INCOMING_NAPI_ID = 0x38
|
||||
SO_INQ = 0x54
|
||||
SO_KEEPALIVE = 0x9
|
||||
SO_LINGER = 0xd
|
||||
SO_LOCK_FILTER = 0x2c
|
||||
@@ -601,6 +604,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x59)
|
||||
EDOTDOT = syscall.Errno(0x49)
|
||||
EDQUOT = syscall.Errno(0x7a)
|
||||
EFSBADCRC = syscall.Errno(0x4a)
|
||||
EFSCORRUPTED = syscall.Errno(0x75)
|
||||
EHOSTDOWN = syscall.Errno(0x70)
|
||||
EHOSTUNREACH = syscall.Errno(0x71)
|
||||
EHWPOISON = syscall.Errno(0x85)
|
||||
@@ -824,7 +829,7 @@ var errorList = [...]struct {
|
||||
{114, "EALREADY", "operation already in progress"},
|
||||
{115, "EINPROGRESS", "operation now in progress"},
|
||||
{116, "ESTALE", "stale file handle"},
|
||||
{117, "EUCLEAN", "structure needs cleaning"},
|
||||
{117, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{118, "ENOTNAM", "not a XENIX named type file"},
|
||||
{119, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{120, "EISNAM", "is a named type file"},
|
||||
|
||||
+6
-1
@@ -161,6 +161,7 @@ const (
|
||||
NFDBITS = 0x40
|
||||
NLDLY = 0x100
|
||||
NOFLSH = 0x80
|
||||
NS_GET_ID = 0x8008b70d
|
||||
NS_GET_MNTNS_ID = 0x8008b705
|
||||
NS_GET_NSTYPE = 0xb703
|
||||
NS_GET_OWNER_UID = 0xb704
|
||||
@@ -304,6 +305,7 @@ const (
|
||||
RTC_WKALM_SET = 0x4028700f
|
||||
SCM_DEVMEM_DMABUF = 0x4f
|
||||
SCM_DEVMEM_LINEAR = 0x4e
|
||||
SCM_INQ = 0x54
|
||||
SCM_TIMESTAMPING = 0x25
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x36
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3a
|
||||
@@ -351,6 +353,7 @@ const (
|
||||
SO_ERROR = 0x4
|
||||
SO_INCOMING_CPU = 0x31
|
||||
SO_INCOMING_NAPI_ID = 0x38
|
||||
SO_INQ = 0x54
|
||||
SO_KEEPALIVE = 0x9
|
||||
SO_LINGER = 0xd
|
||||
SO_LOCK_FILTER = 0x2c
|
||||
@@ -598,6 +601,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x59)
|
||||
EDOTDOT = syscall.Errno(0x49)
|
||||
EDQUOT = syscall.Errno(0x7a)
|
||||
EFSBADCRC = syscall.Errno(0x4a)
|
||||
EFSCORRUPTED = syscall.Errno(0x75)
|
||||
EHOSTDOWN = syscall.Errno(0x70)
|
||||
EHOSTUNREACH = syscall.Errno(0x71)
|
||||
EHWPOISON = syscall.Errno(0x85)
|
||||
@@ -821,7 +826,7 @@ var errorList = [...]struct {
|
||||
{114, "EALREADY", "operation already in progress"},
|
||||
{115, "EINPROGRESS", "operation now in progress"},
|
||||
{116, "ESTALE", "stale file handle"},
|
||||
{117, "EUCLEAN", "structure needs cleaning"},
|
||||
{117, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{118, "ENOTNAM", "not a XENIX named type file"},
|
||||
{119, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{120, "EISNAM", "is a named type file"},
|
||||
|
||||
+6
-1
@@ -160,6 +160,7 @@ const (
|
||||
NFDBITS = 0x40
|
||||
NLDLY = 0x100
|
||||
NOFLSH = 0x80
|
||||
NS_GET_ID = 0x8008b70d
|
||||
NS_GET_MNTNS_ID = 0x8008b705
|
||||
NS_GET_NSTYPE = 0xb703
|
||||
NS_GET_OWNER_UID = 0xb704
|
||||
@@ -298,6 +299,7 @@ const (
|
||||
RTC_WKALM_SET = 0x4028700f
|
||||
SCM_DEVMEM_DMABUF = 0x4f
|
||||
SCM_DEVMEM_LINEAR = 0x4e
|
||||
SCM_INQ = 0x54
|
||||
SCM_TIMESTAMPING = 0x25
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x36
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3a
|
||||
@@ -345,6 +347,7 @@ const (
|
||||
SO_ERROR = 0x4
|
||||
SO_INCOMING_CPU = 0x31
|
||||
SO_INCOMING_NAPI_ID = 0x38
|
||||
SO_INQ = 0x54
|
||||
SO_KEEPALIVE = 0x9
|
||||
SO_LINGER = 0xd
|
||||
SO_LOCK_FILTER = 0x2c
|
||||
@@ -588,6 +591,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x59)
|
||||
EDOTDOT = syscall.Errno(0x49)
|
||||
EDQUOT = syscall.Errno(0x7a)
|
||||
EFSBADCRC = syscall.Errno(0x4a)
|
||||
EFSCORRUPTED = syscall.Errno(0x75)
|
||||
EHOSTDOWN = syscall.Errno(0x70)
|
||||
EHOSTUNREACH = syscall.Errno(0x71)
|
||||
EHWPOISON = syscall.Errno(0x85)
|
||||
@@ -811,7 +816,7 @@ var errorList = [...]struct {
|
||||
{114, "EALREADY", "operation already in progress"},
|
||||
{115, "EINPROGRESS", "operation now in progress"},
|
||||
{116, "ESTALE", "stale file handle"},
|
||||
{117, "EUCLEAN", "structure needs cleaning"},
|
||||
{117, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{118, "ENOTNAM", "not a XENIX named type file"},
|
||||
{119, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{120, "EISNAM", "is a named type file"},
|
||||
|
||||
+6
-1
@@ -156,6 +156,7 @@ const (
|
||||
NFDBITS = 0x20
|
||||
NLDLY = 0x100
|
||||
NOFLSH = 0x80
|
||||
NS_GET_ID = 0x4008b70d
|
||||
NS_GET_MNTNS_ID = 0x4008b705
|
||||
NS_GET_NSTYPE = 0x2000b703
|
||||
NS_GET_OWNER_UID = 0x2000b704
|
||||
@@ -304,6 +305,7 @@ const (
|
||||
RTC_WKALM_SET = 0x8028700f
|
||||
SCM_DEVMEM_DMABUF = 0x4f
|
||||
SCM_DEVMEM_LINEAR = 0x4e
|
||||
SCM_INQ = 0x54
|
||||
SCM_TIMESTAMPING = 0x25
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x36
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3a
|
||||
@@ -351,6 +353,7 @@ const (
|
||||
SO_ERROR = 0x1007
|
||||
SO_INCOMING_CPU = 0x31
|
||||
SO_INCOMING_NAPI_ID = 0x38
|
||||
SO_INQ = 0x54
|
||||
SO_KEEPALIVE = 0x8
|
||||
SO_LINGER = 0x80
|
||||
SO_LOCK_FILTER = 0x2c
|
||||
@@ -597,6 +600,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x60)
|
||||
EDOTDOT = syscall.Errno(0x49)
|
||||
EDQUOT = syscall.Errno(0x46d)
|
||||
EFSBADCRC = syscall.Errno(0x4d)
|
||||
EFSCORRUPTED = syscall.Errno(0x87)
|
||||
EHOSTDOWN = syscall.Errno(0x93)
|
||||
EHOSTUNREACH = syscall.Errno(0x94)
|
||||
EHWPOISON = syscall.Errno(0xa8)
|
||||
@@ -814,7 +819,7 @@ var errorList = [...]struct {
|
||||
{132, "ENOBUFS", "no buffer space available"},
|
||||
{133, "EISCONN", "transport endpoint is already connected"},
|
||||
{134, "ENOTCONN", "transport endpoint is not connected"},
|
||||
{135, "EUCLEAN", "structure needs cleaning"},
|
||||
{135, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{137, "ENOTNAM", "not a XENIX named type file"},
|
||||
{138, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{139, "EISNAM", "is a named type file"},
|
||||
|
||||
+6
-1
@@ -156,6 +156,7 @@ const (
|
||||
NFDBITS = 0x40
|
||||
NLDLY = 0x100
|
||||
NOFLSH = 0x80
|
||||
NS_GET_ID = 0x4008b70d
|
||||
NS_GET_MNTNS_ID = 0x4008b705
|
||||
NS_GET_NSTYPE = 0x2000b703
|
||||
NS_GET_OWNER_UID = 0x2000b704
|
||||
@@ -304,6 +305,7 @@ const (
|
||||
RTC_WKALM_SET = 0x8028700f
|
||||
SCM_DEVMEM_DMABUF = 0x4f
|
||||
SCM_DEVMEM_LINEAR = 0x4e
|
||||
SCM_INQ = 0x54
|
||||
SCM_TIMESTAMPING = 0x25
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x36
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3a
|
||||
@@ -351,6 +353,7 @@ const (
|
||||
SO_ERROR = 0x1007
|
||||
SO_INCOMING_CPU = 0x31
|
||||
SO_INCOMING_NAPI_ID = 0x38
|
||||
SO_INQ = 0x54
|
||||
SO_KEEPALIVE = 0x8
|
||||
SO_LINGER = 0x80
|
||||
SO_LOCK_FILTER = 0x2c
|
||||
@@ -597,6 +600,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x60)
|
||||
EDOTDOT = syscall.Errno(0x49)
|
||||
EDQUOT = syscall.Errno(0x46d)
|
||||
EFSBADCRC = syscall.Errno(0x4d)
|
||||
EFSCORRUPTED = syscall.Errno(0x87)
|
||||
EHOSTDOWN = syscall.Errno(0x93)
|
||||
EHOSTUNREACH = syscall.Errno(0x94)
|
||||
EHWPOISON = syscall.Errno(0xa8)
|
||||
@@ -814,7 +819,7 @@ var errorList = [...]struct {
|
||||
{132, "ENOBUFS", "no buffer space available"},
|
||||
{133, "EISCONN", "transport endpoint is already connected"},
|
||||
{134, "ENOTCONN", "transport endpoint is not connected"},
|
||||
{135, "EUCLEAN", "structure needs cleaning"},
|
||||
{135, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{137, "ENOTNAM", "not a XENIX named type file"},
|
||||
{138, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{139, "EISNAM", "is a named type file"},
|
||||
|
||||
+6
-1
@@ -156,6 +156,7 @@ const (
|
||||
NFDBITS = 0x40
|
||||
NLDLY = 0x100
|
||||
NOFLSH = 0x80
|
||||
NS_GET_ID = 0x4008b70d
|
||||
NS_GET_MNTNS_ID = 0x4008b705
|
||||
NS_GET_NSTYPE = 0x2000b703
|
||||
NS_GET_OWNER_UID = 0x2000b704
|
||||
@@ -304,6 +305,7 @@ const (
|
||||
RTC_WKALM_SET = 0x8028700f
|
||||
SCM_DEVMEM_DMABUF = 0x4f
|
||||
SCM_DEVMEM_LINEAR = 0x4e
|
||||
SCM_INQ = 0x54
|
||||
SCM_TIMESTAMPING = 0x25
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x36
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3a
|
||||
@@ -351,6 +353,7 @@ const (
|
||||
SO_ERROR = 0x1007
|
||||
SO_INCOMING_CPU = 0x31
|
||||
SO_INCOMING_NAPI_ID = 0x38
|
||||
SO_INQ = 0x54
|
||||
SO_KEEPALIVE = 0x8
|
||||
SO_LINGER = 0x80
|
||||
SO_LOCK_FILTER = 0x2c
|
||||
@@ -597,6 +600,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x60)
|
||||
EDOTDOT = syscall.Errno(0x49)
|
||||
EDQUOT = syscall.Errno(0x46d)
|
||||
EFSBADCRC = syscall.Errno(0x4d)
|
||||
EFSCORRUPTED = syscall.Errno(0x87)
|
||||
EHOSTDOWN = syscall.Errno(0x93)
|
||||
EHOSTUNREACH = syscall.Errno(0x94)
|
||||
EHWPOISON = syscall.Errno(0xa8)
|
||||
@@ -814,7 +819,7 @@ var errorList = [...]struct {
|
||||
{132, "ENOBUFS", "no buffer space available"},
|
||||
{133, "EISCONN", "transport endpoint is already connected"},
|
||||
{134, "ENOTCONN", "transport endpoint is not connected"},
|
||||
{135, "EUCLEAN", "structure needs cleaning"},
|
||||
{135, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{137, "ENOTNAM", "not a XENIX named type file"},
|
||||
{138, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{139, "EISNAM", "is a named type file"},
|
||||
|
||||
+6
-1
@@ -156,6 +156,7 @@ const (
|
||||
NFDBITS = 0x20
|
||||
NLDLY = 0x100
|
||||
NOFLSH = 0x80
|
||||
NS_GET_ID = 0x4008b70d
|
||||
NS_GET_MNTNS_ID = 0x4008b705
|
||||
NS_GET_NSTYPE = 0x2000b703
|
||||
NS_GET_OWNER_UID = 0x2000b704
|
||||
@@ -304,6 +305,7 @@ const (
|
||||
RTC_WKALM_SET = 0x8028700f
|
||||
SCM_DEVMEM_DMABUF = 0x4f
|
||||
SCM_DEVMEM_LINEAR = 0x4e
|
||||
SCM_INQ = 0x54
|
||||
SCM_TIMESTAMPING = 0x25
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x36
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3a
|
||||
@@ -351,6 +353,7 @@ const (
|
||||
SO_ERROR = 0x1007
|
||||
SO_INCOMING_CPU = 0x31
|
||||
SO_INCOMING_NAPI_ID = 0x38
|
||||
SO_INQ = 0x54
|
||||
SO_KEEPALIVE = 0x8
|
||||
SO_LINGER = 0x80
|
||||
SO_LOCK_FILTER = 0x2c
|
||||
@@ -597,6 +600,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x60)
|
||||
EDOTDOT = syscall.Errno(0x49)
|
||||
EDQUOT = syscall.Errno(0x46d)
|
||||
EFSBADCRC = syscall.Errno(0x4d)
|
||||
EFSCORRUPTED = syscall.Errno(0x87)
|
||||
EHOSTDOWN = syscall.Errno(0x93)
|
||||
EHOSTUNREACH = syscall.Errno(0x94)
|
||||
EHWPOISON = syscall.Errno(0xa8)
|
||||
@@ -814,7 +819,7 @@ var errorList = [...]struct {
|
||||
{132, "ENOBUFS", "no buffer space available"},
|
||||
{133, "EISCONN", "transport endpoint is already connected"},
|
||||
{134, "ENOTCONN", "transport endpoint is not connected"},
|
||||
{135, "EUCLEAN", "structure needs cleaning"},
|
||||
{135, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{137, "ENOTNAM", "not a XENIX named type file"},
|
||||
{138, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{139, "EISNAM", "is a named type file"},
|
||||
|
||||
+6
-1
@@ -158,6 +158,7 @@ const (
|
||||
NL3 = 0x300
|
||||
NLDLY = 0x300
|
||||
NOFLSH = 0x80000000
|
||||
NS_GET_ID = 0x4008b70d
|
||||
NS_GET_MNTNS_ID = 0x4008b705
|
||||
NS_GET_NSTYPE = 0x2000b703
|
||||
NS_GET_OWNER_UID = 0x2000b704
|
||||
@@ -359,6 +360,7 @@ const (
|
||||
RTC_WKALM_SET = 0x8028700f
|
||||
SCM_DEVMEM_DMABUF = 0x4f
|
||||
SCM_DEVMEM_LINEAR = 0x4e
|
||||
SCM_INQ = 0x54
|
||||
SCM_TIMESTAMPING = 0x25
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x36
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3a
|
||||
@@ -406,6 +408,7 @@ const (
|
||||
SO_ERROR = 0x4
|
||||
SO_INCOMING_CPU = 0x31
|
||||
SO_INCOMING_NAPI_ID = 0x38
|
||||
SO_INQ = 0x54
|
||||
SO_KEEPALIVE = 0x9
|
||||
SO_LINGER = 0xd
|
||||
SO_LOCK_FILTER = 0x2c
|
||||
@@ -653,6 +656,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x59)
|
||||
EDOTDOT = syscall.Errno(0x49)
|
||||
EDQUOT = syscall.Errno(0x7a)
|
||||
EFSBADCRC = syscall.Errno(0x4a)
|
||||
EFSCORRUPTED = syscall.Errno(0x75)
|
||||
EHOSTDOWN = syscall.Errno(0x70)
|
||||
EHOSTUNREACH = syscall.Errno(0x71)
|
||||
EHWPOISON = syscall.Errno(0x85)
|
||||
@@ -877,7 +882,7 @@ var errorList = [...]struct {
|
||||
{114, "EALREADY", "operation already in progress"},
|
||||
{115, "EINPROGRESS", "operation now in progress"},
|
||||
{116, "ESTALE", "stale file handle"},
|
||||
{117, "EUCLEAN", "structure needs cleaning"},
|
||||
{117, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{118, "ENOTNAM", "not a XENIX named type file"},
|
||||
{119, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{120, "EISNAM", "is a named type file"},
|
||||
|
||||
+6
-1
@@ -158,6 +158,7 @@ const (
|
||||
NL3 = 0x300
|
||||
NLDLY = 0x300
|
||||
NOFLSH = 0x80000000
|
||||
NS_GET_ID = 0x4008b70d
|
||||
NS_GET_MNTNS_ID = 0x4008b705
|
||||
NS_GET_NSTYPE = 0x2000b703
|
||||
NS_GET_OWNER_UID = 0x2000b704
|
||||
@@ -363,6 +364,7 @@ const (
|
||||
RTC_WKALM_SET = 0x8028700f
|
||||
SCM_DEVMEM_DMABUF = 0x4f
|
||||
SCM_DEVMEM_LINEAR = 0x4e
|
||||
SCM_INQ = 0x54
|
||||
SCM_TIMESTAMPING = 0x25
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x36
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3a
|
||||
@@ -410,6 +412,7 @@ const (
|
||||
SO_ERROR = 0x4
|
||||
SO_INCOMING_CPU = 0x31
|
||||
SO_INCOMING_NAPI_ID = 0x38
|
||||
SO_INQ = 0x54
|
||||
SO_KEEPALIVE = 0x9
|
||||
SO_LINGER = 0xd
|
||||
SO_LOCK_FILTER = 0x2c
|
||||
@@ -657,6 +660,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x59)
|
||||
EDOTDOT = syscall.Errno(0x49)
|
||||
EDQUOT = syscall.Errno(0x7a)
|
||||
EFSBADCRC = syscall.Errno(0x4a)
|
||||
EFSCORRUPTED = syscall.Errno(0x75)
|
||||
EHOSTDOWN = syscall.Errno(0x70)
|
||||
EHOSTUNREACH = syscall.Errno(0x71)
|
||||
EHWPOISON = syscall.Errno(0x85)
|
||||
@@ -881,7 +886,7 @@ var errorList = [...]struct {
|
||||
{114, "EALREADY", "operation already in progress"},
|
||||
{115, "EINPROGRESS", "operation now in progress"},
|
||||
{116, "ESTALE", "stale file handle"},
|
||||
{117, "EUCLEAN", "structure needs cleaning"},
|
||||
{117, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{118, "ENOTNAM", "not a XENIX named type file"},
|
||||
{119, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{120, "EISNAM", "is a named type file"},
|
||||
|
||||
+6
-1
@@ -158,6 +158,7 @@ const (
|
||||
NL3 = 0x300
|
||||
NLDLY = 0x300
|
||||
NOFLSH = 0x80000000
|
||||
NS_GET_ID = 0x4008b70d
|
||||
NS_GET_MNTNS_ID = 0x4008b705
|
||||
NS_GET_NSTYPE = 0x2000b703
|
||||
NS_GET_OWNER_UID = 0x2000b704
|
||||
@@ -363,6 +364,7 @@ const (
|
||||
RTC_WKALM_SET = 0x8028700f
|
||||
SCM_DEVMEM_DMABUF = 0x4f
|
||||
SCM_DEVMEM_LINEAR = 0x4e
|
||||
SCM_INQ = 0x54
|
||||
SCM_TIMESTAMPING = 0x25
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x36
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3a
|
||||
@@ -410,6 +412,7 @@ const (
|
||||
SO_ERROR = 0x4
|
||||
SO_INCOMING_CPU = 0x31
|
||||
SO_INCOMING_NAPI_ID = 0x38
|
||||
SO_INQ = 0x54
|
||||
SO_KEEPALIVE = 0x9
|
||||
SO_LINGER = 0xd
|
||||
SO_LOCK_FILTER = 0x2c
|
||||
@@ -657,6 +660,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x59)
|
||||
EDOTDOT = syscall.Errno(0x49)
|
||||
EDQUOT = syscall.Errno(0x7a)
|
||||
EFSBADCRC = syscall.Errno(0x4a)
|
||||
EFSCORRUPTED = syscall.Errno(0x75)
|
||||
EHOSTDOWN = syscall.Errno(0x70)
|
||||
EHOSTUNREACH = syscall.Errno(0x71)
|
||||
EHWPOISON = syscall.Errno(0x85)
|
||||
@@ -881,7 +886,7 @@ var errorList = [...]struct {
|
||||
{114, "EALREADY", "operation already in progress"},
|
||||
{115, "EINPROGRESS", "operation now in progress"},
|
||||
{116, "ESTALE", "stale file handle"},
|
||||
{117, "EUCLEAN", "structure needs cleaning"},
|
||||
{117, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{118, "ENOTNAM", "not a XENIX named type file"},
|
||||
{119, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{120, "EISNAM", "is a named type file"},
|
||||
|
||||
+566
-548
File diff suppressed because it is too large
Load Diff
+6
-1
@@ -156,6 +156,7 @@ const (
|
||||
NFDBITS = 0x40
|
||||
NLDLY = 0x100
|
||||
NOFLSH = 0x80
|
||||
NS_GET_ID = 0x8008b70d
|
||||
NS_GET_MNTNS_ID = 0x8008b705
|
||||
NS_GET_NSTYPE = 0xb703
|
||||
NS_GET_OWNER_UID = 0xb704
|
||||
@@ -367,6 +368,7 @@ const (
|
||||
RTC_WKALM_SET = 0x4028700f
|
||||
SCM_DEVMEM_DMABUF = 0x4f
|
||||
SCM_DEVMEM_LINEAR = 0x4e
|
||||
SCM_INQ = 0x54
|
||||
SCM_TIMESTAMPING = 0x25
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x36
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3a
|
||||
@@ -414,6 +416,7 @@ const (
|
||||
SO_ERROR = 0x4
|
||||
SO_INCOMING_CPU = 0x31
|
||||
SO_INCOMING_NAPI_ID = 0x38
|
||||
SO_INQ = 0x54
|
||||
SO_KEEPALIVE = 0x9
|
||||
SO_LINGER = 0xd
|
||||
SO_LOCK_FILTER = 0x2c
|
||||
@@ -657,6 +660,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x59)
|
||||
EDOTDOT = syscall.Errno(0x49)
|
||||
EDQUOT = syscall.Errno(0x7a)
|
||||
EFSBADCRC = syscall.Errno(0x4a)
|
||||
EFSCORRUPTED = syscall.Errno(0x75)
|
||||
EHOSTDOWN = syscall.Errno(0x70)
|
||||
EHOSTUNREACH = syscall.Errno(0x71)
|
||||
EHWPOISON = syscall.Errno(0x85)
|
||||
@@ -880,7 +885,7 @@ var errorList = [...]struct {
|
||||
{114, "EALREADY", "operation already in progress"},
|
||||
{115, "EINPROGRESS", "operation now in progress"},
|
||||
{116, "ESTALE", "stale file handle"},
|
||||
{117, "EUCLEAN", "structure needs cleaning"},
|
||||
{117, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{118, "ENOTNAM", "not a XENIX named type file"},
|
||||
{119, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{120, "EISNAM", "is a named type file"},
|
||||
|
||||
+6
-1
@@ -161,6 +161,7 @@ const (
|
||||
NFDBITS = 0x40
|
||||
NLDLY = 0x100
|
||||
NOFLSH = 0x80
|
||||
NS_GET_ID = 0x4008b70d
|
||||
NS_GET_MNTNS_ID = 0x4008b705
|
||||
NS_GET_NSTYPE = 0x2000b703
|
||||
NS_GET_OWNER_UID = 0x2000b704
|
||||
@@ -358,6 +359,7 @@ const (
|
||||
RTC_WKALM_SET = 0x8028700f
|
||||
SCM_DEVMEM_DMABUF = 0x58
|
||||
SCM_DEVMEM_LINEAR = 0x57
|
||||
SCM_INQ = 0x5d
|
||||
SCM_TIMESTAMPING = 0x23
|
||||
SCM_TIMESTAMPING_OPT_STATS = 0x38
|
||||
SCM_TIMESTAMPING_PKTINFO = 0x3c
|
||||
@@ -453,6 +455,7 @@ const (
|
||||
SO_ERROR = 0x1007
|
||||
SO_INCOMING_CPU = 0x33
|
||||
SO_INCOMING_NAPI_ID = 0x3a
|
||||
SO_INQ = 0x5d
|
||||
SO_KEEPALIVE = 0x8
|
||||
SO_LINGER = 0x80
|
||||
SO_LOCK_FILTER = 0x28
|
||||
@@ -694,6 +697,8 @@ const (
|
||||
EDESTADDRREQ = syscall.Errno(0x27)
|
||||
EDOTDOT = syscall.Errno(0x58)
|
||||
EDQUOT = syscall.Errno(0x45)
|
||||
EFSBADCRC = syscall.Errno(0x4c)
|
||||
EFSCORRUPTED = syscall.Errno(0x75)
|
||||
EHOSTDOWN = syscall.Errno(0x40)
|
||||
EHOSTUNREACH = syscall.Errno(0x41)
|
||||
EHWPOISON = syscall.Errno(0x87)
|
||||
@@ -921,7 +926,7 @@ var errorList = [...]struct {
|
||||
{114, "ELIBACC", "can not access a needed shared library"},
|
||||
{115, "ENOTUNIQ", "name not unique on network"},
|
||||
{116, "ERESTART", "interrupted system call should be restarted"},
|
||||
{117, "EUCLEAN", "structure needs cleaning"},
|
||||
{117, "EFSCORRUPTED", "structure needs cleaning"},
|
||||
{118, "ENOTNAM", "not a XENIX named type file"},
|
||||
{119, "ENAVAIL", "no XENIX semaphores available"},
|
||||
{120, "EISNAM", "is a named type file"},
|
||||
|
||||
+6
-6
@@ -1785,7 +1785,7 @@ func writev(fd int, iovs []Iovec) (n int, err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func preadv(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr) (n int, err error) {
|
||||
func preadvSyscall(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovs[0])
|
||||
@@ -1802,7 +1802,7 @@ func preadv(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr) (n int, err er
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pwritev(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr) (n int, err error) {
|
||||
func pwritevSyscall(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovs[0])
|
||||
@@ -1819,7 +1819,7 @@ func pwritev(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr) (n int, err e
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func preadv2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) {
|
||||
func preadv2Syscall(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovs[0])
|
||||
@@ -1836,7 +1836,7 @@ func preadv2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pwritev2(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) {
|
||||
func pwritev2Syscall(fd int, iovs []Iovec, offs_l uintptr, offs_h uintptr, flags int) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovs[0])
|
||||
@@ -2241,8 +2241,8 @@ func Mseal(b []byte, flags uint) (err error) {
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func setMemPolicy(mode int, mask *CPUSet, size int) (err error) {
|
||||
_, _, e1 := Syscall(SYS_SET_MEMPOLICY, uintptr(mode), uintptr(unsafe.Pointer(mask)), uintptr(size))
|
||||
func setMemPolicy(mode int, mask unsafe.Pointer, size uintptr) (err error) {
|
||||
_, _, e1 := Syscall(SYS_SET_MEMPOLICY, uintptr(mode), uintptr(mask), uintptr(size))
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
|
||||
+84
@@ -1633,6 +1633,90 @@ var libc_pwrite_trampoline_addr uintptr
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func readv(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_readv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_readv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_readv readv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func writev(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_writev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_writev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_writev writev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_preadv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), uintptr(offset>>32), 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_preadv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_preadv preadv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_pwritev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), uintptr(offset>>32), 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_pwritev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_pwritev pwritev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
||||
+20
@@ -498,6 +498,26 @@ TEXT libc_pwrite_trampoline<>(SB),NOSPLIT,$0-0
|
||||
GLOBL ·libc_pwrite_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_pwrite_trampoline_addr(SB)/4, $libc_pwrite_trampoline<>(SB)
|
||||
|
||||
TEXT libc_readv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_readv(SB)
|
||||
GLOBL ·libc_readv_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_readv_trampoline_addr(SB)/4, $libc_readv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_writev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_writev(SB)
|
||||
GLOBL ·libc_writev_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_writev_trampoline_addr(SB)/4, $libc_writev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_preadv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_preadv(SB)
|
||||
GLOBL ·libc_preadv_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_preadv_trampoline_addr(SB)/4, $libc_preadv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_pwritev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_pwritev(SB)
|
||||
GLOBL ·libc_pwritev_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_pwritev_trampoline_addr(SB)/4, $libc_pwritev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_read_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_read(SB)
|
||||
GLOBL ·libc_read_trampoline_addr(SB), RODATA, $4
|
||||
|
||||
+84
@@ -1633,6 +1633,90 @@ var libc_pwrite_trampoline_addr uintptr
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func readv(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_readv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_readv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_readv readv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func writev(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_writev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_writev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_writev writev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_preadv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_preadv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_preadv preadv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_pwritev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_pwritev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_pwritev pwritev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
||||
+20
@@ -498,6 +498,26 @@ TEXT libc_pwrite_trampoline<>(SB),NOSPLIT,$0-0
|
||||
GLOBL ·libc_pwrite_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_pwrite_trampoline_addr(SB)/8, $libc_pwrite_trampoline<>(SB)
|
||||
|
||||
TEXT libc_readv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_readv(SB)
|
||||
GLOBL ·libc_readv_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_readv_trampoline_addr(SB)/8, $libc_readv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_writev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_writev(SB)
|
||||
GLOBL ·libc_writev_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_writev_trampoline_addr(SB)/8, $libc_writev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_preadv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_preadv(SB)
|
||||
GLOBL ·libc_preadv_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_preadv_trampoline_addr(SB)/8, $libc_preadv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_pwritev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_pwritev(SB)
|
||||
GLOBL ·libc_pwritev_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_pwritev_trampoline_addr(SB)/8, $libc_pwritev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_read_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_read(SB)
|
||||
GLOBL ·libc_read_trampoline_addr(SB), RODATA, $8
|
||||
|
||||
+84
@@ -1633,6 +1633,90 @@ var libc_pwrite_trampoline_addr uintptr
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func readv(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_readv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_readv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_readv readv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func writev(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_writev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_writev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_writev writev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_preadv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), 0, uintptr(offset), uintptr(offset>>32))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_preadv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_preadv preadv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_pwritev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), 0, uintptr(offset), uintptr(offset>>32))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_pwritev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_pwritev pwritev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
||||
+20
@@ -498,6 +498,26 @@ TEXT libc_pwrite_trampoline<>(SB),NOSPLIT,$0-0
|
||||
GLOBL ·libc_pwrite_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_pwrite_trampoline_addr(SB)/4, $libc_pwrite_trampoline<>(SB)
|
||||
|
||||
TEXT libc_readv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_readv(SB)
|
||||
GLOBL ·libc_readv_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_readv_trampoline_addr(SB)/4, $libc_readv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_writev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_writev(SB)
|
||||
GLOBL ·libc_writev_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_writev_trampoline_addr(SB)/4, $libc_writev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_preadv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_preadv(SB)
|
||||
GLOBL ·libc_preadv_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_preadv_trampoline_addr(SB)/4, $libc_preadv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_pwritev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_pwritev(SB)
|
||||
GLOBL ·libc_pwritev_trampoline_addr(SB), RODATA, $4
|
||||
DATA ·libc_pwritev_trampoline_addr(SB)/4, $libc_pwritev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_read_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_read(SB)
|
||||
GLOBL ·libc_read_trampoline_addr(SB), RODATA, $4
|
||||
|
||||
+84
@@ -1633,6 +1633,90 @@ var libc_pwrite_trampoline_addr uintptr
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func readv(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_readv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_readv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_readv readv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func writev(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_writev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_writev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_writev writev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_preadv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_preadv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_preadv preadv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_pwritev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_pwritev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_pwritev pwritev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
||||
+20
@@ -498,6 +498,26 @@ TEXT libc_pwrite_trampoline<>(SB),NOSPLIT,$0-0
|
||||
GLOBL ·libc_pwrite_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_pwrite_trampoline_addr(SB)/8, $libc_pwrite_trampoline<>(SB)
|
||||
|
||||
TEXT libc_readv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_readv(SB)
|
||||
GLOBL ·libc_readv_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_readv_trampoline_addr(SB)/8, $libc_readv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_writev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_writev(SB)
|
||||
GLOBL ·libc_writev_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_writev_trampoline_addr(SB)/8, $libc_writev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_preadv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_preadv(SB)
|
||||
GLOBL ·libc_preadv_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_preadv_trampoline_addr(SB)/8, $libc_preadv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_pwritev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_pwritev(SB)
|
||||
GLOBL ·libc_pwritev_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_pwritev_trampoline_addr(SB)/8, $libc_pwritev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_read_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_read(SB)
|
||||
GLOBL ·libc_read_trampoline_addr(SB), RODATA, $8
|
||||
|
||||
+84
@@ -1633,6 +1633,90 @@ var libc_pwrite_trampoline_addr uintptr
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func readv(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_readv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_readv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_readv readv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func writev(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_writev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_writev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_writev writev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_preadv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_preadv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_preadv preadv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_pwritev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_pwritev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_pwritev pwritev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
||||
+20
@@ -498,6 +498,26 @@ TEXT libc_pwrite_trampoline<>(SB),NOSPLIT,$0-0
|
||||
GLOBL ·libc_pwrite_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_pwrite_trampoline_addr(SB)/8, $libc_pwrite_trampoline<>(SB)
|
||||
|
||||
TEXT libc_readv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_readv(SB)
|
||||
GLOBL ·libc_readv_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_readv_trampoline_addr(SB)/8, $libc_readv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_writev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_writev(SB)
|
||||
GLOBL ·libc_writev_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_writev_trampoline_addr(SB)/8, $libc_writev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_preadv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_preadv(SB)
|
||||
GLOBL ·libc_preadv_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_preadv_trampoline_addr(SB)/8, $libc_preadv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_pwritev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_pwritev(SB)
|
||||
GLOBL ·libc_pwritev_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_pwritev_trampoline_addr(SB)/8, $libc_pwritev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_read_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_read(SB)
|
||||
GLOBL ·libc_read_trampoline_addr(SB), RODATA, $8
|
||||
|
||||
+84
@@ -1633,6 +1633,90 @@ var libc_pwrite_trampoline_addr uintptr
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func readv(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_readv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_readv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_readv readv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func writev(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_writev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_writev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_writev writev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_preadv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_preadv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_preadv preadv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_pwritev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_pwritev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_pwritev pwritev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
||||
+24
@@ -597,6 +597,30 @@ TEXT libc_pwrite_trampoline<>(SB),NOSPLIT,$0-0
|
||||
GLOBL ·libc_pwrite_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_pwrite_trampoline_addr(SB)/8, $libc_pwrite_trampoline<>(SB)
|
||||
|
||||
TEXT libc_readv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
CALL libc_readv(SB)
|
||||
RET
|
||||
GLOBL ·libc_readv_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_readv_trampoline_addr(SB)/8, $libc_readv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_writev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
CALL libc_writev(SB)
|
||||
RET
|
||||
GLOBL ·libc_writev_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_writev_trampoline_addr(SB)/8, $libc_writev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_preadv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
CALL libc_preadv(SB)
|
||||
RET
|
||||
GLOBL ·libc_preadv_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_preadv_trampoline_addr(SB)/8, $libc_preadv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_pwritev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
CALL libc_pwritev(SB)
|
||||
RET
|
||||
GLOBL ·libc_pwritev_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_pwritev_trampoline_addr(SB)/8, $libc_pwritev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_read_trampoline<>(SB),NOSPLIT,$0-0
|
||||
CALL libc_read(SB)
|
||||
RET
|
||||
|
||||
+84
@@ -1633,6 +1633,90 @@ var libc_pwrite_trampoline_addr uintptr
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func readv(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_readv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_readv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_readv readv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func writev(fd int, iovecs []Iovec) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall(libc_writev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_writev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_writev writev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_preadv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_preadv_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_preadv preadv "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(iovecs) > 0 {
|
||||
_p0 = unsafe.Pointer(&iovecs[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := syscall_syscall6(libc_pwritev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_pwritev_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_pwritev pwritev "libc.so"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
||||
+20
@@ -498,6 +498,26 @@ TEXT libc_pwrite_trampoline<>(SB),NOSPLIT,$0-0
|
||||
GLOBL ·libc_pwrite_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_pwrite_trampoline_addr(SB)/8, $libc_pwrite_trampoline<>(SB)
|
||||
|
||||
TEXT libc_readv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_readv(SB)
|
||||
GLOBL ·libc_readv_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_readv_trampoline_addr(SB)/8, $libc_readv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_writev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_writev(SB)
|
||||
GLOBL ·libc_writev_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_writev_trampoline_addr(SB)/8, $libc_writev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_preadv_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_preadv(SB)
|
||||
GLOBL ·libc_preadv_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_preadv_trampoline_addr(SB)/8, $libc_preadv_trampoline<>(SB)
|
||||
|
||||
TEXT libc_pwritev_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_pwritev(SB)
|
||||
GLOBL ·libc_pwritev_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_pwritev_trampoline_addr(SB)/8, $libc_pwritev_trampoline<>(SB)
|
||||
|
||||
TEXT libc_read_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_read(SB)
|
||||
GLOBL ·libc_read_trampoline_addr(SB), RODATA, $8
|
||||
|
||||
+4
@@ -463,4 +463,8 @@ const (
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
SYS_FILE_GETATTR = 468
|
||||
SYS_FILE_SETATTR = 469
|
||||
SYS_LISTNS = 470
|
||||
SYS_RSEQ_SLICE_YIELD = 471
|
||||
)
|
||||
|
||||
+5
@@ -342,6 +342,7 @@ const (
|
||||
SYS_IO_PGETEVENTS = 333
|
||||
SYS_RSEQ = 334
|
||||
SYS_URETPROBE = 335
|
||||
SYS_UPROBE = 336
|
||||
SYS_PIDFD_SEND_SIGNAL = 424
|
||||
SYS_IO_URING_SETUP = 425
|
||||
SYS_IO_URING_ENTER = 426
|
||||
@@ -386,4 +387,8 @@ const (
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
SYS_FILE_GETATTR = 468
|
||||
SYS_FILE_SETATTR = 469
|
||||
SYS_LISTNS = 470
|
||||
SYS_RSEQ_SLICE_YIELD = 471
|
||||
)
|
||||
|
||||
+4
@@ -427,4 +427,8 @@ const (
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
SYS_FILE_GETATTR = 468
|
||||
SYS_FILE_SETATTR = 469
|
||||
SYS_LISTNS = 470
|
||||
SYS_RSEQ_SLICE_YIELD = 471
|
||||
)
|
||||
|
||||
+4
@@ -330,4 +330,8 @@ const (
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
SYS_FILE_GETATTR = 468
|
||||
SYS_FILE_SETATTR = 469
|
||||
SYS_LISTNS = 470
|
||||
SYS_RSEQ_SLICE_YIELD = 471
|
||||
)
|
||||
|
||||
+5
@@ -306,6 +306,7 @@ const (
|
||||
SYS_LANDLOCK_CREATE_RULESET = 444
|
||||
SYS_LANDLOCK_ADD_RULE = 445
|
||||
SYS_LANDLOCK_RESTRICT_SELF = 446
|
||||
SYS_MEMFD_SECRET = 447
|
||||
SYS_PROCESS_MRELEASE = 448
|
||||
SYS_FUTEX_WAITV = 449
|
||||
SYS_SET_MEMPOLICY_HOME_NODE = 450
|
||||
@@ -326,4 +327,8 @@ const (
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
SYS_FILE_GETATTR = 468
|
||||
SYS_FILE_SETATTR = 469
|
||||
SYS_LISTNS = 470
|
||||
SYS_RSEQ_SLICE_YIELD = 471
|
||||
)
|
||||
|
||||
+4
@@ -447,4 +447,8 @@ const (
|
||||
SYS_LISTXATTRAT = 4465
|
||||
SYS_REMOVEXATTRAT = 4466
|
||||
SYS_OPEN_TREE_ATTR = 4467
|
||||
SYS_FILE_GETATTR = 4468
|
||||
SYS_FILE_SETATTR = 4469
|
||||
SYS_LISTNS = 4470
|
||||
SYS_RSEQ_SLICE_YIELD = 4471
|
||||
)
|
||||
|
||||
+4
@@ -377,4 +377,8 @@ const (
|
||||
SYS_LISTXATTRAT = 5465
|
||||
SYS_REMOVEXATTRAT = 5466
|
||||
SYS_OPEN_TREE_ATTR = 5467
|
||||
SYS_FILE_GETATTR = 5468
|
||||
SYS_FILE_SETATTR = 5469
|
||||
SYS_LISTNS = 5470
|
||||
SYS_RSEQ_SLICE_YIELD = 5471
|
||||
)
|
||||
|
||||
+4
@@ -377,4 +377,8 @@ const (
|
||||
SYS_LISTXATTRAT = 5465
|
||||
SYS_REMOVEXATTRAT = 5466
|
||||
SYS_OPEN_TREE_ATTR = 5467
|
||||
SYS_FILE_GETATTR = 5468
|
||||
SYS_FILE_SETATTR = 5469
|
||||
SYS_LISTNS = 5470
|
||||
SYS_RSEQ_SLICE_YIELD = 5471
|
||||
)
|
||||
|
||||
+4
@@ -447,4 +447,8 @@ const (
|
||||
SYS_LISTXATTRAT = 4465
|
||||
SYS_REMOVEXATTRAT = 4466
|
||||
SYS_OPEN_TREE_ATTR = 4467
|
||||
SYS_FILE_GETATTR = 4468
|
||||
SYS_FILE_SETATTR = 4469
|
||||
SYS_LISTNS = 4470
|
||||
SYS_RSEQ_SLICE_YIELD = 4471
|
||||
)
|
||||
|
||||
+4
@@ -454,4 +454,8 @@ const (
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
SYS_FILE_GETATTR = 468
|
||||
SYS_FILE_SETATTR = 469
|
||||
SYS_LISTNS = 470
|
||||
SYS_RSEQ_SLICE_YIELD = 471
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user