mirror of https://github.com/docker/cli.git
Merge pull request #3774 from thaJeztah/bump_x_net
vendor: golang.org/x/net v0.0.0-20220906165146-f3363e06e74c
This commit is contained in:
commit
2ae9e21c1a
|
@ -37,7 +37,7 @@ require (
|
||||||
github.com/tonistiigi/go-rosetta v0.0.0-20200727161949-f79598599c5d
|
github.com/tonistiigi/go-rosetta v0.0.0-20200727161949-f79598599c5d
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0
|
github.com/xeipuuv/gojsonschema v1.2.0
|
||||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64
|
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64
|
||||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||||
golang.org/x/text v0.3.7
|
golang.org/x/text v0.3.7
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
gotest.tools/v3 v3.1.0
|
gotest.tools/v3 v3.1.0
|
||||||
|
@ -68,7 +68,7 @@ require (
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
go.etcd.io/etcd/raft/v3 v3.5.2 // indirect
|
go.etcd.io/etcd/raft/v3 v3.5.2 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
|
||||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f // indirect
|
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c // indirect; updated for CVE-2022-27664
|
||||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
|
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
|
||||||
|
|
|
@ -496,8 +496,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM=
|
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c h1:yKufUcDwucU5urd+50/Opbt4AYpqthk7wHpHok8f1lo=
|
||||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -569,8 +569,8 @@ golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27sp
|
||||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
# This source code refers to The Go Authors for copyright purposes.
|
|
||||||
# The master list of authors is in the main Go distribution,
|
|
||||||
# visible at http://tip.golang.org/AUTHORS.
|
|
|
@ -1,3 +0,0 @@
|
||||||
# This source code was written by the Go contributors.
|
|
||||||
# The master list of contributors is in the main Go distribution,
|
|
||||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
|
|
@ -173,11 +173,13 @@ func tokenEqual(t1, t2 string) bool {
|
||||||
|
|
||||||
// isLWS reports whether b is linear white space, according
|
// isLWS reports whether b is linear white space, according
|
||||||
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
||||||
|
//
|
||||||
// LWS = [CRLF] 1*( SP | HT )
|
// LWS = [CRLF] 1*( SP | HT )
|
||||||
func isLWS(b byte) bool { return b == ' ' || b == '\t' }
|
func isLWS(b byte) bool { return b == ' ' || b == '\t' }
|
||||||
|
|
||||||
// isCTL reports whether b is a control byte, according
|
// isCTL reports whether b is a control byte, according
|
||||||
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
||||||
|
//
|
||||||
// CTL = <any US-ASCII control character
|
// CTL = <any US-ASCII control character
|
||||||
// (octets 0 - 31) and DEL (127)>
|
// (octets 0 - 31) and DEL (127)>
|
||||||
func isCTL(b byte) bool {
|
func isCTL(b byte) bool {
|
||||||
|
@ -190,6 +192,7 @@ func isCTL(b byte) bool {
|
||||||
// letters are not allowed.
|
// letters are not allowed.
|
||||||
//
|
//
|
||||||
// RFC 7230 says:
|
// RFC 7230 says:
|
||||||
|
//
|
||||||
// header-field = field-name ":" OWS field-value OWS
|
// header-field = field-name ":" OWS field-value OWS
|
||||||
// field-name = token
|
// field-name = token
|
||||||
// token = 1*tchar
|
// token = 1*tchar
|
||||||
|
@ -282,6 +285,7 @@ var validHostByte = [256]bool{
|
||||||
// (octets 0 - 31) and DEL (127)>
|
// (octets 0 - 31) and DEL (127)>
|
||||||
//
|
//
|
||||||
// RFC 7230 says:
|
// RFC 7230 says:
|
||||||
|
//
|
||||||
// field-value = *( field-content / obs-fold )
|
// field-value = *( field-content / obs-fold )
|
||||||
// obj-fold = N/A to http2, and deprecated
|
// obj-fold = N/A to http2, and deprecated
|
||||||
// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
||||||
|
|
|
@ -139,7 +139,6 @@ func (p *clientConnPool) getStartDialLocked(ctx context.Context, addr string) *d
|
||||||
func (c *dialCall) dial(ctx context.Context, addr string) {
|
func (c *dialCall) dial(ctx context.Context, addr string) {
|
||||||
const singleUse = false // shared conn
|
const singleUse = false // shared conn
|
||||||
c.res, c.err = c.p.t.dialClientConn(ctx, addr, singleUse)
|
c.res, c.err = c.p.t.dialClientConn(ctx, addr, singleUse)
|
||||||
close(c.done)
|
|
||||||
|
|
||||||
c.p.mu.Lock()
|
c.p.mu.Lock()
|
||||||
delete(c.p.dialing, addr)
|
delete(c.p.dialing, addr)
|
||||||
|
@ -147,6 +146,8 @@ func (c *dialCall) dial(ctx context.Context, addr string) {
|
||||||
c.p.addConnLocked(addr, c.res)
|
c.p.addConnLocked(addr, c.res)
|
||||||
}
|
}
|
||||||
c.p.mu.Unlock()
|
c.p.mu.Unlock()
|
||||||
|
|
||||||
|
close(c.done)
|
||||||
}
|
}
|
||||||
|
|
||||||
// addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't
|
// addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't
|
||||||
|
|
|
@ -136,7 +136,7 @@ func (e headerFieldNameError) Error() string {
|
||||||
type headerFieldValueError string
|
type headerFieldValueError string
|
||||||
|
|
||||||
func (e headerFieldValueError) Error() string {
|
func (e headerFieldValueError) Error() string {
|
||||||
return fmt.Sprintf("invalid header field value %q", string(e))
|
return fmt.Sprintf("invalid header field value for %q", string(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -23,7 +23,7 @@ const frameHeaderLen = 9
|
||||||
var padZeros = make([]byte, 255) // zeros for padding
|
var padZeros = make([]byte, 255) // zeros for padding
|
||||||
|
|
||||||
// A FrameType is a registered frame type as defined in
|
// A FrameType is a registered frame type as defined in
|
||||||
// http://http2.github.io/http2-spec/#rfc.section.11.2
|
// https://httpwg.org/specs/rfc7540.html#rfc.section.11.2
|
||||||
type FrameType uint8
|
type FrameType uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -146,7 +146,7 @@ func typeFrameParser(t FrameType) frameParser {
|
||||||
|
|
||||||
// A FrameHeader is the 9 byte header of all HTTP/2 frames.
|
// A FrameHeader is the 9 byte header of all HTTP/2 frames.
|
||||||
//
|
//
|
||||||
// See http://http2.github.io/http2-spec/#FrameHeader
|
// See https://httpwg.org/specs/rfc7540.html#FrameHeader
|
||||||
type FrameHeader struct {
|
type FrameHeader struct {
|
||||||
valid bool // caller can access []byte fields in the Frame
|
valid bool // caller can access []byte fields in the Frame
|
||||||
|
|
||||||
|
@ -575,7 +575,7 @@ func (fr *Framer) checkFrameOrder(f Frame) error {
|
||||||
|
|
||||||
// A DataFrame conveys arbitrary, variable-length sequences of octets
|
// A DataFrame conveys arbitrary, variable-length sequences of octets
|
||||||
// associated with a stream.
|
// associated with a stream.
|
||||||
// See http://http2.github.io/http2-spec/#rfc.section.6.1
|
// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.1
|
||||||
type DataFrame struct {
|
type DataFrame struct {
|
||||||
FrameHeader
|
FrameHeader
|
||||||
data []byte
|
data []byte
|
||||||
|
@ -698,7 +698,7 @@ func (f *Framer) WriteDataPadded(streamID uint32, endStream bool, data, pad []by
|
||||||
// endpoints communicate, such as preferences and constraints on peer
|
// endpoints communicate, such as preferences and constraints on peer
|
||||||
// behavior.
|
// behavior.
|
||||||
//
|
//
|
||||||
// See http://http2.github.io/http2-spec/#SETTINGS
|
// See https://httpwg.org/specs/rfc7540.html#SETTINGS
|
||||||
type SettingsFrame struct {
|
type SettingsFrame struct {
|
||||||
FrameHeader
|
FrameHeader
|
||||||
p []byte
|
p []byte
|
||||||
|
@ -837,7 +837,7 @@ func (f *Framer) WriteSettingsAck() error {
|
||||||
// A PingFrame is a mechanism for measuring a minimal round trip time
|
// A PingFrame is a mechanism for measuring a minimal round trip time
|
||||||
// from the sender, as well as determining whether an idle connection
|
// from the sender, as well as determining whether an idle connection
|
||||||
// is still functional.
|
// is still functional.
|
||||||
// See http://http2.github.io/http2-spec/#rfc.section.6.7
|
// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.7
|
||||||
type PingFrame struct {
|
type PingFrame struct {
|
||||||
FrameHeader
|
FrameHeader
|
||||||
Data [8]byte
|
Data [8]byte
|
||||||
|
@ -870,7 +870,7 @@ func (f *Framer) WritePing(ack bool, data [8]byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A GoAwayFrame informs the remote peer to stop creating streams on this connection.
|
// A GoAwayFrame informs the remote peer to stop creating streams on this connection.
|
||||||
// See http://http2.github.io/http2-spec/#rfc.section.6.8
|
// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.8
|
||||||
type GoAwayFrame struct {
|
type GoAwayFrame struct {
|
||||||
FrameHeader
|
FrameHeader
|
||||||
LastStreamID uint32
|
LastStreamID uint32
|
||||||
|
@ -934,7 +934,7 @@ func parseUnknownFrame(_ *frameCache, fh FrameHeader, countError func(string), p
|
||||||
}
|
}
|
||||||
|
|
||||||
// A WindowUpdateFrame is used to implement flow control.
|
// A WindowUpdateFrame is used to implement flow control.
|
||||||
// See http://http2.github.io/http2-spec/#rfc.section.6.9
|
// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.9
|
||||||
type WindowUpdateFrame struct {
|
type WindowUpdateFrame struct {
|
||||||
FrameHeader
|
FrameHeader
|
||||||
Increment uint32 // never read with high bit set
|
Increment uint32 // never read with high bit set
|
||||||
|
@ -1123,7 +1123,7 @@ func (f *Framer) WriteHeaders(p HeadersFrameParam) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A PriorityFrame specifies the sender-advised priority of a stream.
|
// A PriorityFrame specifies the sender-advised priority of a stream.
|
||||||
// See http://http2.github.io/http2-spec/#rfc.section.6.3
|
// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.3
|
||||||
type PriorityFrame struct {
|
type PriorityFrame struct {
|
||||||
FrameHeader
|
FrameHeader
|
||||||
PriorityParam
|
PriorityParam
|
||||||
|
@ -1193,7 +1193,7 @@ func (f *Framer) WritePriority(streamID uint32, p PriorityParam) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A RSTStreamFrame allows for abnormal termination of a stream.
|
// A RSTStreamFrame allows for abnormal termination of a stream.
|
||||||
// See http://http2.github.io/http2-spec/#rfc.section.6.4
|
// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.4
|
||||||
type RSTStreamFrame struct {
|
type RSTStreamFrame struct {
|
||||||
FrameHeader
|
FrameHeader
|
||||||
ErrCode ErrCode
|
ErrCode ErrCode
|
||||||
|
@ -1225,7 +1225,7 @@ func (f *Framer) WriteRSTStream(streamID uint32, code ErrCode) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ContinuationFrame is used to continue a sequence of header block fragments.
|
// A ContinuationFrame is used to continue a sequence of header block fragments.
|
||||||
// See http://http2.github.io/http2-spec/#rfc.section.6.10
|
// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.10
|
||||||
type ContinuationFrame struct {
|
type ContinuationFrame struct {
|
||||||
FrameHeader
|
FrameHeader
|
||||||
headerFragBuf []byte
|
headerFragBuf []byte
|
||||||
|
@ -1266,7 +1266,7 @@ func (f *Framer) WriteContinuation(streamID uint32, endHeaders bool, headerBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
// A PushPromiseFrame is used to initiate a server stream.
|
// A PushPromiseFrame is used to initiate a server stream.
|
||||||
// See http://http2.github.io/http2-spec/#rfc.section.6.6
|
// See https://httpwg.org/specs/rfc7540.html#rfc.section.6.6
|
||||||
type PushPromiseFrame struct {
|
type PushPromiseFrame struct {
|
||||||
FrameHeader
|
FrameHeader
|
||||||
PromiseID uint32
|
PromiseID uint32
|
||||||
|
@ -1532,7 +1532,8 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
|
||||||
fr.debugReadLoggerf("http2: decoded hpack field %+v", hf)
|
fr.debugReadLoggerf("http2: decoded hpack field %+v", hf)
|
||||||
}
|
}
|
||||||
if !httpguts.ValidHeaderFieldValue(hf.Value) {
|
if !httpguts.ValidHeaderFieldValue(hf.Value) {
|
||||||
invalid = headerFieldValueError(hf.Value)
|
// Don't include the value in the error, because it may be sensitive.
|
||||||
|
invalid = headerFieldValueError(hf.Name)
|
||||||
}
|
}
|
||||||
isPseudo := strings.HasPrefix(hf.Name, ":")
|
isPseudo := strings.HasPrefix(hf.Name, ":")
|
||||||
if isPseudo {
|
if isPseudo {
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tlsUnderlyingConn(tc *tls.Conn) net.Conn {
|
||||||
|
return tc.NetConn()
|
||||||
|
}
|
|
@ -191,7 +191,7 @@ func appendTableSize(dst []byte, v uint32) []byte {
|
||||||
// bit prefix, to dst and returns the extended buffer.
|
// bit prefix, to dst and returns the extended buffer.
|
||||||
//
|
//
|
||||||
// See
|
// See
|
||||||
// http://http2.github.io/http2-spec/compression.html#integer.representation
|
// https://httpwg.org/specs/rfc7541.html#integer.representation
|
||||||
func appendVarInt(dst []byte, n byte, i uint64) []byte {
|
func appendVarInt(dst []byte, n byte, i uint64) []byte {
|
||||||
k := uint64((1 << n) - 1)
|
k := uint64((1 << n) - 1)
|
||||||
if i < k {
|
if i < k {
|
||||||
|
|
|
@ -59,7 +59,7 @@ func (hf HeaderField) String() string {
|
||||||
|
|
||||||
// Size returns the size of an entry per RFC 7541 section 4.1.
|
// Size returns the size of an entry per RFC 7541 section 4.1.
|
||||||
func (hf HeaderField) Size() uint32 {
|
func (hf HeaderField) Size() uint32 {
|
||||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.4.1
|
// https://httpwg.org/specs/rfc7541.html#rfc.section.4.1
|
||||||
// "The size of the dynamic table is the sum of the size of
|
// "The size of the dynamic table is the sum of the size of
|
||||||
// its entries. The size of an entry is the sum of its name's
|
// its entries. The size of an entry is the sum of its name's
|
||||||
// length in octets (as defined in Section 5.2), its value's
|
// length in octets (as defined in Section 5.2), its value's
|
||||||
|
@ -158,7 +158,7 @@ func (d *Decoder) SetAllowedMaxDynamicTableSize(v uint32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type dynamicTable struct {
|
type dynamicTable struct {
|
||||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.2.3.2
|
// https://httpwg.org/specs/rfc7541.html#rfc.section.2.3.2
|
||||||
table headerFieldTable
|
table headerFieldTable
|
||||||
size uint32 // in bytes
|
size uint32 // in bytes
|
||||||
maxSize uint32 // current maxSize
|
maxSize uint32 // current maxSize
|
||||||
|
@ -307,27 +307,27 @@ func (d *Decoder) parseHeaderFieldRepr() error {
|
||||||
case b&128 != 0:
|
case b&128 != 0:
|
||||||
// Indexed representation.
|
// Indexed representation.
|
||||||
// High bit set?
|
// High bit set?
|
||||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.1
|
// https://httpwg.org/specs/rfc7541.html#rfc.section.6.1
|
||||||
return d.parseFieldIndexed()
|
return d.parseFieldIndexed()
|
||||||
case b&192 == 64:
|
case b&192 == 64:
|
||||||
// 6.2.1 Literal Header Field with Incremental Indexing
|
// 6.2.1 Literal Header Field with Incremental Indexing
|
||||||
// 0b10xxxxxx: top two bits are 10
|
// 0b10xxxxxx: top two bits are 10
|
||||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.1
|
// https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.1
|
||||||
return d.parseFieldLiteral(6, indexedTrue)
|
return d.parseFieldLiteral(6, indexedTrue)
|
||||||
case b&240 == 0:
|
case b&240 == 0:
|
||||||
// 6.2.2 Literal Header Field without Indexing
|
// 6.2.2 Literal Header Field without Indexing
|
||||||
// 0b0000xxxx: top four bits are 0000
|
// 0b0000xxxx: top four bits are 0000
|
||||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.2
|
// https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.2
|
||||||
return d.parseFieldLiteral(4, indexedFalse)
|
return d.parseFieldLiteral(4, indexedFalse)
|
||||||
case b&240 == 16:
|
case b&240 == 16:
|
||||||
// 6.2.3 Literal Header Field never Indexed
|
// 6.2.3 Literal Header Field never Indexed
|
||||||
// 0b0001xxxx: top four bits are 0001
|
// 0b0001xxxx: top four bits are 0001
|
||||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.3
|
// https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.3
|
||||||
return d.parseFieldLiteral(4, indexedNever)
|
return d.parseFieldLiteral(4, indexedNever)
|
||||||
case b&224 == 32:
|
case b&224 == 32:
|
||||||
// 6.3 Dynamic Table Size Update
|
// 6.3 Dynamic Table Size Update
|
||||||
// Top three bits are '001'.
|
// Top three bits are '001'.
|
||||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.3
|
// https://httpwg.org/specs/rfc7541.html#rfc.section.6.3
|
||||||
return d.parseDynamicTableSizeUpdate()
|
return d.parseDynamicTableSizeUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,7 +420,7 @@ var errVarintOverflow = DecodingError{errors.New("varint integer overflow")}
|
||||||
|
|
||||||
// readVarInt reads an unsigned variable length integer off the
|
// readVarInt reads an unsigned variable length integer off the
|
||||||
// beginning of p. n is the parameter as described in
|
// beginning of p. n is the parameter as described in
|
||||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.5.1.
|
// https://httpwg.org/specs/rfc7541.html#rfc.section.5.1.
|
||||||
//
|
//
|
||||||
// n must always be between 1 and 8.
|
// n must always be between 1 and 8.
|
||||||
//
|
//
|
||||||
|
|
|
@ -169,25 +169,50 @@ func buildRootHuffmanNode() {
|
||||||
// AppendHuffmanString appends s, as encoded in Huffman codes, to dst
|
// AppendHuffmanString appends s, as encoded in Huffman codes, to dst
|
||||||
// and returns the extended buffer.
|
// and returns the extended buffer.
|
||||||
func AppendHuffmanString(dst []byte, s string) []byte {
|
func AppendHuffmanString(dst []byte, s string) []byte {
|
||||||
rembits := uint8(8)
|
// This relies on the maximum huffman code length being 30 (See tables.go huffmanCodeLen array)
|
||||||
|
// So if a uint64 buffer has less than 32 valid bits can always accommodate another huffmanCode.
|
||||||
|
var (
|
||||||
|
x uint64 // buffer
|
||||||
|
n uint // number valid of bits present in x
|
||||||
|
)
|
||||||
for i := 0; i < len(s); i++ {
|
for i := 0; i < len(s); i++ {
|
||||||
if rembits == 8 {
|
c := s[i]
|
||||||
dst = append(dst, 0)
|
n += uint(huffmanCodeLen[c])
|
||||||
|
x <<= huffmanCodeLen[c] % 64
|
||||||
|
x |= uint64(huffmanCodes[c])
|
||||||
|
if n >= 32 {
|
||||||
|
n %= 32 // Normally would be -= 32 but %= 32 informs compiler 0 <= n <= 31 for upcoming shift
|
||||||
|
y := uint32(x >> n) // Compiler doesn't combine memory writes if y isn't uint32
|
||||||
|
dst = append(dst, byte(y>>24), byte(y>>16), byte(y>>8), byte(y))
|
||||||
}
|
}
|
||||||
dst, rembits = appendByteToHuffmanCode(dst, rembits, s[i])
|
|
||||||
}
|
}
|
||||||
|
// Add padding bits if necessary
|
||||||
if rembits < 8 {
|
if over := n % 8; over > 0 {
|
||||||
// special EOS symbol
|
const (
|
||||||
code := uint32(0x3fffffff)
|
eosCode = 0x3fffffff
|
||||||
nbits := uint8(30)
|
eosNBits = 30
|
||||||
|
eosPadByte = eosCode >> (eosNBits - 8)
|
||||||
t := uint8(code >> (nbits - rembits))
|
)
|
||||||
dst[len(dst)-1] |= t
|
pad := 8 - over
|
||||||
|
x = (x << pad) | (eosPadByte >> over)
|
||||||
|
n += pad // 8 now divides into n exactly
|
||||||
}
|
}
|
||||||
|
// n in (0, 8, 16, 24, 32)
|
||||||
|
switch n / 8 {
|
||||||
|
case 0:
|
||||||
return dst
|
return dst
|
||||||
|
case 1:
|
||||||
|
return append(dst, byte(x))
|
||||||
|
case 2:
|
||||||
|
y := uint16(x)
|
||||||
|
return append(dst, byte(y>>8), byte(y))
|
||||||
|
case 3:
|
||||||
|
y := uint16(x >> 8)
|
||||||
|
return append(dst, byte(y>>8), byte(y), byte(x))
|
||||||
|
}
|
||||||
|
// case 4:
|
||||||
|
y := uint32(x)
|
||||||
|
return append(dst, byte(y>>24), byte(y>>16), byte(y>>8), byte(y))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HuffmanEncodeLength returns the number of bytes required to encode
|
// HuffmanEncodeLength returns the number of bytes required to encode
|
||||||
|
@ -199,35 +224,3 @@ func HuffmanEncodeLength(s string) uint64 {
|
||||||
}
|
}
|
||||||
return (n + 7) / 8
|
return (n + 7) / 8
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendByteToHuffmanCode appends Huffman code for c to dst and
|
|
||||||
// returns the extended buffer and the remaining bits in the last
|
|
||||||
// element. The appending is not byte aligned and the remaining bits
|
|
||||||
// in the last element of dst is given in rembits.
|
|
||||||
func appendByteToHuffmanCode(dst []byte, rembits uint8, c byte) ([]byte, uint8) {
|
|
||||||
code := huffmanCodes[c]
|
|
||||||
nbits := huffmanCodeLen[c]
|
|
||||||
|
|
||||||
for {
|
|
||||||
if rembits > nbits {
|
|
||||||
t := uint8(code << (rembits - nbits))
|
|
||||||
dst[len(dst)-1] |= t
|
|
||||||
rembits -= nbits
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
t := uint8(code >> (nbits - rembits))
|
|
||||||
dst[len(dst)-1] |= t
|
|
||||||
|
|
||||||
nbits -= rembits
|
|
||||||
rembits = 8
|
|
||||||
|
|
||||||
if nbits == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
dst = append(dst, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return dst, rembits
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
// See https://http2.github.io/ for more information on HTTP/2.
|
// See https://http2.github.io/ for more information on HTTP/2.
|
||||||
//
|
//
|
||||||
// See https://http2.golang.org/ for a test server running this code.
|
// See https://http2.golang.org/ for a test server running this code.
|
||||||
//
|
|
||||||
package http2 // import "golang.org/x/net/http2"
|
package http2 // import "golang.org/x/net/http2"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -56,14 +55,14 @@ const (
|
||||||
ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||||
|
|
||||||
// SETTINGS_MAX_FRAME_SIZE default
|
// SETTINGS_MAX_FRAME_SIZE default
|
||||||
// http://http2.github.io/http2-spec/#rfc.section.6.5.2
|
// https://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2
|
||||||
initialMaxFrameSize = 16384
|
initialMaxFrameSize = 16384
|
||||||
|
|
||||||
// NextProtoTLS is the NPN/ALPN protocol negotiated during
|
// NextProtoTLS is the NPN/ALPN protocol negotiated during
|
||||||
// HTTP/2's TLS setup.
|
// HTTP/2's TLS setup.
|
||||||
NextProtoTLS = "h2"
|
NextProtoTLS = "h2"
|
||||||
|
|
||||||
// http://http2.github.io/http2-spec/#SettingValues
|
// https://httpwg.org/specs/rfc7540.html#SettingValues
|
||||||
initialHeaderTableSize = 4096
|
initialHeaderTableSize = 4096
|
||||||
|
|
||||||
initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size
|
initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size
|
||||||
|
@ -112,7 +111,7 @@ func (st streamState) String() string {
|
||||||
// Setting is a setting parameter: which setting it is, and its value.
|
// Setting is a setting parameter: which setting it is, and its value.
|
||||||
type Setting struct {
|
type Setting struct {
|
||||||
// ID is which setting is being set.
|
// ID is which setting is being set.
|
||||||
// See http://http2.github.io/http2-spec/#SettingValues
|
// See https://httpwg.org/specs/rfc7540.html#SettingFormat
|
||||||
ID SettingID
|
ID SettingID
|
||||||
|
|
||||||
// Val is the value.
|
// Val is the value.
|
||||||
|
@ -144,7 +143,7 @@ func (s Setting) Valid() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A SettingID is an HTTP/2 setting as defined in
|
// A SettingID is an HTTP/2 setting as defined in
|
||||||
// http://http2.github.io/http2-spec/#iana-settings
|
// https://httpwg.org/specs/rfc7540.html#iana-settings
|
||||||
type SettingID uint16
|
type SettingID uint16
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -176,6 +175,7 @@ func (s SettingID) String() string {
|
||||||
// name (key). See httpguts.ValidHeaderName for the base rules.
|
// name (key). See httpguts.ValidHeaderName for the base rules.
|
||||||
//
|
//
|
||||||
// Further, http2 says:
|
// Further, http2 says:
|
||||||
|
//
|
||||||
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
||||||
// characters that are compared in a case-insensitive
|
// characters that are compared in a case-insensitive
|
||||||
// fashion. However, header field names MUST be converted to
|
// fashion. However, header field names MUST be converted to
|
||||||
|
@ -365,8 +365,8 @@ func (s *sorter) SortStrings(ss []string) {
|
||||||
// validPseudoPath reports whether v is a valid :path pseudo-header
|
// validPseudoPath reports whether v is a valid :path pseudo-header
|
||||||
// value. It must be either:
|
// value. It must be either:
|
||||||
//
|
//
|
||||||
// *) a non-empty string starting with '/'
|
// - a non-empty string starting with '/'
|
||||||
// *) the string '*', for OPTIONS requests.
|
// - the string '*', for OPTIONS requests.
|
||||||
//
|
//
|
||||||
// For now this is only used a quick check for deciding when to clean
|
// For now this is only used a quick check for deciding when to clean
|
||||||
// up Opaque URLs before sending requests from the Transport.
|
// up Opaque URLs before sending requests from the Transport.
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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
|
||||||
|
// +build !go1.18
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tlsUnderlyingConn(tc *tls.Conn) net.Conn {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -315,6 +315,20 @@ type ServeConnOpts struct {
|
||||||
// requests. If nil, BaseConfig.Handler is used. If BaseConfig
|
// requests. If nil, BaseConfig.Handler is used. If BaseConfig
|
||||||
// or BaseConfig.Handler is nil, http.DefaultServeMux is used.
|
// or BaseConfig.Handler is nil, http.DefaultServeMux is used.
|
||||||
Handler http.Handler
|
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 {
|
func (o *ServeConnOpts) context() context.Context {
|
||||||
|
@ -383,6 +397,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||||
headerTableSize: initialHeaderTableSize,
|
headerTableSize: initialHeaderTableSize,
|
||||||
serveG: newGoroutineLock(),
|
serveG: newGoroutineLock(),
|
||||||
pushEnabled: true,
|
pushEnabled: true,
|
||||||
|
sawClientPreface: opts.SawClientPreface,
|
||||||
}
|
}
|
||||||
|
|
||||||
s.state.registerConn(sc)
|
s.state.registerConn(sc)
|
||||||
|
@ -400,7 +415,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||||
if s.NewWriteScheduler != nil {
|
if s.NewWriteScheduler != nil {
|
||||||
sc.writeSched = s.NewWriteScheduler()
|
sc.writeSched = s.NewWriteScheduler()
|
||||||
} else {
|
} else {
|
||||||
sc.writeSched = NewRandomWriteScheduler()
|
sc.writeSched = NewPriorityWriteScheduler(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// These start at the RFC-specified defaults. If there is a higher
|
// These start at the RFC-specified defaults. If there is a higher
|
||||||
|
@ -465,9 +480,27 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.Settings != nil {
|
||||||
|
fr := &SettingsFrame{
|
||||||
|
FrameHeader: FrameHeader{valid: true},
|
||||||
|
p: opts.Settings,
|
||||||
|
}
|
||||||
|
if err := fr.ForeachSetting(sc.processSetting); err != nil {
|
||||||
|
sc.rejectConn(ErrCodeProtocol, "invalid settings")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
opts.Settings = nil
|
||||||
|
}
|
||||||
|
|
||||||
if hook := testHookGetServerConn; hook != nil {
|
if hook := testHookGetServerConn; hook != nil {
|
||||||
hook(sc)
|
hook(sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.UpgradeRequest != nil {
|
||||||
|
sc.upgradeRequest(opts.UpgradeRequest)
|
||||||
|
opts.UpgradeRequest = nil
|
||||||
|
}
|
||||||
|
|
||||||
sc.serve()
|
sc.serve()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,6 +545,7 @@ type serverConn struct {
|
||||||
// Everything following is owned by the serve loop; use serveG.check():
|
// Everything following is owned by the serve loop; use serveG.check():
|
||||||
serveG goroutineLock // used to verify funcs are on serve()
|
serveG goroutineLock // used to verify funcs are on serve()
|
||||||
pushEnabled bool
|
pushEnabled bool
|
||||||
|
sawClientPreface bool // preface has already been read, used in h2c upgrade
|
||||||
sawFirstSettings bool // got the initial SETTINGS frame after the preface
|
sawFirstSettings bool // got the initial SETTINGS frame after the preface
|
||||||
needToSendSettingsAck bool
|
needToSendSettingsAck bool
|
||||||
unackedSettings int // how many SETTINGS have we sent without ACKs?
|
unackedSettings int // how many SETTINGS have we sent without ACKs?
|
||||||
|
@ -974,6 +1008,9 @@ var errPrefaceTimeout = errors.New("timeout waiting for client preface")
|
||||||
// returns errPrefaceTimeout on timeout, or an error if the greeting
|
// returns errPrefaceTimeout on timeout, or an error if the greeting
|
||||||
// is invalid.
|
// is invalid.
|
||||||
func (sc *serverConn) readPreface() error {
|
func (sc *serverConn) readPreface() error {
|
||||||
|
if sc.sawClientPreface {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
errc := make(chan error, 1)
|
errc := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
// Read the client preface
|
// Read the client preface
|
||||||
|
@ -1334,6 +1371,9 @@ func (sc *serverConn) startGracefulShutdownInternal() {
|
||||||
func (sc *serverConn) goAway(code ErrCode) {
|
func (sc *serverConn) goAway(code ErrCode) {
|
||||||
sc.serveG.check()
|
sc.serveG.check()
|
||||||
if sc.inGoAway {
|
if sc.inGoAway {
|
||||||
|
if sc.goAwayCode == ErrCodeNo {
|
||||||
|
sc.goAwayCode = code
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sc.inGoAway = true
|
sc.inGoAway = true
|
||||||
|
@ -1710,6 +1750,12 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
||||||
|
|
||||||
// Sender sending more than they'd declared?
|
// Sender sending more than they'd declared?
|
||||||
if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
|
if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
|
||||||
|
if sc.inflow.available() < int32(f.Length) {
|
||||||
|
return sc.countError("data_flow", streamError(id, ErrCodeFlowControl))
|
||||||
|
}
|
||||||
|
sc.inflow.take(int32(f.Length))
|
||||||
|
sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
|
||||||
|
|
||||||
st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
|
st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
|
||||||
// RFC 7540, sec 8.1.2.6: A request or response is also malformed if the
|
// RFC 7540, sec 8.1.2.6: A request or response is also malformed if the
|
||||||
// value of a content-length header field does not equal the sum of the
|
// value of a content-length header field does not equal the sum of the
|
||||||
|
@ -1915,6 +1961,26 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sc *serverConn) upgradeRequest(req *http.Request) {
|
||||||
|
sc.serveG.check()
|
||||||
|
id := uint32(1)
|
||||||
|
sc.maxClientStreamID = id
|
||||||
|
st := sc.newStream(id, 0, stateHalfClosedRemote)
|
||||||
|
st.reqTrailer = req.Trailer
|
||||||
|
if st.reqTrailer != nil {
|
||||||
|
st.trailer = make(http.Header)
|
||||||
|
}
|
||||||
|
rw := sc.newResponseWriter(st, req)
|
||||||
|
|
||||||
|
// Disable any read deadline set by the net/http package
|
||||||
|
// prior to the upgrade.
|
||||||
|
if sc.hs.ReadTimeout != 0 {
|
||||||
|
sc.conn.SetReadDeadline(time.Time{})
|
||||||
|
}
|
||||||
|
|
||||||
|
go sc.runHandler(rw, req, sc.handler.ServeHTTP)
|
||||||
|
}
|
||||||
|
|
||||||
func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
||||||
sc := st.sc
|
sc := st.sc
|
||||||
sc.serveG.check()
|
sc.serveG.check()
|
||||||
|
@ -2145,6 +2211,11 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
|
||||||
}
|
}
|
||||||
req = req.WithContext(st.ctx)
|
req = req.WithContext(st.ctx)
|
||||||
|
|
||||||
|
rw := sc.newResponseWriter(st, req)
|
||||||
|
return rw, req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *serverConn) newResponseWriter(st *stream, req *http.Request) *responseWriter {
|
||||||
rws := responseWriterStatePool.Get().(*responseWriterState)
|
rws := responseWriterStatePool.Get().(*responseWriterState)
|
||||||
bwSave := rws.bw
|
bwSave := rws.bw
|
||||||
*rws = responseWriterState{} // zero all the fields
|
*rws = responseWriterState{} // zero all the fields
|
||||||
|
@ -2153,10 +2224,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
|
||||||
rws.bw.Reset(chunkWriter{rws})
|
rws.bw.Reset(chunkWriter{rws})
|
||||||
rws.stream = st
|
rws.stream = st
|
||||||
rws.req = req
|
rws.req = req
|
||||||
rws.body = body
|
return &responseWriter{rws: rws}
|
||||||
|
|
||||||
rw := &responseWriter{rws: rws}
|
|
||||||
return rw, req, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run on its own goroutine.
|
// Run on its own goroutine.
|
||||||
|
@ -2164,6 +2232,9 @@ func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler
|
||||||
didPanic := true
|
didPanic := true
|
||||||
defer func() {
|
defer func() {
|
||||||
rw.rws.stream.cancelCtx()
|
rw.rws.stream.cancelCtx()
|
||||||
|
if req.MultipartForm != nil {
|
||||||
|
req.MultipartForm.RemoveAll()
|
||||||
|
}
|
||||||
if didPanic {
|
if didPanic {
|
||||||
e := recover()
|
e := recover()
|
||||||
sc.writeFrameFromHandler(FrameWriteRequest{
|
sc.writeFrameFromHandler(FrameWriteRequest{
|
||||||
|
@ -2316,17 +2387,18 @@ type requestBody struct {
|
||||||
_ incomparable
|
_ incomparable
|
||||||
stream *stream
|
stream *stream
|
||||||
conn *serverConn
|
conn *serverConn
|
||||||
closed bool // for use by Close only
|
closeOnce sync.Once // for use by Close only
|
||||||
sawEOF bool // for use by Read only
|
sawEOF bool // for use by Read only
|
||||||
pipe *pipe // non-nil if we have a HTTP entity message body
|
pipe *pipe // non-nil if we have a HTTP entity message body
|
||||||
needsContinue bool // need to send a 100-continue
|
needsContinue bool // need to send a 100-continue
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *requestBody) Close() error {
|
func (b *requestBody) Close() error {
|
||||||
if b.pipe != nil && !b.closed {
|
b.closeOnce.Do(func() {
|
||||||
|
if b.pipe != nil {
|
||||||
b.pipe.BreakWithError(errClosedBody)
|
b.pipe.BreakWithError(errClosedBody)
|
||||||
}
|
}
|
||||||
b.closed = true
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2370,7 +2442,6 @@ type responseWriterState struct {
|
||||||
// immutable within a request:
|
// immutable within a request:
|
||||||
stream *stream
|
stream *stream
|
||||||
req *http.Request
|
req *http.Request
|
||||||
body *requestBody // to close at end of request, if DATA frames didn't
|
|
||||||
conn *serverConn
|
conn *serverConn
|
||||||
|
|
||||||
// TODO: adjust buffer writing sizes based on server config, frame size updates from peer, etc
|
// TODO: adjust buffer writing sizes based on server config, frame size updates from peer, etc
|
||||||
|
@ -2546,6 +2617,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
||||||
// prior to the headers being written. If the set of trailers is fixed
|
// 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
|
// or known before the header is written, the normal Go trailers mechanism
|
||||||
// is preferred:
|
// is preferred:
|
||||||
|
//
|
||||||
// https://golang.org/pkg/net/http/#ResponseWriter
|
// https://golang.org/pkg/net/http/#ResponseWriter
|
||||||
// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
|
// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
|
||||||
const TrailerPrefix = "Trailer:"
|
const TrailerPrefix = "Trailer:"
|
||||||
|
@ -2643,8 +2715,7 @@ func checkWriteHeaderCode(code int) {
|
||||||
// Issue 22880: require valid WriteHeader status codes.
|
// Issue 22880: require valid WriteHeader status codes.
|
||||||
// For now we only enforce that it's three digits.
|
// For now we only enforce that it's three digits.
|
||||||
// In the future we might block things over 599 (600 and above aren't defined
|
// In the future we might block things over 599 (600 and above aren't defined
|
||||||
// at http://httpwg.org/specs/rfc7231.html#status.codes)
|
// at http://httpwg.org/specs/rfc7231.html#status.codes).
|
||||||
// and we might block under 200 (once we have more mature 1xx support).
|
|
||||||
// But for now any three digits.
|
// But for now any three digits.
|
||||||
//
|
//
|
||||||
// We used to send "HTTP/1.1 000 0" on the wire in responses but there's
|
// We used to send "HTTP/1.1 000 0" on the wire in responses but there's
|
||||||
|
@ -2665,15 +2736,43 @@ func (w *responseWriter) WriteHeader(code int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rws *responseWriterState) writeHeader(code int) {
|
func (rws *responseWriterState) writeHeader(code int) {
|
||||||
if !rws.wroteHeader {
|
if rws.wroteHeader {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
checkWriteHeaderCode(code)
|
checkWriteHeaderCode(code)
|
||||||
|
|
||||||
|
// Handle informational headers
|
||||||
|
if code >= 100 && code <= 199 {
|
||||||
|
// Per RFC 8297 we must not clear the current header map
|
||||||
|
h := rws.handlerHeader
|
||||||
|
|
||||||
|
_, cl := h["Content-Length"]
|
||||||
|
_, te := h["Transfer-Encoding"]
|
||||||
|
if cl || te {
|
||||||
|
h = h.Clone()
|
||||||
|
h.Del("Content-Length")
|
||||||
|
h.Del("Transfer-Encoding")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rws.conn.writeHeaders(rws.stream, &writeResHeaders{
|
||||||
|
streamID: rws.stream.id,
|
||||||
|
httpResCode: code,
|
||||||
|
h: h,
|
||||||
|
endStream: rws.handlerDone && !rws.hasTrailers(),
|
||||||
|
}) != nil {
|
||||||
|
rws.dirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
rws.wroteHeader = true
|
rws.wroteHeader = true
|
||||||
rws.status = code
|
rws.status = code
|
||||||
if len(rws.handlerHeader) > 0 {
|
if len(rws.handlerHeader) > 0 {
|
||||||
rws.snapHeader = cloneHeader(rws.handlerHeader)
|
rws.snapHeader = cloneHeader(rws.handlerHeader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func cloneHeader(h http.Header) http.Header {
|
func cloneHeader(h http.Header) http.Header {
|
||||||
h2 := make(http.Header, len(h))
|
h2 := make(http.Header, len(h))
|
||||||
|
|
|
@ -16,7 +16,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
mathrand "math/rand"
|
mathrand "math/rand"
|
||||||
|
@ -68,13 +67,23 @@ const (
|
||||||
// A Transport internally caches connections to servers. It is safe
|
// A Transport internally caches connections to servers. It is safe
|
||||||
// for concurrent use by multiple goroutines.
|
// for concurrent use by multiple goroutines.
|
||||||
type Transport struct {
|
type Transport struct {
|
||||||
// DialTLS specifies an optional dial function for creating
|
// DialTLSContext specifies an optional dial function with context for
|
||||||
// TLS connections for requests.
|
// creating TLS connections for requests.
|
||||||
//
|
//
|
||||||
// If DialTLS is nil, tls.Dial is used.
|
// If DialTLSContext and DialTLS is nil, tls.Dial is used.
|
||||||
//
|
//
|
||||||
// If the returned net.Conn has a ConnectionState method like tls.Conn,
|
// If the returned net.Conn has a ConnectionState method like tls.Conn,
|
||||||
// it will be used to set http.Response.TLS.
|
// 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)
|
DialTLS func(network, addr string, cfg *tls.Config) (net.Conn, error)
|
||||||
|
|
||||||
// TLSClientConfig specifies the TLS configuration to use with
|
// TLSClientConfig specifies the TLS configuration to use with
|
||||||
|
@ -501,12 +510,14 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
|
||||||
if req, err = shouldRetryRequest(req, err); err == nil {
|
if req, err = shouldRetryRequest(req, err); err == nil {
|
||||||
// After the first retry, do exponential backoff with 10% jitter.
|
// After the first retry, do exponential backoff with 10% jitter.
|
||||||
if retry == 0 {
|
if retry == 0 {
|
||||||
|
t.vlogf("RoundTrip retrying after failure: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
backoff := float64(uint(1) << (uint(retry) - 1))
|
backoff := float64(uint(1) << (uint(retry) - 1))
|
||||||
backoff += backoff * (0.1 * mathrand.Float64())
|
backoff += backoff * (0.1 * mathrand.Float64())
|
||||||
select {
|
select {
|
||||||
case <-time.After(time.Second * time.Duration(backoff)):
|
case <-time.After(time.Second * time.Duration(backoff)):
|
||||||
|
t.vlogf("RoundTrip retrying after failure: %v", err)
|
||||||
continue
|
continue
|
||||||
case <-req.Context().Done():
|
case <-req.Context().Done():
|
||||||
err = req.Context().Err()
|
err = req.Context().Err()
|
||||||
|
@ -591,7 +602,7 @@ func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse b
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tconn, err := t.dialTLS(ctx)("tcp", addr, t.newTLSConfig(host))
|
tconn, err := t.dialTLS(ctx, "tcp", addr, t.newTLSConfig(host))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -612,12 +623,14 @@ func (t *Transport) newTLSConfig(host string) *tls.Config {
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) dialTLS(ctx context.Context) func(string, string, *tls.Config) (net.Conn, error) {
|
func (t *Transport) dialTLS(ctx context.Context, network, addr string, tlsCfg *tls.Config) (net.Conn, error) {
|
||||||
if t.DialTLS != nil {
|
if t.DialTLSContext != nil {
|
||||||
return t.DialTLS
|
return t.DialTLSContext(ctx, network, addr, tlsCfg)
|
||||||
|
} else if t.DialTLS != nil {
|
||||||
|
return t.DialTLS(network, addr, tlsCfg)
|
||||||
}
|
}
|
||||||
return func(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
|
||||||
tlsCn, err := t.dialTLSWithContext(ctx, network, addr, cfg)
|
tlsCn, err := t.dialTLSWithContext(ctx, network, addr, tlsCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -630,7 +643,6 @@ func (t *Transport) dialTLS(ctx context.Context) func(string, string, *tls.Confi
|
||||||
}
|
}
|
||||||
return tlsCn, nil
|
return tlsCn, nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// disableKeepAlives reports whether connections should be closed as
|
// disableKeepAlives reports whether connections should be closed as
|
||||||
// soon as possible after handling the first request.
|
// soon as possible after handling the first request.
|
||||||
|
@ -732,11 +744,13 @@ func (cc *ClientConn) healthCheck() {
|
||||||
// trigger the healthCheck again if there is no frame received.
|
// trigger the healthCheck again if there is no frame received.
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), pingTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), pingTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
cc.vlogf("http2: Transport sending health check")
|
||||||
err := cc.Ping(ctx)
|
err := cc.Ping(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
cc.vlogf("http2: Transport health check failure: %v", err)
|
||||||
cc.closeForLostPing()
|
cc.closeForLostPing()
|
||||||
cc.t.connPool().MarkDead(cc)
|
} else {
|
||||||
return
|
cc.vlogf("http2: Transport health check success")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -907,6 +921,24 @@ func (cc *ClientConn) onIdleTimeout() {
|
||||||
cc.closeIfIdle()
|
cc.closeIfIdle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cc *ClientConn) closeConn() error {
|
||||||
|
t := time.AfterFunc(250*time.Millisecond, cc.forceCloseConn)
|
||||||
|
defer t.Stop()
|
||||||
|
return cc.tconn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A tls.Conn.Close can hang for a long time if the peer is unresponsive.
|
||||||
|
// Try to shut it down more aggressively.
|
||||||
|
func (cc *ClientConn) forceCloseConn() {
|
||||||
|
tc, ok := cc.tconn.(*tls.Conn)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if nc := tlsUnderlyingConn(tc); nc != nil {
|
||||||
|
nc.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) closeIfIdle() {
|
func (cc *ClientConn) closeIfIdle() {
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
if len(cc.streams) > 0 || cc.streamsReserved > 0 {
|
if len(cc.streams) > 0 || cc.streamsReserved > 0 {
|
||||||
|
@ -921,7 +953,7 @@ func (cc *ClientConn) closeIfIdle() {
|
||||||
if VerboseLogs {
|
if VerboseLogs {
|
||||||
cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, nextID-2)
|
cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, nextID-2)
|
||||||
}
|
}
|
||||||
cc.tconn.Close()
|
cc.closeConn()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) isDoNotReuseAndIdle() bool {
|
func (cc *ClientConn) isDoNotReuseAndIdle() bool {
|
||||||
|
@ -938,7 +970,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Wait for all in-flight streams to complete or connection to close
|
// Wait for all in-flight streams to complete or connection to close
|
||||||
done := make(chan error, 1)
|
done := make(chan struct{})
|
||||||
cancelled := false // guarded by cc.mu
|
cancelled := false // guarded by cc.mu
|
||||||
go func() {
|
go func() {
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
|
@ -946,7 +978,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
|
||||||
for {
|
for {
|
||||||
if len(cc.streams) == 0 || cc.closed {
|
if len(cc.streams) == 0 || cc.closed {
|
||||||
cc.closed = true
|
cc.closed = true
|
||||||
done <- cc.tconn.Close()
|
close(done)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if cancelled {
|
if cancelled {
|
||||||
|
@ -957,8 +989,8 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
|
||||||
}()
|
}()
|
||||||
shutdownEnterWaitStateHook()
|
shutdownEnterWaitStateHook()
|
||||||
select {
|
select {
|
||||||
case err := <-done:
|
case <-done:
|
||||||
return err
|
return cc.closeConn()
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
// Free the goroutine above
|
// Free the goroutine above
|
||||||
|
@ -1001,9 +1033,9 @@ func (cc *ClientConn) closeForError(err error) error {
|
||||||
for _, cs := range cc.streams {
|
for _, cs := range cc.streams {
|
||||||
cs.abortStreamLocked(err)
|
cs.abortStreamLocked(err)
|
||||||
}
|
}
|
||||||
defer cc.cond.Broadcast()
|
cc.cond.Broadcast()
|
||||||
defer cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
return cc.tconn.Close()
|
return cc.closeConn()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the client connection immediately.
|
// Close closes the client connection immediately.
|
||||||
|
@ -1748,7 +1780,8 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
||||||
}
|
}
|
||||||
for _, v := range vv {
|
for _, v := range vv {
|
||||||
if !httpguts.ValidHeaderFieldValue(v) {
|
if !httpguts.ValidHeaderFieldValue(v) {
|
||||||
return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k)
|
// Don't include the value in the error, because it may be sensitive.
|
||||||
|
return nil, fmt.Errorf("invalid HTTP header value for header %q", k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1978,7 +2011,7 @@ func (cc *ClientConn) forgetStreamID(id uint32) {
|
||||||
cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, cc.nextStreamID-2)
|
cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, cc.nextStreamID-2)
|
||||||
}
|
}
|
||||||
cc.closed = true
|
cc.closed = true
|
||||||
defer cc.tconn.Close()
|
defer cc.closeConn()
|
||||||
}
|
}
|
||||||
|
|
||||||
cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
|
@ -2025,8 +2058,8 @@ func isEOFOrNetReadError(err error) bool {
|
||||||
|
|
||||||
func (rl *clientConnReadLoop) cleanup() {
|
func (rl *clientConnReadLoop) cleanup() {
|
||||||
cc := rl.cc
|
cc := rl.cc
|
||||||
defer cc.tconn.Close()
|
cc.t.connPool().MarkDead(cc)
|
||||||
defer cc.t.connPool().MarkDead(cc)
|
defer cc.closeConn()
|
||||||
defer close(cc.readerDone)
|
defer close(cc.readerDone)
|
||||||
|
|
||||||
if cc.idleTimer != nil {
|
if cc.idleTimer != nil {
|
||||||
|
@ -2881,7 +2914,12 @@ func (t *Transport) logf(format string, args ...interface{}) {
|
||||||
log.Printf(format, args...)
|
log.Printf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
var noBody io.ReadCloser = ioutil.NopCloser(bytes.NewReader(nil))
|
var noBody io.ReadCloser = noBodyReader{}
|
||||||
|
|
||||||
|
type noBodyReader struct{}
|
||||||
|
|
||||||
|
func (noBodyReader) Close() error { return nil }
|
||||||
|
func (noBodyReader) Read([]byte) (int, error) { return 0, io.EOF }
|
||||||
|
|
||||||
type missingBody struct{}
|
type missingBody struct{}
|
||||||
|
|
||||||
|
|
|
@ -383,16 +383,15 @@ func (ws *priorityWriteScheduler) AdjustStream(streamID uint32, priority Priorit
|
||||||
|
|
||||||
func (ws *priorityWriteScheduler) Push(wr FrameWriteRequest) {
|
func (ws *priorityWriteScheduler) Push(wr FrameWriteRequest) {
|
||||||
var n *priorityNode
|
var n *priorityNode
|
||||||
if id := wr.StreamID(); id == 0 {
|
if wr.isControl() {
|
||||||
n = &ws.root
|
n = &ws.root
|
||||||
} else {
|
} else {
|
||||||
|
id := wr.StreamID()
|
||||||
n = ws.nodes[id]
|
n = ws.nodes[id]
|
||||||
if n == nil {
|
if n == nil {
|
||||||
// id is an idle or closed stream. wr should not be a HEADERS or
|
// id is an idle or closed stream. wr should not be a HEADERS or
|
||||||
// DATA frame. However, wr can be a RST_STREAM. In this case, we
|
// DATA frame. In other case, we push wr onto the root, rather
|
||||||
// push wr onto the root, rather than creating a new priorityNode,
|
// than creating a new priorityNode.
|
||||||
// since RST_STREAM is tiny and the stream's priority is unknown
|
|
||||||
// anyway. See issue #17919.
|
|
||||||
if wr.DataSize() > 0 {
|
if wr.DataSize() > 0 {
|
||||||
panic("add DATA on non-open stream")
|
panic("add DATA on non-open stream")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
issuerepo: golang/go
|
|
@ -12,6 +12,8 @@
|
||||||
// panic(err)
|
// panic(err)
|
||||||
// }
|
// }
|
||||||
// defer term.Restore(int(os.Stdin.Fd()), oldState)
|
// defer term.Restore(int(os.Stdin.Fd()), oldState)
|
||||||
|
//
|
||||||
|
// Note that on non-Unix systems os.Stdin.Fd() may not be 0.
|
||||||
package term
|
package term
|
||||||
|
|
||||||
// State contains the state of a terminal.
|
// State contains the state of a terminal.
|
||||||
|
|
|
@ -259,7 +259,7 @@ go.etcd.io/etcd/raft/v3/raftpb
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
golang.org/x/crypto/ed25519
|
golang.org/x/crypto/ed25519
|
||||||
golang.org/x/crypto/pbkdf2
|
golang.org/x/crypto/pbkdf2
|
||||||
# golang.org/x/net v0.0.0-20211216030914-fe4d6282115f
|
# golang.org/x/net v0.0.0-20220906165146-f3363e06e74c
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
golang.org/x/net/http/httpguts
|
golang.org/x/net/http/httpguts
|
||||||
golang.org/x/net/http2
|
golang.org/x/net/http2
|
||||||
|
@ -276,7 +276,7 @@ golang.org/x/sys/internal/unsafeheader
|
||||||
golang.org/x/sys/plan9
|
golang.org/x/sys/plan9
|
||||||
golang.org/x/sys/unix
|
golang.org/x/sys/unix
|
||||||
golang.org/x/sys/windows
|
golang.org/x/sys/windows
|
||||||
# golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
|
# golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||||
## explicit; go 1.17
|
## explicit; go 1.17
|
||||||
golang.org/x/term
|
golang.org/x/term
|
||||||
# golang.org/x/text v0.3.7
|
# golang.org/x/text v0.3.7
|
||||||
|
|
Loading…
Reference in New Issue