зеркало из https://github.com/Azure/aks-engine.git
chore(deps): bump golang.org/x/net from 0.0.0-20220127200216-cd36cc0744dd to 0.7.0 (#5080)
* chore(deps): bump golang.org/x/net Bumps [golang.org/x/net](https://github.com/golang/net) from 0.0.0-20220127200216-cd36cc0744dd to 0.7.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/commits/v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * go mod tidy in test/e2e --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jack Francis <jackfrancis@gmail.com>
This commit is contained in:
Родитель
3b039911c5
Коммит
07aaf2bd1b
6
go.mod
6
go.mod
|
@ -78,10 +78,10 @@ require (
|
|||
github.com/nxadm/tail v1.4.4 // indirect
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/term v0.5.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
|
10
go.sum
10
go.sum
|
@ -424,8 +424,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
|||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -500,11 +501,12 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
|
|
@ -421,8 +421,9 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
|||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
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-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -500,12 +501,14 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
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.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.
|
|
@ -21,9 +21,9 @@
|
|||
// explicitly to each function that needs it. The Context should be the first
|
||||
// parameter, typically named ctx:
|
||||
//
|
||||
// func DoSomething(ctx context.Context, arg Arg) error {
|
||||
// // ... use ctx ...
|
||||
// }
|
||||
// func DoSomething(ctx context.Context, arg Arg) error {
|
||||
// // ... use ctx ...
|
||||
// }
|
||||
//
|
||||
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
|
||||
// if you are unsure about which Context to use.
|
||||
|
|
|
@ -32,7 +32,7 @@ var DeadlineExceeded = context.DeadlineExceeded
|
|||
// call cancel as soon as the operations running in this Context complete.
|
||||
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
||||
ctx, f := context.WithCancel(parent)
|
||||
return ctx, CancelFunc(f)
|
||||
return ctx, f
|
||||
}
|
||||
|
||||
// WithDeadline returns a copy of the parent context with the deadline adjusted
|
||||
|
@ -46,7 +46,7 @@ func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
|||
// call cancel as soon as the operations running in this Context complete.
|
||||
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
|
||||
ctx, f := context.WithDeadline(parent, deadline)
|
||||
return ctx, CancelFunc(f)
|
||||
return ctx, f
|
||||
}
|
||||
|
||||
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
|
||||
|
@ -54,11 +54,11 @@ func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
|
|||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete:
|
||||
//
|
||||
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
|
||||
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
||||
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
||||
// return slowOperation(ctx)
|
||||
// }
|
||||
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
|
||||
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
||||
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
||||
// return slowOperation(ctx)
|
||||
// }
|
||||
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
|
||||
return WithDeadline(parent, time.Now().Add(timeout))
|
||||
}
|
||||
|
|
|
@ -264,11 +264,11 @@ func (c *timerCtx) cancel(removeFromParent bool, err error) {
|
|||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete:
|
||||
//
|
||||
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
|
||||
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
||||
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
||||
// return slowOperation(ctx)
|
||||
// }
|
||||
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
|
||||
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
||||
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
||||
// return slowOperation(ctx)
|
||||
// }
|
||||
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
|
||||
return WithDeadline(parent, time.Now().Add(timeout))
|
||||
}
|
||||
|
|
|
@ -184,7 +184,7 @@ func (p *parser) clearStackToContext(s scope) {
|
|||
}
|
||||
}
|
||||
|
||||
// parseGenericRawTextElements implements the generic raw text element parsing
|
||||
// parseGenericRawTextElement implements the generic raw text element parsing
|
||||
// algorithm defined in 12.2.6.2.
|
||||
// https://html.spec.whatwg.org/multipage/parsing.html#parsing-elements-that-contain-only-text
|
||||
// TODO: Since both RAWTEXT and RCDATA states are treated as tokenizer's part
|
||||
|
@ -734,7 +734,7 @@ func inHeadIM(p *parser) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// 12.2.6.4.5.
|
||||
// Section 12.2.6.4.5.
|
||||
func inHeadNoscriptIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case DoctypeToken:
|
||||
|
|
|
@ -85,7 +85,7 @@ func render1(w writer, n *Node) error {
|
|||
if _, err := w.WriteString("<!--"); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.WriteString(n.Data); err != nil {
|
||||
if err := escape(w, n.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.WriteString("-->"); err != nil {
|
||||
|
@ -96,7 +96,7 @@ func render1(w writer, n *Node) error {
|
|||
if _, err := w.WriteString("<!DOCTYPE "); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.WriteString(n.Data); err != nil {
|
||||
if err := escape(w, n.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
if n.Attr != nil {
|
||||
|
|
|
@ -110,9 +110,9 @@ func (t Token) String() string {
|
|||
case SelfClosingTagToken:
|
||||
return "<" + t.tagString() + "/>"
|
||||
case CommentToken:
|
||||
return "<!--" + t.Data + "-->"
|
||||
return "<!--" + EscapeString(t.Data) + "-->"
|
||||
case DoctypeToken:
|
||||
return "<!DOCTYPE " + t.Data + ">"
|
||||
return "<!DOCTYPE " + EscapeString(t.Data) + ">"
|
||||
}
|
||||
return "Invalid(" + strconv.Itoa(int(t.Type)) + ")"
|
||||
}
|
||||
|
@ -598,6 +598,11 @@ scriptDataDoubleEscapeEnd:
|
|||
// readComment reads the next comment token starting with "<!--". The opening
|
||||
// "<!--" has already been consumed.
|
||||
func (z *Tokenizer) readComment() {
|
||||
// When modifying this function, consider manually increasing the suffixLen
|
||||
// constant in func TestComments, from 6 to e.g. 9 or more. That increase
|
||||
// should only be temporary, not committed, as it exponentially affects the
|
||||
// test running time.
|
||||
|
||||
z.data.start = z.raw.end
|
||||
defer func() {
|
||||
if z.data.end < z.data.start {
|
||||
|
@ -605,14 +610,13 @@ func (z *Tokenizer) readComment() {
|
|||
z.data.end = z.data.start
|
||||
}
|
||||
}()
|
||||
for dashCount := 2; ; {
|
||||
|
||||
var dashCount int
|
||||
beginning := true
|
||||
for {
|
||||
c := z.readByte()
|
||||
if z.err != nil {
|
||||
// Ignore up to two dashes at EOF.
|
||||
if dashCount > 2 {
|
||||
dashCount = 2
|
||||
}
|
||||
z.data.end = z.raw.end - dashCount
|
||||
z.data.end = z.calculateAbruptCommentDataEnd()
|
||||
return
|
||||
}
|
||||
switch c {
|
||||
|
@ -620,7 +624,7 @@ func (z *Tokenizer) readComment() {
|
|||
dashCount++
|
||||
continue
|
||||
case '>':
|
||||
if dashCount >= 2 {
|
||||
if dashCount >= 2 || beginning {
|
||||
z.data.end = z.raw.end - len("-->")
|
||||
return
|
||||
}
|
||||
|
@ -628,19 +632,52 @@ func (z *Tokenizer) readComment() {
|
|||
if dashCount >= 2 {
|
||||
c = z.readByte()
|
||||
if z.err != nil {
|
||||
z.data.end = z.raw.end
|
||||
z.data.end = z.calculateAbruptCommentDataEnd()
|
||||
return
|
||||
}
|
||||
if c == '>' {
|
||||
} else if c == '>' {
|
||||
z.data.end = z.raw.end - len("--!>")
|
||||
return
|
||||
} else if c == '-' {
|
||||
dashCount = 1
|
||||
beginning = false
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
dashCount = 0
|
||||
beginning = false
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Tokenizer) calculateAbruptCommentDataEnd() int {
|
||||
raw := z.Raw()
|
||||
const prefixLen = len("<!--")
|
||||
if len(raw) >= prefixLen {
|
||||
raw = raw[prefixLen:]
|
||||
if hasSuffix(raw, "--!") {
|
||||
return z.raw.end - 3
|
||||
} else if hasSuffix(raw, "--") {
|
||||
return z.raw.end - 2
|
||||
} else if hasSuffix(raw, "-") {
|
||||
return z.raw.end - 1
|
||||
}
|
||||
}
|
||||
return z.raw.end
|
||||
}
|
||||
|
||||
func hasSuffix(b []byte, suffix string) bool {
|
||||
if len(b) < len(suffix) {
|
||||
return false
|
||||
}
|
||||
b = b[len(b)-len(suffix):]
|
||||
for i := range b {
|
||||
if b[i] != suffix[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// readUntilCloseAngle reads until the next ">".
|
||||
func (z *Tokenizer) readUntilCloseAngle() {
|
||||
z.data.start = z.raw.end
|
||||
|
|
|
@ -173,13 +173,15 @@ func tokenEqual(t1, t2 string) bool {
|
|||
|
||||
// isLWS reports whether b is linear white space, according
|
||||
// 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' }
|
||||
|
||||
// isCTL reports whether b is a control byte, according
|
||||
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
||||
// CTL = <any US-ASCII control character
|
||||
// (octets 0 - 31) and DEL (127)>
|
||||
//
|
||||
// CTL = <any US-ASCII control character
|
||||
// (octets 0 - 31) and DEL (127)>
|
||||
func isCTL(b byte) bool {
|
||||
const del = 0x7f // a CTL
|
||||
return b < ' ' || b == del
|
||||
|
@ -189,12 +191,13 @@ func isCTL(b byte) bool {
|
|||
// HTTP/2 imposes the additional restriction that uppercase ASCII
|
||||
// letters are not allowed.
|
||||
//
|
||||
// RFC 7230 says:
|
||||
// header-field = field-name ":" OWS field-value OWS
|
||||
// field-name = token
|
||||
// token = 1*tchar
|
||||
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
|
||||
// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
|
||||
// RFC 7230 says:
|
||||
//
|
||||
// header-field = field-name ":" OWS field-value OWS
|
||||
// field-name = token
|
||||
// token = 1*tchar
|
||||
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
|
||||
// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
|
||||
func ValidHeaderFieldName(v string) bool {
|
||||
if len(v) == 0 {
|
||||
return false
|
||||
|
@ -267,27 +270,28 @@ var validHostByte = [256]bool{
|
|||
// ValidHeaderFieldValue reports whether v is a valid "field-value" according to
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 :
|
||||
//
|
||||
// message-header = field-name ":" [ field-value ]
|
||||
// field-value = *( field-content | LWS )
|
||||
// field-content = <the OCTETs making up the field-value
|
||||
// and consisting of either *TEXT or combinations
|
||||
// of token, separators, and quoted-string>
|
||||
// message-header = field-name ":" [ field-value ]
|
||||
// field-value = *( field-content | LWS )
|
||||
// field-content = <the OCTETs making up the field-value
|
||||
// and consisting of either *TEXT or combinations
|
||||
// of token, separators, and quoted-string>
|
||||
//
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 :
|
||||
//
|
||||
// TEXT = <any OCTET except CTLs,
|
||||
// but including LWS>
|
||||
// LWS = [CRLF] 1*( SP | HT )
|
||||
// CTL = <any US-ASCII control character
|
||||
// (octets 0 - 31) and DEL (127)>
|
||||
// TEXT = <any OCTET except CTLs,
|
||||
// but including LWS>
|
||||
// LWS = [CRLF] 1*( SP | HT )
|
||||
// CTL = <any US-ASCII control character
|
||||
// (octets 0 - 31) and DEL (127)>
|
||||
//
|
||||
// RFC 7230 says:
|
||||
// field-value = *( field-content / obs-fold )
|
||||
// obj-fold = N/A to http2, and deprecated
|
||||
// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
||||
// field-vchar = VCHAR / obs-text
|
||||
// obs-text = %x80-FF
|
||||
// VCHAR = "any visible [USASCII] character"
|
||||
//
|
||||
// field-value = *( field-content / obs-fold )
|
||||
// obj-fold = N/A to http2, and deprecated
|
||||
// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
||||
// field-vchar = VCHAR / obs-text
|
||||
// obs-text = %x80-FF
|
||||
// VCHAR = "any visible [USASCII] character"
|
||||
//
|
||||
// http2 further says: "Similarly, HTTP/2 allows header field values
|
||||
// that are not valid. While most of the values that can be encoded
|
||||
|
|
|
@ -81,8 +81,7 @@ type config struct {
|
|||
|
||||
// FromEnvironment returns a Config instance populated from the
|
||||
// environment variables HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the
|
||||
// lowercase versions thereof). HTTPS_PROXY takes precedence over
|
||||
// HTTP_PROXY for https requests.
|
||||
// lowercase versions thereof).
|
||||
//
|
||||
// The environment values may be either a complete URL or a
|
||||
// "host[:port]", in which case the "http" scheme is assumed. An error
|
||||
|
@ -267,6 +266,9 @@ func (c *config) init() {
|
|||
matchHost = true
|
||||
phost = "." + phost
|
||||
}
|
||||
if v, err := idnaASCII(phost); err == nil {
|
||||
phost = v
|
||||
}
|
||||
c.domainMatchers = append(c.domainMatchers, domainMatch{host: phost, port: pport, matchHost: matchHost})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,7 +139,6 @@ func (p *clientConnPool) getStartDialLocked(ctx context.Context, addr string) *d
|
|||
func (c *dialCall) dial(ctx context.Context, addr string) {
|
||||
const singleUse = false // shared conn
|
||||
c.res, c.err = c.p.t.dialClientConn(ctx, addr, singleUse)
|
||||
close(c.done)
|
||||
|
||||
c.p.mu.Lock()
|
||||
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.mu.Unlock()
|
||||
|
||||
close(c.done)
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
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 (
|
||||
|
|
|
@ -6,23 +6,91 @@
|
|||
|
||||
package http2
|
||||
|
||||
// flow is the flow control window's size.
|
||||
type flow struct {
|
||||
// inflowMinRefresh is the minimum number of bytes we'll send for a
|
||||
// flow control window update.
|
||||
const inflowMinRefresh = 4 << 10
|
||||
|
||||
// inflow accounts for an inbound flow control window.
|
||||
// It tracks both the latest window sent to the peer (used for enforcement)
|
||||
// and the accumulated unsent window.
|
||||
type inflow struct {
|
||||
avail int32
|
||||
unsent int32
|
||||
}
|
||||
|
||||
// init sets the initial window.
|
||||
func (f *inflow) init(n int32) {
|
||||
f.avail = n
|
||||
}
|
||||
|
||||
// add adds n bytes to the window, with a maximum window size of max,
|
||||
// indicating that the peer can now send us more data.
|
||||
// For example, the user read from a {Request,Response} body and consumed
|
||||
// some of the buffered data, so the peer can now send more.
|
||||
// It returns the number of bytes to send in a WINDOW_UPDATE frame to the peer.
|
||||
// Window updates are accumulated and sent when the unsent capacity
|
||||
// is at least inflowMinRefresh or will at least double the peer's available window.
|
||||
func (f *inflow) add(n int) (connAdd int32) {
|
||||
if n < 0 {
|
||||
panic("negative update")
|
||||
}
|
||||
unsent := int64(f.unsent) + int64(n)
|
||||
// "A sender MUST NOT allow a flow-control window to exceed 2^31-1 octets."
|
||||
// RFC 7540 Section 6.9.1.
|
||||
const maxWindow = 1<<31 - 1
|
||||
if unsent+int64(f.avail) > maxWindow {
|
||||
panic("flow control update exceeds maximum window size")
|
||||
}
|
||||
f.unsent = int32(unsent)
|
||||
if f.unsent < inflowMinRefresh && f.unsent < f.avail {
|
||||
// If there aren't at least inflowMinRefresh bytes of window to send,
|
||||
// and this update won't at least double the window, buffer the update for later.
|
||||
return 0
|
||||
}
|
||||
f.avail += f.unsent
|
||||
f.unsent = 0
|
||||
return int32(unsent)
|
||||
}
|
||||
|
||||
// take attempts to take n bytes from the peer's flow control window.
|
||||
// It reports whether the window has available capacity.
|
||||
func (f *inflow) take(n uint32) bool {
|
||||
if n > uint32(f.avail) {
|
||||
return false
|
||||
}
|
||||
f.avail -= int32(n)
|
||||
return true
|
||||
}
|
||||
|
||||
// takeInflows attempts to take n bytes from two inflows,
|
||||
// typically connection-level and stream-level flows.
|
||||
// It reports whether both windows have available capacity.
|
||||
func takeInflows(f1, f2 *inflow, n uint32) bool {
|
||||
if n > uint32(f1.avail) || n > uint32(f2.avail) {
|
||||
return false
|
||||
}
|
||||
f1.avail -= int32(n)
|
||||
f2.avail -= int32(n)
|
||||
return true
|
||||
}
|
||||
|
||||
// outflow is the outbound flow control window's size.
|
||||
type outflow struct {
|
||||
_ incomparable
|
||||
|
||||
// n is the number of DATA bytes we're allowed to send.
|
||||
// A flow is kept both on a conn and a per-stream.
|
||||
// An outflow is kept both on a conn and a per-stream.
|
||||
n int32
|
||||
|
||||
// conn points to the shared connection-level flow that is
|
||||
// shared by all streams on that conn. It is nil for the flow
|
||||
// conn points to the shared connection-level outflow that is
|
||||
// shared by all streams on that conn. It is nil for the outflow
|
||||
// that's on the conn directly.
|
||||
conn *flow
|
||||
conn *outflow
|
||||
}
|
||||
|
||||
func (f *flow) setConnFlow(cf *flow) { f.conn = cf }
|
||||
func (f *outflow) setConnFlow(cf *outflow) { f.conn = cf }
|
||||
|
||||
func (f *flow) available() int32 {
|
||||
func (f *outflow) available() int32 {
|
||||
n := f.n
|
||||
if f.conn != nil && f.conn.n < n {
|
||||
n = f.conn.n
|
||||
|
@ -30,7 +98,7 @@ func (f *flow) available() int32 {
|
|||
return n
|
||||
}
|
||||
|
||||
func (f *flow) take(n int32) {
|
||||
func (f *outflow) take(n int32) {
|
||||
if n > f.available() {
|
||||
panic("internal error: took too much")
|
||||
}
|
||||
|
@ -42,7 +110,7 @@ func (f *flow) take(n int32) {
|
|||
|
||||
// add adds n bytes (positive or negative) to the flow control window.
|
||||
// It returns false if the sum would exceed 2^31-1.
|
||||
func (f *flow) add(n int32) bool {
|
||||
func (f *outflow) add(n int32) bool {
|
||||
sum := f.n + n
|
||||
if (sum > n) == (f.n > 0) {
|
||||
f.n = sum
|
||||
|
|
|
@ -23,7 +23,7 @@ const frameHeaderLen = 9
|
|||
var padZeros = make([]byte, 255) // zeros for padding
|
||||
|
||||
// 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
|
||||
|
||||
const (
|
||||
|
@ -146,7 +146,7 @@ func typeFrameParser(t FrameType) frameParser {
|
|||
|
||||
// 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 {
|
||||
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
|
||||
// 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 {
|
||||
FrameHeader
|
||||
data []byte
|
||||
|
@ -662,6 +662,15 @@ func (f *Framer) WriteData(streamID uint32, endStream bool, data []byte) error {
|
|||
// It is the caller's responsibility not to violate the maximum frame size
|
||||
// and to not call other Write methods concurrently.
|
||||
func (f *Framer) WriteDataPadded(streamID uint32, endStream bool, data, pad []byte) error {
|
||||
if err := f.startWriteDataPadded(streamID, endStream, data, pad); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.endWrite()
|
||||
}
|
||||
|
||||
// startWriteDataPadded is WriteDataPadded, but only writes the frame to the Framer's internal buffer.
|
||||
// The caller should call endWrite to flush the frame to the underlying writer.
|
||||
func (f *Framer) startWriteDataPadded(streamID uint32, endStream bool, data, pad []byte) error {
|
||||
if !validStreamID(streamID) && !f.AllowIllegalWrites {
|
||||
return errStreamID
|
||||
}
|
||||
|
@ -691,14 +700,14 @@ func (f *Framer) WriteDataPadded(streamID uint32, endStream bool, data, pad []by
|
|||
}
|
||||
f.wbuf = append(f.wbuf, data...)
|
||||
f.wbuf = append(f.wbuf, pad...)
|
||||
return f.endWrite()
|
||||
return nil
|
||||
}
|
||||
|
||||
// A SettingsFrame conveys configuration parameters that affect how
|
||||
// endpoints communicate, such as preferences and constraints on peer
|
||||
// behavior.
|
||||
//
|
||||
// See http://http2.github.io/http2-spec/#SETTINGS
|
||||
// See https://httpwg.org/specs/rfc7540.html#SETTINGS
|
||||
type SettingsFrame struct {
|
||||
FrameHeader
|
||||
p []byte
|
||||
|
@ -837,7 +846,7 @@ func (f *Framer) WriteSettingsAck() error {
|
|||
// A PingFrame is a mechanism for measuring a minimal round trip time
|
||||
// from the sender, as well as determining whether an idle connection
|
||||
// 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 {
|
||||
FrameHeader
|
||||
Data [8]byte
|
||||
|
@ -870,7 +879,7 @@ func (f *Framer) WritePing(ack bool, data [8]byte) error {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
FrameHeader
|
||||
LastStreamID uint32
|
||||
|
@ -934,7 +943,7 @@ func parseUnknownFrame(_ *frameCache, fh FrameHeader, countError func(string), p
|
|||
}
|
||||
|
||||
// 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 {
|
||||
FrameHeader
|
||||
Increment uint32 // never read with high bit set
|
||||
|
@ -1123,7 +1132,7 @@ func (f *Framer) WriteHeaders(p HeadersFrameParam) error {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
FrameHeader
|
||||
PriorityParam
|
||||
|
@ -1193,7 +1202,7 @@ func (f *Framer) WritePriority(streamID uint32, p PriorityParam) error {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
FrameHeader
|
||||
ErrCode ErrCode
|
||||
|
@ -1225,7 +1234,7 @@ func (f *Framer) WriteRSTStream(streamID uint32, code ErrCode) error {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
FrameHeader
|
||||
headerFragBuf []byte
|
||||
|
@ -1266,7 +1275,7 @@ func (f *Framer) WriteContinuation(streamID uint32, endHeaders bool, headerBlock
|
|||
}
|
||||
|
||||
// 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 {
|
||||
FrameHeader
|
||||
PromiseID uint32
|
||||
|
@ -1532,7 +1541,8 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
|
|||
fr.debugReadLoggerf("http2: decoded hpack field %+v", hf)
|
||||
}
|
||||
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, ":")
|
||||
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()
|
||||
}
|
|
@ -27,7 +27,14 @@ func buildCommonHeaderMaps() {
|
|||
"accept-language",
|
||||
"accept-ranges",
|
||||
"age",
|
||||
"access-control-allow-credentials",
|
||||
"access-control-allow-headers",
|
||||
"access-control-allow-methods",
|
||||
"access-control-allow-origin",
|
||||
"access-control-expose-headers",
|
||||
"access-control-max-age",
|
||||
"access-control-request-headers",
|
||||
"access-control-request-method",
|
||||
"allow",
|
||||
"authorization",
|
||||
"cache-control",
|
||||
|
@ -53,6 +60,7 @@ func buildCommonHeaderMaps() {
|
|||
"link",
|
||||
"location",
|
||||
"max-forwards",
|
||||
"origin",
|
||||
"proxy-authenticate",
|
||||
"proxy-authorization",
|
||||
"range",
|
||||
|
@ -68,6 +76,8 @@ func buildCommonHeaderMaps() {
|
|||
"vary",
|
||||
"via",
|
||||
"www-authenticate",
|
||||
"x-forwarded-for",
|
||||
"x-forwarded-proto",
|
||||
}
|
||||
commonLowerHeader = make(map[string]string, len(common))
|
||||
commonCanonHeader = make(map[string]string, len(common))
|
||||
|
@ -85,3 +95,11 @@ func lowerHeader(v string) (lower string, ascii bool) {
|
|||
}
|
||||
return asciiToLower(v)
|
||||
}
|
||||
|
||||
func canonicalHeader(v string) string {
|
||||
buildCommonHeaderMapsOnce()
|
||||
if s, ok := commonCanonHeader[v]; ok {
|
||||
return s
|
||||
}
|
||||
return http.CanonicalHeaderKey(v)
|
||||
}
|
||||
|
|
|
@ -116,6 +116,11 @@ func (e *Encoder) SetMaxDynamicTableSize(v uint32) {
|
|||
e.dynTab.setMaxSize(v)
|
||||
}
|
||||
|
||||
// MaxDynamicTableSize returns the current dynamic header table size.
|
||||
func (e *Encoder) MaxDynamicTableSize() (v uint32) {
|
||||
return e.dynTab.maxSize
|
||||
}
|
||||
|
||||
// SetMaxDynamicTableSizeLimit changes the maximum value that can be
|
||||
// specified in SetMaxDynamicTableSize to v. By default, it is set to
|
||||
// 4096, which is the same size of the default dynamic header table
|
||||
|
@ -191,7 +196,7 @@ func appendTableSize(dst []byte, v uint32) []byte {
|
|||
// bit prefix, to dst and returns the extended buffer.
|
||||
//
|
||||
// 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 {
|
||||
k := uint64((1 << n) - 1)
|
||||
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.
|
||||
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
|
||||
// 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
|
||||
|
@ -158,7 +158,7 @@ func (d *Decoder) SetAllowedMaxDynamicTableSize(v uint32) {
|
|||
}
|
||||
|
||||
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
|
||||
size uint32 // in bytes
|
||||
maxSize uint32 // current maxSize
|
||||
|
@ -211,7 +211,7 @@ func (d *Decoder) at(i uint64) (hf HeaderField, ok bool) {
|
|||
return dt.ents[dt.len()-(int(i)-staticTable.len())], true
|
||||
}
|
||||
|
||||
// Decode decodes an entire block.
|
||||
// DecodeFull decodes an entire block.
|
||||
//
|
||||
// TODO: remove this method and make it incremental later? This is
|
||||
// easier for debugging now.
|
||||
|
@ -307,27 +307,27 @@ func (d *Decoder) parseHeaderFieldRepr() error {
|
|||
case b&128 != 0:
|
||||
// Indexed representation.
|
||||
// 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()
|
||||
case b&192 == 64:
|
||||
// 6.2.1 Literal Header Field with Incremental Indexing
|
||||
// 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)
|
||||
case b&240 == 0:
|
||||
// 6.2.2 Literal Header Field without Indexing
|
||||
// 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)
|
||||
case b&240 == 16:
|
||||
// 6.2.3 Literal Header Field never Indexed
|
||||
// 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)
|
||||
case b&224 == 32:
|
||||
// 6.3 Dynamic Table Size Update
|
||||
// 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()
|
||||
}
|
||||
|
||||
|
@ -359,6 +359,7 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error {
|
|||
|
||||
var hf HeaderField
|
||||
wantStr := d.emitEnabled || it.indexed()
|
||||
var undecodedName undecodedString
|
||||
if nameIdx > 0 {
|
||||
ihf, ok := d.at(nameIdx)
|
||||
if !ok {
|
||||
|
@ -366,15 +367,27 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error {
|
|||
}
|
||||
hf.Name = ihf.Name
|
||||
} else {
|
||||
hf.Name, buf, err = d.readString(buf, wantStr)
|
||||
undecodedName, buf, err = d.readString(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
hf.Value, buf, err = d.readString(buf, wantStr)
|
||||
undecodedValue, buf, err := d.readString(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if wantStr {
|
||||
if nameIdx <= 0 {
|
||||
hf.Name, err = d.decodeString(undecodedName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
hf.Value, err = d.decodeString(undecodedValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
d.buf = buf
|
||||
if it.indexed() {
|
||||
d.dynTab.add(hf)
|
||||
|
@ -420,7 +433,7 @@ var errVarintOverflow = DecodingError{errors.New("varint integer overflow")}
|
|||
|
||||
// readVarInt reads an unsigned variable length integer off the
|
||||
// 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.
|
||||
//
|
||||
|
@ -459,46 +472,52 @@ func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) {
|
|||
return 0, origP, errNeedMore
|
||||
}
|
||||
|
||||
// readString decodes an hpack string from p.
|
||||
// readString reads an hpack string from p.
|
||||
//
|
||||
// wantStr is whether s will be used. If false, decompression and
|
||||
// []byte->string garbage are skipped if s will be ignored
|
||||
// anyway. This does mean that huffman decoding errors for non-indexed
|
||||
// strings past the MAX_HEADER_LIST_SIZE are ignored, but the server
|
||||
// is returning an error anyway, and because they're not indexed, the error
|
||||
// won't affect the decoding state.
|
||||
func (d *Decoder) readString(p []byte, wantStr bool) (s string, remain []byte, err error) {
|
||||
// It returns a reference to the encoded string data to permit deferring decode costs
|
||||
// until after the caller verifies all data is present.
|
||||
func (d *Decoder) readString(p []byte) (u undecodedString, remain []byte, err error) {
|
||||
if len(p) == 0 {
|
||||
return "", p, errNeedMore
|
||||
return u, p, errNeedMore
|
||||
}
|
||||
isHuff := p[0]&128 != 0
|
||||
strLen, p, err := readVarInt(7, p)
|
||||
if err != nil {
|
||||
return "", p, err
|
||||
return u, p, err
|
||||
}
|
||||
if d.maxStrLen != 0 && strLen > uint64(d.maxStrLen) {
|
||||
return "", nil, ErrStringLength
|
||||
// Returning an error here means Huffman decoding errors
|
||||
// for non-indexed strings past the maximum string length
|
||||
// are ignored, but the server is returning an error anyway
|
||||
// and because the string is not indexed the error will not
|
||||
// affect the decoding state.
|
||||
return u, nil, ErrStringLength
|
||||
}
|
||||
if uint64(len(p)) < strLen {
|
||||
return "", p, errNeedMore
|
||||
return u, p, errNeedMore
|
||||
}
|
||||
if !isHuff {
|
||||
if wantStr {
|
||||
s = string(p[:strLen])
|
||||
}
|
||||
return s, p[strLen:], nil
|
||||
}
|
||||
|
||||
if wantStr {
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
buf.Reset() // don't trust others
|
||||
defer bufPool.Put(buf)
|
||||
if err := huffmanDecode(buf, d.maxStrLen, p[:strLen]); err != nil {
|
||||
buf.Reset()
|
||||
return "", nil, err
|
||||
}
|
||||
s = buf.String()
|
||||
buf.Reset() // be nice to GC
|
||||
}
|
||||
return s, p[strLen:], nil
|
||||
u.isHuff = isHuff
|
||||
u.b = p[:strLen]
|
||||
return u, p[strLen:], nil
|
||||
}
|
||||
|
||||
type undecodedString struct {
|
||||
isHuff bool
|
||||
b []byte
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeString(u undecodedString) (string, error) {
|
||||
if !u.isHuff {
|
||||
return string(u.b), nil
|
||||
}
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
buf.Reset() // don't trust others
|
||||
var s string
|
||||
err := huffmanDecode(buf, d.maxStrLen, u.b)
|
||||
if err == nil {
|
||||
s = buf.String()
|
||||
}
|
||||
buf.Reset() // be nice to GC
|
||||
bufPool.Put(buf)
|
||||
return s, err
|
||||
}
|
||||
|
|
|
@ -169,25 +169,50 @@ func buildRootHuffmanNode() {
|
|||
// AppendHuffmanString appends s, as encoded in Huffman codes, to dst
|
||||
// and returns the extended buffer.
|
||||
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++ {
|
||||
if rembits == 8 {
|
||||
dst = append(dst, 0)
|
||||
c := s[i]
|
||||
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])
|
||||
}
|
||||
|
||||
if rembits < 8 {
|
||||
// special EOS symbol
|
||||
code := uint32(0x3fffffff)
|
||||
nbits := uint8(30)
|
||||
|
||||
t := uint8(code >> (nbits - rembits))
|
||||
dst[len(dst)-1] |= t
|
||||
// Add padding bits if necessary
|
||||
if over := n % 8; over > 0 {
|
||||
const (
|
||||
eosCode = 0x3fffffff
|
||||
eosNBits = 30
|
||||
eosPadByte = eosCode >> (eosNBits - 8)
|
||||
)
|
||||
pad := 8 - over
|
||||
x = (x << pad) | (eosPadByte >> over)
|
||||
n += pad // 8 now divides into n exactly
|
||||
}
|
||||
|
||||
return dst
|
||||
// n in (0, 8, 16, 24, 32)
|
||||
switch n / 8 {
|
||||
case 0:
|
||||
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
|
||||
|
@ -199,35 +224,3 @@ func HuffmanEncodeLength(s string) uint64 {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
// go generate gen.go
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
package hpack
|
||||
|
||||
var staticTable = &headerFieldTable{
|
||||
evictCount: 0,
|
||||
byName: map[string]uint64{
|
||||
":authority": 1,
|
||||
":method": 3,
|
||||
":path": 5,
|
||||
":scheme": 7,
|
||||
":status": 14,
|
||||
"accept-charset": 15,
|
||||
"accept-encoding": 16,
|
||||
"accept-language": 17,
|
||||
"accept-ranges": 18,
|
||||
"accept": 19,
|
||||
"access-control-allow-origin": 20,
|
||||
"age": 21,
|
||||
"allow": 22,
|
||||
"authorization": 23,
|
||||
"cache-control": 24,
|
||||
"content-disposition": 25,
|
||||
"content-encoding": 26,
|
||||
"content-language": 27,
|
||||
"content-length": 28,
|
||||
"content-location": 29,
|
||||
"content-range": 30,
|
||||
"content-type": 31,
|
||||
"cookie": 32,
|
||||
"date": 33,
|
||||
"etag": 34,
|
||||
"expect": 35,
|
||||
"expires": 36,
|
||||
"from": 37,
|
||||
"host": 38,
|
||||
"if-match": 39,
|
||||
"if-modified-since": 40,
|
||||
"if-none-match": 41,
|
||||
"if-range": 42,
|
||||
"if-unmodified-since": 43,
|
||||
"last-modified": 44,
|
||||
"link": 45,
|
||||
"location": 46,
|
||||
"max-forwards": 47,
|
||||
"proxy-authenticate": 48,
|
||||
"proxy-authorization": 49,
|
||||
"range": 50,
|
||||
"referer": 51,
|
||||
"refresh": 52,
|
||||
"retry-after": 53,
|
||||
"server": 54,
|
||||
"set-cookie": 55,
|
||||
"strict-transport-security": 56,
|
||||
"transfer-encoding": 57,
|
||||
"user-agent": 58,
|
||||
"vary": 59,
|
||||
"via": 60,
|
||||
"www-authenticate": 61,
|
||||
},
|
||||
byNameValue: map[pairNameValue]uint64{
|
||||
{name: ":authority", value: ""}: 1,
|
||||
{name: ":method", value: "GET"}: 2,
|
||||
{name: ":method", value: "POST"}: 3,
|
||||
{name: ":path", value: "/"}: 4,
|
||||
{name: ":path", value: "/index.html"}: 5,
|
||||
{name: ":scheme", value: "http"}: 6,
|
||||
{name: ":scheme", value: "https"}: 7,
|
||||
{name: ":status", value: "200"}: 8,
|
||||
{name: ":status", value: "204"}: 9,
|
||||
{name: ":status", value: "206"}: 10,
|
||||
{name: ":status", value: "304"}: 11,
|
||||
{name: ":status", value: "400"}: 12,
|
||||
{name: ":status", value: "404"}: 13,
|
||||
{name: ":status", value: "500"}: 14,
|
||||
{name: "accept-charset", value: ""}: 15,
|
||||
{name: "accept-encoding", value: "gzip, deflate"}: 16,
|
||||
{name: "accept-language", value: ""}: 17,
|
||||
{name: "accept-ranges", value: ""}: 18,
|
||||
{name: "accept", value: ""}: 19,
|
||||
{name: "access-control-allow-origin", value: ""}: 20,
|
||||
{name: "age", value: ""}: 21,
|
||||
{name: "allow", value: ""}: 22,
|
||||
{name: "authorization", value: ""}: 23,
|
||||
{name: "cache-control", value: ""}: 24,
|
||||
{name: "content-disposition", value: ""}: 25,
|
||||
{name: "content-encoding", value: ""}: 26,
|
||||
{name: "content-language", value: ""}: 27,
|
||||
{name: "content-length", value: ""}: 28,
|
||||
{name: "content-location", value: ""}: 29,
|
||||
{name: "content-range", value: ""}: 30,
|
||||
{name: "content-type", value: ""}: 31,
|
||||
{name: "cookie", value: ""}: 32,
|
||||
{name: "date", value: ""}: 33,
|
||||
{name: "etag", value: ""}: 34,
|
||||
{name: "expect", value: ""}: 35,
|
||||
{name: "expires", value: ""}: 36,
|
||||
{name: "from", value: ""}: 37,
|
||||
{name: "host", value: ""}: 38,
|
||||
{name: "if-match", value: ""}: 39,
|
||||
{name: "if-modified-since", value: ""}: 40,
|
||||
{name: "if-none-match", value: ""}: 41,
|
||||
{name: "if-range", value: ""}: 42,
|
||||
{name: "if-unmodified-since", value: ""}: 43,
|
||||
{name: "last-modified", value: ""}: 44,
|
||||
{name: "link", value: ""}: 45,
|
||||
{name: "location", value: ""}: 46,
|
||||
{name: "max-forwards", value: ""}: 47,
|
||||
{name: "proxy-authenticate", value: ""}: 48,
|
||||
{name: "proxy-authorization", value: ""}: 49,
|
||||
{name: "range", value: ""}: 50,
|
||||
{name: "referer", value: ""}: 51,
|
||||
{name: "refresh", value: ""}: 52,
|
||||
{name: "retry-after", value: ""}: 53,
|
||||
{name: "server", value: ""}: 54,
|
||||
{name: "set-cookie", value: ""}: 55,
|
||||
{name: "strict-transport-security", value: ""}: 56,
|
||||
{name: "transfer-encoding", value: ""}: 57,
|
||||
{name: "user-agent", value: ""}: 58,
|
||||
{name: "vary", value: ""}: 59,
|
||||
{name: "via", value: ""}: 60,
|
||||
{name: "www-authenticate", value: ""}: 61,
|
||||
},
|
||||
ents: []HeaderField{
|
||||
{Name: ":authority", Value: "", Sensitive: false},
|
||||
{Name: ":method", Value: "GET", Sensitive: false},
|
||||
{Name: ":method", Value: "POST", Sensitive: false},
|
||||
{Name: ":path", Value: "/", Sensitive: false},
|
||||
{Name: ":path", Value: "/index.html", Sensitive: false},
|
||||
{Name: ":scheme", Value: "http", Sensitive: false},
|
||||
{Name: ":scheme", Value: "https", Sensitive: false},
|
||||
{Name: ":status", Value: "200", Sensitive: false},
|
||||
{Name: ":status", Value: "204", Sensitive: false},
|
||||
{Name: ":status", Value: "206", Sensitive: false},
|
||||
{Name: ":status", Value: "304", Sensitive: false},
|
||||
{Name: ":status", Value: "400", Sensitive: false},
|
||||
{Name: ":status", Value: "404", Sensitive: false},
|
||||
{Name: ":status", Value: "500", Sensitive: false},
|
||||
{Name: "accept-charset", Value: "", Sensitive: false},
|
||||
{Name: "accept-encoding", Value: "gzip, deflate", Sensitive: false},
|
||||
{Name: "accept-language", Value: "", Sensitive: false},
|
||||
{Name: "accept-ranges", Value: "", Sensitive: false},
|
||||
{Name: "accept", Value: "", Sensitive: false},
|
||||
{Name: "access-control-allow-origin", Value: "", Sensitive: false},
|
||||
{Name: "age", Value: "", Sensitive: false},
|
||||
{Name: "allow", Value: "", Sensitive: false},
|
||||
{Name: "authorization", Value: "", Sensitive: false},
|
||||
{Name: "cache-control", Value: "", Sensitive: false},
|
||||
{Name: "content-disposition", Value: "", Sensitive: false},
|
||||
{Name: "content-encoding", Value: "", Sensitive: false},
|
||||
{Name: "content-language", Value: "", Sensitive: false},
|
||||
{Name: "content-length", Value: "", Sensitive: false},
|
||||
{Name: "content-location", Value: "", Sensitive: false},
|
||||
{Name: "content-range", Value: "", Sensitive: false},
|
||||
{Name: "content-type", Value: "", Sensitive: false},
|
||||
{Name: "cookie", Value: "", Sensitive: false},
|
||||
{Name: "date", Value: "", Sensitive: false},
|
||||
{Name: "etag", Value: "", Sensitive: false},
|
||||
{Name: "expect", Value: "", Sensitive: false},
|
||||
{Name: "expires", Value: "", Sensitive: false},
|
||||
{Name: "from", Value: "", Sensitive: false},
|
||||
{Name: "host", Value: "", Sensitive: false},
|
||||
{Name: "if-match", Value: "", Sensitive: false},
|
||||
{Name: "if-modified-since", Value: "", Sensitive: false},
|
||||
{Name: "if-none-match", Value: "", Sensitive: false},
|
||||
{Name: "if-range", Value: "", Sensitive: false},
|
||||
{Name: "if-unmodified-since", Value: "", Sensitive: false},
|
||||
{Name: "last-modified", Value: "", Sensitive: false},
|
||||
{Name: "link", Value: "", Sensitive: false},
|
||||
{Name: "location", Value: "", Sensitive: false},
|
||||
{Name: "max-forwards", Value: "", Sensitive: false},
|
||||
{Name: "proxy-authenticate", Value: "", Sensitive: false},
|
||||
{Name: "proxy-authorization", Value: "", Sensitive: false},
|
||||
{Name: "range", Value: "", Sensitive: false},
|
||||
{Name: "referer", Value: "", Sensitive: false},
|
||||
{Name: "refresh", Value: "", Sensitive: false},
|
||||
{Name: "retry-after", Value: "", Sensitive: false},
|
||||
{Name: "server", Value: "", Sensitive: false},
|
||||
{Name: "set-cookie", Value: "", Sensitive: false},
|
||||
{Name: "strict-transport-security", Value: "", Sensitive: false},
|
||||
{Name: "transfer-encoding", Value: "", Sensitive: false},
|
||||
{Name: "user-agent", Value: "", Sensitive: false},
|
||||
{Name: "vary", Value: "", Sensitive: false},
|
||||
{Name: "via", Value: "", Sensitive: false},
|
||||
{Name: "www-authenticate", Value: "", Sensitive: false},
|
||||
},
|
||||
}
|
|
@ -96,8 +96,7 @@ func (t *headerFieldTable) evictOldest(n int) {
|
|||
// meaning t.ents is reversed for dynamic tables. Hence, when t is a dynamic
|
||||
// table, the return value i actually refers to the entry t.ents[t.len()-i].
|
||||
//
|
||||
// All tables are assumed to be a dynamic tables except for the global
|
||||
// staticTable pointer.
|
||||
// All tables are assumed to be a dynamic tables except for the global staticTable.
|
||||
//
|
||||
// See Section 2.3.3.
|
||||
func (t *headerFieldTable) search(f HeaderField) (i uint64, nameValueMatch bool) {
|
||||
|
@ -125,81 +124,6 @@ func (t *headerFieldTable) idToIndex(id uint64) uint64 {
|
|||
return k + 1
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B
|
||||
var staticTable = newStaticTable()
|
||||
var staticTableEntries = [...]HeaderField{
|
||||
{Name: ":authority"},
|
||||
{Name: ":method", Value: "GET"},
|
||||
{Name: ":method", Value: "POST"},
|
||||
{Name: ":path", Value: "/"},
|
||||
{Name: ":path", Value: "/index.html"},
|
||||
{Name: ":scheme", Value: "http"},
|
||||
{Name: ":scheme", Value: "https"},
|
||||
{Name: ":status", Value: "200"},
|
||||
{Name: ":status", Value: "204"},
|
||||
{Name: ":status", Value: "206"},
|
||||
{Name: ":status", Value: "304"},
|
||||
{Name: ":status", Value: "400"},
|
||||
{Name: ":status", Value: "404"},
|
||||
{Name: ":status", Value: "500"},
|
||||
{Name: "accept-charset"},
|
||||
{Name: "accept-encoding", Value: "gzip, deflate"},
|
||||
{Name: "accept-language"},
|
||||
{Name: "accept-ranges"},
|
||||
{Name: "accept"},
|
||||
{Name: "access-control-allow-origin"},
|
||||
{Name: "age"},
|
||||
{Name: "allow"},
|
||||
{Name: "authorization"},
|
||||
{Name: "cache-control"},
|
||||
{Name: "content-disposition"},
|
||||
{Name: "content-encoding"},
|
||||
{Name: "content-language"},
|
||||
{Name: "content-length"},
|
||||
{Name: "content-location"},
|
||||
{Name: "content-range"},
|
||||
{Name: "content-type"},
|
||||
{Name: "cookie"},
|
||||
{Name: "date"},
|
||||
{Name: "etag"},
|
||||
{Name: "expect"},
|
||||
{Name: "expires"},
|
||||
{Name: "from"},
|
||||
{Name: "host"},
|
||||
{Name: "if-match"},
|
||||
{Name: "if-modified-since"},
|
||||
{Name: "if-none-match"},
|
||||
{Name: "if-range"},
|
||||
{Name: "if-unmodified-since"},
|
||||
{Name: "last-modified"},
|
||||
{Name: "link"},
|
||||
{Name: "location"},
|
||||
{Name: "max-forwards"},
|
||||
{Name: "proxy-authenticate"},
|
||||
{Name: "proxy-authorization"},
|
||||
{Name: "range"},
|
||||
{Name: "referer"},
|
||||
{Name: "refresh"},
|
||||
{Name: "retry-after"},
|
||||
{Name: "server"},
|
||||
{Name: "set-cookie"},
|
||||
{Name: "strict-transport-security"},
|
||||
{Name: "transfer-encoding"},
|
||||
{Name: "user-agent"},
|
||||
{Name: "vary"},
|
||||
{Name: "via"},
|
||||
{Name: "www-authenticate"},
|
||||
}
|
||||
|
||||
func newStaticTable() *headerFieldTable {
|
||||
t := &headerFieldTable{}
|
||||
t.init()
|
||||
for _, e := range staticTableEntries[:] {
|
||||
t.addEntry(e)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
var huffmanCodes = [256]uint32{
|
||||
0x1ff8,
|
||||
0x7fffd8,
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
// See https://http2.github.io/ for more information on HTTP/2.
|
||||
//
|
||||
// See https://http2.golang.org/ for a test server running this code.
|
||||
//
|
||||
package http2 // import "golang.org/x/net/http2"
|
||||
|
||||
import (
|
||||
|
@ -56,14 +55,14 @@ const (
|
|||
ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||
|
||||
// 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
|
||||
|
||||
// NextProtoTLS is the NPN/ALPN protocol negotiated during
|
||||
// HTTP/2's TLS setup.
|
||||
NextProtoTLS = "h2"
|
||||
|
||||
// http://http2.github.io/http2-spec/#SettingValues
|
||||
// https://httpwg.org/specs/rfc7540.html#SettingValues
|
||||
initialHeaderTableSize = 4096
|
||||
|
||||
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.
|
||||
type Setting struct {
|
||||
// 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
|
||||
|
||||
// Val is the value.
|
||||
|
@ -144,7 +143,7 @@ func (s Setting) Valid() error {
|
|||
}
|
||||
|
||||
// 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
|
||||
|
||||
const (
|
||||
|
@ -176,10 +175,11 @@ func (s SettingID) String() string {
|
|||
// name (key). See httpguts.ValidHeaderName for the base rules.
|
||||
//
|
||||
// Further, http2 says:
|
||||
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
||||
// characters that are compared in a case-insensitive
|
||||
// fashion. However, header field names MUST be converted to
|
||||
// lowercase prior to their encoding in HTTP/2. "
|
||||
//
|
||||
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
||||
// characters that are compared in a case-insensitive
|
||||
// fashion. However, header field names MUST be converted to
|
||||
// lowercase prior to their encoding in HTTP/2. "
|
||||
func validWireHeaderFieldName(v string) bool {
|
||||
if len(v) == 0 {
|
||||
return false
|
||||
|
@ -365,8 +365,8 @@ func (s *sorter) SortStrings(ss []string) {
|
|||
// validPseudoPath reports whether v is a valid :path pseudo-header
|
||||
// value. It must be either:
|
||||
//
|
||||
// *) a non-empty string starting with '/'
|
||||
// *) the string '*', for OPTIONS requests.
|
||||
// - a non-empty string starting with '/'
|
||||
// - the string '*', for OPTIONS requests.
|
||||
//
|
||||
// For now this is only used a quick check for deciding when to clean
|
||||
// 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
|
||||
}
|
|
@ -98,6 +98,19 @@ type Server struct {
|
|||
// 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
|
||||
|
@ -143,7 +156,7 @@ type Server struct {
|
|||
}
|
||||
|
||||
func (s *Server) initialConnRecvWindowSize() int32 {
|
||||
if s.MaxUploadBufferPerConnection > initialWindowSize {
|
||||
if s.MaxUploadBufferPerConnection >= initialWindowSize {
|
||||
return s.MaxUploadBufferPerConnection
|
||||
}
|
||||
return 1 << 20
|
||||
|
@ -170,6 +183,20 @@ func (s *Server) maxConcurrentStreams() uint32 {
|
|||
return defaultMaxStreams
|
||||
}
|
||||
|
||||
func (s *Server) maxDecoderHeaderTableSize() uint32 {
|
||||
if v := s.MaxDecoderHeaderTableSize; v > 0 {
|
||||
return v
|
||||
}
|
||||
return initialHeaderTableSize
|
||||
}
|
||||
|
||||
func (s *Server) maxEncoderHeaderTableSize() uint32 {
|
||||
if v := s.MaxEncoderHeaderTableSize; v > 0 {
|
||||
return v
|
||||
}
|
||||
return initialHeaderTableSize
|
||||
}
|
||||
|
||||
// maxQueuedControlFrames is the maximum number of control frames like
|
||||
// SETTINGS, PING and RST_STREAM that will be queued for writing before
|
||||
// the connection is closed to prevent memory exhaustion attacks.
|
||||
|
@ -315,6 +342,20 @@ type ServeConnOpts struct {
|
|||
// 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 {
|
||||
|
@ -380,9 +421,9 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
|||
advMaxStreams: s.maxConcurrentStreams(),
|
||||
initialStreamSendWindowSize: initialWindowSize,
|
||||
maxFrameSize: initialMaxFrameSize,
|
||||
headerTableSize: initialHeaderTableSize,
|
||||
serveG: newGoroutineLock(),
|
||||
pushEnabled: true,
|
||||
sawClientPreface: opts.SawClientPreface,
|
||||
}
|
||||
|
||||
s.state.registerConn(sc)
|
||||
|
@ -400,21 +441,22 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
|||
if s.NewWriteScheduler != nil {
|
||||
sc.writeSched = s.NewWriteScheduler()
|
||||
} else {
|
||||
sc.writeSched = NewRandomWriteScheduler()
|
||||
sc.writeSched = NewPriorityWriteScheduler(nil)
|
||||
}
|
||||
|
||||
// These start at the RFC-specified defaults. If there is a higher
|
||||
// configured value for inflow, that will be updated when we send a
|
||||
// WINDOW_UPDATE shortly after sending SETTINGS.
|
||||
sc.flow.add(initialWindowSize)
|
||||
sc.inflow.add(initialWindowSize)
|
||||
sc.inflow.init(initialWindowSize)
|
||||
sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
|
||||
sc.hpackEncoder.SetMaxDynamicTableSizeLimit(s.maxEncoderHeaderTableSize())
|
||||
|
||||
fr := NewFramer(sc.bw, c)
|
||||
if s.CountError != nil {
|
||||
fr.countError = s.CountError
|
||||
}
|
||||
fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil)
|
||||
fr.ReadMetaHeaders = hpack.NewDecoder(s.maxDecoderHeaderTableSize(), nil)
|
||||
fr.MaxHeaderListSize = sc.maxHeaderListSize()
|
||||
fr.SetMaxReadFrameSize(s.maxReadFrameSize())
|
||||
sc.framer = fr
|
||||
|
@ -465,9 +507,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 {
|
||||
hook(sc)
|
||||
}
|
||||
|
||||
if opts.UpgradeRequest != nil {
|
||||
sc.upgradeRequest(opts.UpgradeRequest)
|
||||
opts.UpgradeRequest = nil
|
||||
}
|
||||
|
||||
sc.serve()
|
||||
}
|
||||
|
||||
|
@ -503,8 +563,8 @@ type serverConn struct {
|
|||
wroteFrameCh chan frameWriteResult // from writeFrameAsync -> serve, tickles more frame writes
|
||||
bodyReadCh chan bodyReadMsg // from handlers -> serve
|
||||
serveMsgCh chan interface{} // misc messages & code to send to / run on the serve loop
|
||||
flow flow // conn-wide (not stream-specific) outbound flow control
|
||||
inflow flow // conn-wide inbound flow control
|
||||
flow outflow // conn-wide (not stream-specific) outbound flow control
|
||||
inflow inflow // conn-wide inbound flow control
|
||||
tlsState *tls.ConnectionState // shared by all handlers, like net/http
|
||||
remoteAddrStr string
|
||||
writeSched WriteScheduler
|
||||
|
@ -512,6 +572,7 @@ type serverConn struct {
|
|||
// Everything following is owned by the serve loop; use serveG.check():
|
||||
serveG goroutineLock // used to verify funcs are on serve()
|
||||
pushEnabled bool
|
||||
sawClientPreface bool // preface has already been read, used in h2c upgrade
|
||||
sawFirstSettings bool // got the initial SETTINGS frame after the preface
|
||||
needToSendSettingsAck bool
|
||||
unackedSettings int // how many SETTINGS have we sent without ACKs?
|
||||
|
@ -525,9 +586,9 @@ type serverConn struct {
|
|||
streams map[uint32]*stream
|
||||
initialStreamSendWindowSize int32
|
||||
maxFrameSize int32
|
||||
headerTableSize uint32
|
||||
peerMaxHeaderListSize uint32 // zero means unknown (default)
|
||||
canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case
|
||||
canonHeaderKeysSize int // canonHeader keys size in bytes
|
||||
writingFrame bool // started writing a frame (on serve goroutine or separate)
|
||||
writingFrameAsync bool // started a frame on its own goroutine but haven't heard back on wroteFrameCh
|
||||
needsFrameFlush bool // last frame write wasn't a flush
|
||||
|
@ -580,15 +641,17 @@ type stream struct {
|
|||
cancelCtx func()
|
||||
|
||||
// owned by serverConn's serve loop:
|
||||
bodyBytes int64 // body bytes seen so far
|
||||
declBodyBytes int64 // or -1 if undeclared
|
||||
flow flow // limits writing from Handler to client
|
||||
inflow flow // what the client is allowed to POST/etc to us
|
||||
bodyBytes int64 // body bytes seen so far
|
||||
declBodyBytes int64 // or -1 if undeclared
|
||||
flow outflow // limits writing from Handler to client
|
||||
inflow inflow // what the client is allowed to POST/etc to us
|
||||
state streamState
|
||||
resetQueued bool // RST_STREAM queued for write; set by sc.resetStream
|
||||
gotTrailerHeader bool // HEADER frame for trailers was seen
|
||||
wroteHeaders bool // whether we wrote headers (not status 100)
|
||||
readDeadline *time.Timer // nil if unused
|
||||
writeDeadline *time.Timer // nil if unused
|
||||
closeErr error // set before cw is closed
|
||||
|
||||
trailer http.Header // accumulated trailers
|
||||
reqTrailer http.Header // handler's Request.Trailer
|
||||
|
@ -704,6 +767,13 @@ func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
// maxCachedCanonicalHeadersKeysSize is an arbitrarily-chosen limit on the size
|
||||
// of the entries in the canonHeader cache.
|
||||
// This should be larger than the size of unique, uncommon header keys likely to
|
||||
// be sent by the peer, while not so high as to permit unreasonable memory usage
|
||||
// if the peer sends an unbounded number of unique header keys.
|
||||
const maxCachedCanonicalHeadersKeysSize = 2048
|
||||
|
||||
func (sc *serverConn) canonicalHeader(v string) string {
|
||||
sc.serveG.check()
|
||||
buildCommonHeaderMapsOnce()
|
||||
|
@ -719,14 +789,10 @@ func (sc *serverConn) canonicalHeader(v string) string {
|
|||
sc.canonHeader = make(map[string]string)
|
||||
}
|
||||
cv = http.CanonicalHeaderKey(v)
|
||||
// maxCachedCanonicalHeaders is an arbitrarily-chosen limit on the number of
|
||||
// entries in the canonHeader cache. This should be larger than the number
|
||||
// of unique, uncommon header keys likely to be sent by the peer, while not
|
||||
// so high as to permit unreasonable memory usage if the peer sends an unbounded
|
||||
// number of unique header keys.
|
||||
const maxCachedCanonicalHeaders = 32
|
||||
if len(sc.canonHeader) < maxCachedCanonicalHeaders {
|
||||
size := 100 + len(v)*2 // 100 bytes of map overhead + key + value
|
||||
if sc.canonHeaderKeysSize+size <= maxCachedCanonicalHeadersKeysSize {
|
||||
sc.canonHeader[v] = cv
|
||||
sc.canonHeaderKeysSize += size
|
||||
}
|
||||
return cv
|
||||
}
|
||||
|
@ -777,8 +843,13 @@ type frameWriteResult struct {
|
|||
// and then reports when it's done.
|
||||
// At most one goroutine can be running writeFrameAsync at a time per
|
||||
// serverConn.
|
||||
func (sc *serverConn) writeFrameAsync(wr FrameWriteRequest) {
|
||||
err := wr.write.writeFrame(sc)
|
||||
func (sc *serverConn) writeFrameAsync(wr FrameWriteRequest, wd *writeData) {
|
||||
var err error
|
||||
if wd == nil {
|
||||
err = wr.write.writeFrame(sc)
|
||||
} else {
|
||||
err = sc.framer.endWrite()
|
||||
}
|
||||
sc.wroteFrameCh <- frameWriteResult{wr: wr, err: err}
|
||||
}
|
||||
|
||||
|
@ -828,6 +899,7 @@ func (sc *serverConn) serve() {
|
|||
{SettingMaxFrameSize, sc.srv.maxReadFrameSize()},
|
||||
{SettingMaxConcurrentStreams, sc.advMaxStreams},
|
||||
{SettingMaxHeaderListSize, sc.maxHeaderListSize()},
|
||||
{SettingHeaderTableSize, sc.srv.maxDecoderHeaderTableSize()},
|
||||
{SettingInitialWindowSize, uint32(sc.srv.initialStreamRecvWindowSize())},
|
||||
},
|
||||
})
|
||||
|
@ -914,6 +986,8 @@ func (sc *serverConn) serve() {
|
|||
}
|
||||
case *startPushRequest:
|
||||
sc.startPush(v)
|
||||
case func(*serverConn):
|
||||
v(sc)
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected type %T", v))
|
||||
}
|
||||
|
@ -974,6 +1048,9 @@ var errPrefaceTimeout = errors.New("timeout waiting for client preface")
|
|||
// returns errPrefaceTimeout on timeout, or an error if the greeting
|
||||
// is invalid.
|
||||
func (sc *serverConn) readPreface() error {
|
||||
if sc.sawClientPreface {
|
||||
return nil
|
||||
}
|
||||
errc := make(chan error, 1)
|
||||
go func() {
|
||||
// Read the client preface
|
||||
|
@ -1179,9 +1256,16 @@ func (sc *serverConn) startFrameWrite(wr FrameWriteRequest) {
|
|||
sc.writingFrameAsync = false
|
||||
err := wr.write.writeFrame(sc)
|
||||
sc.wroteFrame(frameWriteResult{wr: wr, err: err})
|
||||
} else if wd, ok := wr.write.(*writeData); ok {
|
||||
// Encode the frame in the serve goroutine, to ensure we don't have
|
||||
// any lingering asynchronous references to data passed to Write.
|
||||
// See https://go.dev/issue/58446.
|
||||
sc.framer.startWriteDataPadded(wd.streamID, wd.endStream, wd.p, nil)
|
||||
sc.writingFrameAsync = true
|
||||
go sc.writeFrameAsync(wr, wd)
|
||||
} else {
|
||||
sc.writingFrameAsync = true
|
||||
go sc.writeFrameAsync(wr)
|
||||
go sc.writeFrameAsync(wr, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1334,6 +1418,9 @@ func (sc *serverConn) startGracefulShutdownInternal() {
|
|||
func (sc *serverConn) goAway(code ErrCode) {
|
||||
sc.serveG.check()
|
||||
if sc.inGoAway {
|
||||
if sc.goAwayCode == ErrCodeNo {
|
||||
sc.goAwayCode = code
|
||||
}
|
||||
return
|
||||
}
|
||||
sc.inGoAway = true
|
||||
|
@ -1421,6 +1508,21 @@ func (sc *serverConn) processFrame(f Frame) error {
|
|||
sc.sawFirstSettings = true
|
||||
}
|
||||
|
||||
// Discard frames for streams initiated after the identified last
|
||||
// stream sent in a GOAWAY, or all frames after sending an error.
|
||||
// We still need to return connection-level flow control for DATA frames.
|
||||
// RFC 9113 Section 6.8.
|
||||
if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || f.Header().StreamID > sc.maxClientStreamID) {
|
||||
|
||||
if f, ok := f.(*DataFrame); ok {
|
||||
if !sc.inflow.take(f.Length) {
|
||||
return sc.countError("data_flow", streamError(f.Header().StreamID, ErrCodeFlowControl))
|
||||
}
|
||||
sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch f := f.(type) {
|
||||
case *SettingsFrame:
|
||||
return sc.processSettings(f)
|
||||
|
@ -1463,9 +1565,6 @@ func (sc *serverConn) processPing(f *PingFrame) error {
|
|||
// PROTOCOL_ERROR."
|
||||
return sc.countError("ping_on_stream", ConnectionError(ErrCodeProtocol))
|
||||
}
|
||||
if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
|
||||
return nil
|
||||
}
|
||||
sc.writeFrame(FrameWriteRequest{write: writePingAck{f}})
|
||||
return nil
|
||||
}
|
||||
|
@ -1527,6 +1626,9 @@ func (sc *serverConn) closeStream(st *stream, err error) {
|
|||
panic(fmt.Sprintf("invariant; can't close stream in state %v", st.state))
|
||||
}
|
||||
st.state = stateClosed
|
||||
if st.readDeadline != nil {
|
||||
st.readDeadline.Stop()
|
||||
}
|
||||
if st.writeDeadline != nil {
|
||||
st.writeDeadline.Stop()
|
||||
}
|
||||
|
@ -1552,6 +1654,14 @@ func (sc *serverConn) closeStream(st *stream, err error) {
|
|||
|
||||
p.CloseWithError(err)
|
||||
}
|
||||
if e, ok := err.(StreamError); ok {
|
||||
if e.Cause != nil {
|
||||
err = e.Cause
|
||||
} else {
|
||||
err = errStreamClosed
|
||||
}
|
||||
}
|
||||
st.closeErr = err
|
||||
st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc
|
||||
sc.writeSched.CloseStream(st.id)
|
||||
}
|
||||
|
@ -1594,7 +1704,6 @@ func (sc *serverConn) processSetting(s Setting) error {
|
|||
}
|
||||
switch s.ID {
|
||||
case SettingHeaderTableSize:
|
||||
sc.headerTableSize = s.Val
|
||||
sc.hpackEncoder.SetMaxDynamicTableSize(s.Val)
|
||||
case SettingEnablePush:
|
||||
sc.pushEnabled = s.Val != 0
|
||||
|
@ -1648,16 +1757,6 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
|
|||
func (sc *serverConn) processData(f *DataFrame) error {
|
||||
sc.serveG.check()
|
||||
id := f.Header().StreamID
|
||||
if sc.inGoAway && (sc.goAwayCode != ErrCodeNo || id > sc.maxClientStreamID) {
|
||||
// Discard all DATA frames if the GOAWAY is due to an
|
||||
// error, or:
|
||||
//
|
||||
// Section 6.8: After sending a GOAWAY frame, the sender
|
||||
// can discard frames for streams initiated by the
|
||||
// receiver with identifiers higher than the identified
|
||||
// last stream.
|
||||
return nil
|
||||
}
|
||||
|
||||
data := f.Data()
|
||||
state, st := sc.state(id)
|
||||
|
@ -1688,14 +1787,9 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|||
// But still enforce their connection-level flow control,
|
||||
// and return any flow control bytes since we're not going
|
||||
// to consume them.
|
||||
if sc.inflow.available() < int32(f.Length) {
|
||||
if !sc.inflow.take(f.Length) {
|
||||
return sc.countError("data_flow", streamError(id, ErrCodeFlowControl))
|
||||
}
|
||||
// Deduct the flow control from inflow, since we're
|
||||
// going to immediately add it back in
|
||||
// sendWindowUpdate, which also schedules sending the
|
||||
// frames.
|
||||
sc.inflow.take(int32(f.Length))
|
||||
sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
|
||||
|
||||
if st != nil && st.resetQueued {
|
||||
|
@ -1710,6 +1804,11 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|||
|
||||
// Sender sending more than they'd declared?
|
||||
if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
|
||||
if !sc.inflow.take(f.Length) {
|
||||
return sc.countError("data_flow", streamError(id, ErrCodeFlowControl))
|
||||
}
|
||||
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))
|
||||
// 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
|
||||
|
@ -1718,10 +1817,9 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|||
}
|
||||
if f.Length > 0 {
|
||||
// Check whether the client has flow control quota.
|
||||
if st.inflow.available() < int32(f.Length) {
|
||||
if !takeInflows(&sc.inflow, &st.inflow, f.Length) {
|
||||
return sc.countError("flow_on_data_length", streamError(id, ErrCodeFlowControl))
|
||||
}
|
||||
st.inflow.take(int32(f.Length))
|
||||
|
||||
if len(data) > 0 {
|
||||
wrote, err := st.body.Write(data)
|
||||
|
@ -1737,10 +1835,12 @@ func (sc *serverConn) processData(f *DataFrame) error {
|
|||
|
||||
// Return any padded flow control now, since we won't
|
||||
// refund it later on body reads.
|
||||
if pad := int32(f.Length) - int32(len(data)); pad > 0 {
|
||||
sc.sendWindowUpdate32(nil, pad)
|
||||
sc.sendWindowUpdate32(st, pad)
|
||||
}
|
||||
// Call sendWindowUpdate even if there is no padding,
|
||||
// to return buffered flow control credit if the sent
|
||||
// window has shrunk.
|
||||
pad := int32(f.Length) - int32(len(data))
|
||||
sc.sendWindowUpdate32(nil, pad)
|
||||
sc.sendWindowUpdate32(st, pad)
|
||||
}
|
||||
if f.StreamEnded() {
|
||||
st.endStream()
|
||||
|
@ -1794,19 +1894,27 @@ func (st *stream) copyTrailersToHandlerRequest() {
|
|||
}
|
||||
}
|
||||
|
||||
// onReadTimeout is run on its own goroutine (from time.AfterFunc)
|
||||
// when the stream's ReadTimeout has fired.
|
||||
func (st *stream) onReadTimeout() {
|
||||
// Wrap the ErrDeadlineExceeded to avoid callers depending on us
|
||||
// returning the bare error.
|
||||
st.body.CloseWithError(fmt.Errorf("%w", os.ErrDeadlineExceeded))
|
||||
}
|
||||
|
||||
// onWriteTimeout is run on its own goroutine (from time.AfterFunc)
|
||||
// when the stream's WriteTimeout has fired.
|
||||
func (st *stream) onWriteTimeout() {
|
||||
st.sc.writeFrameFromHandler(FrameWriteRequest{write: streamError(st.id, ErrCodeInternal)})
|
||||
st.sc.writeFrameFromHandler(FrameWriteRequest{write: StreamError{
|
||||
StreamID: st.id,
|
||||
Code: ErrCodeInternal,
|
||||
Cause: os.ErrDeadlineExceeded,
|
||||
}})
|
||||
}
|
||||
|
||||
func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
||||
sc.serveG.check()
|
||||
id := f.StreamID
|
||||
if sc.inGoAway {
|
||||
// Ignore.
|
||||
return nil
|
||||
}
|
||||
// http://tools.ietf.org/html/rfc7540#section-5.1.1
|
||||
// Streams initiated by a client MUST use odd-numbered stream
|
||||
// identifiers. [...] An endpoint that receives an unexpected
|
||||
|
@ -1909,12 +2017,35 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
|||
// (in Go 1.8), though. That's a more sane option anyway.
|
||||
if sc.hs.ReadTimeout != 0 {
|
||||
sc.conn.SetReadDeadline(time.Time{})
|
||||
if st.body != nil {
|
||||
st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
go sc.runHandler(rw, req, handler)
|
||||
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 {
|
||||
sc := st.sc
|
||||
sc.serveG.check()
|
||||
|
@ -1957,9 +2088,6 @@ func (sc *serverConn) checkPriority(streamID uint32, p PriorityParam) error {
|
|||
}
|
||||
|
||||
func (sc *serverConn) processPriority(f *PriorityFrame) error {
|
||||
if sc.inGoAway {
|
||||
return nil
|
||||
}
|
||||
if err := sc.checkPriority(f.StreamID, f.PriorityParam); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1984,8 +2112,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream
|
|||
st.cw.Init()
|
||||
st.flow.conn = &sc.flow // link to conn-level counter
|
||||
st.flow.add(sc.initialStreamSendWindowSize)
|
||||
st.inflow.conn = &sc.inflow // link to conn-level counter
|
||||
st.inflow.add(sc.srv.initialStreamRecvWindowSize())
|
||||
st.inflow.init(sc.srv.initialStreamRecvWindowSize())
|
||||
if sc.hs.WriteTimeout != 0 {
|
||||
st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout)
|
||||
}
|
||||
|
@ -2033,12 +2160,6 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res
|
|||
return nil, nil, sc.countError("bad_path_method", streamError(f.StreamID, ErrCodeProtocol))
|
||||
}
|
||||
|
||||
bodyOpen := !f.StreamEnded()
|
||||
if rp.method == "HEAD" && bodyOpen {
|
||||
// HEAD requests can't have bodies
|
||||
return nil, nil, sc.countError("head_body", streamError(f.StreamID, ErrCodeProtocol))
|
||||
}
|
||||
|
||||
rp.header = make(http.Header)
|
||||
for _, hf := range f.RegularFields() {
|
||||
rp.header.Add(sc.canonicalHeader(hf.Name), hf.Value)
|
||||
|
@ -2051,6 +2172,7 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
bodyOpen := !f.StreamEnded()
|
||||
if bodyOpen {
|
||||
if vv, ok := rp.header["Content-Length"]; ok {
|
||||
if cl, err := strconv.ParseUint(vv[0], 10, 63); err == nil {
|
||||
|
@ -2082,7 +2204,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
|
|||
tlsState = sc.tlsState
|
||||
}
|
||||
|
||||
needsContinue := rp.header.Get("Expect") == "100-continue"
|
||||
needsContinue := httpguts.HeaderValuesContainsToken(rp.header["Expect"], "100-continue")
|
||||
if needsContinue {
|
||||
rp.header.Del("Expect")
|
||||
}
|
||||
|
@ -2145,6 +2267,11 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
|
|||
}
|
||||
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)
|
||||
bwSave := rws.bw
|
||||
*rws = responseWriterState{} // zero all the fields
|
||||
|
@ -2153,10 +2280,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
|
|||
rws.bw.Reset(chunkWriter{rws})
|
||||
rws.stream = st
|
||||
rws.req = req
|
||||
rws.body = body
|
||||
|
||||
rw := &responseWriter{rws: rws}
|
||||
return rw, req, nil
|
||||
return &responseWriter{rws: rws}
|
||||
}
|
||||
|
||||
// Run on its own goroutine.
|
||||
|
@ -2164,6 +2288,9 @@ func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler
|
|||
didPanic := true
|
||||
defer func() {
|
||||
rw.rws.stream.cancelCtx()
|
||||
if req.MultipartForm != nil {
|
||||
req.MultipartForm.RemoveAll()
|
||||
}
|
||||
if didPanic {
|
||||
e := recover()
|
||||
sc.writeFrameFromHandler(FrameWriteRequest{
|
||||
|
@ -2267,47 +2394,28 @@ func (sc *serverConn) noteBodyRead(st *stream, n int) {
|
|||
}
|
||||
|
||||
// st may be nil for conn-level
|
||||
func (sc *serverConn) sendWindowUpdate(st *stream, n int) {
|
||||
sc.serveG.check()
|
||||
// "The legal range for the increment to the flow control
|
||||
// window is 1 to 2^31-1 (2,147,483,647) octets."
|
||||
// A Go Read call on 64-bit machines could in theory read
|
||||
// a larger Read than this. Very unlikely, but we handle it here
|
||||
// rather than elsewhere for now.
|
||||
const maxUint31 = 1<<31 - 1
|
||||
for n >= maxUint31 {
|
||||
sc.sendWindowUpdate32(st, maxUint31)
|
||||
n -= maxUint31
|
||||
}
|
||||
sc.sendWindowUpdate32(st, int32(n))
|
||||
func (sc *serverConn) sendWindowUpdate32(st *stream, n int32) {
|
||||
sc.sendWindowUpdate(st, int(n))
|
||||
}
|
||||
|
||||
// st may be nil for conn-level
|
||||
func (sc *serverConn) sendWindowUpdate32(st *stream, n int32) {
|
||||
func (sc *serverConn) sendWindowUpdate(st *stream, n int) {
|
||||
sc.serveG.check()
|
||||
if n == 0 {
|
||||
var streamID uint32
|
||||
var send int32
|
||||
if st == nil {
|
||||
send = sc.inflow.add(n)
|
||||
} else {
|
||||
streamID = st.id
|
||||
send = st.inflow.add(n)
|
||||
}
|
||||
if send == 0 {
|
||||
return
|
||||
}
|
||||
if n < 0 {
|
||||
panic("negative update")
|
||||
}
|
||||
var streamID uint32
|
||||
if st != nil {
|
||||
streamID = st.id
|
||||
}
|
||||
sc.writeFrame(FrameWriteRequest{
|
||||
write: writeWindowUpdate{streamID: streamID, n: uint32(n)},
|
||||
write: writeWindowUpdate{streamID: streamID, n: uint32(send)},
|
||||
stream: st,
|
||||
})
|
||||
var ok bool
|
||||
if st == nil {
|
||||
ok = sc.inflow.add(n)
|
||||
} else {
|
||||
ok = st.inflow.add(n)
|
||||
}
|
||||
if !ok {
|
||||
panic("internal error; sent too many window updates without decrements?")
|
||||
}
|
||||
}
|
||||
|
||||
// requestBody is the Handler's Request.Body type.
|
||||
|
@ -2316,17 +2424,18 @@ type requestBody struct {
|
|||
_ incomparable
|
||||
stream *stream
|
||||
conn *serverConn
|
||||
closed bool // for use by Close only
|
||||
sawEOF bool // for use by Read only
|
||||
pipe *pipe // non-nil if we have a HTTP entity message body
|
||||
needsContinue bool // need to send a 100-continue
|
||||
closeOnce sync.Once // for use by Close only
|
||||
sawEOF bool // for use by Read only
|
||||
pipe *pipe // non-nil if we have a HTTP entity message body
|
||||
needsContinue bool // need to send a 100-continue
|
||||
}
|
||||
|
||||
func (b *requestBody) Close() error {
|
||||
if b.pipe != nil && !b.closed {
|
||||
b.pipe.BreakWithError(errClosedBody)
|
||||
}
|
||||
b.closed = true
|
||||
b.closeOnce.Do(func() {
|
||||
if b.pipe != nil {
|
||||
b.pipe.BreakWithError(errClosedBody)
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -2370,7 +2479,6 @@ type responseWriterState struct {
|
|||
// immutable within a request:
|
||||
stream *stream
|
||||
req *http.Request
|
||||
body *requestBody // to close at end of request, if DATA frames didn't
|
||||
conn *serverConn
|
||||
|
||||
// TODO: adjust buffer writing sizes based on server config, frame size updates from peer, etc
|
||||
|
@ -2395,7 +2503,15 @@ type responseWriterState struct {
|
|||
|
||||
type chunkWriter struct{ rws *responseWriterState }
|
||||
|
||||
func (cw chunkWriter) Write(p []byte) (n int, err error) { return cw.rws.writeChunk(p) }
|
||||
func (cw chunkWriter) Write(p []byte) (n int, err error) {
|
||||
n, err = cw.rws.writeChunk(p)
|
||||
if err == errStreamClosed {
|
||||
// If writing failed because the stream has been closed,
|
||||
// return the reason it was closed.
|
||||
err = cw.rws.stream.closeErr
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (rws *responseWriterState) hasTrailers() bool { return len(rws.trailers) > 0 }
|
||||
|
||||
|
@ -2434,6 +2550,10 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
|||
rws.writeHeader(200)
|
||||
}
|
||||
|
||||
if rws.handlerDone {
|
||||
rws.promoteUndeclaredTrailers()
|
||||
}
|
||||
|
||||
isHeadResp := rws.req.Method == "HEAD"
|
||||
if !rws.sentHeader {
|
||||
rws.sentHeader = true
|
||||
|
@ -2505,10 +2625,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
if rws.handlerDone {
|
||||
rws.promoteUndeclaredTrailers()
|
||||
}
|
||||
|
||||
// only send trailers if they have actually been defined by the
|
||||
// server handler.
|
||||
hasNonemptyTrailers := rws.hasNonemptyTrailers()
|
||||
|
@ -2546,8 +2662,9 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
|||
// 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
|
||||
//
|
||||
// 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
|
||||
|
@ -2588,23 +2705,85 @@ func (rws *responseWriterState) promoteUndeclaredTrailers() {
|
|||
}
|
||||
}
|
||||
|
||||
func (w *responseWriter) SetReadDeadline(deadline time.Time) error {
|
||||
st := w.rws.stream
|
||||
if !deadline.IsZero() && deadline.Before(time.Now()) {
|
||||
// If we're setting a deadline in the past, reset the stream immediately
|
||||
// so writes after SetWriteDeadline returns will fail.
|
||||
st.onReadTimeout()
|
||||
return nil
|
||||
}
|
||||
w.rws.conn.sendServeMsg(func(sc *serverConn) {
|
||||
if st.readDeadline != nil {
|
||||
if !st.readDeadline.Stop() {
|
||||
// Deadline already exceeded, or stream has been closed.
|
||||
return
|
||||
}
|
||||
}
|
||||
if deadline.IsZero() {
|
||||
st.readDeadline = nil
|
||||
} else if st.readDeadline == nil {
|
||||
st.readDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onReadTimeout)
|
||||
} else {
|
||||
st.readDeadline.Reset(deadline.Sub(time.Now()))
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *responseWriter) SetWriteDeadline(deadline time.Time) error {
|
||||
st := w.rws.stream
|
||||
if !deadline.IsZero() && deadline.Before(time.Now()) {
|
||||
// If we're setting a deadline in the past, reset the stream immediately
|
||||
// so writes after SetWriteDeadline returns will fail.
|
||||
st.onWriteTimeout()
|
||||
return nil
|
||||
}
|
||||
w.rws.conn.sendServeMsg(func(sc *serverConn) {
|
||||
if st.writeDeadline != nil {
|
||||
if !st.writeDeadline.Stop() {
|
||||
// Deadline already exceeded, or stream has been closed.
|
||||
return
|
||||
}
|
||||
}
|
||||
if deadline.IsZero() {
|
||||
st.writeDeadline = nil
|
||||
} else if st.writeDeadline == nil {
|
||||
st.writeDeadline = time.AfterFunc(deadline.Sub(time.Now()), st.onWriteTimeout)
|
||||
} else {
|
||||
st.writeDeadline.Reset(deadline.Sub(time.Now()))
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *responseWriter) Flush() {
|
||||
w.FlushError()
|
||||
}
|
||||
|
||||
func (w *responseWriter) FlushError() error {
|
||||
rws := w.rws
|
||||
if rws == nil {
|
||||
panic("Header called after Handler finished")
|
||||
}
|
||||
var err error
|
||||
if rws.bw.Buffered() > 0 {
|
||||
if err := rws.bw.Flush(); err != nil {
|
||||
// Ignore the error. The frame writer already knows.
|
||||
return
|
||||
}
|
||||
err = rws.bw.Flush()
|
||||
} else {
|
||||
// The bufio.Writer won't call chunkWriter.Write
|
||||
// (writeChunk with zero bytes, so we have to do it
|
||||
// ourselves to force the HTTP response header and/or
|
||||
// final DATA frame (with END_STREAM) to be sent.
|
||||
rws.writeChunk(nil)
|
||||
_, err = chunkWriter{rws}.Write(nil)
|
||||
if err == nil {
|
||||
select {
|
||||
case <-rws.stream.cw:
|
||||
err = rws.stream.closeErr
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *responseWriter) CloseNotify() <-chan bool {
|
||||
|
@ -2643,8 +2822,7 @@ func checkWriteHeaderCode(code int) {
|
|||
// Issue 22880: require valid WriteHeader status codes.
|
||||
// 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
|
||||
// at http://httpwg.org/specs/rfc7231.html#status.codes)
|
||||
// and we might block under 200 (once we have more mature 1xx support).
|
||||
// at http://httpwg.org/specs/rfc7231.html#status.codes).
|
||||
// But for now any three digits.
|
||||
//
|
||||
// We used to send "HTTP/1.1 000 0" on the wire in responses but there's
|
||||
|
@ -2665,13 +2843,41 @@ func (w *responseWriter) WriteHeader(code int) {
|
|||
}
|
||||
|
||||
func (rws *responseWriterState) writeHeader(code int) {
|
||||
if !rws.wroteHeader {
|
||||
checkWriteHeaderCode(code)
|
||||
rws.wroteHeader = true
|
||||
rws.status = code
|
||||
if len(rws.handlerHeader) > 0 {
|
||||
rws.snapHeader = cloneHeader(rws.handlerHeader)
|
||||
if rws.wroteHeader {
|
||||
return
|
||||
}
|
||||
|
||||
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.status = code
|
||||
if len(rws.handlerHeader) > 0 {
|
||||
rws.snapHeader = cloneHeader(rws.handlerHeader)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"io/fs"
|
||||
"log"
|
||||
"math"
|
||||
mathrand "math/rand"
|
||||
|
@ -47,10 +47,6 @@ const (
|
|||
// we buffer per stream.
|
||||
transportDefaultStreamFlow = 4 << 20
|
||||
|
||||
// transportDefaultStreamMinRefresh is the minimum number of bytes we'll send
|
||||
// a stream-level WINDOW_UPDATE for at a time.
|
||||
transportDefaultStreamMinRefresh = 4 << 10
|
||||
|
||||
defaultUserAgent = "Go-http-client/2.0"
|
||||
|
||||
// initialMaxConcurrentStreams is a connections maxConcurrentStreams until
|
||||
|
@ -68,13 +64,23 @@ const (
|
|||
// A Transport internally caches connections to servers. It is safe
|
||||
// for concurrent use by multiple goroutines.
|
||||
type Transport struct {
|
||||
// DialTLS specifies an optional dial function for creating
|
||||
// TLS connections for requests.
|
||||
// DialTLSContext specifies an optional dial function with context for
|
||||
// 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,
|
||||
// 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
|
||||
|
@ -108,6 +114,28 @@ type Transport struct {
|
|||
// 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
|
||||
|
@ -161,6 +189,19 @@ func (t *Transport) maxHeaderListSize() uint32 {
|
|||
return t.MaxHeaderListSize
|
||||
}
|
||||
|
||||
func (t *Transport) maxFrameReadSize() uint32 {
|
||||
if t.MaxReadFrameSize == 0 {
|
||||
return 0 // use the default provided by the peer
|
||||
}
|
||||
if t.MaxReadFrameSize < minMaxFrameSize {
|
||||
return minMaxFrameSize
|
||||
}
|
||||
if t.MaxReadFrameSize > maxFrameSize {
|
||||
return maxFrameSize
|
||||
}
|
||||
return t.MaxReadFrameSize
|
||||
}
|
||||
|
||||
func (t *Transport) disableCompression() bool {
|
||||
return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression)
|
||||
}
|
||||
|
@ -249,7 +290,8 @@ func (t *Transport) initConnPool() {
|
|||
// HTTP/2 server.
|
||||
type ClientConn struct {
|
||||
t *Transport
|
||||
tconn net.Conn // usually *tls.Conn, except specialized impls
|
||||
tconn net.Conn // usually *tls.Conn, except specialized impls
|
||||
tconnClosed bool
|
||||
tlsState *tls.ConnectionState // nil only for specialized impls
|
||||
reused uint32 // whether conn is being reused; atomic
|
||||
singleUse bool // whether being used for a single http.Request
|
||||
|
@ -264,8 +306,8 @@ type ClientConn struct {
|
|||
|
||||
mu sync.Mutex // guards following
|
||||
cond *sync.Cond // hold mu; broadcast on flow/closed changes
|
||||
flow flow // our conn-level flow control quota (cs.flow is per stream)
|
||||
inflow flow // peer's conn-level flow control
|
||||
flow outflow // our conn-level flow control quota (cs.outflow is per stream)
|
||||
inflow inflow // peer's conn-level flow control
|
||||
doNotReuse bool // whether conn is marked to not be reused for any future requests
|
||||
closing bool
|
||||
closed bool
|
||||
|
@ -282,10 +324,11 @@ type ClientConn struct {
|
|||
lastActive time.Time
|
||||
lastIdle time.Time // time last idle
|
||||
// Settings from peer: (also guarded by wmu)
|
||||
maxFrameSize uint32
|
||||
maxConcurrentStreams uint32
|
||||
peerMaxHeaderListSize uint64
|
||||
initialWindowSize uint32
|
||||
maxFrameSize uint32
|
||||
maxConcurrentStreams uint32
|
||||
peerMaxHeaderListSize uint64
|
||||
peerMaxHeaderTableSize uint32
|
||||
initialWindowSize uint32
|
||||
|
||||
// reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests.
|
||||
// Write to reqHeaderMu to lock it, read from it to unlock.
|
||||
|
@ -329,14 +372,14 @@ type clientStream struct {
|
|||
respHeaderRecv chan struct{} // closed when headers are received
|
||||
res *http.Response // set if respHeaderRecv is closed
|
||||
|
||||
flow flow // guarded by cc.mu
|
||||
inflow flow // guarded by cc.mu
|
||||
bytesRemain int64 // -1 means unknown; owned by transportResponseBody.Read
|
||||
readErr error // sticky read error; owned by transportResponseBody.Read
|
||||
flow outflow // guarded by cc.mu
|
||||
inflow inflow // guarded by cc.mu
|
||||
bytesRemain int64 // -1 means unknown; owned by transportResponseBody.Read
|
||||
readErr error // sticky read error; owned by transportResponseBody.Read
|
||||
|
||||
reqBody io.ReadCloser
|
||||
reqBodyContentLength int64 // -1 means unknown
|
||||
reqBodyClosed bool // body has been closed; guarded by cc.mu
|
||||
reqBodyContentLength int64 // -1 means unknown
|
||||
reqBodyClosed chan struct{} // guarded by cc.mu; non-nil on Close, closed when done
|
||||
|
||||
// owned by writeRequest:
|
||||
sentEndStream bool // sent an END_STREAM flag to the peer
|
||||
|
@ -376,9 +419,8 @@ func (cs *clientStream) abortStreamLocked(err error) {
|
|||
cs.abortErr = err
|
||||
close(cs.abort)
|
||||
})
|
||||
if cs.reqBody != nil && !cs.reqBodyClosed {
|
||||
cs.reqBody.Close()
|
||||
cs.reqBodyClosed = true
|
||||
if cs.reqBody != nil {
|
||||
cs.closeReqBodyLocked()
|
||||
}
|
||||
// TODO(dneil): Clean up tests where cs.cc.cond is nil.
|
||||
if cs.cc.cond != nil {
|
||||
|
@ -391,13 +433,24 @@ func (cs *clientStream) abortRequestBodyWrite() {
|
|||
cc := cs.cc
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
if cs.reqBody != nil && !cs.reqBodyClosed {
|
||||
cs.reqBody.Close()
|
||||
cs.reqBodyClosed = true
|
||||
if cs.reqBody != nil && cs.reqBodyClosed == nil {
|
||||
cs.closeReqBodyLocked()
|
||||
cc.cond.Broadcast()
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *clientStream) closeReqBodyLocked() {
|
||||
if cs.reqBodyClosed != nil {
|
||||
return
|
||||
}
|
||||
cs.reqBodyClosed = make(chan struct{})
|
||||
reqBodyClosed := cs.reqBodyClosed
|
||||
go func() {
|
||||
cs.reqBody.Close()
|
||||
close(reqBodyClosed)
|
||||
}()
|
||||
}
|
||||
|
||||
type stickyErrWriter struct {
|
||||
conn net.Conn
|
||||
timeout time.Duration
|
||||
|
@ -481,6 +534,15 @@ func authorityAddr(scheme string, authority string) (addr string) {
|
|||
return net.JoinHostPort(host, port)
|
||||
}
|
||||
|
||||
var retryBackoffHook func(time.Duration) *time.Timer
|
||||
|
||||
func backoffNewTimer(d time.Duration) *time.Timer {
|
||||
if retryBackoffHook != nil {
|
||||
return retryBackoffHook(d)
|
||||
}
|
||||
return time.NewTimer(d)
|
||||
}
|
||||
|
||||
// RoundTripOpt is like RoundTrip, but takes options.
|
||||
func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
|
||||
if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTP)) {
|
||||
|
@ -501,14 +563,19 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
|
|||
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", err)
|
||||
continue
|
||||
}
|
||||
backoff := float64(uint(1) << (uint(retry) - 1))
|
||||
backoff += backoff * (0.1 * mathrand.Float64())
|
||||
d := time.Second * time.Duration(backoff)
|
||||
timer := backoffNewTimer(d)
|
||||
select {
|
||||
case <-time.After(time.Second * time.Duration(backoff)):
|
||||
case <-timer.C:
|
||||
t.vlogf("RoundTrip retrying after failure: %v", err)
|
||||
continue
|
||||
case <-req.Context().Done():
|
||||
timer.Stop()
|
||||
err = req.Context().Err()
|
||||
}
|
||||
}
|
||||
|
@ -591,7 +658,7 @@ func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse b
|
|||
if err != nil {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -612,24 +679,25 @@ func (t *Transport) newTLSConfig(host string) *tls.Config {
|
|||
return cfg
|
||||
}
|
||||
|
||||
func (t *Transport) dialTLS(ctx context.Context) func(string, string, *tls.Config) (net.Conn, error) {
|
||||
if t.DialTLS != nil {
|
||||
return t.DialTLS
|
||||
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)
|
||||
}
|
||||
return func(network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||
tlsCn, err := t.dialTLSWithContext(ctx, network, addr, cfg)
|
||||
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
|
||||
|
||||
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
|
||||
|
@ -645,6 +713,20 @@ func (t *Transport) expectContinueTimeout() time.Duration {
|
|||
return t.t1.ExpectContinueTimeout
|
||||
}
|
||||
|
||||
func (t *Transport) maxDecoderHeaderTableSize() uint32 {
|
||||
if v := t.MaxDecoderHeaderTableSize; v > 0 {
|
||||
return v
|
||||
}
|
||||
return initialHeaderTableSize
|
||||
}
|
||||
|
||||
func (t *Transport) maxEncoderHeaderTableSize() uint32 {
|
||||
if v := t.MaxEncoderHeaderTableSize; v > 0 {
|
||||
return v
|
||||
}
|
||||
return initialHeaderTableSize
|
||||
}
|
||||
|
||||
func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
|
||||
return t.newClientConn(c, t.disableKeepAlives())
|
||||
}
|
||||
|
@ -685,15 +767,19 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
|
|||
})
|
||||
cc.br = bufio.NewReader(c)
|
||||
cc.fr = NewFramer(cc.bw, cc.br)
|
||||
if t.maxFrameReadSize() != 0 {
|
||||
cc.fr.SetMaxReadFrameSize(t.maxFrameReadSize())
|
||||
}
|
||||
if t.CountError != nil {
|
||||
cc.fr.countError = t.CountError
|
||||
}
|
||||
cc.fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil)
|
||||
maxHeaderTableSize := t.maxDecoderHeaderTableSize()
|
||||
cc.fr.ReadMetaHeaders = hpack.NewDecoder(maxHeaderTableSize, nil)
|
||||
cc.fr.MaxHeaderListSize = t.maxHeaderListSize()
|
||||
|
||||
// TODO: SetMaxDynamicTableSize, SetMaxDynamicTableSizeLimit on
|
||||
// henc in response to SETTINGS frames?
|
||||
cc.henc = hpack.NewEncoder(&cc.hbuf)
|
||||
cc.henc.SetMaxDynamicTableSizeLimit(t.maxEncoderHeaderTableSize())
|
||||
cc.peerMaxHeaderTableSize = initialHeaderTableSize
|
||||
|
||||
if t.AllowHTTP {
|
||||
cc.nextStreamID = 3
|
||||
|
@ -708,14 +794,20 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
|
|||
{ID: SettingEnablePush, Val: 0},
|
||||
{ID: SettingInitialWindowSize, Val: transportDefaultStreamFlow},
|
||||
}
|
||||
if max := t.maxFrameReadSize(); max != 0 {
|
||||
initialSettings = append(initialSettings, Setting{ID: SettingMaxFrameSize, Val: max})
|
||||
}
|
||||
if max := t.maxHeaderListSize(); max != 0 {
|
||||
initialSettings = append(initialSettings, Setting{ID: SettingMaxHeaderListSize, Val: max})
|
||||
}
|
||||
if maxHeaderTableSize != initialHeaderTableSize {
|
||||
initialSettings = append(initialSettings, Setting{ID: SettingHeaderTableSize, Val: maxHeaderTableSize})
|
||||
}
|
||||
|
||||
cc.bw.Write(clientPreface)
|
||||
cc.fr.WriteSettings(initialSettings...)
|
||||
cc.fr.WriteWindowUpdate(0, transportDefaultConnFlow)
|
||||
cc.inflow.add(transportDefaultConnFlow + initialWindowSize)
|
||||
cc.inflow.init(transportDefaultConnFlow + initialWindowSize)
|
||||
cc.bw.Flush()
|
||||
if cc.werr != nil {
|
||||
cc.Close()
|
||||
|
@ -732,11 +824,13 @@ func (cc *ClientConn) healthCheck() {
|
|||
// trigger the healthCheck again if there is no frame received.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), pingTimeout)
|
||||
defer cancel()
|
||||
cc.vlogf("http2: Transport sending health check")
|
||||
err := cc.Ping(ctx)
|
||||
if err != nil {
|
||||
cc.vlogf("http2: Transport health check failure: %v", err)
|
||||
cc.closeForLostPing()
|
||||
cc.t.connPool().MarkDead(cc)
|
||||
return
|
||||
} else {
|
||||
cc.vlogf("http2: Transport health check success")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -907,6 +1001,24 @@ func (cc *ClientConn) onIdleTimeout() {
|
|||
cc.closeIfIdle()
|
||||
}
|
||||
|
||||
func (cc *ClientConn) closeConn() {
|
||||
t := time.AfterFunc(250*time.Millisecond, cc.forceCloseConn)
|
||||
defer t.Stop()
|
||||
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() {
|
||||
cc.mu.Lock()
|
||||
if len(cc.streams) > 0 || cc.streamsReserved > 0 {
|
||||
|
@ -921,7 +1033,7 @@ func (cc *ClientConn) closeIfIdle() {
|
|||
if VerboseLogs {
|
||||
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 {
|
||||
|
@ -938,7 +1050,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
// 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
|
||||
go func() {
|
||||
cc.mu.Lock()
|
||||
|
@ -946,7 +1058,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
|
|||
for {
|
||||
if len(cc.streams) == 0 || cc.closed {
|
||||
cc.closed = true
|
||||
done <- cc.tconn.Close()
|
||||
close(done)
|
||||
break
|
||||
}
|
||||
if cancelled {
|
||||
|
@ -957,8 +1069,9 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
|
|||
}()
|
||||
shutdownEnterWaitStateHook()
|
||||
select {
|
||||
case err := <-done:
|
||||
return err
|
||||
case <-done:
|
||||
cc.closeConn()
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
cc.mu.Lock()
|
||||
// Free the goroutine above
|
||||
|
@ -995,15 +1108,15 @@ func (cc *ClientConn) sendGoAway() error {
|
|||
|
||||
// closes the client connection immediately. In-flight requests are interrupted.
|
||||
// err is sent to streams.
|
||||
func (cc *ClientConn) closeForError(err error) error {
|
||||
func (cc *ClientConn) closeForError(err error) {
|
||||
cc.mu.Lock()
|
||||
cc.closed = true
|
||||
for _, cs := range cc.streams {
|
||||
cs.abortStreamLocked(err)
|
||||
}
|
||||
defer cc.cond.Broadcast()
|
||||
defer cc.mu.Unlock()
|
||||
return cc.tconn.Close()
|
||||
cc.cond.Broadcast()
|
||||
cc.mu.Unlock()
|
||||
cc.closeConn()
|
||||
}
|
||||
|
||||
// Close closes the client connection immediately.
|
||||
|
@ -1011,16 +1124,17 @@ func (cc *ClientConn) closeForError(err error) error {
|
|||
// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead.
|
||||
func (cc *ClientConn) Close() error {
|
||||
err := errors.New("http2: client connection force closed via ClientConn.Close")
|
||||
return cc.closeForError(err)
|
||||
cc.closeForError(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// closes the client connection immediately. In-flight requests are interrupted.
|
||||
func (cc *ClientConn) closeForLostPing() error {
|
||||
func (cc *ClientConn) closeForLostPing() {
|
||||
err := errors.New("http2: client connection lost")
|
||||
if f := cc.t.CountError; f != nil {
|
||||
f("conn_close_lost_ping")
|
||||
}
|
||||
return cc.closeForError(err)
|
||||
cc.closeForError(err)
|
||||
}
|
||||
|
||||
// errRequestCanceled is a copy of net/http's errRequestCanceled because it's not
|
||||
|
@ -1030,7 +1144,7 @@ var errRequestCanceled = errors.New("net/http: request canceled")
|
|||
func commaSeparatedTrailers(req *http.Request) (string, error) {
|
||||
keys := make([]string, 0, len(req.Trailer))
|
||||
for k := range req.Trailer {
|
||||
k = http.CanonicalHeaderKey(k)
|
||||
k = canonicalHeader(k)
|
||||
switch k {
|
||||
case "Transfer-Encoding", "Trailer", "Content-Length":
|
||||
return "", fmt.Errorf("invalid Trailer key %q", k)
|
||||
|
@ -1398,11 +1512,19 @@ func (cs *clientStream) cleanupWriteRequest(err error) {
|
|||
// and in multiple cases: server replies <=299 and >299
|
||||
// while still writing request body
|
||||
cc.mu.Lock()
|
||||
mustCloseBody := false
|
||||
if cs.reqBody != nil && cs.reqBodyClosed == nil {
|
||||
mustCloseBody = true
|
||||
cs.reqBodyClosed = make(chan struct{})
|
||||
}
|
||||
bodyClosed := cs.reqBodyClosed
|
||||
cs.reqBodyClosed = true
|
||||
cc.mu.Unlock()
|
||||
if !bodyClosed && cs.reqBody != nil {
|
||||
if mustCloseBody {
|
||||
cs.reqBody.Close()
|
||||
close(bodyClosed)
|
||||
}
|
||||
if bodyClosed != nil {
|
||||
<-bodyClosed
|
||||
}
|
||||
|
||||
if err != nil && cs.sentEndStream {
|
||||
|
@ -1447,7 +1569,7 @@ func (cs *clientStream) cleanupWriteRequest(err error) {
|
|||
close(cs.donec)
|
||||
}
|
||||
|
||||
// awaitOpenSlotForStream waits until len(streams) < maxConcurrentStreams.
|
||||
// awaitOpenSlotForStreamLocked waits until len(streams) < maxConcurrentStreams.
|
||||
// Must hold cc.mu.
|
||||
func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error {
|
||||
for {
|
||||
|
@ -1559,7 +1681,7 @@ func (cs *clientStream) writeRequestBody(req *http.Request) (err error) {
|
|||
|
||||
var sawEOF bool
|
||||
for !sawEOF {
|
||||
n, err := body.Read(buf[:len(buf)])
|
||||
n, err := body.Read(buf)
|
||||
if hasContentLen {
|
||||
remainLen -= int64(n)
|
||||
if remainLen == 0 && err == nil {
|
||||
|
@ -1582,7 +1704,7 @@ func (cs *clientStream) writeRequestBody(req *http.Request) (err error) {
|
|||
}
|
||||
if err != nil {
|
||||
cc.mu.Lock()
|
||||
bodyClosed := cs.reqBodyClosed
|
||||
bodyClosed := cs.reqBodyClosed != nil
|
||||
cc.mu.Unlock()
|
||||
switch {
|
||||
case bodyClosed:
|
||||
|
@ -1677,7 +1799,7 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error)
|
|||
if cc.closed {
|
||||
return 0, errClientConnClosed
|
||||
}
|
||||
if cs.reqBodyClosed {
|
||||
if cs.reqBodyClosed != nil {
|
||||
return 0, errStopReqBodyWrite
|
||||
}
|
||||
select {
|
||||
|
@ -1748,7 +1870,8 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
|||
}
|
||||
for _, v := range vv {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1861,7 +1984,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
|||
|
||||
// Header list size is ok. Write the headers.
|
||||
enumerateHeaders(func(name, value string) {
|
||||
name, ascii := asciiToLower(name)
|
||||
name, ascii := lowerHeader(name)
|
||||
if !ascii {
|
||||
// Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header
|
||||
// field names have to be ASCII characters (just as in HTTP/1.x).
|
||||
|
@ -1914,7 +2037,7 @@ func (cc *ClientConn) encodeTrailers(trailer http.Header) ([]byte, error) {
|
|||
}
|
||||
|
||||
for k, vv := range trailer {
|
||||
lowKey, ascii := asciiToLower(k)
|
||||
lowKey, ascii := lowerHeader(k)
|
||||
if !ascii {
|
||||
// Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header
|
||||
// field names have to be ASCII characters (just as in HTTP/1.x).
|
||||
|
@ -1946,8 +2069,7 @@ type resAndError struct {
|
|||
func (cc *ClientConn) addStreamLocked(cs *clientStream) {
|
||||
cs.flow.add(int32(cc.initialWindowSize))
|
||||
cs.flow.setConnFlow(&cc.flow)
|
||||
cs.inflow.add(transportDefaultStreamFlow)
|
||||
cs.inflow.setConnFlow(&cc.inflow)
|
||||
cs.inflow.init(transportDefaultStreamFlow)
|
||||
cs.ID = cc.nextStreamID
|
||||
cc.nextStreamID += 2
|
||||
cc.streams[cs.ID] = cs
|
||||
|
@ -1972,13 +2094,13 @@ func (cc *ClientConn) forgetStreamID(id uint32) {
|
|||
// wake up RoundTrip if there is a pending request.
|
||||
cc.cond.Broadcast()
|
||||
|
||||
closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives()
|
||||
closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil
|
||||
if closeOnIdle && cc.streamsReserved == 0 && len(cc.streams) == 0 {
|
||||
if VerboseLogs {
|
||||
cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, cc.nextStreamID-2)
|
||||
}
|
||||
cc.closed = true
|
||||
defer cc.tconn.Close()
|
||||
defer cc.closeConn()
|
||||
}
|
||||
|
||||
cc.mu.Unlock()
|
||||
|
@ -2025,8 +2147,8 @@ func isEOFOrNetReadError(err error) bool {
|
|||
|
||||
func (rl *clientConnReadLoop) cleanup() {
|
||||
cc := rl.cc
|
||||
defer cc.tconn.Close()
|
||||
defer cc.t.connPool().MarkDead(cc)
|
||||
cc.t.connPool().MarkDead(cc)
|
||||
defer cc.closeConn()
|
||||
defer close(cc.readerDone)
|
||||
|
||||
if cc.idleTimer != nil {
|
||||
|
@ -2048,6 +2170,7 @@ func (rl *clientConnReadLoop) cleanup() {
|
|||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
cc.closed = true
|
||||
|
||||
for _, cs := range cc.streams {
|
||||
select {
|
||||
case <-cs.peerClosed:
|
||||
|
@ -2246,7 +2369,7 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
|||
Status: status + " " + http.StatusText(statusCode),
|
||||
}
|
||||
for _, hf := range regularFields {
|
||||
key := http.CanonicalHeaderKey(hf.Name)
|
||||
key := canonicalHeader(hf.Name)
|
||||
if key == "Trailer" {
|
||||
t := res.Trailer
|
||||
if t == nil {
|
||||
|
@ -2254,7 +2377,7 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
|||
res.Trailer = t
|
||||
}
|
||||
foreachHeaderElement(hf.Value, func(v string) {
|
||||
t[http.CanonicalHeaderKey(v)] = nil
|
||||
t[canonicalHeader(v)] = nil
|
||||
})
|
||||
} else {
|
||||
vv := header[key]
|
||||
|
@ -2359,7 +2482,7 @@ func (rl *clientConnReadLoop) processTrailers(cs *clientStream, f *MetaHeadersFr
|
|||
|
||||
trailer := make(http.Header)
|
||||
for _, hf := range f.RegularFields() {
|
||||
key := http.CanonicalHeaderKey(hf.Name)
|
||||
key := canonicalHeader(hf.Name)
|
||||
trailer[key] = append(trailer[key], hf.Value)
|
||||
}
|
||||
cs.trailer = trailer
|
||||
|
@ -2405,21 +2528,10 @@ func (b transportResponseBody) Read(p []byte) (n int, err error) {
|
|||
}
|
||||
|
||||
cc.mu.Lock()
|
||||
var connAdd, streamAdd int32
|
||||
// Check the conn-level first, before the stream-level.
|
||||
if v := cc.inflow.available(); v < transportDefaultConnFlow/2 {
|
||||
connAdd = transportDefaultConnFlow - v
|
||||
cc.inflow.add(connAdd)
|
||||
}
|
||||
connAdd := cc.inflow.add(n)
|
||||
var streamAdd int32
|
||||
if err == nil { // No need to refresh if the stream is over or failed.
|
||||
// Consider any buffered body data (read from the conn but not
|
||||
// consumed by the client) when computing flow control for this
|
||||
// stream.
|
||||
v := int(cs.inflow.available()) + cs.bufPipe.Len()
|
||||
if v < transportDefaultStreamFlow-transportDefaultStreamMinRefresh {
|
||||
streamAdd = int32(transportDefaultStreamFlow - v)
|
||||
cs.inflow.add(streamAdd)
|
||||
}
|
||||
streamAdd = cs.inflow.add(n)
|
||||
}
|
||||
cc.mu.Unlock()
|
||||
|
||||
|
@ -2447,17 +2559,15 @@ func (b transportResponseBody) Close() error {
|
|||
if unread > 0 {
|
||||
cc.mu.Lock()
|
||||
// Return connection-level flow control.
|
||||
if unread > 0 {
|
||||
cc.inflow.add(int32(unread))
|
||||
}
|
||||
connAdd := cc.inflow.add(unread)
|
||||
cc.mu.Unlock()
|
||||
|
||||
// TODO(dneil): Acquiring this mutex can block indefinitely.
|
||||
// Move flow control return to a goroutine?
|
||||
cc.wmu.Lock()
|
||||
// Return connection-level flow control.
|
||||
if unread > 0 {
|
||||
cc.fr.WriteWindowUpdate(0, uint32(unread))
|
||||
if connAdd > 0 {
|
||||
cc.fr.WriteWindowUpdate(0, uint32(connAdd))
|
||||
}
|
||||
cc.bw.Flush()
|
||||
cc.wmu.Unlock()
|
||||
|
@ -2500,13 +2610,18 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
|
|||
// But at least return their flow control:
|
||||
if f.Length > 0 {
|
||||
cc.mu.Lock()
|
||||
cc.inflow.add(int32(f.Length))
|
||||
ok := cc.inflow.take(f.Length)
|
||||
connAdd := cc.inflow.add(int(f.Length))
|
||||
cc.mu.Unlock()
|
||||
|
||||
cc.wmu.Lock()
|
||||
cc.fr.WriteWindowUpdate(0, uint32(f.Length))
|
||||
cc.bw.Flush()
|
||||
cc.wmu.Unlock()
|
||||
if !ok {
|
||||
return ConnectionError(ErrCodeFlowControl)
|
||||
}
|
||||
if connAdd > 0 {
|
||||
cc.wmu.Lock()
|
||||
cc.fr.WriteWindowUpdate(0, uint32(connAdd))
|
||||
cc.bw.Flush()
|
||||
cc.wmu.Unlock()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -2537,9 +2652,7 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
|
|||
}
|
||||
// Check connection-level flow control.
|
||||
cc.mu.Lock()
|
||||
if cs.inflow.available() >= int32(f.Length) {
|
||||
cs.inflow.take(int32(f.Length))
|
||||
} else {
|
||||
if !takeInflows(&cc.inflow, &cs.inflow, f.Length) {
|
||||
cc.mu.Unlock()
|
||||
return ConnectionError(ErrCodeFlowControl)
|
||||
}
|
||||
|
@ -2561,19 +2674,20 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
|
|||
}
|
||||
}
|
||||
|
||||
if refund > 0 {
|
||||
cc.inflow.add(int32(refund))
|
||||
if !didReset {
|
||||
cs.inflow.add(int32(refund))
|
||||
}
|
||||
sendConn := cc.inflow.add(refund)
|
||||
var sendStream int32
|
||||
if !didReset {
|
||||
sendStream = cs.inflow.add(refund)
|
||||
}
|
||||
cc.mu.Unlock()
|
||||
|
||||
if refund > 0 {
|
||||
if sendConn > 0 || sendStream > 0 {
|
||||
cc.wmu.Lock()
|
||||
cc.fr.WriteWindowUpdate(0, uint32(refund))
|
||||
if !didReset {
|
||||
cc.fr.WriteWindowUpdate(cs.ID, uint32(refund))
|
||||
if sendConn > 0 {
|
||||
cc.fr.WriteWindowUpdate(0, uint32(sendConn))
|
||||
}
|
||||
if sendStream > 0 {
|
||||
cc.fr.WriteWindowUpdate(cs.ID, uint32(sendStream))
|
||||
}
|
||||
cc.bw.Flush()
|
||||
cc.wmu.Unlock()
|
||||
|
@ -2641,7 +2755,6 @@ func (rl *clientConnReadLoop) processGoAway(f *GoAwayFrame) error {
|
|||
if fn := cc.t.CountError; fn != nil {
|
||||
fn("recv_goaway_" + f.ErrCode.stringToken())
|
||||
}
|
||||
|
||||
}
|
||||
cc.setGoAway(f)
|
||||
return nil
|
||||
|
@ -2706,8 +2819,10 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
|
|||
cc.cond.Broadcast()
|
||||
|
||||
cc.initialWindowSize = s.Val
|
||||
case SettingHeaderTableSize:
|
||||
cc.henc.SetMaxDynamicTableSize(s.Val)
|
||||
cc.peerMaxHeaderTableSize = s.Val
|
||||
default:
|
||||
// TODO(bradfitz): handle more settings? SETTINGS_HEADER_TABLE_SIZE probably.
|
||||
cc.vlogf("Unhandled Setting: %v", s)
|
||||
}
|
||||
return nil
|
||||
|
@ -2881,7 +2996,12 @@ func (t *Transport) logf(format string, args ...interface{}) {
|
|||
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{}
|
||||
|
||||
|
@ -2926,7 +3046,11 @@ func (gz *gzipReader) Read(p []byte) (n int, err error) {
|
|||
}
|
||||
|
||||
func (gz *gzipReader) Close() error {
|
||||
return gz.body.Close()
|
||||
if err := gz.body.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
gz.zerr = fs.ErrClosed
|
||||
return nil
|
||||
}
|
||||
|
||||
type errorReader struct{ err error }
|
||||
|
@ -2990,7 +3114,7 @@ func traceGotConn(req *http.Request, cc *ClientConn, reused bool) {
|
|||
cc.mu.Lock()
|
||||
ci.WasIdle = len(cc.streams) == 0 && reused
|
||||
if ci.WasIdle && !cc.lastActive.IsZero() {
|
||||
ci.IdleTime = time.Now().Sub(cc.lastActive)
|
||||
ci.IdleTime = time.Since(cc.lastActive)
|
||||
}
|
||||
cc.mu.Unlock()
|
||||
|
||||
|
|
|
@ -383,16 +383,15 @@ func (ws *priorityWriteScheduler) AdjustStream(streamID uint32, priority Priorit
|
|||
|
||||
func (ws *priorityWriteScheduler) Push(wr FrameWriteRequest) {
|
||||
var n *priorityNode
|
||||
if id := wr.StreamID(); id == 0 {
|
||||
if wr.isControl() {
|
||||
n = &ws.root
|
||||
} else {
|
||||
id := wr.StreamID()
|
||||
n = ws.nodes[id]
|
||||
if n == nil {
|
||||
// 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
|
||||
// push wr onto the root, rather than creating a new priorityNode,
|
||||
// since RST_STREAM is tiny and the stream's priority is unknown
|
||||
// anyway. See issue #17919.
|
||||
// DATA frame. In other case, we push wr onto the root, rather
|
||||
// than creating a new priorityNode.
|
||||
if wr.DataSize() > 0 {
|
||||
panic("add DATA on non-open stream")
|
||||
}
|
||||
|
|
|
@ -17,23 +17,23 @@ package idna
|
|||
//
|
||||
// The per-rune values have the following format:
|
||||
//
|
||||
// if mapped {
|
||||
// if inlinedXOR {
|
||||
// 15..13 inline XOR marker
|
||||
// 12..11 unused
|
||||
// 10..3 inline XOR mask
|
||||
// } else {
|
||||
// 15..3 index into xor or mapping table
|
||||
// }
|
||||
// } else {
|
||||
// 15..14 unused
|
||||
// 13 mayNeedNorm
|
||||
// 12..11 attributes
|
||||
// 10..8 joining type
|
||||
// 7..3 category type
|
||||
// }
|
||||
// 2 use xor pattern
|
||||
// 1..0 mapped category
|
||||
// if mapped {
|
||||
// if inlinedXOR {
|
||||
// 15..13 inline XOR marker
|
||||
// 12..11 unused
|
||||
// 10..3 inline XOR mask
|
||||
// } else {
|
||||
// 15..3 index into xor or mapping table
|
||||
// }
|
||||
// } else {
|
||||
// 15..14 unused
|
||||
// 13 mayNeedNorm
|
||||
// 12..11 attributes
|
||||
// 10..8 joining type
|
||||
// 7..3 category type
|
||||
// }
|
||||
// 2 use xor pattern
|
||||
// 1..0 mapped category
|
||||
//
|
||||
// See the definitions below for a more detailed description of the various
|
||||
// bits.
|
||||
|
|
|
@ -6,7 +6,10 @@ package cpu
|
|||
|
||||
import "runtime"
|
||||
|
||||
const cacheLineSize = 64
|
||||
// cacheLineSize is used to prevent false sharing of cache lines.
|
||||
// We choose 128 because Apple Silicon, a.k.a. M1, has 128-byte cache line size.
|
||||
// It doesn't cost much and is much more future-proof.
|
||||
const cacheLineSize = 128
|
||||
|
||||
func initOptions() {
|
||||
options = []option{
|
||||
|
@ -41,13 +44,10 @@ func archInit() {
|
|||
switch runtime.GOOS {
|
||||
case "freebsd":
|
||||
readARM64Registers()
|
||||
case "linux", "netbsd":
|
||||
case "linux", "netbsd", "openbsd":
|
||||
doinit()
|
||||
default:
|
||||
// Most platforms don't seem to allow reading these registers.
|
||||
//
|
||||
// OpenBSD:
|
||||
// See https://golang.org/issue/31746
|
||||
// Many platforms don't seem to allow reading these registers.
|
||||
setMinimalFeatures()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (386 || amd64 || amd64p32) && gccgo
|
||||
// +build 386 amd64 amd64p32
|
||||
// +build gccgo
|
||||
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// HWCAP/HWCAP2 bits. These are exposed by Linux.
|
||||
const (
|
||||
hwcap_FP = 1 << 0
|
||||
|
@ -32,10 +37,45 @@ const (
|
|||
hwcap_ASIMDFHM = 1 << 23
|
||||
)
|
||||
|
||||
// linuxKernelCanEmulateCPUID reports whether we're running
|
||||
// on Linux 4.11+. Ideally we'd like to ask the question about
|
||||
// whether the current kernel contains
|
||||
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=77c97b4ee21290f5f083173d957843b615abbff2
|
||||
// but the version number will have to do.
|
||||
func linuxKernelCanEmulateCPUID() bool {
|
||||
var un syscall.Utsname
|
||||
syscall.Uname(&un)
|
||||
var sb strings.Builder
|
||||
for _, b := range un.Release[:] {
|
||||
if b == 0 {
|
||||
break
|
||||
}
|
||||
sb.WriteByte(byte(b))
|
||||
}
|
||||
major, minor, _, ok := parseRelease(sb.String())
|
||||
return ok && (major > 4 || major == 4 && minor >= 11)
|
||||
}
|
||||
|
||||
func doinit() {
|
||||
if err := readHWCAP(); err != nil {
|
||||
// failed to read /proc/self/auxv, try reading registers directly
|
||||
readARM64Registers()
|
||||
// We failed to read /proc/self/auxv. This can happen if the binary has
|
||||
// been given extra capabilities(7) with /bin/setcap.
|
||||
//
|
||||
// When this happens, we have two options. If the Linux kernel is new
|
||||
// enough (4.11+), we can read the arm64 registers directly which'll
|
||||
// trap into the kernel and then return back to userspace.
|
||||
//
|
||||
// But on older kernels, such as Linux 4.4.180 as used on many Synology
|
||||
// devices, calling readARM64Registers (specifically getisar0) will
|
||||
// cause a SIGILL and we'll die. So for older kernels, parse /proc/cpuinfo
|
||||
// instead.
|
||||
//
|
||||
// See golang/go#57336.
|
||||
if linuxKernelCanEmulateCPUID() {
|
||||
readARM64Registers()
|
||||
} else {
|
||||
readLinuxProcCPUInfo()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Minimal copy of functionality from x/sys/unix so the cpu package can call
|
||||
// sysctl without depending on x/sys/unix.
|
||||
|
||||
const (
|
||||
// From OpenBSD's sys/sysctl.h.
|
||||
_CTL_MACHDEP = 7
|
||||
|
||||
// From OpenBSD's machine/cpu.h.
|
||||
_CPU_ID_AA64ISAR0 = 2
|
||||
_CPU_ID_AA64ISAR1 = 3
|
||||
)
|
||||
|
||||
// Implemented in the runtime package (runtime/sys_openbsd3.go)
|
||||
func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||
|
||||
//go:linkname syscall_syscall6 syscall.syscall6
|
||||
|
||||
func sysctl(mib []uint32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) {
|
||||
_, _, errno := syscall_syscall6(libc_sysctl_trampoline_addr, uintptr(unsafe.Pointer(&mib[0])), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen))
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var libc_sysctl_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_sysctl sysctl "libc.so"
|
||||
|
||||
func sysctlUint64(mib []uint32) (uint64, bool) {
|
||||
var out uint64
|
||||
nout := unsafe.Sizeof(out)
|
||||
if err := sysctl(mib, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0); err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return out, true
|
||||
}
|
||||
|
||||
func doinit() {
|
||||
setMinimalFeatures()
|
||||
|
||||
// Get ID_AA64ISAR0 and ID_AA64ISAR1 from sysctl.
|
||||
isar0, ok := sysctlUint64([]uint32{_CTL_MACHDEP, _CPU_ID_AA64ISAR0})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
isar1, ok := sysctlUint64([]uint32{_CTL_MACHDEP, _CPU_ID_AA64ISAR1})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
parseARM64SystemRegisters(isar0, isar1, 0)
|
||||
|
||||
Initialized = true
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0
|
||||
JMP libc_sysctl(SB)
|
||||
|
||||
GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8
|
||||
DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB)
|
|
@ -2,8 +2,8 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !linux && !netbsd && arm64
|
||||
// +build !linux,!netbsd,arm64
|
||||
//go:build !linux && !netbsd && !openbsd && arm64
|
||||
// +build !linux,!netbsd,!openbsd,arm64
|
||||
|
||||
package cpu
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !aix && !linux && (ppc64 || ppc64le)
|
||||
// +build !aix
|
||||
// +build !linux
|
||||
// +build ppc64 ppc64le
|
||||
|
||||
package cpu
|
||||
|
||||
func archInit() {
|
||||
PPC64.IsPOWER8 = true
|
||||
Initialized = true
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright 2023 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 armbe || arm64be || m68k || mips || mips64 || mips64p32 || ppc || ppc64 || s390 || s390x || shbe || sparc || sparc64
|
||||
// +build armbe arm64be m68k mips mips64 mips64p32 ppc ppc64 s390 s390x shbe sparc sparc64
|
||||
|
||||
package cpu
|
||||
|
||||
// IsBigEndian records whether the GOARCH's byte order is big endian.
|
||||
const IsBigEndian = true
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright 2023 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 386 || amd64 || amd64p32 || alpha || arm || arm64 || loong64 || mipsle || mips64le || mips64p32le || nios2 || ppc64le || riscv || riscv64 || sh
|
||||
// +build 386 amd64 amd64p32 alpha arm arm64 loong64 mipsle mips64le mips64p32le nios2 ppc64le riscv riscv64 sh
|
||||
|
||||
package cpu
|
||||
|
||||
// IsBigEndian records whether the GOARCH's byte order is big endian.
|
||||
const IsBigEndian = false
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cpu
|
||||
|
||||
import "strconv"
|
||||
|
||||
// parseRelease parses a dot-separated version number. It follows the semver
|
||||
// syntax, but allows the minor and patch versions to be elided.
|
||||
//
|
||||
// This is a copy of the Go runtime's parseRelease from
|
||||
// https://golang.org/cl/209597.
|
||||
func parseRelease(rel string) (major, minor, patch int, ok bool) {
|
||||
// Strip anything after a dash or plus.
|
||||
for i := 0; i < len(rel); i++ {
|
||||
if rel[i] == '-' || rel[i] == '+' {
|
||||
rel = rel[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
next := func() (int, bool) {
|
||||
for i := 0; i < len(rel); i++ {
|
||||
if rel[i] == '.' {
|
||||
ver, err := strconv.Atoi(rel[:i])
|
||||
rel = rel[i+1:]
|
||||
return ver, err == nil
|
||||
}
|
||||
}
|
||||
ver, err := strconv.Atoi(rel)
|
||||
rel = ""
|
||||
return ver, err == nil
|
||||
}
|
||||
if major, ok = next(); !ok || rel == "" {
|
||||
return
|
||||
}
|
||||
if minor, ok = next(); !ok || rel == "" {
|
||||
return
|
||||
}
|
||||
patch, ok = next()
|
||||
return
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux && arm64
|
||||
// +build linux,arm64
|
||||
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func readLinuxProcCPUInfo() error {
|
||||
f, err := os.Open("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var buf [1 << 10]byte // enough for first CPU
|
||||
n, err := io.ReadFull(f, buf[:])
|
||||
if err != nil && err != io.ErrUnexpectedEOF {
|
||||
return err
|
||||
}
|
||||
in := string(buf[:n])
|
||||
const features = "\nFeatures : "
|
||||
i := strings.Index(in, features)
|
||||
if i == -1 {
|
||||
return errors.New("no CPU features found")
|
||||
}
|
||||
in = in[i+len(features):]
|
||||
if i := strings.Index(in, "\n"); i != -1 {
|
||||
in = in[:i]
|
||||
}
|
||||
m := map[string]*bool{}
|
||||
|
||||
initOptions() // need it early here; it's harmless to call twice
|
||||
for _, o := range options {
|
||||
m[o.Name] = o.Feature
|
||||
}
|
||||
// The EVTSTRM field has alias "evstrm" in Go, but Linux calls it "evtstrm".
|
||||
m["evtstrm"] = &ARM64.HasEVTSTRM
|
||||
|
||||
for _, f := range strings.Fields(in) {
|
||||
if p, ok := m[f]; ok {
|
||||
*p = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -126,7 +126,7 @@ errors=$(
|
|||
signals=$(
|
||||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' |
|
||||
egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' |
|
||||
grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT' |
|
||||
sort
|
||||
)
|
||||
|
||||
|
@ -136,7 +136,7 @@ echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags |
|
|||
sort >_error.grep
|
||||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' |
|
||||
egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT)' |
|
||||
grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT' |
|
||||
sort >_signal.grep
|
||||
|
||||
echo '// mkerrors.sh' "$@"
|
||||
|
|
|
@ -29,8 +29,6 @@ import (
|
|||
"bytes"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/internal/unsafeheader"
|
||||
)
|
||||
|
||||
// ByteSliceFromString returns a NUL-terminated slice of bytes
|
||||
|
@ -82,13 +80,7 @@ func BytePtrToString(p *byte) string {
|
|||
ptr = unsafe.Pointer(uintptr(ptr) + 1)
|
||||
}
|
||||
|
||||
var s []byte
|
||||
h := (*unsafeheader.Slice)(unsafe.Pointer(&s))
|
||||
h.Data = unsafe.Pointer(p)
|
||||
h.Len = n
|
||||
h.Cap = n
|
||||
|
||||
return string(s)
|
||||
return string(unsafe.Slice(p, n))
|
||||
}
|
||||
|
||||
// Single-word zero for use when we need a valid pointer to 0 bytes.
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||
// +build darwin freebsd netbsd openbsd
|
||||
// +build gc
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ppc64, BSD
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP syscall·Syscall9(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
|
@ -2,8 +2,8 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
||||
|
||||
package unix
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gccgo && !aix
|
||||
// +build gccgo,!aix
|
||||
//go:build gccgo && !aix && !hurd
|
||||
// +build gccgo,!aix,!hurd
|
||||
|
||||
package unix
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build gccgo
|
||||
// +build !aix
|
||||
//go:build gccgo && !aix && !hurd
|
||||
// +build gccgo,!aix,!hurd
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
//go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris
|
||||
// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris
|
||||
|
||||
package unix
|
||||
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
import "unsafe"
|
||||
|
||||
// IoctlRetInt performs an ioctl operation specified by req on a device
|
||||
// associated with opened file descriptor fd, and returns a non-negative
|
||||
|
@ -217,3 +215,19 @@ func IoctlKCMAttach(fd int, info KCMAttach) error {
|
|||
func IoctlKCMUnattach(fd int, info KCMUnattach) error {
|
||||
return ioctlPtr(fd, SIOCKCMUNATTACH, unsafe.Pointer(&info))
|
||||
}
|
||||
|
||||
// IoctlLoopGetStatus64 gets the status of the loop device associated with the
|
||||
// file descriptor fd using the LOOP_GET_STATUS64 operation.
|
||||
func IoctlLoopGetStatus64(fd int) (*LoopInfo64, error) {
|
||||
var value LoopInfo64
|
||||
if err := ioctlPtr(fd, LOOP_GET_STATUS64, unsafe.Pointer(&value)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &value, nil
|
||||
}
|
||||
|
||||
// IoctlLoopSetStatus64 sets the status of the loop device associated with the
|
||||
// file descriptor fd using the LOOP_SET_STATUS64 operation.
|
||||
func IoctlLoopSetStatus64(fd int, value *LoopInfo64) error {
|
||||
return ioctlPtr(fd, LOOP_SET_STATUS64, unsafe.Pointer(value))
|
||||
}
|
||||
|
|
|
@ -73,12 +73,12 @@ aix_ppc64)
|
|||
darwin_amd64)
|
||||
mkerrors="$mkerrors -m64"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
mkasm="go run mkasm_darwin.go"
|
||||
mkasm="go run mkasm.go"
|
||||
;;
|
||||
darwin_arm64)
|
||||
mkerrors="$mkerrors -m64"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
mkasm="go run mkasm_darwin.go"
|
||||
mkasm="go run mkasm.go"
|
||||
;;
|
||||
dragonfly_amd64)
|
||||
mkerrors="$mkerrors -m64"
|
||||
|
@ -142,42 +142,60 @@ netbsd_arm64)
|
|||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
openbsd_386)
|
||||
mkasm="go run mkasm.go"
|
||||
mkerrors="$mkerrors -m32"
|
||||
mksyscall="go run mksyscall.go -l32 -openbsd"
|
||||
mksyscall="go run mksyscall.go -l32 -openbsd -libc"
|
||||
mksysctl="go run mksysctl_openbsd.go"
|
||||
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
openbsd_amd64)
|
||||
mkasm="go run mkasm.go"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="go run mksyscall.go -openbsd"
|
||||
mksyscall="go run mksyscall.go -openbsd -libc"
|
||||
mksysctl="go run mksysctl_openbsd.go"
|
||||
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
openbsd_arm)
|
||||
mkasm="go run mkasm.go"
|
||||
mkerrors="$mkerrors"
|
||||
mksyscall="go run mksyscall.go -l32 -openbsd -arm"
|
||||
mksyscall="go run mksyscall.go -l32 -openbsd -arm -libc"
|
||||
mksysctl="go run mksysctl_openbsd.go"
|
||||
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
|
||||
# Let the type of C char be signed for making the bare syscall
|
||||
# API consistent across platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
;;
|
||||
openbsd_arm64)
|
||||
mkasm="go run mkasm.go"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="go run mksyscall.go -openbsd"
|
||||
mksyscall="go run mksyscall.go -openbsd -libc"
|
||||
mksysctl="go run mksysctl_openbsd.go"
|
||||
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
|
||||
# Let the type of C char be signed for making the bare syscall
|
||||
# API consistent across platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
;;
|
||||
openbsd_mips64)
|
||||
mkasm="go run mkasm.go"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="go run mksyscall.go -openbsd"
|
||||
mksyscall="go run mksyscall.go -openbsd -libc"
|
||||
mksysctl="go run mksysctl_openbsd.go"
|
||||
# Let the type of C char be signed for making the bare syscall
|
||||
# API consistent across platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
;;
|
||||
openbsd_ppc64)
|
||||
mkasm="go run mkasm.go"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="go run mksyscall.go -openbsd -libc"
|
||||
mksysctl="go run mksysctl_openbsd.go"
|
||||
# Let the type of C char be signed for making the bare syscall
|
||||
# API consistent across platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
;;
|
||||
openbsd_riscv64)
|
||||
mkasm="go run mkasm.go"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="go run mksyscall.go -openbsd -libc"
|
||||
mksysctl="go run mksysctl_openbsd.go"
|
||||
mksysnum="go run mksysnum.go 'https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master'"
|
||||
# Let the type of C char be signed for making the bare syscall
|
||||
# API consistent across platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
|
@ -214,11 +232,6 @@ esac
|
|||
if [ "$GOOSARCH" == "aix_ppc64" ]; then
|
||||
# aix/ppc64 script generates files instead of writing to stdin.
|
||||
echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in && gofmt -w zsyscall_$GOOSARCH.go && gofmt -w zsyscall_"$GOOSARCH"_gccgo.go && gofmt -w zsyscall_"$GOOSARCH"_gc.go " ;
|
||||
elif [ "$GOOS" == "darwin" ]; then
|
||||
# 1.12 and later, syscalls via libSystem
|
||||
echo "$mksyscall -tags $GOOS,$GOARCH,go1.12 $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go";
|
||||
# 1.13 and later, syscalls via libSystem (including syscallPtr)
|
||||
echo "$mksyscall -tags $GOOS,$GOARCH,go1.13 syscall_darwin.1_13.go |gofmt >zsyscall_$GOOSARCH.1_13.go";
|
||||
elif [ "$GOOS" == "illumos" ]; then
|
||||
# illumos code generation requires a --illumos switch
|
||||
echo "$mksyscall -illumos -tags illumos,$GOARCH syscall_illumos.go |gofmt > zsyscall_illumos_$GOARCH.go";
|
||||
|
@ -232,5 +245,5 @@ esac
|
|||
if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi
|
||||
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
|
||||
if [ -n "$mktypes" ]; then echo "$mktypes types_$GOOS.go | go run mkpost.go > ztypes_$GOOSARCH.go"; fi
|
||||
if [ -n "$mkasm" ]; then echo "$mkasm $GOARCH"; fi
|
||||
if [ -n "$mkasm" ]; then echo "$mkasm $GOOS $GOARCH"; fi
|
||||
) | $run
|
||||
|
|
|
@ -642,7 +642,7 @@ errors=$(
|
|||
signals=$(
|
||||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' |
|
||||
egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT|SIGMAX64)' |
|
||||
grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT\|SIGMAX64' |
|
||||
sort
|
||||
)
|
||||
|
||||
|
@ -652,7 +652,7 @@ echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags |
|
|||
sort >_error.grep
|
||||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' |
|
||||
egrep -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT|SIGMAX64)' |
|
||||
grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT\|SIGMAX64' |
|
||||
sort >_signal.grep
|
||||
|
||||
echo '// mkerrors.sh' "$@"
|
||||
|
|
|
@ -52,6 +52,20 @@ func ParseSocketControlMessage(b []byte) ([]SocketControlMessage, error) {
|
|||
return msgs, nil
|
||||
}
|
||||
|
||||
// ParseOneSocketControlMessage parses a single socket control message from b, returning the message header,
|
||||
// message data (a slice of b), and the remainder of b after that single message.
|
||||
// When there are no remaining messages, len(remainder) == 0.
|
||||
func ParseOneSocketControlMessage(b []byte) (hdr Cmsghdr, data []byte, remainder []byte, err error) {
|
||||
h, dbuf, err := socketControlMessageHeaderAndData(b)
|
||||
if err != nil {
|
||||
return Cmsghdr{}, nil, nil, err
|
||||
}
|
||||
if i := cmsgAlignOf(int(h.Len)); i < len(b) {
|
||||
remainder = b[i:]
|
||||
}
|
||||
return *h, dbuf, remainder, nil
|
||||
}
|
||||
|
||||
func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) {
|
||||
h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
|
||||
if h.Len < SizeofCmsghdr || uint64(h.Len) > uint64(len(b)) {
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
// Copyright 2009 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 aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package unix
|
||||
|
||||
func itoa(val int) string { // do it here rather than with fmt to avoid dependency
|
||||
if val < 0 {
|
||||
return "-" + uitoa(uint(-val))
|
||||
}
|
||||
return uitoa(uint(val))
|
||||
}
|
||||
|
||||
func uitoa(val uint) string {
|
||||
var buf [32]byte // big enough for int64
|
||||
i := len(buf) - 1
|
||||
for val >= 10 {
|
||||
buf[i] = byte(val%10 + '0')
|
||||
i--
|
||||
val /= 10
|
||||
}
|
||||
buf[i] = byte(val + '0')
|
||||
return string(buf[i:])
|
||||
}
|
|
@ -29,8 +29,6 @@ import (
|
|||
"bytes"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/internal/unsafeheader"
|
||||
)
|
||||
|
||||
// ByteSliceFromString returns a NUL-terminated slice of bytes
|
||||
|
@ -82,13 +80,7 @@ func BytePtrToString(p *byte) string {
|
|||
ptr = unsafe.Pointer(uintptr(ptr) + 1)
|
||||
}
|
||||
|
||||
var s []byte
|
||||
h := (*unsafeheader.Slice)(unsafe.Pointer(&s))
|
||||
h.Data = unsafe.Pointer(p)
|
||||
h.Len = n
|
||||
h.Cap = n
|
||||
|
||||
return string(s)
|
||||
return string(unsafe.Slice(p, n))
|
||||
}
|
||||
|
||||
// Single-word zero for use when we need a valid pointer to 0 bytes.
|
||||
|
|
|
@ -218,13 +218,62 @@ func Accept(fd int) (nfd int, sa Sockaddr, err error) {
|
|||
}
|
||||
|
||||
func recvmsgRaw(fd int, iov []Iovec, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
|
||||
// Recvmsg not implemented on AIX
|
||||
return -1, -1, -1, ENOSYS
|
||||
var msg Msghdr
|
||||
msg.Name = (*byte)(unsafe.Pointer(rsa))
|
||||
msg.Namelen = uint32(SizeofSockaddrAny)
|
||||
var dummy byte
|
||||
if len(oob) > 0 {
|
||||
// receive at least one normal byte
|
||||
if emptyIovecs(iov) {
|
||||
var iova [1]Iovec
|
||||
iova[0].Base = &dummy
|
||||
iova[0].SetLen(1)
|
||||
iov = iova[:]
|
||||
}
|
||||
msg.Control = (*byte)(unsafe.Pointer(&oob[0]))
|
||||
msg.SetControllen(len(oob))
|
||||
}
|
||||
if len(iov) > 0 {
|
||||
msg.Iov = &iov[0]
|
||||
msg.SetIovlen(len(iov))
|
||||
}
|
||||
if n, err = recvmsg(fd, &msg, flags); n == -1 {
|
||||
return
|
||||
}
|
||||
oobn = int(msg.Controllen)
|
||||
recvflags = int(msg.Flags)
|
||||
return
|
||||
}
|
||||
|
||||
func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
|
||||
// SendmsgN not implemented on AIX
|
||||
return -1, ENOSYS
|
||||
var msg Msghdr
|
||||
msg.Name = (*byte)(unsafe.Pointer(ptr))
|
||||
msg.Namelen = uint32(salen)
|
||||
var dummy byte
|
||||
var empty bool
|
||||
if len(oob) > 0 {
|
||||
// send at least one normal byte
|
||||
empty = emptyIovecs(iov)
|
||||
if empty {
|
||||
var iova [1]Iovec
|
||||
iova[0].Base = &dummy
|
||||
iova[0].SetLen(1)
|
||||
iov = iova[:]
|
||||
}
|
||||
msg.Control = (*byte)(unsafe.Pointer(&oob[0]))
|
||||
msg.SetControllen(len(oob))
|
||||
}
|
||||
if len(iov) > 0 {
|
||||
msg.Iov = &iov[0]
|
||||
msg.SetIovlen(len(iov))
|
||||
}
|
||||
if n, err = sendmsg(fd, &msg, flags); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(oob) > 0 && empty {
|
||||
n = 0
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
|
||||
|
|
|
@ -363,7 +363,7 @@ func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Sockle
|
|||
var empty bool
|
||||
if len(oob) > 0 {
|
||||
// send at least one normal byte
|
||||
empty := emptyIovecs(iov)
|
||||
empty = emptyIovecs(iov)
|
||||
if empty {
|
||||
var iova [1]Iovec
|
||||
iova[0].Base = &dummy
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
// Copyright 2019 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 && go1.12 && !go1.13
|
||||
// +build darwin,go1.12,!go1.13
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const _SYS_GETDIRENTRIES64 = 344
|
||||
|
||||
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
|
||||
// To implement this using libSystem we'd need syscall_syscallPtr for
|
||||
// fdopendir. However, syscallPtr was only added in Go 1.13, so we fall
|
||||
// back to raw syscalls for this func on Go 1.12.
|
||||
var p unsafe.Pointer
|
||||
if len(buf) > 0 {
|
||||
p = unsafe.Pointer(&buf[0])
|
||||
} else {
|
||||
p = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(_SYS_GETDIRENTRIES64, uintptr(fd), uintptr(p), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
return n, errnoErr(e1)
|
||||
}
|
||||
return n, nil
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
// Copyright 2019 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 && go1.13
|
||||
// +build darwin,go1.13
|
||||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/internal/unsafeheader"
|
||||
)
|
||||
|
||||
//sys closedir(dir uintptr) (err error)
|
||||
//sys readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno)
|
||||
|
||||
func fdopendir(fd int) (dir uintptr, err error) {
|
||||
r0, _, e1 := syscall_syscallPtr(libc_fdopendir_trampoline_addr, uintptr(fd), 0, 0)
|
||||
dir = uintptr(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_fdopendir_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_fdopendir fdopendir "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
|
||||
// Simulate Getdirentries using fdopendir/readdir_r/closedir.
|
||||
// We store the number of entries to skip in the seek
|
||||
// offset of fd. See issue #31368.
|
||||
// It's not the full required semantics, but should handle the case
|
||||
// of calling Getdirentries or ReadDirent repeatedly.
|
||||
// It won't handle assigning the results of lseek to *basep, or handle
|
||||
// the directory being edited underfoot.
|
||||
skip, err := Seek(fd, 0, 1 /* SEEK_CUR */)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// We need to duplicate the incoming file descriptor
|
||||
// because the caller expects to retain control of it, but
|
||||
// fdopendir expects to take control of its argument.
|
||||
// Just Dup'ing the file descriptor is not enough, as the
|
||||
// result shares underlying state. Use Openat to make a really
|
||||
// new file descriptor referring to the same directory.
|
||||
fd2, err := Openat(fd, ".", O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d, err := fdopendir(fd2)
|
||||
if err != nil {
|
||||
Close(fd2)
|
||||
return 0, err
|
||||
}
|
||||
defer closedir(d)
|
||||
|
||||
var cnt int64
|
||||
for {
|
||||
var entry Dirent
|
||||
var entryp *Dirent
|
||||
e := readdir_r(d, &entry, &entryp)
|
||||
if e != 0 {
|
||||
return n, errnoErr(e)
|
||||
}
|
||||
if entryp == nil {
|
||||
break
|
||||
}
|
||||
if skip > 0 {
|
||||
skip--
|
||||
cnt++
|
||||
continue
|
||||
}
|
||||
|
||||
reclen := int(entry.Reclen)
|
||||
if reclen > len(buf) {
|
||||
// Not enough room. Return for now.
|
||||
// The counter will let us know where we should start up again.
|
||||
// Note: this strategy for suspending in the middle and
|
||||
// restarting is O(n^2) in the length of the directory. Oh well.
|
||||
break
|
||||
}
|
||||
|
||||
// Copy entry into return buffer.
|
||||
var s []byte
|
||||
hdr := (*unsafeheader.Slice)(unsafe.Pointer(&s))
|
||||
hdr.Data = unsafe.Pointer(&entry)
|
||||
hdr.Cap = reclen
|
||||
hdr.Len = reclen
|
||||
copy(buf, s)
|
||||
|
||||
buf = buf[reclen:]
|
||||
n += reclen
|
||||
cnt++
|
||||
}
|
||||
// Set the seek offset of the input fd to record
|
||||
// how many files we've already returned.
|
||||
_, err = Seek(fd, cnt, 0 /* SEEK_SET */)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
|
@ -19,6 +19,96 @@ import (
|
|||
"unsafe"
|
||||
)
|
||||
|
||||
//sys closedir(dir uintptr) (err error)
|
||||
//sys readdir_r(dir uintptr, entry *Dirent, result **Dirent) (res Errno)
|
||||
|
||||
func fdopendir(fd int) (dir uintptr, err error) {
|
||||
r0, _, e1 := syscall_syscallPtr(libc_fdopendir_trampoline_addr, uintptr(fd), 0, 0)
|
||||
dir = uintptr(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var libc_fdopendir_trampoline_addr uintptr
|
||||
|
||||
//go:cgo_import_dynamic libc_fdopendir fdopendir "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
|
||||
// Simulate Getdirentries using fdopendir/readdir_r/closedir.
|
||||
// We store the number of entries to skip in the seek
|
||||
// offset of fd. See issue #31368.
|
||||
// It's not the full required semantics, but should handle the case
|
||||
// of calling Getdirentries or ReadDirent repeatedly.
|
||||
// It won't handle assigning the results of lseek to *basep, or handle
|
||||
// the directory being edited underfoot.
|
||||
skip, err := Seek(fd, 0, 1 /* SEEK_CUR */)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// We need to duplicate the incoming file descriptor
|
||||
// because the caller expects to retain control of it, but
|
||||
// fdopendir expects to take control of its argument.
|
||||
// Just Dup'ing the file descriptor is not enough, as the
|
||||
// result shares underlying state. Use Openat to make a really
|
||||
// new file descriptor referring to the same directory.
|
||||
fd2, err := Openat(fd, ".", O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d, err := fdopendir(fd2)
|
||||
if err != nil {
|
||||
Close(fd2)
|
||||
return 0, err
|
||||
}
|
||||
defer closedir(d)
|
||||
|
||||
var cnt int64
|
||||
for {
|
||||
var entry Dirent
|
||||
var entryp *Dirent
|
||||
e := readdir_r(d, &entry, &entryp)
|
||||
if e != 0 {
|
||||
return n, errnoErr(e)
|
||||
}
|
||||
if entryp == nil {
|
||||
break
|
||||
}
|
||||
if skip > 0 {
|
||||
skip--
|
||||
cnt++
|
||||
continue
|
||||
}
|
||||
|
||||
reclen := int(entry.Reclen)
|
||||
if reclen > len(buf) {
|
||||
// Not enough room. Return for now.
|
||||
// The counter will let us know where we should start up again.
|
||||
// Note: this strategy for suspending in the middle and
|
||||
// restarting is O(n^2) in the length of the directory. Oh well.
|
||||
break
|
||||
}
|
||||
|
||||
// Copy entry into return buffer.
|
||||
s := unsafe.Slice((*byte)(unsafe.Pointer(&entry)), reclen)
|
||||
copy(buf, s)
|
||||
|
||||
buf = buf[reclen:]
|
||||
n += reclen
|
||||
cnt++
|
||||
}
|
||||
// Set the seek offset of the input fd to record
|
||||
// how many files we've already returned.
|
||||
_, err = Seek(fd, cnt, 0 /* SEEK_SET */)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// SockaddrDatalink implements the Sockaddr interface for AF_LINK type sockets.
|
||||
type SockaddrDatalink struct {
|
||||
Len uint8
|
||||
|
@ -140,6 +230,7 @@ func direntNamlen(buf []byte) (uint64, bool) {
|
|||
|
||||
func PtraceAttach(pid int) (err error) { return ptrace(PT_ATTACH, pid, 0, 0) }
|
||||
func PtraceDetach(pid int) (err error) { return ptrace(PT_DETACH, pid, 0, 0) }
|
||||
func PtraceDenyAttach() (err error) { return ptrace(PT_DENY_ATTACH, 0, 0, 0) }
|
||||
|
||||
//sysnb pipe(p *[2]int32) (err error)
|
||||
|
||||
|
|
|
@ -255,6 +255,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
|
|||
//sys Chmod(path string, mode uint32) (err error)
|
||||
//sys Chown(path string, uid int, gid int) (err error)
|
||||
//sys Chroot(path string) (err error)
|
||||
//sys ClockGettime(clockid int32, time *Timespec) (err error)
|
||||
//sys Close(fd int) (err error)
|
||||
//sys Dup(fd int) (nfd int, err error)
|
||||
//sys Dup2(from int, to int) (err error)
|
||||
|
|
|
@ -319,6 +319,7 @@ func PtraceSingleStep(pid int) (err error) {
|
|||
//sys Chmod(path string, mode uint32) (err error)
|
||||
//sys Chown(path string, uid int, gid int) (err error)
|
||||
//sys Chroot(path string) (err error)
|
||||
//sys ClockGettime(clockid int32, time *Timespec) (err error)
|
||||
//sys Close(fd int) (err error)
|
||||
//sys Dup(fd int) (nfd int, err error)
|
||||
//sys Dup2(from int, to int) (err error)
|
||||
|
|
|
@ -60,8 +60,13 @@ func PtraceGetFsBase(pid int, fsbase *int64) (err error) {
|
|||
return ptrace(PT_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0)
|
||||
}
|
||||
|
||||
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint32(countin)}
|
||||
func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{
|
||||
Op: int32(req),
|
||||
Offs: offs,
|
||||
Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe.
|
||||
Len: uint32(countin),
|
||||
}
|
||||
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
|
||||
return int(ioDesc.Len), err
|
||||
}
|
||||
|
|
|
@ -60,8 +60,13 @@ func PtraceGetFsBase(pid int, fsbase *int64) (err error) {
|
|||
return ptrace(PT_GETFSBASE, pid, uintptr(unsafe.Pointer(fsbase)), 0)
|
||||
}
|
||||
|
||||
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint64(countin)}
|
||||
func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{
|
||||
Op: int32(req),
|
||||
Offs: offs,
|
||||
Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe.
|
||||
Len: uint64(countin),
|
||||
}
|
||||
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
|
||||
return int(ioDesc.Len), err
|
||||
}
|
||||
|
|
|
@ -56,8 +56,13 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
|
|||
|
||||
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||
|
||||
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint32(countin)}
|
||||
func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{
|
||||
Op: int32(req),
|
||||
Offs: offs,
|
||||
Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe.
|
||||
Len: uint32(countin),
|
||||
}
|
||||
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
|
||||
return int(ioDesc.Len), err
|
||||
}
|
||||
|
|
|
@ -56,8 +56,13 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
|
|||
|
||||
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||
|
||||
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint64(countin)}
|
||||
func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{
|
||||
Op: int32(req),
|
||||
Offs: offs,
|
||||
Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe.
|
||||
Len: uint64(countin),
|
||||
}
|
||||
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
|
||||
return int(ioDesc.Len), err
|
||||
}
|
||||
|
|
|
@ -56,8 +56,13 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
|
|||
|
||||
func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||
|
||||
func PtraceIO(req int, pid int, addr uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{Op: int32(req), Offs: (*byte)(unsafe.Pointer(addr)), Addr: (*byte)(unsafe.Pointer(&out[0])), Len: uint64(countin)}
|
||||
func PtraceIO(req int, pid int, offs uintptr, out []byte, countin int) (count int, err error) {
|
||||
ioDesc := PtraceIoDesc{
|
||||
Op: int32(req),
|
||||
Offs: offs,
|
||||
Addr: uintptr(unsafe.Pointer(&out[0])), // TODO(#58351): this is not safe.
|
||||
Len: uint64(countin),
|
||||
}
|
||||
err = ptrace(PT_IO, pid, uintptr(unsafe.Pointer(&ioDesc)), 0)
|
||||
return int(ioDesc.Len), err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build hurd
|
||||
// +build hurd
|
||||
|
||||
package unix
|
||||
|
||||
/*
|
||||
#include <stdint.h>
|
||||
int ioctl(int, unsigned long int, uintptr_t);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func ioctl(fd int, req uint, arg uintptr) (err error) {
|
||||
r0, er := C.ioctl(C.int(fd), C.ulong(req), C.uintptr_t(arg))
|
||||
if r0 == -1 && er != nil {
|
||||
err = er
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build 386 && hurd
|
||||
// +build 386,hurd
|
||||
|
||||
package unix
|
||||
|
||||
const (
|
||||
TIOCGETA = 0x62251713
|
||||
)
|
||||
|
||||
type Winsize struct {
|
||||
Row uint16
|
||||
Col uint16
|
||||
Xpixel uint16
|
||||
Ypixel uint16
|
||||
}
|
||||
|
||||
type Termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
Cflag uint32
|
||||
Lflag uint32
|
||||
Cc [20]uint8
|
||||
Ispeed int32
|
||||
Ospeed int32
|
||||
}
|
|
@ -10,8 +10,6 @@
|
|||
package unix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
|
@ -79,107 +77,3 @@ func Accept4(fd int, flags int) (nfd int, sa Sockaddr, err error) {
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
//sys putmsg(fd int, clptr *strbuf, dataptr *strbuf, flags int) (err error)
|
||||
|
||||
func Putmsg(fd int, cl []byte, data []byte, flags int) (err error) {
|
||||
var clp, datap *strbuf
|
||||
if len(cl) > 0 {
|
||||
clp = &strbuf{
|
||||
Len: int32(len(cl)),
|
||||
Buf: (*int8)(unsafe.Pointer(&cl[0])),
|
||||
}
|
||||
}
|
||||
if len(data) > 0 {
|
||||
datap = &strbuf{
|
||||
Len: int32(len(data)),
|
||||
Buf: (*int8)(unsafe.Pointer(&data[0])),
|
||||
}
|
||||
}
|
||||
return putmsg(fd, clp, datap, flags)
|
||||
}
|
||||
|
||||
//sys getmsg(fd int, clptr *strbuf, dataptr *strbuf, flags *int) (err error)
|
||||
|
||||
func Getmsg(fd int, cl []byte, data []byte) (retCl []byte, retData []byte, flags int, err error) {
|
||||
var clp, datap *strbuf
|
||||
if len(cl) > 0 {
|
||||
clp = &strbuf{
|
||||
Maxlen: int32(len(cl)),
|
||||
Buf: (*int8)(unsafe.Pointer(&cl[0])),
|
||||
}
|
||||
}
|
||||
if len(data) > 0 {
|
||||
datap = &strbuf{
|
||||
Maxlen: int32(len(data)),
|
||||
Buf: (*int8)(unsafe.Pointer(&data[0])),
|
||||
}
|
||||
}
|
||||
|
||||
if err = getmsg(fd, clp, datap, &flags); err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
if len(cl) > 0 {
|
||||
retCl = cl[:clp.Len]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
retData = data[:datap.Len]
|
||||
}
|
||||
return retCl, retData, flags, nil
|
||||
}
|
||||
|
||||
func IoctlSetIntRetInt(fd int, req uint, arg int) (int, error) {
|
||||
return ioctlRet(fd, req, uintptr(arg))
|
||||
}
|
||||
|
||||
func IoctlSetString(fd int, req uint, val string) error {
|
||||
bs := make([]byte, len(val)+1)
|
||||
copy(bs[:len(bs)-1], val)
|
||||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&bs[0])))
|
||||
runtime.KeepAlive(&bs[0])
|
||||
return err
|
||||
}
|
||||
|
||||
// Lifreq Helpers
|
||||
|
||||
func (l *Lifreq) SetName(name string) error {
|
||||
if len(name) >= len(l.Name) {
|
||||
return fmt.Errorf("name cannot be more than %d characters", len(l.Name)-1)
|
||||
}
|
||||
for i := range name {
|
||||
l.Name[i] = int8(name[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Lifreq) SetLifruInt(d int) {
|
||||
*(*int)(unsafe.Pointer(&l.Lifru[0])) = d
|
||||
}
|
||||
|
||||
func (l *Lifreq) GetLifruInt() int {
|
||||
return *(*int)(unsafe.Pointer(&l.Lifru[0]))
|
||||
}
|
||||
|
||||
func (l *Lifreq) SetLifruUint(d uint) {
|
||||
*(*uint)(unsafe.Pointer(&l.Lifru[0])) = d
|
||||
}
|
||||
|
||||
func (l *Lifreq) GetLifruUint() uint {
|
||||
return *(*uint)(unsafe.Pointer(&l.Lifru[0]))
|
||||
}
|
||||
|
||||
func IoctlLifreq(fd int, req uint, l *Lifreq) error {
|
||||
return ioctl(fd, req, uintptr(unsafe.Pointer(l)))
|
||||
}
|
||||
|
||||
// Strioctl Helpers
|
||||
|
||||
func (s *Strioctl) SetInt(i int) {
|
||||
s.Len = int32(unsafe.Sizeof(i))
|
||||
s.Dp = (*int8)(unsafe.Pointer(&i))
|
||||
}
|
||||
|
||||
func IoctlSetStrioctlRetInt(fd int, req uint, s *Strioctl) (int, error) {
|
||||
return ioctlRet(fd, req, uintptr(unsafe.Pointer(s)))
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ package unix
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
@ -233,7 +234,7 @@ func Futimesat(dirfd int, path string, tv []Timeval) error {
|
|||
func Futimes(fd int, tv []Timeval) (err error) {
|
||||
// Believe it or not, this is the best we can do on Linux
|
||||
// (and is what glibc does).
|
||||
return Utimes("/proc/self/fd/"+itoa(fd), tv)
|
||||
return Utimes("/proc/self/fd/"+strconv.Itoa(fd), tv)
|
||||
}
|
||||
|
||||
const ImplementsGetwd = true
|
||||
|
@ -1541,7 +1542,7 @@ func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Sockle
|
|||
var dummy byte
|
||||
var empty bool
|
||||
if len(oob) > 0 {
|
||||
empty := emptyIovecs(iov)
|
||||
empty = emptyIovecs(iov)
|
||||
if empty {
|
||||
var sockType int
|
||||
sockType, err = GetsockoptInt(fd, SOL_SOCKET, SO_TYPE)
|
||||
|
@ -1553,6 +1554,7 @@ func sendmsgN(fd int, iov []Iovec, oob []byte, ptr unsafe.Pointer, salen _Sockle
|
|||
var iova [1]Iovec
|
||||
iova[0].Base = &dummy
|
||||
iova[0].SetLen(1)
|
||||
iov = iova[:]
|
||||
}
|
||||
}
|
||||
msg.Control = &oob[0]
|
||||
|
@ -1798,6 +1800,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
|
|||
//sysnb Capset(hdr *CapUserHeader, data *CapUserData) (err error)
|
||||
//sys Chdir(path string) (err error)
|
||||
//sys Chroot(path string) (err error)
|
||||
//sys ClockAdjtime(clockid int32, buf *Timex) (state int, err error)
|
||||
//sys ClockGetres(clockid int32, res *Timespec) (err error)
|
||||
//sys ClockGettime(clockid int32, time *Timespec) (err error)
|
||||
//sys ClockNanosleep(clockid int32, flags int, request *Timespec, remain *Timespec) (err error)
|
||||
|
@ -1891,17 +1894,28 @@ func PrctlRetInt(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uint
|
|||
return int(ret), nil
|
||||
}
|
||||
|
||||
// issue 1435.
|
||||
// On linux Setuid and Setgid only affects the current thread, not the process.
|
||||
// This does not match what most callers expect so we must return an error
|
||||
// here rather than letting the caller think that the call succeeded.
|
||||
|
||||
func Setuid(uid int) (err error) {
|
||||
return EOPNOTSUPP
|
||||
return syscall.Setuid(uid)
|
||||
}
|
||||
|
||||
func Setgid(uid int) (err error) {
|
||||
return EOPNOTSUPP
|
||||
func Setgid(gid int) (err error) {
|
||||
return syscall.Setgid(gid)
|
||||
}
|
||||
|
||||
func Setreuid(ruid, euid int) (err error) {
|
||||
return syscall.Setreuid(ruid, euid)
|
||||
}
|
||||
|
||||
func Setregid(rgid, egid int) (err error) {
|
||||
return syscall.Setregid(rgid, egid)
|
||||
}
|
||||
|
||||
func Setresuid(ruid, euid, suid int) (err error) {
|
||||
return syscall.Setresuid(ruid, euid, suid)
|
||||
}
|
||||
|
||||
func Setresgid(rgid, egid, sgid int) (err error) {
|
||||
return syscall.Setresgid(rgid, egid, sgid)
|
||||
}
|
||||
|
||||
// SetfsgidRetGid sets fsgid for current thread and returns previous fsgid set.
|
||||
|
@ -1960,36 +1974,46 @@ func Signalfd(fd int, sigmask *Sigset_t, flags int) (newfd int, err error) {
|
|||
//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
|
||||
|
||||
func bytes2iovec(bs [][]byte) []Iovec {
|
||||
iovecs := make([]Iovec, len(bs))
|
||||
for i, b := range bs {
|
||||
iovecs[i].SetLen(len(b))
|
||||
// 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 {
|
||||
iovecs[i].Base = &b[0]
|
||||
v.Base = &b[0]
|
||||
} else {
|
||||
iovecs[i].Base = (*byte)(unsafe.Pointer(&_zero))
|
||||
v.Base = (*byte)(unsafe.Pointer(&_zero))
|
||||
}
|
||||
vecs = append(vecs, v)
|
||||
}
|
||||
return iovecs
|
||||
return vecs
|
||||
}
|
||||
|
||||
// offs2lohi splits offs into its lower and upper unsigned long. On 64-bit
|
||||
// systems, hi will always be 0. On 32-bit systems, offs will be split in half.
|
||||
// preadv/pwritev chose this calling convention so they don't need to add a
|
||||
// padding-register for alignment on ARM.
|
||||
// offs2lohi splits offs into its low and high order bits.
|
||||
func offs2lohi(offs int64) (lo, hi uintptr) {
|
||||
return uintptr(offs), uintptr(uint64(offs) >> SizeofLong)
|
||||
const longBits = SizeofLong * 8
|
||||
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 := bytes2iovec(iovs)
|
||||
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 := bytes2iovec(iovs)
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
lo, hi := offs2lohi(offset)
|
||||
n, err = preadv(fd, iovecs, lo, hi)
|
||||
readvRacedetect(iovecs, n, err)
|
||||
|
@ -1997,7 +2021,8 @@ func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) {
|
|||
}
|
||||
|
||||
func Preadv2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) {
|
||||
iovecs := bytes2iovec(iovs)
|
||||
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)
|
||||
|
@ -2024,7 +2049,8 @@ func readvRacedetect(iovecs []Iovec, n int, err error) {
|
|||
}
|
||||
|
||||
func Writev(fd int, iovs [][]byte) (n int, err error) {
|
||||
iovecs := bytes2iovec(iovs)
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
if raceenabled {
|
||||
raceReleaseMerge(unsafe.Pointer(&ioSync))
|
||||
}
|
||||
|
@ -2034,7 +2060,8 @@ func Writev(fd int, iovs [][]byte) (n int, err error) {
|
|||
}
|
||||
|
||||
func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
|
||||
iovecs := bytes2iovec(iovs)
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
if raceenabled {
|
||||
raceReleaseMerge(unsafe.Pointer(&ioSync))
|
||||
}
|
||||
|
@ -2045,7 +2072,8 @@ func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
|
|||
}
|
||||
|
||||
func Pwritev2(fd int, iovs [][]byte, offset int64, flags int) (n int, err error) {
|
||||
iovecs := bytes2iovec(iovs)
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
if raceenabled {
|
||||
raceReleaseMerge(unsafe.Pointer(&ioSync))
|
||||
}
|
||||
|
@ -2240,7 +2268,7 @@ func (fh *FileHandle) Bytes() []byte {
|
|||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
return (*[1 << 30]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&fh.fileHandle.Type)) + 4))[:n:n]
|
||||
return unsafe.Slice((*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&fh.fileHandle.Type))+4)), n)
|
||||
}
|
||||
|
||||
// NameToHandleAt wraps the name_to_handle_at system call; it obtains
|
||||
|
@ -2356,6 +2384,16 @@ func Setitimer(which ItimerWhich, it Itimerval) (Itimerval, error) {
|
|||
return prev, nil
|
||||
}
|
||||
|
||||
//sysnb rtSigprocmask(how int, set *Sigset_t, oldset *Sigset_t, sigsetsize uintptr) (err error) = SYS_RT_SIGPROCMASK
|
||||
|
||||
func PthreadSigmask(how int, set, oldset *Sigset_t) error {
|
||||
if oldset != nil {
|
||||
// Explicitly clear in case Sigset_t is larger than _C__NSIG.
|
||||
*oldset = Sigset_t{}
|
||||
}
|
||||
return rtSigprocmask(how, set, oldset, _C__NSIG/8)
|
||||
}
|
||||
|
||||
/*
|
||||
* Unimplemented
|
||||
*/
|
||||
|
@ -2414,7 +2452,6 @@ func Setitimer(which ItimerWhich, it Itimerval) (Itimerval, error) {
|
|||
// RestartSyscall
|
||||
// RtSigaction
|
||||
// RtSigpending
|
||||
// RtSigprocmask
|
||||
// RtSigqueueinfo
|
||||
// RtSigreturn
|
||||
// RtSigsuspend
|
||||
|
|
|
@ -41,10 +41,6 @@ func setTimeval(sec, usec int64) Timeval {
|
|||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
|
||||
//sys setfsgid(gid int) (prev int, err error) = SYS_SETFSGID32
|
||||
//sys setfsuid(uid int) (prev int, err error) = SYS_SETFSUID32
|
||||
//sysnb Setregid(rgid int, egid int) (err error) = SYS_SETREGID32
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error) = SYS_SETRESGID32
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error) = SYS_SETRESUID32
|
||||
//sysnb Setreuid(ruid int, euid int) (err error) = SYS_SETREUID32
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error)
|
||||
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64
|
||||
//sys SyncFileRange(fd int, off int64, n int64, flags int) (err error)
|
||||
|
|
|
@ -46,11 +46,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
|
|||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
|
||||
|
|
|
@ -62,10 +62,6 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
|
|||
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS__NEWSELECT
|
||||
//sys setfsgid(gid int) (prev int, err error) = SYS_SETFSGID32
|
||||
//sys setfsuid(uid int) (prev int, err error) = SYS_SETFSUID32
|
||||
//sysnb Setregid(rgid int, egid int) (err error) = SYS_SETREGID32
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error) = SYS_SETRESGID32
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error) = SYS_SETRESUID32
|
||||
//sysnb Setreuid(ruid int, euid int) (err error) = SYS_SETREUID32
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error)
|
||||
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64
|
||||
|
|
|
@ -39,11 +39,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
|
|||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
|
||||
|
|
|
@ -34,10 +34,6 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
|
|||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
|
||||
|
|
|
@ -37,11 +37,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
|
|||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
//sys Statfs(path string, buf *Statfs_t) (err error)
|
||||
|
|
|
@ -32,10 +32,6 @@ func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr,
|
|||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error)
|
||||
//sys SyncFileRange(fd int, off int64, n int64, flags int) (err error)
|
||||
|
|
|
@ -34,10 +34,6 @@ import (
|
|||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) = SYS_SENDFILE64
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error)
|
||||
//sys Stat(path string, stat *Stat_t) (err error) = SYS_STAT64
|
||||
|
|
|
@ -34,11 +34,7 @@ package unix
|
|||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
//sys Stat(path string, stat *Stat_t) (err error)
|
||||
|
|
|
@ -38,11 +38,7 @@ func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err
|
|||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
|
||||
|
|
|
@ -34,11 +34,7 @@ import (
|
|||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
//sys Stat(path string, stat *Stat_t) (err error)
|
||||
//sys Statfs(path string, buf *Statfs_t) (err error)
|
||||
|
|
|
@ -31,11 +31,7 @@ package unix
|
|||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys setfsgid(gid int) (prev int, err error)
|
||||
//sys setfsuid(uid int) (prev int, err error)
|
||||
//sysnb Setregid(rgid int, egid int) (err error)
|
||||
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
|
||||
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
|
||||
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
|
||||
//sysnb Setreuid(ruid int, euid int) (err error)
|
||||
//sys Shutdown(fd int, how int) (err error)
|
||||
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
|
||||
//sys Stat(path string, stat *Stat_t) (err error)
|
||||
|
|
|
@ -110,6 +110,20 @@ func direntNamlen(buf []byte) (uint64, bool) {
|
|||
return readInt(buf, unsafe.Offsetof(Dirent{}.Namlen), unsafe.Sizeof(Dirent{}.Namlen))
|
||||
}
|
||||
|
||||
func SysctlUvmexp(name string) (*Uvmexp, error) {
|
||||
mib, err := sysctlmib(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n := uintptr(SizeofUvmexp)
|
||||
var u Uvmexp
|
||||
if err := sysctl(mib, (*byte)(unsafe.Pointer(&u)), &n, nil, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
func Pipe(p []int) (err error) {
|
||||
return Pipe2(p, 0)
|
||||
}
|
||||
|
@ -245,6 +259,7 @@ func Statvfs(path string, buf *Statvfs_t) (err error) {
|
|||
//sys Chmod(path string, mode uint32) (err error)
|
||||
//sys Chown(path string, uid int, gid int) (err error)
|
||||
//sys Chroot(path string) (err error)
|
||||
//sys ClockGettime(clockid int32, time *Timespec) (err error)
|
||||
//sys Close(fd int) (err error)
|
||||
//sys Dup(fd int) (nfd int, err error)
|
||||
//sys Dup2(from int, to int) (err error)
|
||||
|
|
|
@ -220,6 +220,7 @@ func Uname(uname *Utsname) error {
|
|||
//sys Chmod(path string, mode uint32) (err error)
|
||||
//sys Chown(path string, uid int, gid int) (err error)
|
||||
//sys Chroot(path string) (err error)
|
||||
//sys ClockGettime(clockid int32, time *Timespec) (err error)
|
||||
//sys Close(fd int) (err error)
|
||||
//sys Dup(fd int) (nfd int, err error)
|
||||
//sys Dup2(from int, to int) (err error)
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build openbsd
|
||||
// +build openbsd
|
||||
|
||||
package unix
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
// Implemented in the runtime package (runtime/sys_openbsd3.go)
|
||||
func syscall_syscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
func syscall_syscall10(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 uintptr) (r1, r2 uintptr, err Errno)
|
||||
func syscall_rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
func syscall_rawSyscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
|
||||
//go:linkname syscall_syscall syscall.syscall
|
||||
//go:linkname syscall_syscall6 syscall.syscall6
|
||||
//go:linkname syscall_syscall10 syscall.syscall10
|
||||
//go:linkname syscall_rawSyscall syscall.rawSyscall
|
||||
//go:linkname syscall_rawSyscall6 syscall.rawSyscall6
|
||||
|
||||
func syscall_syscall9(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) {
|
||||
return syscall_syscall10(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, 0)
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2019 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 ppc64 && openbsd
|
||||
// +build ppc64,openbsd
|
||||
|
||||
package unix
|
||||
|
||||
func setTimespec(sec, nsec int64) Timespec {
|
||||
return Timespec{Sec: sec, Nsec: nsec}
|
||||
}
|
||||
|
||||
func setTimeval(sec, usec int64) Timeval {
|
||||
return Timeval{Sec: sec, Usec: usec}
|
||||
}
|
||||
|
||||
func SetKevent(k *Kevent_t, fd, mode, flags int) {
|
||||
k.Ident = uint64(fd)
|
||||
k.Filter = int16(mode)
|
||||
k.Flags = uint16(flags)
|
||||
}
|
||||
|
||||
func (iov *Iovec) SetLen(length int) {
|
||||
iov.Len = uint64(length)
|
||||
}
|
||||
|
||||
func (msghdr *Msghdr) SetControllen(length int) {
|
||||
msghdr.Controllen = uint32(length)
|
||||
}
|
||||
|
||||
func (msghdr *Msghdr) SetIovlen(length int) {
|
||||
msghdr.Iovlen = uint32(length)
|
||||
}
|
||||
|
||||
func (cmsg *Cmsghdr) SetLen(length int) {
|
||||
cmsg.Len = uint32(length)
|
||||
}
|
||||
|
||||
// SYS___SYSCTL is used by syscall_bsd.go for all BSDs, but in modern versions
|
||||
// of openbsd/ppc64 the syscall is called sysctl instead of __sysctl.
|
||||
const SYS___SYSCTL = SYS_SYSCTL
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2019 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 riscv64 && openbsd
|
||||
// +build riscv64,openbsd
|
||||
|
||||
package unix
|
||||
|
||||
func setTimespec(sec, nsec int64) Timespec {
|
||||
return Timespec{Sec: sec, Nsec: nsec}
|
||||
}
|
||||
|
||||
func setTimeval(sec, usec int64) Timeval {
|
||||
return Timeval{Sec: sec, Usec: usec}
|
||||
}
|
||||
|
||||
func SetKevent(k *Kevent_t, fd, mode, flags int) {
|
||||
k.Ident = uint64(fd)
|
||||
k.Filter = int16(mode)
|
||||
k.Flags = uint16(flags)
|
||||
}
|
||||
|
||||
func (iov *Iovec) SetLen(length int) {
|
||||
iov.Len = uint64(length)
|
||||
}
|
||||
|
||||
func (msghdr *Msghdr) SetControllen(length int) {
|
||||
msghdr.Controllen = uint32(length)
|
||||
}
|
||||
|
||||
func (msghdr *Msghdr) SetIovlen(length int) {
|
||||
msghdr.Iovlen = uint32(length)
|
||||
}
|
||||
|
||||
func (cmsg *Cmsghdr) SetLen(length int) {
|
||||
cmsg.Len = uint32(length)
|
||||
}
|
||||
|
||||
// SYS___SYSCTL is used by syscall_bsd.go for all BSDs, but in modern versions
|
||||
// of openbsd/riscv64 the syscall is called sysctl instead of __sysctl.
|
||||
const SYS___SYSCTL = SYS_SYSCTL
|
|
@ -590,6 +590,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
|
|||
//sys Chmod(path string, mode uint32) (err error)
|
||||
//sys Chown(path string, uid int, gid int) (err error)
|
||||
//sys Chroot(path string) (err error)
|
||||
//sys ClockGettime(clockid int32, time *Timespec) (err error)
|
||||
//sys Close(fd int) (err error)
|
||||
//sys Creat(path string, mode uint32) (fd int, err error)
|
||||
//sys Dup(fd int) (nfd int, err error)
|
||||
|
@ -750,8 +751,8 @@ type EventPort struct {
|
|||
// we should handle things gracefully. To do so, we need to keep an extra
|
||||
// reference to the cookie around until the event is processed
|
||||
// thus the otherwise seemingly extraneous "cookies" map
|
||||
// The key of this map is a pointer to the corresponding &fCookie.cookie
|
||||
cookies map[*interface{}]*fileObjCookie
|
||||
// The key of this map is a pointer to the corresponding fCookie
|
||||
cookies map[*fileObjCookie]struct{}
|
||||
}
|
||||
|
||||
// PortEvent is an abstraction of the port_event C struct.
|
||||
|
@ -778,7 +779,7 @@ func NewEventPort() (*EventPort, error) {
|
|||
port: port,
|
||||
fds: make(map[uintptr]*fileObjCookie),
|
||||
paths: make(map[string]*fileObjCookie),
|
||||
cookies: make(map[*interface{}]*fileObjCookie),
|
||||
cookies: make(map[*fileObjCookie]struct{}),
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
@ -799,6 +800,7 @@ func (e *EventPort) Close() error {
|
|||
}
|
||||
e.fds = nil
|
||||
e.paths = nil
|
||||
e.cookies = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -826,17 +828,16 @@ func (e *EventPort) AssociatePath(path string, stat os.FileInfo, events int, coo
|
|||
if _, found := e.paths[path]; found {
|
||||
return fmt.Errorf("%v is already associated with this Event Port", path)
|
||||
}
|
||||
fobj, err := createFileObj(path, stat)
|
||||
fCookie, err := createFileObjCookie(path, stat, cookie)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fCookie := &fileObjCookie{fobj, cookie}
|
||||
_, err = port_associate(e.port, PORT_SOURCE_FILE, uintptr(unsafe.Pointer(fobj)), events, (*byte)(unsafe.Pointer(&fCookie.cookie)))
|
||||
_, err = port_associate(e.port, PORT_SOURCE_FILE, uintptr(unsafe.Pointer(fCookie.fobj)), events, (*byte)(unsafe.Pointer(fCookie)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.paths[path] = fCookie
|
||||
e.cookies[&fCookie.cookie] = fCookie
|
||||
e.cookies[fCookie] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -858,7 +859,7 @@ func (e *EventPort) DissociatePath(path string) error {
|
|||
if err == nil {
|
||||
// dissociate was successful, safe to delete the cookie
|
||||
fCookie := e.paths[path]
|
||||
delete(e.cookies, &fCookie.cookie)
|
||||
delete(e.cookies, fCookie)
|
||||
}
|
||||
delete(e.paths, path)
|
||||
return err
|
||||
|
@ -871,13 +872,16 @@ func (e *EventPort) AssociateFd(fd uintptr, events int, cookie interface{}) erro
|
|||
if _, found := e.fds[fd]; found {
|
||||
return fmt.Errorf("%v is already associated with this Event Port", fd)
|
||||
}
|
||||
fCookie := &fileObjCookie{nil, cookie}
|
||||
_, err := port_associate(e.port, PORT_SOURCE_FD, fd, events, (*byte)(unsafe.Pointer(&fCookie.cookie)))
|
||||
fCookie, err := createFileObjCookie("", nil, cookie)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = port_associate(e.port, PORT_SOURCE_FD, fd, events, (*byte)(unsafe.Pointer(fCookie)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.fds[fd] = fCookie
|
||||
e.cookies[&fCookie.cookie] = fCookie
|
||||
e.cookies[fCookie] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -896,27 +900,31 @@ func (e *EventPort) DissociateFd(fd uintptr) error {
|
|||
if err == nil {
|
||||
// dissociate was successful, safe to delete the cookie
|
||||
fCookie := e.fds[fd]
|
||||
delete(e.cookies, &fCookie.cookie)
|
||||
delete(e.cookies, fCookie)
|
||||
}
|
||||
delete(e.fds, fd)
|
||||
return err
|
||||
}
|
||||
|
||||
func createFileObj(name string, stat os.FileInfo) (*fileObj, error) {
|
||||
fobj := new(fileObj)
|
||||
bs, err := ByteSliceFromString(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func createFileObjCookie(name string, stat os.FileInfo, cookie interface{}) (*fileObjCookie, error) {
|
||||
fCookie := new(fileObjCookie)
|
||||
fCookie.cookie = cookie
|
||||
if name != "" && stat != nil {
|
||||
fCookie.fobj = new(fileObj)
|
||||
bs, err := ByteSliceFromString(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fCookie.fobj.Name = (*int8)(unsafe.Pointer(&bs[0]))
|
||||
s := stat.Sys().(*syscall.Stat_t)
|
||||
fCookie.fobj.Atim.Sec = s.Atim.Sec
|
||||
fCookie.fobj.Atim.Nsec = s.Atim.Nsec
|
||||
fCookie.fobj.Mtim.Sec = s.Mtim.Sec
|
||||
fCookie.fobj.Mtim.Nsec = s.Mtim.Nsec
|
||||
fCookie.fobj.Ctim.Sec = s.Ctim.Sec
|
||||
fCookie.fobj.Ctim.Nsec = s.Ctim.Nsec
|
||||
}
|
||||
fobj.Name = (*int8)(unsafe.Pointer(&bs[0]))
|
||||
s := stat.Sys().(*syscall.Stat_t)
|
||||
fobj.Atim.Sec = s.Atim.Sec
|
||||
fobj.Atim.Nsec = s.Atim.Nsec
|
||||
fobj.Mtim.Sec = s.Mtim.Sec
|
||||
fobj.Mtim.Nsec = s.Mtim.Nsec
|
||||
fobj.Ctim.Sec = s.Ctim.Sec
|
||||
fobj.Ctim.Nsec = s.Ctim.Nsec
|
||||
return fobj, nil
|
||||
return fCookie, nil
|
||||
}
|
||||
|
||||
// GetOne wraps port_get(3c) and returns a single PortEvent.
|
||||
|
@ -929,44 +937,50 @@ func (e *EventPort) GetOne(t *Timespec) (*PortEvent, error) {
|
|||
p := new(PortEvent)
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.peIntToExt(pe, p)
|
||||
err = e.peIntToExt(pe, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// peIntToExt converts a cgo portEvent struct into the friendlier PortEvent
|
||||
// NOTE: Always call this function while holding the e.mu mutex
|
||||
func (e *EventPort) peIntToExt(peInt *portEvent, peExt *PortEvent) {
|
||||
func (e *EventPort) peIntToExt(peInt *portEvent, peExt *PortEvent) error {
|
||||
if e.cookies == nil {
|
||||
return fmt.Errorf("this EventPort is already closed")
|
||||
}
|
||||
peExt.Events = peInt.Events
|
||||
peExt.Source = peInt.Source
|
||||
cookie := (*interface{})(unsafe.Pointer(peInt.User))
|
||||
peExt.Cookie = *cookie
|
||||
fCookie := (*fileObjCookie)(unsafe.Pointer(peInt.User))
|
||||
_, found := e.cookies[fCookie]
|
||||
|
||||
if !found {
|
||||
panic("unexpected event port address; may be due to kernel bug; see https://go.dev/issue/54254")
|
||||
}
|
||||
peExt.Cookie = fCookie.cookie
|
||||
delete(e.cookies, fCookie)
|
||||
|
||||
switch peInt.Source {
|
||||
case PORT_SOURCE_FD:
|
||||
delete(e.cookies, cookie)
|
||||
peExt.Fd = uintptr(peInt.Object)
|
||||
// Only remove the fds entry if it exists and this cookie matches
|
||||
if fobj, ok := e.fds[peExt.Fd]; ok {
|
||||
if &fobj.cookie == cookie {
|
||||
if fobj == fCookie {
|
||||
delete(e.fds, peExt.Fd)
|
||||
}
|
||||
}
|
||||
case PORT_SOURCE_FILE:
|
||||
if fCookie, ok := e.cookies[cookie]; ok && uintptr(unsafe.Pointer(fCookie.fobj)) == uintptr(peInt.Object) {
|
||||
// Use our stashed reference rather than using unsafe on what we got back
|
||||
// the unsafe version would be (*fileObj)(unsafe.Pointer(uintptr(peInt.Object)))
|
||||
peExt.fobj = fCookie.fobj
|
||||
} else {
|
||||
panic("mismanaged memory")
|
||||
}
|
||||
delete(e.cookies, cookie)
|
||||
peExt.fobj = fCookie.fobj
|
||||
peExt.Path = BytePtrToString((*byte)(unsafe.Pointer(peExt.fobj.Name)))
|
||||
// Only remove the paths entry if it exists and this cookie matches
|
||||
if fobj, ok := e.paths[peExt.Path]; ok {
|
||||
if &fobj.cookie == cookie {
|
||||
if fobj == fCookie {
|
||||
delete(e.paths, peExt.Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pending wraps port_getn(3c) and returns how many events are pending.
|
||||
|
@ -990,7 +1004,7 @@ func (e *EventPort) Get(s []PortEvent, min int, timeout *Timespec) (int, error)
|
|||
got := uint32(min)
|
||||
max := uint32(len(s))
|
||||
var err error
|
||||
ps := make([]portEvent, max, max)
|
||||
ps := make([]portEvent, max)
|
||||
_, err = port_getn(e.port, &ps[0], max, &got, timeout)
|
||||
// got will be trustworthy with ETIME, but not any other error.
|
||||
if err != nil && err != ETIME {
|
||||
|
@ -998,8 +1012,122 @@ func (e *EventPort) Get(s []PortEvent, min int, timeout *Timespec) (int, error)
|
|||
}
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
valid := 0
|
||||
for i := 0; i < int(got); i++ {
|
||||
e.peIntToExt(&ps[i], &s[i])
|
||||
err2 := e.peIntToExt(&ps[i], &s[i])
|
||||
if err2 != nil {
|
||||
if valid == 0 && err == nil {
|
||||
// If err2 is the only error and there are no valid events
|
||||
// to return, return it to the caller.
|
||||
err = err2
|
||||
}
|
||||
break
|
||||
}
|
||||
valid = i + 1
|
||||
}
|
||||
return int(got), err
|
||||
return valid, err
|
||||
}
|
||||
|
||||
//sys putmsg(fd int, clptr *strbuf, dataptr *strbuf, flags int) (err error)
|
||||
|
||||
func Putmsg(fd int, cl []byte, data []byte, flags int) (err error) {
|
||||
var clp, datap *strbuf
|
||||
if len(cl) > 0 {
|
||||
clp = &strbuf{
|
||||
Len: int32(len(cl)),
|
||||
Buf: (*int8)(unsafe.Pointer(&cl[0])),
|
||||
}
|
||||
}
|
||||
if len(data) > 0 {
|
||||
datap = &strbuf{
|
||||
Len: int32(len(data)),
|
||||
Buf: (*int8)(unsafe.Pointer(&data[0])),
|
||||
}
|
||||
}
|
||||
return putmsg(fd, clp, datap, flags)
|
||||
}
|
||||
|
||||
//sys getmsg(fd int, clptr *strbuf, dataptr *strbuf, flags *int) (err error)
|
||||
|
||||
func Getmsg(fd int, cl []byte, data []byte) (retCl []byte, retData []byte, flags int, err error) {
|
||||
var clp, datap *strbuf
|
||||
if len(cl) > 0 {
|
||||
clp = &strbuf{
|
||||
Maxlen: int32(len(cl)),
|
||||
Buf: (*int8)(unsafe.Pointer(&cl[0])),
|
||||
}
|
||||
}
|
||||
if len(data) > 0 {
|
||||
datap = &strbuf{
|
||||
Maxlen: int32(len(data)),
|
||||
Buf: (*int8)(unsafe.Pointer(&data[0])),
|
||||
}
|
||||
}
|
||||
|
||||
if err = getmsg(fd, clp, datap, &flags); err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
if len(cl) > 0 {
|
||||
retCl = cl[:clp.Len]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
retData = data[:datap.Len]
|
||||
}
|
||||
return retCl, retData, flags, nil
|
||||
}
|
||||
|
||||
func IoctlSetIntRetInt(fd int, req uint, arg int) (int, error) {
|
||||
return ioctlRet(fd, req, uintptr(arg))
|
||||
}
|
||||
|
||||
func IoctlSetString(fd int, req uint, val string) error {
|
||||
bs := make([]byte, len(val)+1)
|
||||
copy(bs[:len(bs)-1], val)
|
||||
err := ioctl(fd, req, uintptr(unsafe.Pointer(&bs[0])))
|
||||
runtime.KeepAlive(&bs[0])
|
||||
return err
|
||||
}
|
||||
|
||||
// Lifreq Helpers
|
||||
|
||||
func (l *Lifreq) SetName(name string) error {
|
||||
if len(name) >= len(l.Name) {
|
||||
return fmt.Errorf("name cannot be more than %d characters", len(l.Name)-1)
|
||||
}
|
||||
for i := range name {
|
||||
l.Name[i] = int8(name[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Lifreq) SetLifruInt(d int) {
|
||||
*(*int)(unsafe.Pointer(&l.Lifru[0])) = d
|
||||
}
|
||||
|
||||
func (l *Lifreq) GetLifruInt() int {
|
||||
return *(*int)(unsafe.Pointer(&l.Lifru[0]))
|
||||
}
|
||||
|
||||
func (l *Lifreq) SetLifruUint(d uint) {
|
||||
*(*uint)(unsafe.Pointer(&l.Lifru[0])) = d
|
||||
}
|
||||
|
||||
func (l *Lifreq) GetLifruUint() uint {
|
||||
return *(*uint)(unsafe.Pointer(&l.Lifru[0]))
|
||||
}
|
||||
|
||||
func IoctlLifreq(fd int, req uint, l *Lifreq) error {
|
||||
return ioctl(fd, req, uintptr(unsafe.Pointer(l)))
|
||||
}
|
||||
|
||||
// Strioctl Helpers
|
||||
|
||||
func (s *Strioctl) SetInt(i int) {
|
||||
s.Len = int32(unsafe.Sizeof(i))
|
||||
s.Dp = (*int8)(unsafe.Pointer(&i))
|
||||
}
|
||||
|
||||
func IoctlSetStrioctlRetInt(fd int, req uint, s *Strioctl) (int, error) {
|
||||
return ioctlRet(fd, req, uintptr(unsafe.Pointer(s)))
|
||||
}
|
||||
|
|
|
@ -13,8 +13,6 @@ import (
|
|||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/internal/unsafeheader"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -117,11 +115,7 @@ func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (d
|
|||
}
|
||||
|
||||
// Use unsafe to convert addr into a []byte.
|
||||
var b []byte
|
||||
hdr := (*unsafeheader.Slice)(unsafe.Pointer(&b))
|
||||
hdr.Data = unsafe.Pointer(addr)
|
||||
hdr.Cap = length
|
||||
hdr.Len = length
|
||||
b := unsafe.Slice((*byte)(unsafe.Pointer(addr)), length)
|
||||
|
||||
// Register mapping in m and return it.
|
||||
p := &b[cap(b)-1]
|
||||
|
@ -337,6 +331,19 @@ func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Recvmsg receives a message from a socket using the recvmsg system call. The
|
||||
// received non-control data will be written to p, and any "out of band"
|
||||
// control data will be written to oob. The flags are passed to recvmsg.
|
||||
//
|
||||
// The results are:
|
||||
// - n is the number of non-control data bytes read into p
|
||||
// - oobn is the number of control data bytes read into oob; this may be interpreted using [ParseSocketControlMessage]
|
||||
// - recvflags is flags returned by recvmsg
|
||||
// - from is the address of the sender
|
||||
//
|
||||
// If the underlying socket type is not SOCK_DGRAM, a received message
|
||||
// containing oob data and a single '\0' of non-control data is treated as if
|
||||
// the message contained only control data, i.e. n will be zero on return.
|
||||
func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
||||
var iov [1]Iovec
|
||||
if len(p) > 0 {
|
||||
|
@ -352,13 +359,9 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
|
|||
return
|
||||
}
|
||||
|
||||
// RecvmsgBuffers receives a message from a socket using the recvmsg
|
||||
// system call. The flags are passed to recvmsg. Any non-control data
|
||||
// read is scattered into the buffers slices. The results are:
|
||||
// - n is the number of non-control data read into bufs
|
||||
// - oobn is the number of control data read into oob; this may be interpreted using [ParseSocketControlMessage]
|
||||
// - recvflags is flags returned by recvmsg
|
||||
// - from is the address of the sender
|
||||
// RecvmsgBuffers receives a message from a socket using the recvmsg system
|
||||
// call. This function is equivalent to Recvmsg, but non-control data read is
|
||||
// scattered into the buffers slices.
|
||||
func RecvmsgBuffers(fd int, buffers [][]byte, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) {
|
||||
iov := make([]Iovec, len(buffers))
|
||||
for i := range buffers {
|
||||
|
@ -377,11 +380,38 @@ func RecvmsgBuffers(fd int, buffers [][]byte, oob []byte, flags int) (n, oobn in
|
|||
return
|
||||
}
|
||||
|
||||
// Sendmsg sends a message on a socket to an address using the sendmsg system
|
||||
// call. This function is equivalent to SendmsgN, but does not return the
|
||||
// number of bytes actually sent.
|
||||
func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
|
||||
_, err = SendmsgN(fd, p, oob, to, flags)
|
||||
return
|
||||
}
|
||||
|
||||
// SendmsgN sends a message on a socket to an address using the sendmsg system
|
||||
// call. p contains the non-control data to send, and oob contains the "out of
|
||||
// band" control data. The flags are passed to sendmsg. The number of
|
||||
// non-control bytes actually written to the socket is returned.
|
||||
//
|
||||
// Some socket types do not support sending control data without accompanying
|
||||
// non-control data. If p is empty, and oob contains control data, and the
|
||||
// underlying socket type is not SOCK_DGRAM, p will be treated as containing a
|
||||
// single '\0' and the return value will indicate zero bytes sent.
|
||||
//
|
||||
// The Go function Recvmsg, if called with an empty p and a non-empty oob,
|
||||
// will read and ignore this additional '\0'. If the message is received by
|
||||
// code that does not use Recvmsg, or that does not use Go at all, that code
|
||||
// will need to be written to expect and ignore the additional '\0'.
|
||||
//
|
||||
// If you need to send non-empty oob with p actually empty, and if the
|
||||
// underlying socket type supports it, you can do so via a raw system call as
|
||||
// follows:
|
||||
//
|
||||
// msg := &unix.Msghdr{
|
||||
// Control: &oob[0],
|
||||
// }
|
||||
// msg.SetControllen(len(oob))
|
||||
// n, _, errno := unix.Syscall(unix.SYS_SENDMSG, uintptr(fd), uintptr(unsafe.Pointer(msg)), flags)
|
||||
func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
||||
var iov [1]Iovec
|
||||
if len(p) > 0 {
|
||||
|
@ -400,9 +430,8 @@ func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error)
|
|||
}
|
||||
|
||||
// SendmsgBuffers sends a message on a socket to an address using the sendmsg
|
||||
// system call. The flags are passed to sendmsg. Any non-control data written
|
||||
// is gathered from buffers. The function returns the number of bytes written
|
||||
// to the socket.
|
||||
// system call. This function is equivalent to SendmsgN, but the non-control
|
||||
// data is gathered from buffers.
|
||||
func SendmsgBuffers(fd int, buffers [][]byte, oob []byte, to Sockaddr, flags int) (n int, err error) {
|
||||
iov := make([]Iovec, len(buffers))
|
||||
for i := range buffers {
|
||||
|
@ -429,11 +458,15 @@ func Send(s int, buf []byte, flags int) (err error) {
|
|||
}
|
||||
|
||||
func Sendto(fd int, p []byte, flags int, to Sockaddr) (err error) {
|
||||
ptr, n, err := to.sockaddr()
|
||||
if err != nil {
|
||||
return err
|
||||
var ptr unsafe.Pointer
|
||||
var salen _Socklen
|
||||
if to != nil {
|
||||
ptr, salen, err = to.sockaddr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return sendto(fd, p, flags, ptr, n)
|
||||
return sendto(fd, p, flags, ptr, salen)
|
||||
}
|
||||
|
||||
func SetsockoptByte(fd, level, opt int, value byte) (err error) {
|
||||
|
@ -545,7 +578,7 @@ func Lutimes(path string, tv []Timeval) error {
|
|||
return UtimesNanoAt(AT_FDCWD, path, ts, AT_SYMLINK_NOFOLLOW)
|
||||
}
|
||||
|
||||
// emptyIovec reports whether there are no bytes in the slice of Iovec.
|
||||
// emptyIovecs reports whether there are no bytes in the slice of Iovec.
|
||||
func emptyIovecs(iov []Iovec) bool {
|
||||
for i := range iov {
|
||||
if iov[i].Len > 0 {
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) && gc && !ppc64le && !ppc64
|
||||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
//go:build (darwin || dragonfly || freebsd || (linux && !ppc64 && !ppc64le) || netbsd || openbsd || solaris) && gc
|
||||
// +build darwin dragonfly freebsd linux,!ppc64,!ppc64le netbsd openbsd solaris
|
||||
// +build gc
|
||||
// +build !ppc64le
|
||||
// +build !ppc64
|
||||
|
||||
package unix
|
||||
|
||||
|
|
|
@ -9,8 +9,10 @@ package unix
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
@ -55,7 +57,13 @@ func (d *Dirent) NameString() string {
|
|||
if d == nil {
|
||||
return ""
|
||||
}
|
||||
return string(d.Name[:d.Namlen])
|
||||
s := string(d.Name[:])
|
||||
idx := strings.IndexByte(s, 0)
|
||||
if idx == -1 {
|
||||
return s
|
||||
} else {
|
||||
return s[:idx]
|
||||
}
|
||||
}
|
||||
|
||||
func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) {
|
||||
|
@ -1230,6 +1238,14 @@ func Readdir(dir uintptr) (*Dirent, error) {
|
|||
return &ent, err
|
||||
}
|
||||
|
||||
func readdir_r(dirp uintptr, entry *direntLE, result **direntLE) (err error) {
|
||||
r0, _, e1 := syscall_syscall(SYS___READDIR_R_A, dirp, uintptr(unsafe.Pointer(entry)), uintptr(unsafe.Pointer(result)))
|
||||
if int64(r0) == -1 {
|
||||
err = errnoErr(Errno(e1))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Closedir(dir uintptr) error {
|
||||
_, _, e := syscall_syscall(SYS_CLOSEDIR, dir, 0, 0)
|
||||
if e != 0 {
|
||||
|
@ -1821,3 +1837,158 @@ func Unmount(name string, mtm int) (err error) {
|
|||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func fdToPath(dirfd int) (path string, err error) {
|
||||
var buffer [1024]byte
|
||||
// w_ctrl()
|
||||
ret := runtime.CallLeFuncByPtr(runtime.XplinkLibvec+SYS_W_IOCTL<<4,
|
||||
[]uintptr{uintptr(dirfd), 17, 1024, uintptr(unsafe.Pointer(&buffer[0]))})
|
||||
if ret == 0 {
|
||||
zb := bytes.IndexByte(buffer[:], 0)
|
||||
if zb == -1 {
|
||||
zb = len(buffer)
|
||||
}
|
||||
// __e2a_l()
|
||||
runtime.CallLeFuncByPtr(runtime.XplinkLibvec+SYS___E2A_L<<4,
|
||||
[]uintptr{uintptr(unsafe.Pointer(&buffer[0])), uintptr(zb)})
|
||||
return string(buffer[:zb]), nil
|
||||
}
|
||||
// __errno()
|
||||
errno := int(*(*int32)(unsafe.Pointer(runtime.CallLeFuncByPtr(runtime.XplinkLibvec+SYS___ERRNO<<4,
|
||||
[]uintptr{}))))
|
||||
// __errno2()
|
||||
errno2 := int(runtime.CallLeFuncByPtr(runtime.XplinkLibvec+SYS___ERRNO2<<4,
|
||||
[]uintptr{}))
|
||||
// strerror_r()
|
||||
ret = runtime.CallLeFuncByPtr(runtime.XplinkLibvec+SYS_STRERROR_R<<4,
|
||||
[]uintptr{uintptr(errno), uintptr(unsafe.Pointer(&buffer[0])), 1024})
|
||||
if ret == 0 {
|
||||
zb := bytes.IndexByte(buffer[:], 0)
|
||||
if zb == -1 {
|
||||
zb = len(buffer)
|
||||
}
|
||||
return "", fmt.Errorf("%s (errno2=0x%x)", buffer[:zb], errno2)
|
||||
} else {
|
||||
return "", fmt.Errorf("fdToPath errno %d (errno2=0x%x)", errno, errno2)
|
||||
}
|
||||
}
|
||||
|
||||
func direntLeToDirentUnix(dirent *direntLE, dir uintptr, path string) (Dirent, error) {
|
||||
var d Dirent
|
||||
|
||||
d.Ino = uint64(dirent.Ino)
|
||||
offset, err := Telldir(dir)
|
||||
if err != nil {
|
||||
return d, err
|
||||
}
|
||||
|
||||
d.Off = int64(offset)
|
||||
s := string(bytes.Split(dirent.Name[:], []byte{0})[0])
|
||||
copy(d.Name[:], s)
|
||||
|
||||
d.Reclen = uint16(24 + len(d.NameString()))
|
||||
var st Stat_t
|
||||
path = path + "/" + s
|
||||
err = Lstat(path, &st)
|
||||
if err != nil {
|
||||
return d, err
|
||||
}
|
||||
|
||||
d.Type = uint8(st.Mode >> 24)
|
||||
return d, err
|
||||
}
|
||||
|
||||
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
|
||||
// Simulation of Getdirentries port from the Darwin implementation.
|
||||
// COMMENTS FROM DARWIN:
|
||||
// It's not the full required semantics, but should handle the case
|
||||
// of calling Getdirentries or ReadDirent repeatedly.
|
||||
// It won't handle assigning the results of lseek to *basep, or handle
|
||||
// the directory being edited underfoot.
|
||||
|
||||
skip, err := Seek(fd, 0, 1 /* SEEK_CUR */)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Get path from fd to avoid unavailable call (fdopendir)
|
||||
path, err := fdToPath(fd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d, err := Opendir(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer Closedir(d)
|
||||
|
||||
var cnt int64
|
||||
for {
|
||||
var entryLE direntLE
|
||||
var entrypLE *direntLE
|
||||
e := readdir_r(d, &entryLE, &entrypLE)
|
||||
if e != nil {
|
||||
return n, e
|
||||
}
|
||||
if entrypLE == nil {
|
||||
break
|
||||
}
|
||||
if skip > 0 {
|
||||
skip--
|
||||
cnt++
|
||||
continue
|
||||
}
|
||||
|
||||
// Dirent on zos has a different structure
|
||||
entry, e := direntLeToDirentUnix(&entryLE, d, path)
|
||||
if e != nil {
|
||||
return n, e
|
||||
}
|
||||
|
||||
reclen := int(entry.Reclen)
|
||||
if reclen > len(buf) {
|
||||
// Not enough room. Return for now.
|
||||
// The counter will let us know where we should start up again.
|
||||
// Note: this strategy for suspending in the middle and
|
||||
// restarting is O(n^2) in the length of the directory. Oh well.
|
||||
break
|
||||
}
|
||||
|
||||
// Copy entry into return buffer.
|
||||
s := unsafe.Slice((*byte)(unsafe.Pointer(&entry)), reclen)
|
||||
copy(buf, s)
|
||||
|
||||
buf = buf[reclen:]
|
||||
n += reclen
|
||||
cnt++
|
||||
}
|
||||
// Set the seek offset of the input fd to record
|
||||
// how many files we've already returned.
|
||||
_, err = Seek(fd, cnt, 0 /* SEEK_SET */)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func ReadDirent(fd int, buf []byte) (n int, err error) {
|
||||
var base = (*uintptr)(unsafe.Pointer(new(uint64)))
|
||||
return Getdirentries(fd, buf, base)
|
||||
}
|
||||
|
||||
func direntIno(buf []byte) (uint64, bool) {
|
||||
return readInt(buf, unsafe.Offsetof(Dirent{}.Ino), unsafe.Sizeof(Dirent{}.Ino))
|
||||
}
|
||||
|
||||
func direntReclen(buf []byte) (uint64, bool) {
|
||||
return readInt(buf, unsafe.Offsetof(Dirent{}.Reclen), unsafe.Sizeof(Dirent{}.Reclen))
|
||||
}
|
||||
|
||||
func direntNamlen(buf []byte) (uint64, bool) {
|
||||
reclen, ok := direntReclen(buf)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
return reclen - uint64(unsafe.Offsetof(Dirent{}.Name)), true
|
||||
}
|
||||
|
|
|
@ -7,11 +7,7 @@
|
|||
|
||||
package unix
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/internal/unsafeheader"
|
||||
)
|
||||
import "unsafe"
|
||||
|
||||
// SysvShmAttach attaches the Sysv shared memory segment associated with the
|
||||
// shared memory identifier id.
|
||||
|
@ -34,12 +30,7 @@ func SysvShmAttach(id int, addr uintptr, flag int) ([]byte, error) {
|
|||
}
|
||||
|
||||
// Use unsafe to convert addr into a []byte.
|
||||
// TODO: convert to unsafe.Slice once we can assume Go 1.17
|
||||
var b []byte
|
||||
hdr := (*unsafeheader.Slice)(unsafe.Pointer(&b))
|
||||
hdr.Data = unsafe.Pointer(addr)
|
||||
hdr.Cap = int(info.Segsz)
|
||||
hdr.Len = int(info.Segsz)
|
||||
b := unsafe.Slice((*byte)(unsafe.Pointer(addr)), int(info.Segsz))
|
||||
return b, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ package unix
|
|||
|
||||
import "time"
|
||||
|
||||
// TimespecToNSec returns the time stored in ts as nanoseconds.
|
||||
// TimespecToNsec returns the time stored in ts as nanoseconds.
|
||||
func TimespecToNsec(ts Timespec) int64 { return ts.Nano() }
|
||||
|
||||
// NsecToTimespec converts a number of nanoseconds into a Timespec.
|
||||
|
|
|
@ -36,9 +36,14 @@ func xattrnamespace(fullattr string) (ns int, attr string, err error) {
|
|||
func initxattrdest(dest []byte, idx int) (d unsafe.Pointer) {
|
||||
if len(dest) > idx {
|
||||
return unsafe.Pointer(&dest[idx])
|
||||
} else {
|
||||
return unsafe.Pointer(_zero)
|
||||
}
|
||||
if dest != nil {
|
||||
// extattr_get_file and extattr_list_file treat NULL differently from
|
||||
// a non-NULL pointer of length zero. Preserve the property of nilness,
|
||||
// even if we can't use dest directly.
|
||||
return unsafe.Pointer(&_zero)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FreeBSD and NetBSD implement their own syscalls to handle extended attributes
|
||||
|
@ -160,13 +165,12 @@ func Lremovexattr(link string, attr string) (err error) {
|
|||
}
|
||||
|
||||
func Listxattr(file string, dest []byte) (sz int, err error) {
|
||||
d := initxattrdest(dest, 0)
|
||||
destsiz := len(dest)
|
||||
|
||||
// FreeBSD won't allow you to list xattrs from multiple namespaces
|
||||
s := 0
|
||||
s, pos := 0, 0
|
||||
for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} {
|
||||
stmp, e := ExtattrListFile(file, nsid, uintptr(d), destsiz)
|
||||
stmp, e := ListxattrNS(file, nsid, dest[pos:])
|
||||
|
||||
/* Errors accessing system attrs are ignored so that
|
||||
* we can implement the Linux-like behavior of omitting errors that
|
||||
|
@ -175,66 +179,102 @@ func Listxattr(file string, dest []byte) (sz int, err error) {
|
|||
* Linux will still error if we ask for user attributes on a file that
|
||||
* we don't have read permissions on, so don't ignore those errors
|
||||
*/
|
||||
if e != nil && e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
|
||||
continue
|
||||
} else if e != nil {
|
||||
if e != nil {
|
||||
if e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
|
||||
continue
|
||||
}
|
||||
return s, e
|
||||
}
|
||||
|
||||
s += stmp
|
||||
destsiz -= s
|
||||
if destsiz < 0 {
|
||||
destsiz = 0
|
||||
pos = s
|
||||
if pos > destsiz {
|
||||
pos = destsiz
|
||||
}
|
||||
d = initxattrdest(dest, s)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func ListxattrNS(file string, nsid int, dest []byte) (sz int, err error) {
|
||||
d := initxattrdest(dest, 0)
|
||||
destsiz := len(dest)
|
||||
|
||||
s, e := ExtattrListFile(file, nsid, uintptr(d), destsiz)
|
||||
if e != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func Flistxattr(fd int, dest []byte) (sz int, err error) {
|
||||
d := initxattrdest(dest, 0)
|
||||
destsiz := len(dest)
|
||||
|
||||
s := 0
|
||||
s, pos := 0, 0
|
||||
for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} {
|
||||
stmp, e := ExtattrListFd(fd, nsid, uintptr(d), destsiz)
|
||||
if e != nil && e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
|
||||
continue
|
||||
} else if e != nil {
|
||||
stmp, e := FlistxattrNS(fd, nsid, dest[pos:])
|
||||
|
||||
if e != nil {
|
||||
if e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
|
||||
continue
|
||||
}
|
||||
return s, e
|
||||
}
|
||||
|
||||
s += stmp
|
||||
destsiz -= s
|
||||
if destsiz < 0 {
|
||||
destsiz = 0
|
||||
pos = s
|
||||
if pos > destsiz {
|
||||
pos = destsiz
|
||||
}
|
||||
d = initxattrdest(dest, s)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func FlistxattrNS(fd int, nsid int, dest []byte) (sz int, err error) {
|
||||
d := initxattrdest(dest, 0)
|
||||
destsiz := len(dest)
|
||||
|
||||
s, e := ExtattrListFd(fd, nsid, uintptr(d), destsiz)
|
||||
if e != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func Llistxattr(link string, dest []byte) (sz int, err error) {
|
||||
d := initxattrdest(dest, 0)
|
||||
destsiz := len(dest)
|
||||
|
||||
s := 0
|
||||
s, pos := 0, 0
|
||||
for _, nsid := range [...]int{EXTATTR_NAMESPACE_USER, EXTATTR_NAMESPACE_SYSTEM} {
|
||||
stmp, e := ExtattrListLink(link, nsid, uintptr(d), destsiz)
|
||||
if e != nil && e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
|
||||
continue
|
||||
} else if e != nil {
|
||||
stmp, e := LlistxattrNS(link, nsid, dest[pos:])
|
||||
|
||||
if e != nil {
|
||||
if e == EPERM && nsid != EXTATTR_NAMESPACE_USER {
|
||||
continue
|
||||
}
|
||||
return s, e
|
||||
}
|
||||
|
||||
s += stmp
|
||||
destsiz -= s
|
||||
if destsiz < 0 {
|
||||
destsiz = 0
|
||||
pos = s
|
||||
if pos > destsiz {
|
||||
pos = destsiz
|
||||
}
|
||||
d = initxattrdest(dest, s)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func LlistxattrNS(link string, nsid int, dest []byte) (sz int, err error) {
|
||||
d := initxattrdest(dest, 0)
|
||||
destsiz := len(dest)
|
||||
|
||||
s, e := ExtattrListLink(link, nsid, uintptr(d), destsiz)
|
||||
if e != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
|
|
|
@ -457,7 +457,6 @@ const (
|
|||
B600 = 0x8
|
||||
B75 = 0x2
|
||||
B9600 = 0xd
|
||||
BALLOON_KVM_MAGIC = 0x13661366
|
||||
BDEVFS_MAGIC = 0x62646576
|
||||
BINDERFS_SUPER_MAGIC = 0x6c6f6f70
|
||||
BINFMTFS_MAGIC = 0x42494e4d
|
||||
|
@ -563,6 +562,7 @@ const (
|
|||
BUS_USB = 0x3
|
||||
BUS_VIRTUAL = 0x6
|
||||
CAN_BCM = 0x2
|
||||
CAN_BUS_OFF_THRESHOLD = 0x100
|
||||
CAN_CTRLMODE_3_SAMPLES = 0x4
|
||||
CAN_CTRLMODE_BERR_REPORTING = 0x10
|
||||
CAN_CTRLMODE_CC_LEN8_DLC = 0x100
|
||||
|
@ -577,9 +577,12 @@ const (
|
|||
CAN_EFF_FLAG = 0x80000000
|
||||
CAN_EFF_ID_BITS = 0x1d
|
||||
CAN_EFF_MASK = 0x1fffffff
|
||||
CAN_ERROR_PASSIVE_THRESHOLD = 0x80
|
||||
CAN_ERROR_WARNING_THRESHOLD = 0x60
|
||||
CAN_ERR_ACK = 0x20
|
||||
CAN_ERR_BUSERROR = 0x80
|
||||
CAN_ERR_BUSOFF = 0x40
|
||||
CAN_ERR_CNT = 0x200
|
||||
CAN_ERR_CRTL = 0x4
|
||||
CAN_ERR_CRTL_ACTIVE = 0x40
|
||||
CAN_ERR_CRTL_RX_OVERFLOW = 0x1
|
||||
|
@ -820,9 +823,9 @@ const (
|
|||
DM_UUID_FLAG = 0x4000
|
||||
DM_UUID_LEN = 0x81
|
||||
DM_VERSION = 0xc138fd00
|
||||
DM_VERSION_EXTRA = "-ioctl (2022-02-22)"
|
||||
DM_VERSION_EXTRA = "-ioctl (2022-07-28)"
|
||||
DM_VERSION_MAJOR = 0x4
|
||||
DM_VERSION_MINOR = 0x2e
|
||||
DM_VERSION_MINOR = 0x2f
|
||||
DM_VERSION_PATCHLEVEL = 0x0
|
||||
DT_BLK = 0x6
|
||||
DT_CHR = 0x2
|
||||
|
@ -1049,6 +1052,7 @@ const (
|
|||
ETH_P_CAIF = 0xf7
|
||||
ETH_P_CAN = 0xc
|
||||
ETH_P_CANFD = 0xd
|
||||
ETH_P_CANXL = 0xe
|
||||
ETH_P_CFM = 0x8902
|
||||
ETH_P_CONTROL = 0x16
|
||||
ETH_P_CUST = 0x6006
|
||||
|
@ -1060,6 +1064,7 @@ const (
|
|||
ETH_P_DNA_RT = 0x6003
|
||||
ETH_P_DSA = 0x1b
|
||||
ETH_P_DSA_8021Q = 0xdadb
|
||||
ETH_P_DSA_A5PSW = 0xe001
|
||||
ETH_P_ECONET = 0x18
|
||||
ETH_P_EDSA = 0xdada
|
||||
ETH_P_ERSPAN = 0x88be
|
||||
|
@ -1194,8 +1199,10 @@ const (
|
|||
FAN_MARK_EVICTABLE = 0x200
|
||||
FAN_MARK_FILESYSTEM = 0x100
|
||||
FAN_MARK_FLUSH = 0x80
|
||||
FAN_MARK_IGNORE = 0x400
|
||||
FAN_MARK_IGNORED_MASK = 0x20
|
||||
FAN_MARK_IGNORED_SURV_MODIFY = 0x40
|
||||
FAN_MARK_IGNORE_SURV = 0x440
|
||||
FAN_MARK_INODE = 0x0
|
||||
FAN_MARK_MOUNT = 0x10
|
||||
FAN_MARK_ONLYDIR = 0x8
|
||||
|
@ -1253,6 +1260,7 @@ const (
|
|||
FSCRYPT_MODE_AES_128_CBC = 0x5
|
||||
FSCRYPT_MODE_AES_128_CTS = 0x6
|
||||
FSCRYPT_MODE_AES_256_CTS = 0x4
|
||||
FSCRYPT_MODE_AES_256_HCTR2 = 0xa
|
||||
FSCRYPT_MODE_AES_256_XTS = 0x1
|
||||
FSCRYPT_POLICY_FLAGS_PAD_16 = 0x2
|
||||
FSCRYPT_POLICY_FLAGS_PAD_32 = 0x3
|
||||
|
@ -1430,6 +1438,7 @@ const (
|
|||
IFF_NOARP = 0x80
|
||||
IFF_NOFILTER = 0x1000
|
||||
IFF_NOTRAILERS = 0x20
|
||||
IFF_NO_CARRIER = 0x40
|
||||
IFF_NO_PI = 0x1000
|
||||
IFF_ONE_QUEUE = 0x2000
|
||||
IFF_PERSIST = 0x800
|
||||
|
@ -1805,6 +1814,7 @@ const (
|
|||
MADV_DONTDUMP = 0x10
|
||||
MADV_DONTFORK = 0xa
|
||||
MADV_DONTNEED = 0x4
|
||||
MADV_DONTNEED_LOCKED = 0x18
|
||||
MADV_FREE = 0x8
|
||||
MADV_HUGEPAGE = 0xe
|
||||
MADV_HWPOISON = 0x64
|
||||
|
@ -1846,7 +1856,7 @@ const (
|
|||
MFD_ALLOW_SEALING = 0x2
|
||||
MFD_CLOEXEC = 0x1
|
||||
MFD_HUGETLB = 0x4
|
||||
MFD_HUGE_16GB = -0x78000000
|
||||
MFD_HUGE_16GB = 0x88000000
|
||||
MFD_HUGE_16MB = 0x60000000
|
||||
MFD_HUGE_1GB = 0x78000000
|
||||
MFD_HUGE_1MB = 0x50000000
|
||||
|
@ -2212,6 +2222,11 @@ const (
|
|||
PERF_AUX_FLAG_PARTIAL = 0x4
|
||||
PERF_AUX_FLAG_PMU_FORMAT_TYPE_MASK = 0xff00
|
||||
PERF_AUX_FLAG_TRUNCATED = 0x1
|
||||
PERF_BR_ARM64_DEBUG_DATA = 0x7
|
||||
PERF_BR_ARM64_DEBUG_EXIT = 0x5
|
||||
PERF_BR_ARM64_DEBUG_HALT = 0x4
|
||||
PERF_BR_ARM64_DEBUG_INST = 0x6
|
||||
PERF_BR_ARM64_FIQ = 0x3
|
||||
PERF_FLAG_FD_CLOEXEC = 0x8
|
||||
PERF_FLAG_FD_NO_GROUP = 0x1
|
||||
PERF_FLAG_FD_OUTPUT = 0x2
|
||||
|
@ -2232,6 +2247,8 @@ const (
|
|||
PERF_MEM_LOCK_NA = 0x1
|
||||
PERF_MEM_LOCK_SHIFT = 0x18
|
||||
PERF_MEM_LVLNUM_ANY_CACHE = 0xb
|
||||
PERF_MEM_LVLNUM_CXL = 0x9
|
||||
PERF_MEM_LVLNUM_IO = 0xa
|
||||
PERF_MEM_LVLNUM_L1 = 0x1
|
||||
PERF_MEM_LVLNUM_L2 = 0x2
|
||||
PERF_MEM_LVLNUM_L3 = 0x3
|
||||
|
@ -2265,6 +2282,7 @@ const (
|
|||
PERF_MEM_REMOTE_REMOTE = 0x1
|
||||
PERF_MEM_REMOTE_SHIFT = 0x25
|
||||
PERF_MEM_SNOOPX_FWD = 0x1
|
||||
PERF_MEM_SNOOPX_PEER = 0x2
|
||||
PERF_MEM_SNOOPX_SHIFT = 0x26
|
||||
PERF_MEM_SNOOP_HIT = 0x4
|
||||
PERF_MEM_SNOOP_HITM = 0x10
|
||||
|
@ -2301,7 +2319,6 @@ const (
|
|||
PERF_SAMPLE_BRANCH_PLM_ALL = 0x7
|
||||
PERF_SAMPLE_WEIGHT_TYPE = 0x1004000
|
||||
PIPEFS_MAGIC = 0x50495045
|
||||
PPC_CMM_MAGIC = 0xc7571590
|
||||
PPPIOCGNPMODE = 0xc008744c
|
||||
PPPIOCNEWUNIT = 0xc004743e
|
||||
PRIO_PGRP = 0x1
|
||||
|
@ -2999,6 +3016,7 @@ const (
|
|||
STATX_BLOCKS = 0x400
|
||||
STATX_BTIME = 0x800
|
||||
STATX_CTIME = 0x80
|
||||
STATX_DIOALIGN = 0x2000
|
||||
STATX_GID = 0x10
|
||||
STATX_INO = 0x100
|
||||
STATX_MNT_ID = 0x1000
|
||||
|
@ -3392,9 +3410,7 @@ const (
|
|||
XDP_ZEROCOPY = 0x4
|
||||
XENFS_SUPER_MAGIC = 0xabba1974
|
||||
XFS_SUPER_MAGIC = 0x58465342
|
||||
Z3FOLD_MAGIC = 0x33
|
||||
ZONEFS_MAGIC = 0x5a4f4653
|
||||
ZSMALLOC_MAGIC = 0x58295829
|
||||
_HIDIOCGRAWNAME_LEN = 0x80
|
||||
_HIDIOCGRAWPHYS_LEN = 0x40
|
||||
_HIDIOCGRAWUNIQ_LEN = 0x40
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// mkerrors.sh -Wall -Werror -static -I/tmp/include -m32
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/386/include -m32
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build 386 && linux
|
||||
// +build 386,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m32 _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/386/include -m32 _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
@ -133,6 +133,7 @@ const (
|
|||
MEMGETREGIONCOUNT = 0x80044d07
|
||||
MEMISLOCKED = 0x80084d17
|
||||
MEMLOCK = 0x40084d05
|
||||
MEMREAD = 0xc03c4d1a
|
||||
MEMREADOOB = 0xc00c4d04
|
||||
MEMSETBADBLOCK = 0x40084d0c
|
||||
MEMUNLOCK = 0x40084d06
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// mkerrors.sh -Wall -Werror -static -I/tmp/include -m64
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/amd64/include -m64
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build amd64 && linux
|
||||
// +build amd64,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include -m64 _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/amd64/include -m64 _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
@ -133,6 +133,7 @@ const (
|
|||
MEMGETREGIONCOUNT = 0x80044d07
|
||||
MEMISLOCKED = 0x80084d17
|
||||
MEMLOCK = 0x40084d05
|
||||
MEMREAD = 0xc0404d1a
|
||||
MEMREADOOB = 0xc0104d04
|
||||
MEMSETBADBLOCK = 0x40084d0c
|
||||
MEMUNLOCK = 0x40084d06
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// mkerrors.sh -Wall -Werror -static -I/tmp/include
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/arm/include
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build arm && linux
|
||||
// +build arm,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/arm/include _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
@ -131,6 +131,7 @@ const (
|
|||
MEMGETREGIONCOUNT = 0x80044d07
|
||||
MEMISLOCKED = 0x80084d17
|
||||
MEMLOCK = 0x40084d05
|
||||
MEMREAD = 0xc0404d1a
|
||||
MEMREADOOB = 0xc00c4d04
|
||||
MEMSETBADBLOCK = 0x40084d0c
|
||||
MEMUNLOCK = 0x40084d06
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// mkerrors.sh -Wall -Werror -static -I/tmp/include -fsigned-char
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/arm64/include -fsigned-char
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build arm64 && linux
|
||||
// +build arm64,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include -fsigned-char _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/arm64/include -fsigned-char _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
@ -134,6 +134,7 @@ const (
|
|||
MEMGETREGIONCOUNT = 0x80044d07
|
||||
MEMISLOCKED = 0x80084d17
|
||||
MEMLOCK = 0x40084d05
|
||||
MEMREAD = 0xc0404d1a
|
||||
MEMREADOOB = 0xc0104d04
|
||||
MEMSETBADBLOCK = 0x40084d0c
|
||||
MEMUNLOCK = 0x40084d06
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// mkerrors.sh -Wall -Werror -static -I/tmp/include
|
||||
// mkerrors.sh -Wall -Werror -static -I/tmp/loong64/include
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build loong64 && linux
|
||||
// +build loong64,linux
|
||||
|
||||
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/include _const.go
|
||||
// cgo -godefs -- -Wall -Werror -static -I/tmp/loong64/include _const.go
|
||||
|
||||
package unix
|
||||
|
||||
|
@ -132,6 +132,7 @@ const (
|
|||
MEMGETREGIONCOUNT = 0x80044d07
|
||||
MEMISLOCKED = 0x80084d17
|
||||
MEMLOCK = 0x40084d05
|
||||
MEMREAD = 0xc0404d1a
|
||||
MEMREADOOB = 0xc0104d04
|
||||
MEMSETBADBLOCK = 0x40084d0c
|
||||
MEMUNLOCK = 0x40084d06
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче