Skip to content

Commit

Permalink
Merge branch 'master' into websocket-http2
Browse files Browse the repository at this point in the history
# Conflicts:
#	http2/transport_test.go
  • Loading branch information
WeidiDeng committed Nov 22, 2024
2 parents 23eded4 + 334afa0 commit cba5ecd
Show file tree
Hide file tree
Showing 19 changed files with 858 additions and 116 deletions.
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@

[![Go Reference](https://pkg.go.dev/badge/golang.org/x/net.svg)](https://pkg.go.dev/golang.org/x/net)

This repository holds supplementary Go networking libraries.
This repository holds supplementary Go networking packages.

## Download/Install
## Report Issues / Send Patches

The easiest way to install is to run `go get -u golang.org/x/net`. You can
also manually git clone the repository to `$GOPATH/src/golang.org/x/net`.
This repository uses Gerrit for code changes. To learn how to submit changes to
this repository, see https://go.dev/doc/contribute.

## Report Issues / Send Patches
The git repository is https://go.googlesource.com/net.

This repository uses Gerrit for code changes. To learn how to submit
changes to this repository, see https://golang.org/doc/contribute.html.
The main issue tracker for the net repository is located at
https://github.com/golang/go/issues. Prefix your issue with "x/net:" in the
https://go.dev/issues. Prefix your issue with "x/net:" in the
subject line, so it is easy to find.
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ module golang.org/x/net
go 1.18

require (
golang.org/x/crypto v0.27.0
golang.org/x/sys v0.25.0
golang.org/x/term v0.24.0
golang.org/x/text v0.18.0
golang.org/x/crypto v0.29.0
golang.org/x/sys v0.27.0
golang.org/x/term v0.26.0
golang.org/x/text v0.20.0
)
16 changes: 8 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
7 changes: 1 addition & 6 deletions html/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,11 @@ example, to process each anchor node in depth-first order:
if err != nil {
// ...
}
var f func(*html.Node)
f = func(n *html.Node) {
for n := range doc.Descendants() {
if n.Type == html.ElementNode && n.Data == "a" {
// Do something with n...
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
f(c)
}
}
f(doc)
The relevant specifications include:
https://html.spec.whatwg.org/multipage/syntax.html and
Expand Down
13 changes: 6 additions & 7 deletions html/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build go1.23

// This example demonstrates parsing HTML data and walking the resulting tree.
package html_test

Expand All @@ -11,6 +13,7 @@ import (
"strings"

"golang.org/x/net/html"
"golang.org/x/net/html/atom"
)

func ExampleParse() {
Expand All @@ -19,21 +22,17 @@ func ExampleParse() {
if err != nil {
log.Fatal(err)
}
var f func(*html.Node)
f = func(n *html.Node) {
if n.Type == html.ElementNode && n.Data == "a" {
for n := range doc.Descendants() {
if n.Type == html.ElementNode && n.DataAtom == atom.A {
for _, a := range n.Attr {
if a.Key == "href" {
fmt.Println(a.Val)
break
}
}
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
f(c)
}
}
f(doc)

// Output:
// foo
// /bar/baz
Expand Down
56 changes: 56 additions & 0 deletions html/iter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2024 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.23

package html

import "iter"

// Ancestors returns an iterator over the ancestors of n, starting with n.Parent.
//
// Mutating a Node or its parents while iterating may have unexpected results.
func (n *Node) Ancestors() iter.Seq[*Node] {
_ = n.Parent // eager nil check

return func(yield func(*Node) bool) {
for p := n.Parent; p != nil && yield(p); p = p.Parent {
}
}
}

// ChildNodes returns an iterator over the immediate children of n,
// starting with n.FirstChild.
//
// Mutating a Node or its children while iterating may have unexpected results.
func (n *Node) ChildNodes() iter.Seq[*Node] {
_ = n.FirstChild // eager nil check

return func(yield func(*Node) bool) {
for c := n.FirstChild; c != nil && yield(c); c = c.NextSibling {
}
}

}

// Descendants returns an iterator over all nodes recursively beneath
// n, excluding n itself. Nodes are visited in depth-first preorder.
//
// Mutating a Node or its descendants while iterating may have unexpected results.
func (n *Node) Descendants() iter.Seq[*Node] {
_ = n.FirstChild // eager nil check

return func(yield func(*Node) bool) {
n.descendants(yield)
}
}

func (n *Node) descendants(yield func(*Node) bool) bool {
for c := range n.ChildNodes() {
if !yield(c) || !c.descendants(yield) {
return false
}
}
return true
}
96 changes: 96 additions & 0 deletions html/iter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2024 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.23

package html

import (
"strings"
"testing"
)

func TestNode_ChildNodes(t *testing.T) {
tests := []struct {
in string
want string
}{
{"", ""},
{"<a></a>", "a"},
{"a", "a"},
{"<a></a><!--b-->", "a b"},
{"a<b></b>c", "a b c"},
{"a<b><!--c--></b>d", "a b d"},
{"<a><b>c<!--d-->e</b></a>f<!--g--><h>i</h>", "a f g h"},
}
for _, test := range tests {
doc, err := Parse(strings.NewReader(test.in))
if err != nil {
t.Fatal(err)
}
// Drill to <html><head></head><body>
n := doc.FirstChild.FirstChild.NextSibling
var results []string
for c := range n.ChildNodes() {
results = append(results, c.Data)
}
if got := strings.Join(results, " "); got != test.want {
t.Errorf("ChildNodes = %q, want %q", got, test.want)
}
}
}

func TestNode_Descendants(t *testing.T) {
tests := []struct {
in string
want string
}{
{"", ""},
{"<a></a>", "a"},
{"<a><b></b></a>", "a b"},
{"<a>b</a>", "a b"},
{"<a><!--b--></a>", "a b"},
{"<a>b<c></c>d</a>", "a b c d"},
{"<a>b<c><!--d--></c>e</a>", "a b c d e"},
{"<a><b><c>d<!--e-->f</c></b>g<!--h--><i>j</i></a>", "a b c d e f g h i j"},
}
for _, test := range tests {
doc, err := Parse(strings.NewReader(test.in))
if err != nil {
t.Fatal(err)
}
// Drill to <html><head></head><body>
n := doc.FirstChild.FirstChild.NextSibling
var results []string
for c := range n.Descendants() {
results = append(results, c.Data)
}
if got := strings.Join(results, " "); got != test.want {
t.Errorf("Descendants = %q; want: %q", got, test.want)
}
}
}

func TestNode_Ancestors(t *testing.T) {
for _, size := range []int{0, 1, 2, 10, 100, 10_000} {
n := buildChain(size)
nParents := 0
for _ = range n.Ancestors() {
nParents++
}
if nParents != size {
t.Errorf("number of Ancestors = %d; want: %d", nParents, size)
}
}
}

func buildChain(size int) *Node {
child := new(Node)
for range size {
parent := child
child = new(Node)
parent.AppendChild(child)
}
return child
}
4 changes: 4 additions & 0 deletions html/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ var scopeMarker = Node{Type: scopeMarkerNode}
// that it looks like "a<b" rather than "a&lt;b". For element nodes, DataAtom
// is the atom for Data, or zero if Data is not a known tag name.
//
// Node trees may be navigated using the link fields (Parent,
// FirstChild, and so on) or a range loop over iterators such as
// [Node.Descendants].
//
// An empty Namespace implies a "http://www.w3.org/1999/xhtml" namespace.
// Similarly, "math" is short for "http://www.w3.org/1998/Math/MathML", and
// "svg" is short for "http://www.w3.org/2000/svg".
Expand Down
8 changes: 4 additions & 4 deletions http2/client_conn_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ package http2

import (
"context"
"crypto/tls"
"errors"
"net"
"net/http"
"sync"
)
Expand Down Expand Up @@ -158,7 +158,7 @@ func (c *dialCall) dial(ctx context.Context, addr string) {
// This code decides which ones live or die.
// The return value used is whether c was used.
// c is never closed.
func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c *tls.Conn) (used bool, err error) {
func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c net.Conn) (used bool, err error) {
p.mu.Lock()
for _, cc := range p.conns[key] {
if cc.CanTakeNewRequest() {
Expand Down Expand Up @@ -194,8 +194,8 @@ type addConnCall struct {
err error
}

func (c *addConnCall) run(t *Transport, key string, tc *tls.Conn) {
cc, err := t.NewClientConn(tc)
func (c *addConnCall) run(t *Transport, key string, nc net.Conn) {
cc, err := t.NewClientConn(nc)

p := c.p
p.mu.Lock()
Expand Down
30 changes: 23 additions & 7 deletions http2/clientconn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package http2
import (
"bytes"
"context"
"crypto/tls"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -112,27 +113,40 @@ func newTestClientConnFromClientConn(t *testing.T, cc *ClientConn) *testClientCo
cc: cc,
group: cc.t.transportTestHooks.group.(*synctestGroup),
}
cli, srv := synctestNetPipe(tc.group)

// srv is the side controlled by the test.
var srv *synctestNetConn
if cc.tconn == nil {
// If cc.tconn is nil, we're being called with a new conn created by the
// Transport's client pool. This path skips dialing the server, and we
// create a test connection pair here.
cc.tconn, srv = synctestNetPipe(tc.group)
} else {
// If cc.tconn is non-nil, we're in a test which provides a conn to the
// Transport via a TLSNextProto hook. Extract the test connection pair.
if tc, ok := cc.tconn.(*tls.Conn); ok {
// Unwrap any *tls.Conn to the underlying net.Conn,
// to avoid dealing with encryption in tests.
cc.tconn = tc.NetConn()
}
srv = cc.tconn.(*synctestNetConn).peer
}

srv.SetReadDeadline(tc.group.Now())
srv.autoWait = true
tc.netconn = srv
tc.enc = hpack.NewEncoder(&tc.encbuf)

// all writes and reads are finished.
//
// cli is the ClientConn's side, srv is the side controlled by the test.
cc.tconn = cli
tc.fr = NewFramer(srv, srv)
tc.testConnFramer = testConnFramer{
t: t,
fr: tc.fr,
dec: hpack.NewDecoder(initialHeaderTableSize, nil),
}

tc.fr.SetMaxReadFrameSize(10 << 20)
t.Cleanup(func() {
tc.closeWrite()
})

return tc
}

Expand Down Expand Up @@ -503,6 +517,8 @@ func newTestTransport(t *testing.T, opts ...any) *testTransport {
o(tr.t1)
case func(*Transport):
o(tr)
case *Transport:
tr = o
}
}
tt.tr = tr
Expand Down
8 changes: 8 additions & 0 deletions http2/http2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,11 @@ func TestNoUnicodeStrings(t *testing.T) {
t.Fatal(err)
}
}

// must returns v if err is nil, or panics otherwise.
func must[T any](v T, err error) T {
if err != nil {
panic(err)
}
return v
}
Loading

0 comments on commit cba5ecd

Please sign in to comment.