Update go dependencies
This commit is contained in:
parent
15ffb51394
commit
bb4d483837
1621 changed files with 86368 additions and 284392 deletions
15
vendor/golang.org/x/net/CONTRIBUTING.md
generated
vendored
15
vendor/golang.org/x/net/CONTRIBUTING.md
generated
vendored
|
|
@ -4,16 +4,15 @@ Go is an open source project.
|
|||
|
||||
It is the work of hundreds of contributors. We appreciate your help!
|
||||
|
||||
|
||||
## Filing issues
|
||||
|
||||
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
|
||||
|
||||
1. What version of Go are you using (`go version`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
5. What did you see instead?
|
||||
1. What version of Go are you using (`go version`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
5. What did you see instead?
|
||||
|
||||
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
|
||||
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
||||
|
|
@ -23,9 +22,5 @@ The gophers there will answer or ask you to file an issue if you've tripped over
|
|||
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
|
||||
before sending patches.
|
||||
|
||||
**We do not accept GitHub pull requests**
|
||||
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
|
||||
|
||||
Unless otherwise noted, the Go source files are distributed under
|
||||
the BSD-style license found in the LICENSE file.
|
||||
|
||||
|
|
|
|||
583
vendor/golang.org/x/net/context/context_test.go
generated
vendored
583
vendor/golang.org/x/net/context/context_test.go
generated
vendored
|
|
@ -1,583 +0,0 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// +build !go1.7
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// otherContext is a Context that's not one of the types defined in context.go.
|
||||
// This lets us test code paths that differ based on the underlying type of the
|
||||
// Context.
|
||||
type otherContext struct {
|
||||
Context
|
||||
}
|
||||
|
||||
func TestBackground(t *testing.T) {
|
||||
c := Background()
|
||||
if c == nil {
|
||||
t.Fatalf("Background returned nil")
|
||||
}
|
||||
select {
|
||||
case x := <-c.Done():
|
||||
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
|
||||
default:
|
||||
}
|
||||
if got, want := fmt.Sprint(c), "context.Background"; got != want {
|
||||
t.Errorf("Background().String() = %q want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTODO(t *testing.T) {
|
||||
c := TODO()
|
||||
if c == nil {
|
||||
t.Fatalf("TODO returned nil")
|
||||
}
|
||||
select {
|
||||
case x := <-c.Done():
|
||||
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
|
||||
default:
|
||||
}
|
||||
if got, want := fmt.Sprint(c), "context.TODO"; got != want {
|
||||
t.Errorf("TODO().String() = %q want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithCancel(t *testing.T) {
|
||||
c1, cancel := WithCancel(Background())
|
||||
|
||||
if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want {
|
||||
t.Errorf("c1.String() = %q want %q", got, want)
|
||||
}
|
||||
|
||||
o := otherContext{c1}
|
||||
c2, _ := WithCancel(o)
|
||||
contexts := []Context{c1, o, c2}
|
||||
|
||||
for i, c := range contexts {
|
||||
if d := c.Done(); d == nil {
|
||||
t.Errorf("c[%d].Done() == %v want non-nil", i, d)
|
||||
}
|
||||
if e := c.Err(); e != nil {
|
||||
t.Errorf("c[%d].Err() == %v want nil", i, e)
|
||||
}
|
||||
|
||||
select {
|
||||
case x := <-c.Done():
|
||||
t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
cancel()
|
||||
time.Sleep(100 * time.Millisecond) // let cancelation propagate
|
||||
|
||||
for i, c := range contexts {
|
||||
select {
|
||||
case <-c.Done():
|
||||
default:
|
||||
t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i)
|
||||
}
|
||||
if e := c.Err(); e != Canceled {
|
||||
t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParentFinishesChild(t *testing.T) {
|
||||
// Context tree:
|
||||
// parent -> cancelChild
|
||||
// parent -> valueChild -> timerChild
|
||||
parent, cancel := WithCancel(Background())
|
||||
cancelChild, stop := WithCancel(parent)
|
||||
defer stop()
|
||||
valueChild := WithValue(parent, "key", "value")
|
||||
timerChild, stop := WithTimeout(valueChild, 10000*time.Hour)
|
||||
defer stop()
|
||||
|
||||
select {
|
||||
case x := <-parent.Done():
|
||||
t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
|
||||
case x := <-cancelChild.Done():
|
||||
t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x)
|
||||
case x := <-timerChild.Done():
|
||||
t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x)
|
||||
case x := <-valueChild.Done():
|
||||
t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x)
|
||||
default:
|
||||
}
|
||||
|
||||
// The parent's children should contain the two cancelable children.
|
||||
pc := parent.(*cancelCtx)
|
||||
cc := cancelChild.(*cancelCtx)
|
||||
tc := timerChild.(*timerCtx)
|
||||
pc.mu.Lock()
|
||||
if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] {
|
||||
t.Errorf("bad linkage: pc.children = %v, want %v and %v",
|
||||
pc.children, cc, tc)
|
||||
}
|
||||
pc.mu.Unlock()
|
||||
|
||||
if p, ok := parentCancelCtx(cc.Context); !ok || p != pc {
|
||||
t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc)
|
||||
}
|
||||
if p, ok := parentCancelCtx(tc.Context); !ok || p != pc {
|
||||
t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc)
|
||||
}
|
||||
|
||||
cancel()
|
||||
|
||||
pc.mu.Lock()
|
||||
if len(pc.children) != 0 {
|
||||
t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children)
|
||||
}
|
||||
pc.mu.Unlock()
|
||||
|
||||
// parent and children should all be finished.
|
||||
check := func(ctx Context, name string) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
default:
|
||||
t.Errorf("<-%s.Done() blocked, but shouldn't have", name)
|
||||
}
|
||||
if e := ctx.Err(); e != Canceled {
|
||||
t.Errorf("%s.Err() == %v want %v", name, e, Canceled)
|
||||
}
|
||||
}
|
||||
check(parent, "parent")
|
||||
check(cancelChild, "cancelChild")
|
||||
check(valueChild, "valueChild")
|
||||
check(timerChild, "timerChild")
|
||||
|
||||
// WithCancel should return a canceled context on a canceled parent.
|
||||
precanceledChild := WithValue(parent, "key", "value")
|
||||
select {
|
||||
case <-precanceledChild.Done():
|
||||
default:
|
||||
t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have")
|
||||
}
|
||||
if e := precanceledChild.Err(); e != Canceled {
|
||||
t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildFinishesFirst(t *testing.T) {
|
||||
cancelable, stop := WithCancel(Background())
|
||||
defer stop()
|
||||
for _, parent := range []Context{Background(), cancelable} {
|
||||
child, cancel := WithCancel(parent)
|
||||
|
||||
select {
|
||||
case x := <-parent.Done():
|
||||
t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
|
||||
case x := <-child.Done():
|
||||
t.Errorf("<-child.Done() == %v want nothing (it should block)", x)
|
||||
default:
|
||||
}
|
||||
|
||||
cc := child.(*cancelCtx)
|
||||
pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background()
|
||||
if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) {
|
||||
t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok)
|
||||
}
|
||||
|
||||
if pcok {
|
||||
pc.mu.Lock()
|
||||
if len(pc.children) != 1 || !pc.children[cc] {
|
||||
t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc)
|
||||
}
|
||||
pc.mu.Unlock()
|
||||
}
|
||||
|
||||
cancel()
|
||||
|
||||
if pcok {
|
||||
pc.mu.Lock()
|
||||
if len(pc.children) != 0 {
|
||||
t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children)
|
||||
}
|
||||
pc.mu.Unlock()
|
||||
}
|
||||
|
||||
// child should be finished.
|
||||
select {
|
||||
case <-child.Done():
|
||||
default:
|
||||
t.Errorf("<-child.Done() blocked, but shouldn't have")
|
||||
}
|
||||
if e := child.Err(); e != Canceled {
|
||||
t.Errorf("child.Err() == %v want %v", e, Canceled)
|
||||
}
|
||||
|
||||
// parent should not be finished.
|
||||
select {
|
||||
case x := <-parent.Done():
|
||||
t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
|
||||
default:
|
||||
}
|
||||
if e := parent.Err(); e != nil {
|
||||
t.Errorf("parent.Err() == %v want nil", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testDeadline(c Context, wait time.Duration, t *testing.T) {
|
||||
select {
|
||||
case <-time.After(wait):
|
||||
t.Fatalf("context should have timed out")
|
||||
case <-c.Done():
|
||||
}
|
||||
if e := c.Err(); e != DeadlineExceeded {
|
||||
t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeadline(t *testing.T) {
|
||||
t.Parallel()
|
||||
const timeUnit = 500 * time.Millisecond
|
||||
c, _ := WithDeadline(Background(), time.Now().Add(1*timeUnit))
|
||||
if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
|
||||
t.Errorf("c.String() = %q want prefix %q", got, prefix)
|
||||
}
|
||||
testDeadline(c, 2*timeUnit, t)
|
||||
|
||||
c, _ = WithDeadline(Background(), time.Now().Add(1*timeUnit))
|
||||
o := otherContext{c}
|
||||
testDeadline(o, 2*timeUnit, t)
|
||||
|
||||
c, _ = WithDeadline(Background(), time.Now().Add(1*timeUnit))
|
||||
o = otherContext{c}
|
||||
c, _ = WithDeadline(o, time.Now().Add(3*timeUnit))
|
||||
testDeadline(c, 2*timeUnit, t)
|
||||
}
|
||||
|
||||
func TestTimeout(t *testing.T) {
|
||||
t.Parallel()
|
||||
const timeUnit = 500 * time.Millisecond
|
||||
c, _ := WithTimeout(Background(), 1*timeUnit)
|
||||
if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
|
||||
t.Errorf("c.String() = %q want prefix %q", got, prefix)
|
||||
}
|
||||
testDeadline(c, 2*timeUnit, t)
|
||||
|
||||
c, _ = WithTimeout(Background(), 1*timeUnit)
|
||||
o := otherContext{c}
|
||||
testDeadline(o, 2*timeUnit, t)
|
||||
|
||||
c, _ = WithTimeout(Background(), 1*timeUnit)
|
||||
o = otherContext{c}
|
||||
c, _ = WithTimeout(o, 3*timeUnit)
|
||||
testDeadline(c, 2*timeUnit, t)
|
||||
}
|
||||
|
||||
func TestCanceledTimeout(t *testing.T) {
|
||||
t.Parallel()
|
||||
const timeUnit = 500 * time.Millisecond
|
||||
c, _ := WithTimeout(Background(), 2*timeUnit)
|
||||
o := otherContext{c}
|
||||
c, cancel := WithTimeout(o, 4*timeUnit)
|
||||
cancel()
|
||||
time.Sleep(1 * timeUnit) // let cancelation propagate
|
||||
select {
|
||||
case <-c.Done():
|
||||
default:
|
||||
t.Errorf("<-c.Done() blocked, but shouldn't have")
|
||||
}
|
||||
if e := c.Err(); e != Canceled {
|
||||
t.Errorf("c.Err() == %v want %v", e, Canceled)
|
||||
}
|
||||
}
|
||||
|
||||
type key1 int
|
||||
type key2 int
|
||||
|
||||
var k1 = key1(1)
|
||||
var k2 = key2(1) // same int as k1, different type
|
||||
var k3 = key2(3) // same type as k2, different int
|
||||
|
||||
func TestValues(t *testing.T) {
|
||||
check := func(c Context, nm, v1, v2, v3 string) {
|
||||
if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {
|
||||
t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)
|
||||
}
|
||||
if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 {
|
||||
t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0)
|
||||
}
|
||||
if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 {
|
||||
t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0)
|
||||
}
|
||||
}
|
||||
|
||||
c0 := Background()
|
||||
check(c0, "c0", "", "", "")
|
||||
|
||||
c1 := WithValue(Background(), k1, "c1k1")
|
||||
check(c1, "c1", "c1k1", "", "")
|
||||
|
||||
if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want {
|
||||
t.Errorf("c.String() = %q want %q", got, want)
|
||||
}
|
||||
|
||||
c2 := WithValue(c1, k2, "c2k2")
|
||||
check(c2, "c2", "c1k1", "c2k2", "")
|
||||
|
||||
c3 := WithValue(c2, k3, "c3k3")
|
||||
check(c3, "c2", "c1k1", "c2k2", "c3k3")
|
||||
|
||||
c4 := WithValue(c3, k1, nil)
|
||||
check(c4, "c4", "", "c2k2", "c3k3")
|
||||
|
||||
o0 := otherContext{Background()}
|
||||
check(o0, "o0", "", "", "")
|
||||
|
||||
o1 := otherContext{WithValue(Background(), k1, "c1k1")}
|
||||
check(o1, "o1", "c1k1", "", "")
|
||||
|
||||
o2 := WithValue(o1, k2, "o2k2")
|
||||
check(o2, "o2", "c1k1", "o2k2", "")
|
||||
|
||||
o3 := otherContext{c4}
|
||||
check(o3, "o3", "", "c2k2", "c3k3")
|
||||
|
||||
o4 := WithValue(o3, k3, nil)
|
||||
check(o4, "o4", "", "c2k2", "")
|
||||
}
|
||||
|
||||
func TestAllocs(t *testing.T) {
|
||||
bg := Background()
|
||||
for _, test := range []struct {
|
||||
desc string
|
||||
f func()
|
||||
limit float64
|
||||
gccgoLimit float64
|
||||
}{
|
||||
{
|
||||
desc: "Background()",
|
||||
f: func() { Background() },
|
||||
limit: 0,
|
||||
gccgoLimit: 0,
|
||||
},
|
||||
{
|
||||
desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1),
|
||||
f: func() {
|
||||
c := WithValue(bg, k1, nil)
|
||||
c.Value(k1)
|
||||
},
|
||||
limit: 3,
|
||||
gccgoLimit: 3,
|
||||
},
|
||||
{
|
||||
desc: "WithTimeout(bg, 15*time.Millisecond)",
|
||||
f: func() {
|
||||
c, _ := WithTimeout(bg, 15*time.Millisecond)
|
||||
<-c.Done()
|
||||
},
|
||||
limit: 8,
|
||||
gccgoLimit: 16,
|
||||
},
|
||||
{
|
||||
desc: "WithCancel(bg)",
|
||||
f: func() {
|
||||
c, cancel := WithCancel(bg)
|
||||
cancel()
|
||||
<-c.Done()
|
||||
},
|
||||
limit: 5,
|
||||
gccgoLimit: 8,
|
||||
},
|
||||
{
|
||||
desc: "WithTimeout(bg, 100*time.Millisecond)",
|
||||
f: func() {
|
||||
c, cancel := WithTimeout(bg, 100*time.Millisecond)
|
||||
cancel()
|
||||
<-c.Done()
|
||||
},
|
||||
limit: 8,
|
||||
gccgoLimit: 25,
|
||||
},
|
||||
} {
|
||||
limit := test.limit
|
||||
if runtime.Compiler == "gccgo" {
|
||||
// gccgo does not yet do escape analysis.
|
||||
// TODO(iant): Remove this when gccgo does do escape analysis.
|
||||
limit = test.gccgoLimit
|
||||
}
|
||||
if n := testing.AllocsPerRun(100, test.f); n > limit {
|
||||
t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimultaneousCancels(t *testing.T) {
|
||||
root, cancel := WithCancel(Background())
|
||||
m := map[Context]CancelFunc{root: cancel}
|
||||
q := []Context{root}
|
||||
// Create a tree of contexts.
|
||||
for len(q) != 0 && len(m) < 100 {
|
||||
parent := q[0]
|
||||
q = q[1:]
|
||||
for i := 0; i < 4; i++ {
|
||||
ctx, cancel := WithCancel(parent)
|
||||
m[ctx] = cancel
|
||||
q = append(q, ctx)
|
||||
}
|
||||
}
|
||||
// Start all the cancels in a random order.
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(m))
|
||||
for _, cancel := range m {
|
||||
go func(cancel CancelFunc) {
|
||||
cancel()
|
||||
wg.Done()
|
||||
}(cancel)
|
||||
}
|
||||
// Wait on all the contexts in a random order.
|
||||
for ctx := range m {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-time.After(1 * time.Second):
|
||||
buf := make([]byte, 10<<10)
|
||||
n := runtime.Stack(buf, true)
|
||||
t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n])
|
||||
}
|
||||
}
|
||||
// Wait for all the cancel functions to return.
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(done)
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(1 * time.Second):
|
||||
buf := make([]byte, 10<<10)
|
||||
n := runtime.Stack(buf, true)
|
||||
t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n])
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterlockedCancels(t *testing.T) {
|
||||
parent, cancelParent := WithCancel(Background())
|
||||
child, cancelChild := WithCancel(parent)
|
||||
go func() {
|
||||
parent.Done()
|
||||
cancelChild()
|
||||
}()
|
||||
cancelParent()
|
||||
select {
|
||||
case <-child.Done():
|
||||
case <-time.After(1 * time.Second):
|
||||
buf := make([]byte, 10<<10)
|
||||
n := runtime.Stack(buf, true)
|
||||
t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n])
|
||||
}
|
||||
}
|
||||
|
||||
func TestLayersCancel(t *testing.T) {
|
||||
testLayers(t, time.Now().UnixNano(), false)
|
||||
}
|
||||
|
||||
func TestLayersTimeout(t *testing.T) {
|
||||
testLayers(t, time.Now().UnixNano(), true)
|
||||
}
|
||||
|
||||
func testLayers(t *testing.T, seed int64, testTimeout bool) {
|
||||
rand.Seed(seed)
|
||||
errorf := func(format string, a ...interface{}) {
|
||||
t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
|
||||
}
|
||||
const (
|
||||
timeout = 200 * time.Millisecond
|
||||
minLayers = 30
|
||||
)
|
||||
type value int
|
||||
var (
|
||||
vals []*value
|
||||
cancels []CancelFunc
|
||||
numTimers int
|
||||
ctx = Background()
|
||||
)
|
||||
for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ {
|
||||
switch rand.Intn(3) {
|
||||
case 0:
|
||||
v := new(value)
|
||||
ctx = WithValue(ctx, v, v)
|
||||
vals = append(vals, v)
|
||||
case 1:
|
||||
var cancel CancelFunc
|
||||
ctx, cancel = WithCancel(ctx)
|
||||
cancels = append(cancels, cancel)
|
||||
case 2:
|
||||
var cancel CancelFunc
|
||||
ctx, cancel = WithTimeout(ctx, timeout)
|
||||
cancels = append(cancels, cancel)
|
||||
numTimers++
|
||||
}
|
||||
}
|
||||
checkValues := func(when string) {
|
||||
for _, key := range vals {
|
||||
if val := ctx.Value(key).(*value); key != val {
|
||||
errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
errorf("ctx should not be canceled yet")
|
||||
default:
|
||||
}
|
||||
if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) {
|
||||
t.Errorf("ctx.String() = %q want prefix %q", s, prefix)
|
||||
}
|
||||
t.Log(ctx)
|
||||
checkValues("before cancel")
|
||||
if testTimeout {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-time.After(timeout + 100*time.Millisecond):
|
||||
errorf("ctx should have timed out")
|
||||
}
|
||||
checkValues("after timeout")
|
||||
} else {
|
||||
cancel := cancels[rand.Intn(len(cancels))]
|
||||
cancel()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
default:
|
||||
errorf("ctx should be canceled")
|
||||
}
|
||||
checkValues("after cancel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelRemoves(t *testing.T) {
|
||||
checkChildren := func(when string, ctx Context, want int) {
|
||||
if got := len(ctx.(*cancelCtx).children); got != want {
|
||||
t.Errorf("%s: context has %d children, want %d", when, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
ctx, _ := WithCancel(Background())
|
||||
checkChildren("after creation", ctx, 0)
|
||||
_, cancel := WithCancel(ctx)
|
||||
checkChildren("with WithCancel child ", ctx, 1)
|
||||
cancel()
|
||||
checkChildren("after cancelling WithCancel child", ctx, 0)
|
||||
|
||||
ctx, _ = WithCancel(Background())
|
||||
checkChildren("after creation", ctx, 0)
|
||||
_, cancel = WithTimeout(ctx, 60*time.Minute)
|
||||
checkChildren("with WithTimeout child ", ctx, 1)
|
||||
cancel()
|
||||
checkChildren("after cancelling WithTimeout child", ctx, 0)
|
||||
}
|
||||
29
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_17_test.go
generated
vendored
29
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_17_test.go
generated
vendored
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// +build !plan9,go1.7
|
||||
|
||||
package ctxhttp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"context"
|
||||
)
|
||||
|
||||
func TestGo17Context(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
io.WriteString(w, "ok")
|
||||
}))
|
||||
defer ts.Close()
|
||||
ctx := context.Background()
|
||||
resp, err := Get(ctx, http.DefaultClient, ts.URL)
|
||||
if resp == nil || err != nil {
|
||||
t.Fatalf("error received from client: %v %v", err, resp)
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
79
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17_test.go
generated
vendored
79
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17_test.go
generated
vendored
|
|
@ -1,79 +0,0 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// +build !plan9,!go1.7
|
||||
|
||||
package ctxhttp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// golang.org/issue/14065
|
||||
func TestClosesResponseBodyOnCancel(t *testing.T) {
|
||||
defer func() { testHookContextDoneBeforeHeaders = nop }()
|
||||
defer func() { testHookDoReturned = nop }()
|
||||
defer func() { testHookDidBodyClose = nop }()
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
defer ts.Close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// closed when Do enters select case <-ctx.Done()
|
||||
enteredDonePath := make(chan struct{})
|
||||
|
||||
testHookContextDoneBeforeHeaders = func() {
|
||||
close(enteredDonePath)
|
||||
}
|
||||
|
||||
testHookDoReturned = func() {
|
||||
// We now have the result (the Flush'd headers) at least,
|
||||
// so we can cancel the request.
|
||||
cancel()
|
||||
|
||||
// But block the client.Do goroutine from sending
|
||||
// until Do enters into the <-ctx.Done() path, since
|
||||
// otherwise if both channels are readable, select
|
||||
// picks a random one.
|
||||
<-enteredDonePath
|
||||
}
|
||||
|
||||
sawBodyClose := make(chan struct{})
|
||||
testHookDidBodyClose = func() { close(sawBodyClose) }
|
||||
|
||||
tr := &http.Transport{}
|
||||
defer tr.CloseIdleConnections()
|
||||
c := &http.Client{Transport: tr}
|
||||
req, _ := http.NewRequest("GET", ts.URL, nil)
|
||||
_, doErr := Do(ctx, c, req)
|
||||
|
||||
select {
|
||||
case <-sawBodyClose:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("timeout waiting for body to close")
|
||||
}
|
||||
|
||||
if doErr != ctx.Err() {
|
||||
t.Errorf("Do error = %v; want %v", doErr, ctx.Err())
|
||||
}
|
||||
}
|
||||
|
||||
type noteCloseConn struct {
|
||||
net.Conn
|
||||
onceClose sync.Once
|
||||
closefn func()
|
||||
}
|
||||
|
||||
func (c *noteCloseConn) Close() error {
|
||||
c.onceClose.Do(c.closefn)
|
||||
return c.Conn.Close()
|
||||
}
|
||||
105
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_test.go
generated
vendored
105
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_test.go
generated
vendored
|
|
@ -1,105 +0,0 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// +build !plan9
|
||||
|
||||
package ctxhttp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
const (
|
||||
requestDuration = 100 * time.Millisecond
|
||||
requestBody = "ok"
|
||||
)
|
||||
|
||||
func okHandler(w http.ResponseWriter, r *http.Request) {
|
||||
time.Sleep(requestDuration)
|
||||
io.WriteString(w, requestBody)
|
||||
}
|
||||
|
||||
func TestNoTimeout(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(okHandler))
|
||||
defer ts.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
res, err := Get(ctx, nil, ts.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
slurp, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(slurp) != requestBody {
|
||||
t.Errorf("body = %q; want %q", slurp, requestBody)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelBeforeHeaders(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
blockServer := make(chan struct{})
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
cancel()
|
||||
<-blockServer
|
||||
io.WriteString(w, requestBody)
|
||||
}))
|
||||
defer ts.Close()
|
||||
defer close(blockServer)
|
||||
|
||||
res, err := Get(ctx, nil, ts.URL)
|
||||
if err == nil {
|
||||
res.Body.Close()
|
||||
t.Fatal("Get returned unexpected nil error")
|
||||
}
|
||||
if err != context.Canceled {
|
||||
t.Errorf("err = %v; want %v", err, context.Canceled)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelAfterHangingRequest(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.(http.Flusher).Flush()
|
||||
<-w.(http.CloseNotifier).CloseNotify()
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
resp, err := Get(ctx, nil, ts.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error in Get: %v", err)
|
||||
}
|
||||
|
||||
// Cancel befer reading the body.
|
||||
// Reading Request.Body should fail, since the request was
|
||||
// canceled before anything was written.
|
||||
cancel()
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if len(b) != 0 || err == nil {
|
||||
t.Errorf(`Read got (%q, %v); want ("", error)`, b, err)
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Errorf("Test timed out")
|
||||
case <-done:
|
||||
}
|
||||
}
|
||||
31
vendor/golang.org/x/net/context/withtimeout_test.go
generated
vendored
31
vendor/golang.org/x/net/context/withtimeout_test.go
generated
vendored
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright 2014 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 context_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// This example passes a context with a timeout to tell a blocking function that
|
||||
// it should abandon its work after the timeout elapses.
|
||||
func ExampleWithTimeout() {
|
||||
// Pass a context with a timeout to tell a blocking function that it
|
||||
// should abandon its work after the timeout elapses.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
select {
|
||||
case <-time.After(1 * time.Second):
|
||||
fmt.Println("overslept")
|
||||
case <-ctx.Done():
|
||||
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
|
||||
}
|
||||
|
||||
// Output:
|
||||
// context deadline exceeded
|
||||
}
|
||||
109
vendor/golang.org/x/net/html/atom/atom_test.go
generated
vendored
109
vendor/golang.org/x/net/html/atom/atom_test.go
generated
vendored
|
|
@ -1,109 +0,0 @@
|
|||
// Copyright 2012 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 atom
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestKnown(t *testing.T) {
|
||||
for _, s := range testAtomList {
|
||||
if atom := Lookup([]byte(s)); atom.String() != s {
|
||||
t.Errorf("Lookup(%q) = %#x (%q)", s, uint32(atom), atom.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHits(t *testing.T) {
|
||||
for _, a := range table {
|
||||
if a == 0 {
|
||||
continue
|
||||
}
|
||||
got := Lookup([]byte(a.String()))
|
||||
if got != a {
|
||||
t.Errorf("Lookup(%q) = %#x, want %#x", a.String(), uint32(got), uint32(a))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMisses(t *testing.T) {
|
||||
testCases := []string{
|
||||
"",
|
||||
"\x00",
|
||||
"\xff",
|
||||
"A",
|
||||
"DIV",
|
||||
"Div",
|
||||
"dIV",
|
||||
"aa",
|
||||
"a\x00",
|
||||
"ab",
|
||||
"abb",
|
||||
"abbr0",
|
||||
"abbr ",
|
||||
" abbr",
|
||||
" a",
|
||||
"acceptcharset",
|
||||
"acceptCharset",
|
||||
"accept_charset",
|
||||
"h0",
|
||||
"h1h2",
|
||||
"h7",
|
||||
"onClick",
|
||||
"λ",
|
||||
// The following string has the same hash (0xa1d7fab7) as "onmouseover".
|
||||
"\x00\x00\x00\x00\x00\x50\x18\xae\x38\xd0\xb7",
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
got := Lookup([]byte(tc))
|
||||
if got != 0 {
|
||||
t.Errorf("Lookup(%q): got %d, want 0", tc, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestForeignObject(t *testing.T) {
|
||||
const (
|
||||
afo = Foreignobject
|
||||
afO = ForeignObject
|
||||
sfo = "foreignobject"
|
||||
sfO = "foreignObject"
|
||||
)
|
||||
if got := Lookup([]byte(sfo)); got != afo {
|
||||
t.Errorf("Lookup(%q): got %#v, want %#v", sfo, got, afo)
|
||||
}
|
||||
if got := Lookup([]byte(sfO)); got != afO {
|
||||
t.Errorf("Lookup(%q): got %#v, want %#v", sfO, got, afO)
|
||||
}
|
||||
if got := afo.String(); got != sfo {
|
||||
t.Errorf("Atom(%#v).String(): got %q, want %q", afo, got, sfo)
|
||||
}
|
||||
if got := afO.String(); got != sfO {
|
||||
t.Errorf("Atom(%#v).String(): got %q, want %q", afO, got, sfO)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLookup(b *testing.B) {
|
||||
sortedTable := make([]string, 0, len(table))
|
||||
for _, a := range table {
|
||||
if a != 0 {
|
||||
sortedTable = append(sortedTable, a.String())
|
||||
}
|
||||
}
|
||||
sort.Strings(sortedTable)
|
||||
|
||||
x := make([][]byte, 1000)
|
||||
for i := range x {
|
||||
x[i] = []byte(sortedTable[i%len(sortedTable)])
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, s := range x {
|
||||
Lookup(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
5
vendor/golang.org/x/net/html/atom/gen.go
generated
vendored
5
vendor/golang.org/x/net/html/atom/gen.go
generated
vendored
|
|
@ -306,7 +306,7 @@ func (t *table) push(i uint32, depth int) bool {
|
|||
|
||||
// The lists of element names and attribute keys were taken from
|
||||
// https://html.spec.whatwg.org/multipage/indices.html#index
|
||||
// as of the "HTML Living Standard - Last Updated 18 September 2017" version.
|
||||
// as of the "HTML Living Standard - Last Updated 16 April 2018" version.
|
||||
|
||||
// "command", "keygen" and "menuitem" have been removed from the spec,
|
||||
// but are kept here for backwards compatibility.
|
||||
|
|
@ -665,6 +665,7 @@ var eventHandlers = []string{
|
|||
|
||||
// extra are ad-hoc values not covered by any of the lists above.
|
||||
var extra = []string{
|
||||
"acronym",
|
||||
"align",
|
||||
"annotation",
|
||||
"annotation-xml",
|
||||
|
|
@ -700,6 +701,8 @@ var extra = []string{
|
|||
"plaintext",
|
||||
"prompt",
|
||||
"public",
|
||||
"rb",
|
||||
"rtc",
|
||||
"spacer",
|
||||
"strike",
|
||||
"svg",
|
||||
|
|
|
|||
1444
vendor/golang.org/x/net/html/atom/table.go
generated
vendored
1444
vendor/golang.org/x/net/html/atom/table.go
generated
vendored
File diff suppressed because it is too large
Load diff
373
vendor/golang.org/x/net/html/atom/table_test.go
generated
vendored
373
vendor/golang.org/x/net/html/atom/table_test.go
generated
vendored
|
|
@ -1,373 +0,0 @@
|
|||
// Code generated by go generate gen.go; DO NOT EDIT.
|
||||
|
||||
//go:generate go run gen.go -test
|
||||
|
||||
package atom
|
||||
|
||||
var testAtomList = []string{
|
||||
"a",
|
||||
"abbr",
|
||||
"accept",
|
||||
"accept-charset",
|
||||
"accesskey",
|
||||
"action",
|
||||
"address",
|
||||
"align",
|
||||
"allowfullscreen",
|
||||
"allowpaymentrequest",
|
||||
"allowusermedia",
|
||||
"alt",
|
||||
"annotation",
|
||||
"annotation-xml",
|
||||
"applet",
|
||||
"area",
|
||||
"article",
|
||||
"as",
|
||||
"aside",
|
||||
"async",
|
||||
"audio",
|
||||
"autocomplete",
|
||||
"autofocus",
|
||||
"autoplay",
|
||||
"b",
|
||||
"base",
|
||||
"basefont",
|
||||
"bdi",
|
||||
"bdo",
|
||||
"bgsound",
|
||||
"big",
|
||||
"blink",
|
||||
"blockquote",
|
||||
"body",
|
||||
"br",
|
||||
"button",
|
||||
"canvas",
|
||||
"caption",
|
||||
"center",
|
||||
"challenge",
|
||||
"charset",
|
||||
"checked",
|
||||
"cite",
|
||||
"class",
|
||||
"code",
|
||||
"col",
|
||||
"colgroup",
|
||||
"color",
|
||||
"cols",
|
||||
"colspan",
|
||||
"command",
|
||||
"content",
|
||||
"contenteditable",
|
||||
"contextmenu",
|
||||
"controls",
|
||||
"coords",
|
||||
"crossorigin",
|
||||
"data",
|
||||
"datalist",
|
||||
"datetime",
|
||||
"dd",
|
||||
"default",
|
||||
"defer",
|
||||
"del",
|
||||
"desc",
|
||||
"details",
|
||||
"dfn",
|
||||
"dialog",
|
||||
"dir",
|
||||
"dirname",
|
||||
"disabled",
|
||||
"div",
|
||||
"dl",
|
||||
"download",
|
||||
"draggable",
|
||||
"dropzone",
|
||||
"dt",
|
||||
"em",
|
||||
"embed",
|
||||
"enctype",
|
||||
"face",
|
||||
"fieldset",
|
||||
"figcaption",
|
||||
"figure",
|
||||
"font",
|
||||
"footer",
|
||||
"for",
|
||||
"foreignObject",
|
||||
"foreignobject",
|
||||
"form",
|
||||
"formaction",
|
||||
"formenctype",
|
||||
"formmethod",
|
||||
"formnovalidate",
|
||||
"formtarget",
|
||||
"frame",
|
||||
"frameset",
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"h4",
|
||||
"h5",
|
||||
"h6",
|
||||
"head",
|
||||
"header",
|
||||
"headers",
|
||||
"height",
|
||||
"hgroup",
|
||||
"hidden",
|
||||
"high",
|
||||
"hr",
|
||||
"href",
|
||||
"hreflang",
|
||||
"html",
|
||||
"http-equiv",
|
||||
"i",
|
||||
"icon",
|
||||
"id",
|
||||
"iframe",
|
||||
"image",
|
||||
"img",
|
||||
"input",
|
||||
"inputmode",
|
||||
"ins",
|
||||
"integrity",
|
||||
"is",
|
||||
"isindex",
|
||||
"ismap",
|
||||
"itemid",
|
||||
"itemprop",
|
||||
"itemref",
|
||||
"itemscope",
|
||||
"itemtype",
|
||||
"kbd",
|
||||
"keygen",
|
||||
"keytype",
|
||||
"kind",
|
||||
"label",
|
||||
"lang",
|
||||
"legend",
|
||||
"li",
|
||||
"link",
|
||||
"list",
|
||||
"listing",
|
||||
"loop",
|
||||
"low",
|
||||
"main",
|
||||
"malignmark",
|
||||
"manifest",
|
||||
"map",
|
||||
"mark",
|
||||
"marquee",
|
||||
"math",
|
||||
"max",
|
||||
"maxlength",
|
||||
"media",
|
||||
"mediagroup",
|
||||
"menu",
|
||||
"menuitem",
|
||||
"meta",
|
||||
"meter",
|
||||
"method",
|
||||
"mglyph",
|
||||
"mi",
|
||||
"min",
|
||||
"minlength",
|
||||
"mn",
|
||||
"mo",
|
||||
"ms",
|
||||
"mtext",
|
||||
"multiple",
|
||||
"muted",
|
||||
"name",
|
||||
"nav",
|
||||
"nobr",
|
||||
"noembed",
|
||||
"noframes",
|
||||
"nomodule",
|
||||
"nonce",
|
||||
"noscript",
|
||||
"novalidate",
|
||||
"object",
|
||||
"ol",
|
||||
"onabort",
|
||||
"onafterprint",
|
||||
"onautocomplete",
|
||||
"onautocompleteerror",
|
||||
"onauxclick",
|
||||
"onbeforeprint",
|
||||
"onbeforeunload",
|
||||
"onblur",
|
||||
"oncancel",
|
||||
"oncanplay",
|
||||
"oncanplaythrough",
|
||||
"onchange",
|
||||
"onclick",
|
||||
"onclose",
|
||||
"oncontextmenu",
|
||||
"oncopy",
|
||||
"oncuechange",
|
||||
"oncut",
|
||||
"ondblclick",
|
||||
"ondrag",
|
||||
"ondragend",
|
||||
"ondragenter",
|
||||
"ondragexit",
|
||||
"ondragleave",
|
||||
"ondragover",
|
||||
"ondragstart",
|
||||
"ondrop",
|
||||
"ondurationchange",
|
||||
"onemptied",
|
||||
"onended",
|
||||
"onerror",
|
||||
"onfocus",
|
||||
"onhashchange",
|
||||
"oninput",
|
||||
"oninvalid",
|
||||
"onkeydown",
|
||||
"onkeypress",
|
||||
"onkeyup",
|
||||
"onlanguagechange",
|
||||
"onload",
|
||||
"onloadeddata",
|
||||
"onloadedmetadata",
|
||||
"onloadend",
|
||||
"onloadstart",
|
||||
"onmessage",
|
||||
"onmessageerror",
|
||||
"onmousedown",
|
||||
"onmouseenter",
|
||||
"onmouseleave",
|
||||
"onmousemove",
|
||||
"onmouseout",
|
||||
"onmouseover",
|
||||
"onmouseup",
|
||||
"onmousewheel",
|
||||
"onoffline",
|
||||
"ononline",
|
||||
"onpagehide",
|
||||
"onpageshow",
|
||||
"onpaste",
|
||||
"onpause",
|
||||
"onplay",
|
||||
"onplaying",
|
||||
"onpopstate",
|
||||
"onprogress",
|
||||
"onratechange",
|
||||
"onrejectionhandled",
|
||||
"onreset",
|
||||
"onresize",
|
||||
"onscroll",
|
||||
"onsecuritypolicyviolation",
|
||||
"onseeked",
|
||||
"onseeking",
|
||||
"onselect",
|
||||
"onshow",
|
||||
"onsort",
|
||||
"onstalled",
|
||||
"onstorage",
|
||||
"onsubmit",
|
||||
"onsuspend",
|
||||
"ontimeupdate",
|
||||
"ontoggle",
|
||||
"onunhandledrejection",
|
||||
"onunload",
|
||||
"onvolumechange",
|
||||
"onwaiting",
|
||||
"onwheel",
|
||||
"open",
|
||||
"optgroup",
|
||||
"optimum",
|
||||
"option",
|
||||
"output",
|
||||
"p",
|
||||
"param",
|
||||
"pattern",
|
||||
"picture",
|
||||
"ping",
|
||||
"placeholder",
|
||||
"plaintext",
|
||||
"playsinline",
|
||||
"poster",
|
||||
"pre",
|
||||
"preload",
|
||||
"progress",
|
||||
"prompt",
|
||||
"public",
|
||||
"q",
|
||||
"radiogroup",
|
||||
"readonly",
|
||||
"referrerpolicy",
|
||||
"rel",
|
||||
"required",
|
||||
"reversed",
|
||||
"rows",
|
||||
"rowspan",
|
||||
"rp",
|
||||
"rt",
|
||||
"ruby",
|
||||
"s",
|
||||
"samp",
|
||||
"sandbox",
|
||||
"scope",
|
||||
"scoped",
|
||||
"script",
|
||||
"seamless",
|
||||
"section",
|
||||
"select",
|
||||
"selected",
|
||||
"shape",
|
||||
"size",
|
||||
"sizes",
|
||||
"slot",
|
||||
"small",
|
||||
"sortable",
|
||||
"sorted",
|
||||
"source",
|
||||
"spacer",
|
||||
"span",
|
||||
"spellcheck",
|
||||
"src",
|
||||
"srcdoc",
|
||||
"srclang",
|
||||
"srcset",
|
||||
"start",
|
||||
"step",
|
||||
"strike",
|
||||
"strong",
|
||||
"style",
|
||||
"sub",
|
||||
"summary",
|
||||
"sup",
|
||||
"svg",
|
||||
"system",
|
||||
"tabindex",
|
||||
"table",
|
||||
"target",
|
||||
"tbody",
|
||||
"td",
|
||||
"template",
|
||||
"textarea",
|
||||
"tfoot",
|
||||
"th",
|
||||
"thead",
|
||||
"time",
|
||||
"title",
|
||||
"tr",
|
||||
"track",
|
||||
"translate",
|
||||
"tt",
|
||||
"type",
|
||||
"typemustmatch",
|
||||
"u",
|
||||
"ul",
|
||||
"updateviacache",
|
||||
"usemap",
|
||||
"value",
|
||||
"var",
|
||||
"video",
|
||||
"wbr",
|
||||
"width",
|
||||
"workertype",
|
||||
"wrap",
|
||||
"xmp",
|
||||
}
|
||||
237
vendor/golang.org/x/net/html/charset/charset_test.go
generated
vendored
237
vendor/golang.org/x/net/html/charset/charset_test.go
generated
vendored
|
|
@ -1,237 +0,0 @@
|
|||
// Copyright 2013 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 charset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
func transformString(t transform.Transformer, s string) (string, error) {
|
||||
r := transform.NewReader(strings.NewReader(s), t)
|
||||
b, err := ioutil.ReadAll(r)
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
utf8, other, otherEncoding string
|
||||
}
|
||||
|
||||
// testCases for encoding and decoding.
|
||||
var testCases = []testCase{
|
||||
{"Résumé", "Résumé", "utf8"},
|
||||
{"Résumé", "R\xe9sum\xe9", "latin1"},
|
||||
{"これは漢字です。", "S0\x8c0o0\"oW[g0Y0\x020", "UTF-16LE"},
|
||||
{"これは漢字です。", "0S0\x8c0oo\"[W0g0Y0\x02", "UTF-16BE"},
|
||||
{"Hello, world", "Hello, world", "ASCII"},
|
||||
{"Gdańsk", "Gda\xf1sk", "ISO-8859-2"},
|
||||
{"Ââ Čč Đđ Ŋŋ Õõ Šš Žž Åå Ää", "\xc2\xe2 \xc8\xe8 \xa9\xb9 \xaf\xbf \xd5\xf5 \xaa\xba \xac\xbc \xc5\xe5 \xc4\xe4", "ISO-8859-10"},
|
||||
{"สำหรับ", "\xca\xd3\xcb\xc3\u047a", "ISO-8859-11"},
|
||||
{"latviešu", "latvie\xf0u", "ISO-8859-13"},
|
||||
{"Seònaid", "Se\xf2naid", "ISO-8859-14"},
|
||||
{"€1 is cheap", "\xa41 is cheap", "ISO-8859-15"},
|
||||
{"românește", "rom\xe2ne\xbate", "ISO-8859-16"},
|
||||
{"nutraĵo", "nutra\xbco", "ISO-8859-3"},
|
||||
{"Kalâdlit", "Kal\xe2dlit", "ISO-8859-4"},
|
||||
{"русский", "\xe0\xe3\xe1\xe1\xda\xd8\xd9", "ISO-8859-5"},
|
||||
{"ελληνικά", "\xe5\xeb\xeb\xe7\xed\xe9\xea\xdc", "ISO-8859-7"},
|
||||
{"Kağan", "Ka\xf0an", "ISO-8859-9"},
|
||||
{"Résumé", "R\x8esum\x8e", "macintosh"},
|
||||
{"Gdańsk", "Gda\xf1sk", "windows-1250"},
|
||||
{"русский", "\xf0\xf3\xf1\xf1\xea\xe8\xe9", "windows-1251"},
|
||||
{"Résumé", "R\xe9sum\xe9", "windows-1252"},
|
||||
{"ελληνικά", "\xe5\xeb\xeb\xe7\xed\xe9\xea\xdc", "windows-1253"},
|
||||
{"Kağan", "Ka\xf0an", "windows-1254"},
|
||||
{"עִבְרִית", "\xf2\xc4\xe1\xc0\xf8\xc4\xe9\xfa", "windows-1255"},
|
||||
{"العربية", "\xc7\xe1\xda\xd1\xc8\xed\xc9", "windows-1256"},
|
||||
{"latviešu", "latvie\xf0u", "windows-1257"},
|
||||
{"Việt", "Vi\xea\xf2t", "windows-1258"},
|
||||
{"สำหรับ", "\xca\xd3\xcb\xc3\u047a", "windows-874"},
|
||||
{"русский", "\xd2\xd5\xd3\xd3\xcb\xc9\xca", "KOI8-R"},
|
||||
{"українська", "\xd5\xcb\xd2\xc1\xa7\xce\xd3\xd8\xcb\xc1", "KOI8-U"},
|
||||
{"Hello 常用國字標準字體表", "Hello \xb1`\xa5\u03b0\xea\xa6r\xbc\u0437\u01e6r\xc5\xe9\xaa\xed", "big5"},
|
||||
{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gbk"},
|
||||
{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gb18030"},
|
||||
{"עִבְרִית", "\x81\x30\xfb\x30\x81\x30\xf6\x34\x81\x30\xf9\x33\x81\x30\xf6\x30\x81\x30\xfb\x36\x81\x30\xf6\x34\x81\x30\xfa\x31\x81\x30\xfb\x38", "gb18030"},
|
||||
{"㧯", "\x82\x31\x89\x38", "gb18030"},
|
||||
{"これは漢字です。", "\x82\xb1\x82\xea\x82\xcd\x8a\xbf\x8e\x9a\x82\xc5\x82\xb7\x81B", "SJIS"},
|
||||
{"Hello, 世界!", "Hello, \x90\xa2\x8aE!", "SJIS"},
|
||||
{"イウエオカ", "\xb2\xb3\xb4\xb5\xb6", "SJIS"},
|
||||
{"これは漢字です。", "\xa4\xb3\xa4\xec\xa4\u03f4\xc1\xbb\xfa\xa4\u01e4\xb9\xa1\xa3", "EUC-JP"},
|
||||
{"Hello, 世界!", "Hello, \x1b$B@$3&\x1b(B!", "ISO-2022-JP"},
|
||||
{"다음과 같은 조건을 따라야 합니다: 저작자표시", "\xb4\xd9\xc0\xbd\xb0\xfa \xb0\xb0\xc0\xba \xc1\xb6\xb0\xc7\xc0\xbb \xb5\xfb\xb6\xf3\xbe\xdf \xc7մϴ\xd9: \xc0\xfa\xc0\xdb\xc0\xdaǥ\xbd\xc3", "EUC-KR"},
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
testCases := append(testCases, []testCase{
|
||||
// Replace multi-byte maximum subpart of ill-formed subsequence with
|
||||
// single replacement character (WhatWG requirement).
|
||||
{"Rés\ufffdumé", "Rés\xe1\x80umé", "utf8"},
|
||||
}...)
|
||||
for _, tc := range testCases {
|
||||
e, _ := Lookup(tc.otherEncoding)
|
||||
if e == nil {
|
||||
t.Errorf("%s: not found", tc.otherEncoding)
|
||||
continue
|
||||
}
|
||||
s, err := transformString(e.NewDecoder(), tc.other)
|
||||
if err != nil {
|
||||
t.Errorf("%s: decode %q: %v", tc.otherEncoding, tc.other, err)
|
||||
continue
|
||||
}
|
||||
if s != tc.utf8 {
|
||||
t.Errorf("%s: got %q, want %q", tc.otherEncoding, s, tc.utf8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
testCases := append(testCases, []testCase{
|
||||
// Use Go-style replacement.
|
||||
{"Rés\xe1\x80umé", "Rés\ufffd\ufffdumé", "utf8"},
|
||||
// U+0144 LATIN SMALL LETTER N WITH ACUTE not supported by encoding.
|
||||
{"Gdańsk", "Gdańsk", "ISO-8859-11"},
|
||||
{"\ufffd", "�", "ISO-8859-11"},
|
||||
{"a\xe1\x80b", "a��b", "ISO-8859-11"},
|
||||
}...)
|
||||
for _, tc := range testCases {
|
||||
e, _ := Lookup(tc.otherEncoding)
|
||||
if e == nil {
|
||||
t.Errorf("%s: not found", tc.otherEncoding)
|
||||
continue
|
||||
}
|
||||
s, err := transformString(e.NewEncoder(), tc.utf8)
|
||||
if err != nil {
|
||||
t.Errorf("%s: encode %q: %s", tc.otherEncoding, tc.utf8, err)
|
||||
continue
|
||||
}
|
||||
if s != tc.other {
|
||||
t.Errorf("%s: got %q, want %q", tc.otherEncoding, s, tc.other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sniffTestCases = []struct {
|
||||
filename, declared, want string
|
||||
}{
|
||||
{"HTTP-charset.html", "text/html; charset=iso-8859-15", "iso-8859-15"},
|
||||
{"UTF-16LE-BOM.html", "", "utf-16le"},
|
||||
{"UTF-16BE-BOM.html", "", "utf-16be"},
|
||||
{"meta-content-attribute.html", "text/html", "iso-8859-15"},
|
||||
{"meta-charset-attribute.html", "text/html", "iso-8859-15"},
|
||||
{"No-encoding-declaration.html", "text/html", "utf-8"},
|
||||
{"HTTP-vs-UTF-8-BOM.html", "text/html; charset=iso-8859-15", "utf-8"},
|
||||
{"HTTP-vs-meta-content.html", "text/html; charset=iso-8859-15", "iso-8859-15"},
|
||||
{"HTTP-vs-meta-charset.html", "text/html; charset=iso-8859-15", "iso-8859-15"},
|
||||
{"UTF-8-BOM-vs-meta-content.html", "text/html", "utf-8"},
|
||||
{"UTF-8-BOM-vs-meta-charset.html", "text/html", "utf-8"},
|
||||
}
|
||||
|
||||
func TestSniff(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "nacl": // platforms that don't permit direct file system access
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
|
||||
for _, tc := range sniffTestCases {
|
||||
content, err := ioutil.ReadFile("testdata/" + tc.filename)
|
||||
if err != nil {
|
||||
t.Errorf("%s: error reading file: %v", tc.filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
_, name, _ := DetermineEncoding(content, tc.declared)
|
||||
if name != tc.want {
|
||||
t.Errorf("%s: got %q, want %q", tc.filename, name, tc.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReader(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "nacl": // platforms that don't permit direct file system access
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
|
||||
for _, tc := range sniffTestCases {
|
||||
content, err := ioutil.ReadFile("testdata/" + tc.filename)
|
||||
if err != nil {
|
||||
t.Errorf("%s: error reading file: %v", tc.filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
r, err := NewReader(bytes.NewReader(content), tc.declared)
|
||||
if err != nil {
|
||||
t.Errorf("%s: error creating reader: %v", tc.filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
got, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Errorf("%s: error reading from charset.NewReader: %v", tc.filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
e, _ := Lookup(tc.want)
|
||||
want, err := ioutil.ReadAll(transform.NewReader(bytes.NewReader(content), e.NewDecoder()))
|
||||
if err != nil {
|
||||
t.Errorf("%s: error decoding with hard-coded charset name: %v", tc.filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("%s: got %q, want %q", tc.filename, got, want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var metaTestCases = []struct {
|
||||
meta, want string
|
||||
}{
|
||||
{"", ""},
|
||||
{"text/html", ""},
|
||||
{"text/html; charset utf-8", ""},
|
||||
{"text/html; charset=latin-2", "latin-2"},
|
||||
{"text/html; charset; charset = utf-8", "utf-8"},
|
||||
{`charset="big5"`, "big5"},
|
||||
{"charset='shift_jis'", "shift_jis"},
|
||||
}
|
||||
|
||||
func TestFromMeta(t *testing.T) {
|
||||
for _, tc := range metaTestCases {
|
||||
got := fromMetaElement(tc.meta)
|
||||
if got != tc.want {
|
||||
t.Errorf("%q: got %q, want %q", tc.meta, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestXML(t *testing.T) {
|
||||
const s = "<?xml version=\"1.0\" encoding=\"windows-1252\"?><a><Word>r\xe9sum\xe9</Word></a>"
|
||||
|
||||
d := xml.NewDecoder(strings.NewReader(s))
|
||||
d.CharsetReader = NewReaderLabel
|
||||
|
||||
var a struct {
|
||||
Word string
|
||||
}
|
||||
err := d.Decode(&a)
|
||||
if err != nil {
|
||||
t.Fatalf("Decode: %v", err)
|
||||
}
|
||||
|
||||
want := "résumé"
|
||||
if a.Word != want {
|
||||
t.Errorf("got %q, want %q", a.Word, want)
|
||||
}
|
||||
}
|
||||
2
vendor/golang.org/x/net/html/const.go
generated
vendored
2
vendor/golang.org/x/net/html/const.go
generated
vendored
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
package html
|
||||
|
||||
// Section 12.2.3.2 of the HTML5 specification says "The following elements
|
||||
// Section 12.2.4.2 of the HTML5 specification says "The following elements
|
||||
// have varying levels of special parsing rules".
|
||||
// https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements
|
||||
var isSpecialElementMap = map[string]bool{
|
||||
|
|
|
|||
8
vendor/golang.org/x/net/html/doc.go
generated
vendored
8
vendor/golang.org/x/net/html/doc.go
generated
vendored
|
|
@ -49,18 +49,18 @@ call to Next. For example, to extract an HTML page's anchor text:
|
|||
for {
|
||||
tt := z.Next()
|
||||
switch tt {
|
||||
case ErrorToken:
|
||||
case html.ErrorToken:
|
||||
return z.Err()
|
||||
case TextToken:
|
||||
case html.TextToken:
|
||||
if depth > 0 {
|
||||
// emitBytes should copy the []byte it receives,
|
||||
// if it doesn't process it immediately.
|
||||
emitBytes(z.Text())
|
||||
}
|
||||
case StartTagToken, EndTagToken:
|
||||
case html.StartTagToken, html.EndTagToken:
|
||||
tn, _ := z.TagName()
|
||||
if len(tn) == 1 && tn[0] == 'a' {
|
||||
if tt == StartTagToken {
|
||||
if tt == html.StartTagToken {
|
||||
depth++
|
||||
} else {
|
||||
depth--
|
||||
|
|
|
|||
4154
vendor/golang.org/x/net/html/entity.go
generated
vendored
4154
vendor/golang.org/x/net/html/entity.go
generated
vendored
File diff suppressed because it is too large
Load diff
29
vendor/golang.org/x/net/html/entity_test.go
generated
vendored
29
vendor/golang.org/x/net/html/entity_test.go
generated
vendored
|
|
@ -1,29 +0,0 @@
|
|||
// Copyright 2010 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 html
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func TestEntityLength(t *testing.T) {
|
||||
// We verify that the length of UTF-8 encoding of each value is <= 1 + len(key).
|
||||
// The +1 comes from the leading "&". This property implies that the length of
|
||||
// unescaped text is <= the length of escaped text.
|
||||
for k, v := range entity {
|
||||
if 1+len(k) < utf8.RuneLen(v) {
|
||||
t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v))
|
||||
}
|
||||
if len(k) > longestEntityWithoutSemicolon && k[len(k)-1] != ';' {
|
||||
t.Errorf("entity name %s is %d characters, but longestEntityWithoutSemicolon=%d", k, len(k), longestEntityWithoutSemicolon)
|
||||
}
|
||||
}
|
||||
for k, v := range entity2 {
|
||||
if 1+len(k) < utf8.RuneLen(v[0])+utf8.RuneLen(v[1]) {
|
||||
t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v[0]) + string(v[1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
97
vendor/golang.org/x/net/html/escape_test.go
generated
vendored
97
vendor/golang.org/x/net/html/escape_test.go
generated
vendored
|
|
@ -1,97 +0,0 @@
|
|||
// Copyright 2013 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 html
|
||||
|
||||
import "testing"
|
||||
|
||||
type unescapeTest struct {
|
||||
// A short description of the test case.
|
||||
desc string
|
||||
// The HTML text.
|
||||
html string
|
||||
// The unescaped text.
|
||||
unescaped string
|
||||
}
|
||||
|
||||
var unescapeTests = []unescapeTest{
|
||||
// Handle no entities.
|
||||
{
|
||||
"copy",
|
||||
"A\ttext\nstring",
|
||||
"A\ttext\nstring",
|
||||
},
|
||||
// Handle simple named entities.
|
||||
{
|
||||
"simple",
|
||||
"& > <",
|
||||
"& > <",
|
||||
},
|
||||
// Handle hitting the end of the string.
|
||||
{
|
||||
"stringEnd",
|
||||
"& &",
|
||||
"& &",
|
||||
},
|
||||
// Handle entities with two codepoints.
|
||||
{
|
||||
"multiCodepoint",
|
||||
"text ⋛︀ blah",
|
||||
"text \u22db\ufe00 blah",
|
||||
},
|
||||
// Handle decimal numeric entities.
|
||||
{
|
||||
"decimalEntity",
|
||||
"Delta = Δ ",
|
||||
"Delta = Δ ",
|
||||
},
|
||||
// Handle hexadecimal numeric entities.
|
||||
{
|
||||
"hexadecimalEntity",
|
||||
"Lambda = λ = λ ",
|
||||
"Lambda = λ = λ ",
|
||||
},
|
||||
// Handle numeric early termination.
|
||||
{
|
||||
"numericEnds",
|
||||
"&# &#x €43 © = ©f = ©",
|
||||
"&# &#x €43 © = ©f = ©",
|
||||
},
|
||||
// Handle numeric ISO-8859-1 entity replacements.
|
||||
{
|
||||
"numericReplacements",
|
||||
"Footnote‡",
|
||||
"Footnote‡",
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnescape(t *testing.T) {
|
||||
for _, tt := range unescapeTests {
|
||||
unescaped := UnescapeString(tt.html)
|
||||
if unescaped != tt.unescaped {
|
||||
t.Errorf("TestUnescape %s: want %q, got %q", tt.desc, tt.unescaped, unescaped)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnescapeEscape(t *testing.T) {
|
||||
ss := []string{
|
||||
``,
|
||||
`abc def`,
|
||||
`a & b`,
|
||||
`a&b`,
|
||||
`a & b`,
|
||||
`"`,
|
||||
`"`,
|
||||
`"<&>"`,
|
||||
`"<&>"`,
|
||||
`3&5==1 && 0<1, "0<1", a+acute=á`,
|
||||
`The special characters are: <, >, &, ' and "`,
|
||||
}
|
||||
for _, s := range ss {
|
||||
if got := UnescapeString(EscapeString(s)); got != s {
|
||||
t.Errorf("got %q want %q", got, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
40
vendor/golang.org/x/net/html/example_test.go
generated
vendored
40
vendor/golang.org/x/net/html/example_test.go
generated
vendored
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// This example demonstrates parsing HTML data and walking the resulting tree.
|
||||
package html_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func ExampleParse() {
|
||||
s := `<p>Links:</p><ul><li><a href="foo">Foo</a><li><a href="/bar/baz">BarBaz</a></ul>`
|
||||
doc, err := html.Parse(strings.NewReader(s))
|
||||
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 _, 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
|
||||
}
|
||||
6
vendor/golang.org/x/net/html/foreign.go
generated
vendored
6
vendor/golang.org/x/net/html/foreign.go
generated
vendored
|
|
@ -67,7 +67,7 @@ func mathMLTextIntegrationPoint(n *Node) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.5.
|
||||
// Section 12.2.6.5.
|
||||
var breakout = map[string]bool{
|
||||
"b": true,
|
||||
"big": true,
|
||||
|
|
@ -115,7 +115,7 @@ var breakout = map[string]bool{
|
|||
"var": true,
|
||||
}
|
||||
|
||||
// Section 12.2.5.5.
|
||||
// Section 12.2.6.5.
|
||||
var svgTagNameAdjustments = map[string]string{
|
||||
"altglyph": "altGlyph",
|
||||
"altglyphdef": "altGlyphDef",
|
||||
|
|
@ -155,7 +155,7 @@ var svgTagNameAdjustments = map[string]string{
|
|||
"textpath": "textPath",
|
||||
}
|
||||
|
||||
// Section 12.2.5.1
|
||||
// Section 12.2.6.1
|
||||
var mathMLAttributeAdjustments = map[string]string{
|
||||
"definitionurl": "definitionURL",
|
||||
}
|
||||
|
|
|
|||
33
vendor/golang.org/x/net/html/node.go
generated
vendored
33
vendor/golang.org/x/net/html/node.go
generated
vendored
|
|
@ -21,9 +21,10 @@ const (
|
|||
scopeMarkerNode
|
||||
)
|
||||
|
||||
// Section 12.2.3.3 says "scope markers are inserted when entering applet
|
||||
// elements, buttons, object elements, marquees, table cells, and table
|
||||
// captions, and are used to prevent formatting from 'leaking'".
|
||||
// Section 12.2.4.3 says "The markers are inserted when entering applet,
|
||||
// object, marquee, template, td, th, and caption elements, and are used
|
||||
// to prevent formatting from "leaking" into applet, object, marquee,
|
||||
// template, td, th, and caption elements".
|
||||
var scopeMarker = Node{Type: scopeMarkerNode}
|
||||
|
||||
// A Node consists of a NodeType and some Data (tag name for element nodes,
|
||||
|
|
@ -173,6 +174,16 @@ func (s *nodeStack) index(n *Node) int {
|
|||
return -1
|
||||
}
|
||||
|
||||
// contains returns whether a is within s.
|
||||
func (s *nodeStack) contains(a atom.Atom) bool {
|
||||
for _, n := range *s {
|
||||
if n.DataAtom == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// insert inserts a node at the given index.
|
||||
func (s *nodeStack) insert(i int, n *Node) {
|
||||
(*s) = append(*s, nil)
|
||||
|
|
@ -191,3 +202,19 @@ func (s *nodeStack) remove(n *Node) {
|
|||
(*s)[j] = nil
|
||||
*s = (*s)[:j]
|
||||
}
|
||||
|
||||
type insertionModeStack []insertionMode
|
||||
|
||||
func (s *insertionModeStack) pop() (im insertionMode) {
|
||||
i := len(*s)
|
||||
im = (*s)[i-1]
|
||||
*s = (*s)[:i-1]
|
||||
return im
|
||||
}
|
||||
|
||||
func (s *insertionModeStack) top() insertionMode {
|
||||
if i := len(*s); i > 0 {
|
||||
return (*s)[i-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
146
vendor/golang.org/x/net/html/node_test.go
generated
vendored
146
vendor/golang.org/x/net/html/node_test.go
generated
vendored
|
|
@ -1,146 +0,0 @@
|
|||
// Copyright 2010 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 html
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// checkTreeConsistency checks that a node and its descendants are all
|
||||
// consistent in their parent/child/sibling relationships.
|
||||
func checkTreeConsistency(n *Node) error {
|
||||
return checkTreeConsistency1(n, 0)
|
||||
}
|
||||
|
||||
func checkTreeConsistency1(n *Node, depth int) error {
|
||||
if depth == 1e4 {
|
||||
return fmt.Errorf("html: tree looks like it contains a cycle")
|
||||
}
|
||||
if err := checkNodeConsistency(n); err != nil {
|
||||
return err
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
if err := checkTreeConsistency1(c, depth+1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkNodeConsistency checks that a node's parent/child/sibling relationships
|
||||
// are consistent.
|
||||
func checkNodeConsistency(n *Node) error {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
nParent := 0
|
||||
for p := n.Parent; p != nil; p = p.Parent {
|
||||
nParent++
|
||||
if nParent == 1e4 {
|
||||
return fmt.Errorf("html: parent list looks like an infinite loop")
|
||||
}
|
||||
}
|
||||
|
||||
nForward := 0
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
nForward++
|
||||
if nForward == 1e6 {
|
||||
return fmt.Errorf("html: forward list of children looks like an infinite loop")
|
||||
}
|
||||
if c.Parent != n {
|
||||
return fmt.Errorf("html: inconsistent child/parent relationship")
|
||||
}
|
||||
}
|
||||
|
||||
nBackward := 0
|
||||
for c := n.LastChild; c != nil; c = c.PrevSibling {
|
||||
nBackward++
|
||||
if nBackward == 1e6 {
|
||||
return fmt.Errorf("html: backward list of children looks like an infinite loop")
|
||||
}
|
||||
if c.Parent != n {
|
||||
return fmt.Errorf("html: inconsistent child/parent relationship")
|
||||
}
|
||||
}
|
||||
|
||||
if n.Parent != nil {
|
||||
if n.Parent == n {
|
||||
return fmt.Errorf("html: inconsistent parent relationship")
|
||||
}
|
||||
if n.Parent == n.FirstChild {
|
||||
return fmt.Errorf("html: inconsistent parent/first relationship")
|
||||
}
|
||||
if n.Parent == n.LastChild {
|
||||
return fmt.Errorf("html: inconsistent parent/last relationship")
|
||||
}
|
||||
if n.Parent == n.PrevSibling {
|
||||
return fmt.Errorf("html: inconsistent parent/prev relationship")
|
||||
}
|
||||
if n.Parent == n.NextSibling {
|
||||
return fmt.Errorf("html: inconsistent parent/next relationship")
|
||||
}
|
||||
|
||||
parentHasNAsAChild := false
|
||||
for c := n.Parent.FirstChild; c != nil; c = c.NextSibling {
|
||||
if c == n {
|
||||
parentHasNAsAChild = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !parentHasNAsAChild {
|
||||
return fmt.Errorf("html: inconsistent parent/child relationship")
|
||||
}
|
||||
}
|
||||
|
||||
if n.PrevSibling != nil && n.PrevSibling.NextSibling != n {
|
||||
return fmt.Errorf("html: inconsistent prev/next relationship")
|
||||
}
|
||||
if n.NextSibling != nil && n.NextSibling.PrevSibling != n {
|
||||
return fmt.Errorf("html: inconsistent next/prev relationship")
|
||||
}
|
||||
|
||||
if (n.FirstChild == nil) != (n.LastChild == nil) {
|
||||
return fmt.Errorf("html: inconsistent first/last relationship")
|
||||
}
|
||||
if n.FirstChild != nil && n.FirstChild == n.LastChild {
|
||||
// We have a sole child.
|
||||
if n.FirstChild.PrevSibling != nil || n.FirstChild.NextSibling != nil {
|
||||
return fmt.Errorf("html: inconsistent sole child's sibling relationship")
|
||||
}
|
||||
}
|
||||
|
||||
seen := map[*Node]bool{}
|
||||
|
||||
var last *Node
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
if seen[c] {
|
||||
return fmt.Errorf("html: inconsistent repeated child")
|
||||
}
|
||||
seen[c] = true
|
||||
last = c
|
||||
}
|
||||
if last != n.LastChild {
|
||||
return fmt.Errorf("html: inconsistent last relationship")
|
||||
}
|
||||
|
||||
var first *Node
|
||||
for c := n.LastChild; c != nil; c = c.PrevSibling {
|
||||
if !seen[c] {
|
||||
return fmt.Errorf("html: inconsistent missing child")
|
||||
}
|
||||
delete(seen, c)
|
||||
first = c
|
||||
}
|
||||
if first != n.FirstChild {
|
||||
return fmt.Errorf("html: inconsistent first relationship")
|
||||
}
|
||||
|
||||
if len(seen) != 0 {
|
||||
return fmt.Errorf("html: inconsistent forwards/backwards child list")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
361
vendor/golang.org/x/net/html/parse.go
generated
vendored
361
vendor/golang.org/x/net/html/parse.go
generated
vendored
|
|
@ -25,20 +25,22 @@ type parser struct {
|
|||
hasSelfClosingToken bool
|
||||
// doc is the document root element.
|
||||
doc *Node
|
||||
// The stack of open elements (section 12.2.3.2) and active formatting
|
||||
// elements (section 12.2.3.3).
|
||||
// The stack of open elements (section 12.2.4.2) and active formatting
|
||||
// elements (section 12.2.4.3).
|
||||
oe, afe nodeStack
|
||||
// Element pointers (section 12.2.3.4).
|
||||
// Element pointers (section 12.2.4.4).
|
||||
head, form *Node
|
||||
// Other parsing state flags (section 12.2.3.5).
|
||||
// Other parsing state flags (section 12.2.4.5).
|
||||
scripting, framesetOK bool
|
||||
// The stack of template insertion modes
|
||||
templateStack insertionModeStack
|
||||
// im is the current insertion mode.
|
||||
im insertionMode
|
||||
// originalIM is the insertion mode to go back to after completing a text
|
||||
// or inTableText insertion mode.
|
||||
originalIM insertionMode
|
||||
// fosterParenting is whether new elements should be inserted according to
|
||||
// the foster parenting rules (section 12.2.5.3).
|
||||
// the foster parenting rules (section 12.2.6.1).
|
||||
fosterParenting bool
|
||||
// quirks is whether the parser is operating in "quirks mode."
|
||||
quirks bool
|
||||
|
|
@ -56,7 +58,7 @@ func (p *parser) top() *Node {
|
|||
return p.doc
|
||||
}
|
||||
|
||||
// Stop tags for use in popUntil. These come from section 12.2.3.2.
|
||||
// Stop tags for use in popUntil. These come from section 12.2.4.2.
|
||||
var (
|
||||
defaultScopeStopTags = map[string][]a.Atom{
|
||||
"": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template},
|
||||
|
|
@ -79,7 +81,7 @@ const (
|
|||
|
||||
// popUntil pops the stack of open elements at the highest element whose tag
|
||||
// is in matchTags, provided there is no higher element in the scope's stop
|
||||
// tags (as defined in section 12.2.3.2). It returns whether or not there was
|
||||
// tags (as defined in section 12.2.4.2). It returns whether or not there was
|
||||
// such an element. If there was not, popUntil leaves the stack unchanged.
|
||||
//
|
||||
// For example, the set of stop tags for table scope is: "html", "table". If
|
||||
|
|
@ -126,7 +128,7 @@ func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int {
|
|||
return -1
|
||||
}
|
||||
case tableScope:
|
||||
if tagAtom == a.Html || tagAtom == a.Table {
|
||||
if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template {
|
||||
return -1
|
||||
}
|
||||
case selectScope:
|
||||
|
|
@ -162,17 +164,17 @@ func (p *parser) clearStackToContext(s scope) {
|
|||
tagAtom := p.oe[i].DataAtom
|
||||
switch s {
|
||||
case tableScope:
|
||||
if tagAtom == a.Html || tagAtom == a.Table {
|
||||
if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template {
|
||||
p.oe = p.oe[:i+1]
|
||||
return
|
||||
}
|
||||
case tableRowScope:
|
||||
if tagAtom == a.Html || tagAtom == a.Tr {
|
||||
if tagAtom == a.Html || tagAtom == a.Tr || tagAtom == a.Template {
|
||||
p.oe = p.oe[:i+1]
|
||||
return
|
||||
}
|
||||
case tableBodyScope:
|
||||
if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead {
|
||||
if tagAtom == a.Html || tagAtom == a.Tbody || tagAtom == a.Tfoot || tagAtom == a.Thead || tagAtom == a.Template {
|
||||
p.oe = p.oe[:i+1]
|
||||
return
|
||||
}
|
||||
|
|
@ -183,7 +185,7 @@ func (p *parser) clearStackToContext(s scope) {
|
|||
}
|
||||
|
||||
// generateImpliedEndTags pops nodes off the stack of open elements as long as
|
||||
// the top node has a tag name of dd, dt, li, option, optgroup, p, rp, or rt.
|
||||
// the top node has a tag name of dd, dt, li, optgroup, option, p, rb, rp, rt or rtc.
|
||||
// If exceptions are specified, nodes with that name will not be popped off.
|
||||
func (p *parser) generateImpliedEndTags(exceptions ...string) {
|
||||
var i int
|
||||
|
|
@ -192,7 +194,7 @@ loop:
|
|||
n := p.oe[i]
|
||||
if n.Type == ElementNode {
|
||||
switch n.DataAtom {
|
||||
case a.Dd, a.Dt, a.Li, a.Option, a.Optgroup, a.P, a.Rp, a.Rt:
|
||||
case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc:
|
||||
for _, except := range exceptions {
|
||||
if n.Data == except {
|
||||
break loop
|
||||
|
|
@ -207,6 +209,27 @@ loop:
|
|||
p.oe = p.oe[:i+1]
|
||||
}
|
||||
|
||||
// generateAllImpliedEndTags pops nodes off the stack of open elements as long as
|
||||
// the top node has a tag name of caption, colgroup, dd, div, dt, li, optgroup, option, p, rb,
|
||||
// rp, rt, rtc, span, tbody, td, tfoot, th, thead or tr.
|
||||
func (p *parser) generateAllImpliedEndTags() {
|
||||
var i int
|
||||
for i = len(p.oe) - 1; i >= 0; i-- {
|
||||
n := p.oe[i]
|
||||
if n.Type == ElementNode {
|
||||
switch n.DataAtom {
|
||||
// TODO: remove this divergence from the HTML5 spec
|
||||
case a.Caption, a.Colgroup, a.Dd, a.Div, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb,
|
||||
a.Rp, a.Rt, a.Rtc, a.Span, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
p.oe = p.oe[:i+1]
|
||||
}
|
||||
|
||||
// addChild adds a child node n to the top element, and pushes n onto the stack
|
||||
// of open elements if it is an element node.
|
||||
func (p *parser) addChild(n *Node) {
|
||||
|
|
@ -234,9 +257,9 @@ func (p *parser) shouldFosterParent() bool {
|
|||
}
|
||||
|
||||
// fosterParent adds a child node according to the foster parenting rules.
|
||||
// Section 12.2.5.3, "foster parenting".
|
||||
// Section 12.2.6.1, "foster parenting".
|
||||
func (p *parser) fosterParent(n *Node) {
|
||||
var table, parent, prev *Node
|
||||
var table, parent, prev, template *Node
|
||||
var i int
|
||||
for i = len(p.oe) - 1; i >= 0; i-- {
|
||||
if p.oe[i].DataAtom == a.Table {
|
||||
|
|
@ -245,6 +268,19 @@ func (p *parser) fosterParent(n *Node) {
|
|||
}
|
||||
}
|
||||
|
||||
var j int
|
||||
for j = len(p.oe) - 1; j >= 0; j-- {
|
||||
if p.oe[j].DataAtom == a.Template {
|
||||
template = p.oe[j]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if template != nil && (table == nil || j < i) {
|
||||
template.AppendChild(n)
|
||||
return
|
||||
}
|
||||
|
||||
if table == nil {
|
||||
// The foster parent is the html element.
|
||||
parent = p.oe[0]
|
||||
|
|
@ -304,7 +340,7 @@ func (p *parser) addElement() {
|
|||
})
|
||||
}
|
||||
|
||||
// Section 12.2.3.3.
|
||||
// Section 12.2.4.3.
|
||||
func (p *parser) addFormattingElement() {
|
||||
tagAtom, attr := p.tok.DataAtom, p.tok.Attr
|
||||
p.addElement()
|
||||
|
|
@ -351,7 +387,7 @@ findIdenticalElements:
|
|||
p.afe = append(p.afe, p.top())
|
||||
}
|
||||
|
||||
// Section 12.2.3.3.
|
||||
// Section 12.2.4.3.
|
||||
func (p *parser) clearActiveFormattingElements() {
|
||||
for {
|
||||
n := p.afe.pop()
|
||||
|
|
@ -361,7 +397,7 @@ func (p *parser) clearActiveFormattingElements() {
|
|||
}
|
||||
}
|
||||
|
||||
// Section 12.2.3.3.
|
||||
// Section 12.2.4.3.
|
||||
func (p *parser) reconstructActiveFormattingElements() {
|
||||
n := p.afe.top()
|
||||
if n == nil {
|
||||
|
|
@ -390,12 +426,12 @@ func (p *parser) reconstructActiveFormattingElements() {
|
|||
}
|
||||
}
|
||||
|
||||
// Section 12.2.4.
|
||||
// Section 12.2.5.
|
||||
func (p *parser) acknowledgeSelfClosingTag() {
|
||||
p.hasSelfClosingToken = false
|
||||
}
|
||||
|
||||
// An insertion mode (section 12.2.3.1) is the state transition function from
|
||||
// An insertion mode (section 12.2.4.1) is the state transition function from
|
||||
// a particular state in the HTML5 parser's state machine. It updates the
|
||||
// parser's fields depending on parser.tok (where ErrorToken means EOF).
|
||||
// It returns whether the token was consumed.
|
||||
|
|
@ -403,7 +439,7 @@ type insertionMode func(*parser) bool
|
|||
|
||||
// setOriginalIM sets the insertion mode to return to after completing a text or
|
||||
// inTableText insertion mode.
|
||||
// Section 12.2.3.1, "using the rules for".
|
||||
// Section 12.2.4.1, "using the rules for".
|
||||
func (p *parser) setOriginalIM() {
|
||||
if p.originalIM != nil {
|
||||
panic("html: bad parser state: originalIM was set twice")
|
||||
|
|
@ -411,18 +447,38 @@ func (p *parser) setOriginalIM() {
|
|||
p.originalIM = p.im
|
||||
}
|
||||
|
||||
// Section 12.2.3.1, "reset the insertion mode".
|
||||
// Section 12.2.4.1, "reset the insertion mode".
|
||||
func (p *parser) resetInsertionMode() {
|
||||
for i := len(p.oe) - 1; i >= 0; i-- {
|
||||
n := p.oe[i]
|
||||
if i == 0 && p.context != nil {
|
||||
last := i == 0
|
||||
if last && p.context != nil {
|
||||
n = p.context
|
||||
}
|
||||
|
||||
switch n.DataAtom {
|
||||
case a.Select:
|
||||
if !last {
|
||||
for ancestor, first := n, p.oe[0]; ancestor != first; {
|
||||
if ancestor == first {
|
||||
break
|
||||
}
|
||||
ancestor = p.oe[p.oe.index(ancestor)-1]
|
||||
switch ancestor.DataAtom {
|
||||
case a.Template:
|
||||
p.im = inSelectIM
|
||||
return
|
||||
case a.Table:
|
||||
p.im = inSelectInTableIM
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
p.im = inSelectIM
|
||||
case a.Td, a.Th:
|
||||
// TODO: remove this divergence from the HTML5 spec.
|
||||
//
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
|
||||
p.im = inCellIM
|
||||
case a.Tr:
|
||||
p.im = inRowIM
|
||||
|
|
@ -434,25 +490,37 @@ func (p *parser) resetInsertionMode() {
|
|||
p.im = inColumnGroupIM
|
||||
case a.Table:
|
||||
p.im = inTableIM
|
||||
case a.Template:
|
||||
p.im = p.templateStack.top()
|
||||
case a.Head:
|
||||
p.im = inBodyIM
|
||||
// TODO: remove this divergence from the HTML5 spec.
|
||||
//
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
|
||||
p.im = inHeadIM
|
||||
case a.Body:
|
||||
p.im = inBodyIM
|
||||
case a.Frameset:
|
||||
p.im = inFramesetIM
|
||||
case a.Html:
|
||||
p.im = beforeHeadIM
|
||||
if p.head == nil {
|
||||
p.im = beforeHeadIM
|
||||
} else {
|
||||
p.im = afterHeadIM
|
||||
}
|
||||
default:
|
||||
if last {
|
||||
p.im = inBodyIM
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
return
|
||||
}
|
||||
p.im = inBodyIM
|
||||
}
|
||||
|
||||
const whitespace = " \t\r\n\f"
|
||||
|
||||
// Section 12.2.5.4.1.
|
||||
// Section 12.2.6.4.1.
|
||||
func initialIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
|
@ -479,7 +547,7 @@ func initialIM(p *parser) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.2.
|
||||
// Section 12.2.6.4.2.
|
||||
func beforeHTMLIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case DoctypeToken:
|
||||
|
|
@ -517,7 +585,7 @@ func beforeHTMLIM(p *parser) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.3.
|
||||
// Section 12.2.6.4.3.
|
||||
func beforeHeadIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
|
@ -560,7 +628,7 @@ func beforeHeadIM(p *parser) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.4.
|
||||
// Section 12.2.6.4.4.
|
||||
func inHeadIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
|
@ -590,19 +658,36 @@ func inHeadIM(p *parser) bool {
|
|||
case a.Head:
|
||||
// Ignore the token.
|
||||
return true
|
||||
case a.Template:
|
||||
p.addElement()
|
||||
p.afe = append(p.afe, &scopeMarker)
|
||||
p.framesetOK = false
|
||||
p.im = inTemplateIM
|
||||
p.templateStack = append(p.templateStack, inTemplateIM)
|
||||
return true
|
||||
}
|
||||
case EndTagToken:
|
||||
switch p.tok.DataAtom {
|
||||
case a.Head:
|
||||
n := p.oe.pop()
|
||||
if n.DataAtom != a.Head {
|
||||
panic("html: bad parser state: <head> element not found, in the in-head insertion mode")
|
||||
}
|
||||
p.oe.pop()
|
||||
p.im = afterHeadIM
|
||||
return true
|
||||
case a.Body, a.Html, a.Br:
|
||||
p.parseImpliedToken(EndTagToken, a.Head, a.Head.String())
|
||||
return false
|
||||
case a.Template:
|
||||
if !p.oe.contains(a.Template) {
|
||||
return true
|
||||
}
|
||||
p.generateAllImpliedEndTags()
|
||||
if n := p.oe.top(); n.DataAtom != a.Template {
|
||||
return true
|
||||
}
|
||||
p.popUntil(defaultScope, a.Template)
|
||||
p.clearActiveFormattingElements()
|
||||
p.templateStack.pop()
|
||||
p.resetInsertionMode()
|
||||
return true
|
||||
default:
|
||||
// Ignore the token.
|
||||
return true
|
||||
|
|
@ -622,7 +707,7 @@ func inHeadIM(p *parser) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.6.
|
||||
// Section 12.2.6.4.6.
|
||||
func afterHeadIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
|
@ -648,7 +733,7 @@ func afterHeadIM(p *parser) bool {
|
|||
p.addElement()
|
||||
p.im = inFramesetIM
|
||||
return true
|
||||
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title:
|
||||
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
|
||||
p.oe = append(p.oe, p.head)
|
||||
defer p.oe.remove(p.head)
|
||||
return inHeadIM(p)
|
||||
|
|
@ -660,6 +745,8 @@ func afterHeadIM(p *parser) bool {
|
|||
switch p.tok.DataAtom {
|
||||
case a.Body, a.Html, a.Br:
|
||||
// Drop down to creating an implied <body> tag.
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
default:
|
||||
// Ignore the token.
|
||||
return true
|
||||
|
|
@ -697,7 +784,7 @@ func copyAttributes(dst *Node, src Token) {
|
|||
}
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.7.
|
||||
// Section 12.2.6.4.7.
|
||||
func inBodyIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
|
@ -727,10 +814,16 @@ func inBodyIM(p *parser) bool {
|
|||
case StartTagToken:
|
||||
switch p.tok.DataAtom {
|
||||
case a.Html:
|
||||
if p.oe.contains(a.Template) {
|
||||
return true
|
||||
}
|
||||
copyAttributes(p.oe[0], p.tok)
|
||||
case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Title:
|
||||
case a.Base, a.Basefont, a.Bgsound, a.Command, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
|
||||
return inHeadIM(p)
|
||||
case a.Body:
|
||||
if p.oe.contains(a.Template) {
|
||||
return true
|
||||
}
|
||||
if len(p.oe) >= 2 {
|
||||
body := p.oe[1]
|
||||
if body.Type == ElementNode && body.DataAtom == a.Body {
|
||||
|
|
@ -767,7 +860,7 @@ func inBodyIM(p *parser) bool {
|
|||
// The newline, if any, will be dealt with by the TextToken case.
|
||||
p.framesetOK = false
|
||||
case a.Form:
|
||||
if p.form == nil {
|
||||
if p.oe.contains(a.Template) || p.form == nil {
|
||||
p.popUntil(buttonScope, a.P)
|
||||
p.addElement()
|
||||
p.form = p.top()
|
||||
|
|
@ -952,11 +1045,16 @@ func inBodyIM(p *parser) bool {
|
|||
}
|
||||
p.reconstructActiveFormattingElements()
|
||||
p.addElement()
|
||||
case a.Rp, a.Rt:
|
||||
case a.Rb, a.Rtc:
|
||||
if p.elementInScope(defaultScope, a.Ruby) {
|
||||
p.generateImpliedEndTags()
|
||||
}
|
||||
p.addElement()
|
||||
case a.Rp, a.Rt:
|
||||
if p.elementInScope(defaultScope, a.Ruby) {
|
||||
p.generateImpliedEndTags("rtc")
|
||||
}
|
||||
p.addElement()
|
||||
case a.Math, a.Svg:
|
||||
p.reconstructActiveFormattingElements()
|
||||
if p.tok.DataAtom == a.Math {
|
||||
|
|
@ -972,7 +1070,13 @@ func inBodyIM(p *parser) bool {
|
|||
p.acknowledgeSelfClosingTag()
|
||||
}
|
||||
return true
|
||||
case a.Caption, a.Col, a.Colgroup, a.Frame, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
|
||||
case a.Frame:
|
||||
// TODO: remove this divergence from the HTML5 spec.
|
||||
if p.oe.contains(a.Template) {
|
||||
p.addElement()
|
||||
return true
|
||||
}
|
||||
case a.Caption, a.Col, a.Colgroup, a.Head, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
|
||||
// Ignore the token.
|
||||
default:
|
||||
p.reconstructActiveFormattingElements()
|
||||
|
|
@ -993,15 +1097,28 @@ func inBodyIM(p *parser) bool {
|
|||
case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Menu, a.Nav, a.Ol, a.Pre, a.Section, a.Summary, a.Ul:
|
||||
p.popUntil(defaultScope, p.tok.DataAtom)
|
||||
case a.Form:
|
||||
node := p.form
|
||||
p.form = nil
|
||||
i := p.indexOfElementInScope(defaultScope, a.Form)
|
||||
if node == nil || i == -1 || p.oe[i] != node {
|
||||
// Ignore the token.
|
||||
return true
|
||||
if p.oe.contains(a.Template) {
|
||||
if !p.oe.contains(a.Form) {
|
||||
// Ignore the token.
|
||||
return true
|
||||
}
|
||||
p.generateImpliedEndTags()
|
||||
if p.tok.DataAtom == a.Form {
|
||||
// Ignore the token.
|
||||
return true
|
||||
}
|
||||
p.popUntil(defaultScope, a.Form)
|
||||
} else {
|
||||
node := p.form
|
||||
p.form = nil
|
||||
i := p.indexOfElementInScope(defaultScope, a.Form)
|
||||
if node == nil || i == -1 || p.oe[i] != node {
|
||||
// Ignore the token.
|
||||
return true
|
||||
}
|
||||
p.generateImpliedEndTags()
|
||||
p.oe.remove(node)
|
||||
}
|
||||
p.generateImpliedEndTags()
|
||||
p.oe.remove(node)
|
||||
case a.P:
|
||||
if !p.elementInScope(buttonScope, a.P) {
|
||||
p.parseImpliedToken(StartTagToken, a.P, a.P.String())
|
||||
|
|
@ -1022,6 +1139,8 @@ func inBodyIM(p *parser) bool {
|
|||
case a.Br:
|
||||
p.tok.Type = StartTagToken
|
||||
return false
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
default:
|
||||
p.inBodyEndTagOther(p.tok.DataAtom)
|
||||
}
|
||||
|
|
@ -1030,6 +1149,21 @@ func inBodyIM(p *parser) bool {
|
|||
Type: CommentNode,
|
||||
Data: p.tok.Data,
|
||||
})
|
||||
case ErrorToken:
|
||||
// TODO: remove this divergence from the HTML5 spec.
|
||||
if len(p.templateStack) > 0 {
|
||||
p.im = inTemplateIM
|
||||
return false
|
||||
} else {
|
||||
for _, e := range p.oe {
|
||||
switch e.DataAtom {
|
||||
case a.Dd, a.Dt, a.Li, a.Optgroup, a.Option, a.P, a.Rb, a.Rp, a.Rt, a.Rtc, a.Tbody, a.Td, a.Tfoot, a.Th,
|
||||
a.Thead, a.Tr, a.Body, a.Html:
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
|
@ -1135,6 +1269,12 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) {
|
|||
switch commonAncestor.DataAtom {
|
||||
case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr:
|
||||
p.fosterParent(lastNode)
|
||||
case a.Template:
|
||||
// TODO: remove namespace checking
|
||||
if commonAncestor.Namespace == "html" {
|
||||
commonAncestor = commonAncestor.LastChild
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
commonAncestor.AppendChild(lastNode)
|
||||
}
|
||||
|
|
@ -1160,7 +1300,7 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom) {
|
|||
}
|
||||
|
||||
// inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM.
|
||||
// "Any other end tag" handling from 12.2.5.5 The rules for parsing tokens in foreign content
|
||||
// "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content
|
||||
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign
|
||||
func (p *parser) inBodyEndTagOther(tagAtom a.Atom) {
|
||||
for i := len(p.oe) - 1; i >= 0; i-- {
|
||||
|
|
@ -1174,7 +1314,7 @@ func (p *parser) inBodyEndTagOther(tagAtom a.Atom) {
|
|||
}
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.8.
|
||||
// Section 12.2.6.4.8.
|
||||
func textIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
|
|
@ -1203,7 +1343,7 @@ func textIM(p *parser) bool {
|
|||
return p.tok.Type == EndTagToken
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.9.
|
||||
// Section 12.2.6.4.9.
|
||||
func inTableIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
|
|
@ -1249,7 +1389,7 @@ func inTableIM(p *parser) bool {
|
|||
}
|
||||
// Ignore the token.
|
||||
return true
|
||||
case a.Style, a.Script:
|
||||
case a.Style, a.Script, a.Template:
|
||||
return inHeadIM(p)
|
||||
case a.Input:
|
||||
for _, t := range p.tok.Attr {
|
||||
|
|
@ -1261,7 +1401,7 @@ func inTableIM(p *parser) bool {
|
|||
}
|
||||
// Otherwise drop down to the default action.
|
||||
case a.Form:
|
||||
if p.form != nil {
|
||||
if p.oe.contains(a.Template) || p.form != nil {
|
||||
// Ignore the token.
|
||||
return true
|
||||
}
|
||||
|
|
@ -1291,6 +1431,8 @@ func inTableIM(p *parser) bool {
|
|||
case a.Body, a.Caption, a.Col, a.Colgroup, a.Html, a.Tbody, a.Td, a.Tfoot, a.Th, a.Thead, a.Tr:
|
||||
// Ignore the token.
|
||||
return true
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
}
|
||||
case CommentToken:
|
||||
p.addChild(&Node{
|
||||
|
|
@ -1309,7 +1451,7 @@ func inTableIM(p *parser) bool {
|
|||
return inBodyIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.11.
|
||||
// Section 12.2.6.4.11.
|
||||
func inCaptionIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken:
|
||||
|
|
@ -1355,7 +1497,7 @@ func inCaptionIM(p *parser) bool {
|
|||
return inBodyIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.12.
|
||||
// Section 12.2.6.4.12.
|
||||
func inColumnGroupIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
|
@ -1386,11 +1528,13 @@ func inColumnGroupIM(p *parser) bool {
|
|||
p.oe.pop()
|
||||
p.acknowledgeSelfClosingTag()
|
||||
return true
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
}
|
||||
case EndTagToken:
|
||||
switch p.tok.DataAtom {
|
||||
case a.Colgroup:
|
||||
if p.oe.top().DataAtom != a.Html {
|
||||
if p.oe.top().DataAtom == a.Colgroup {
|
||||
p.oe.pop()
|
||||
p.im = inTableIM
|
||||
}
|
||||
|
|
@ -1398,17 +1542,19 @@ func inColumnGroupIM(p *parser) bool {
|
|||
case a.Col:
|
||||
// Ignore the token.
|
||||
return true
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
}
|
||||
}
|
||||
if p.oe.top().DataAtom != a.Html {
|
||||
p.oe.pop()
|
||||
p.im = inTableIM
|
||||
return false
|
||||
if p.oe.top().DataAtom != a.Colgroup {
|
||||
return true
|
||||
}
|
||||
return true
|
||||
p.oe.pop()
|
||||
p.im = inTableIM
|
||||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.13.
|
||||
// Section 12.2.6.4.13.
|
||||
func inTableBodyIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken:
|
||||
|
|
@ -1460,7 +1606,7 @@ func inTableBodyIM(p *parser) bool {
|
|||
return inTableIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.14.
|
||||
// Section 12.2.6.4.14.
|
||||
func inRowIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken:
|
||||
|
|
@ -1511,7 +1657,7 @@ func inRowIM(p *parser) bool {
|
|||
return inTableIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.15.
|
||||
// Section 12.2.6.4.15.
|
||||
func inCellIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken:
|
||||
|
|
@ -1560,7 +1706,7 @@ func inCellIM(p *parser) bool {
|
|||
return inBodyIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.16.
|
||||
// Section 12.2.6.4.16.
|
||||
func inSelectIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
|
|
@ -1597,7 +1743,7 @@ func inSelectIM(p *parser) bool {
|
|||
p.tokenizer.NextIsNotRawText()
|
||||
// Ignore the token.
|
||||
return true
|
||||
case a.Script:
|
||||
case a.Script, a.Template:
|
||||
return inHeadIM(p)
|
||||
}
|
||||
case EndTagToken:
|
||||
|
|
@ -1618,6 +1764,8 @@ func inSelectIM(p *parser) bool {
|
|||
if p.popUntil(selectScope, a.Select) {
|
||||
p.resetInsertionMode()
|
||||
}
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
}
|
||||
case CommentToken:
|
||||
p.addChild(&Node{
|
||||
|
|
@ -1632,7 +1780,7 @@ func inSelectIM(p *parser) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.17.
|
||||
// Section 12.2.6.4.17.
|
||||
func inSelectInTableIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case StartTagToken, EndTagToken:
|
||||
|
|
@ -1650,7 +1798,62 @@ func inSelectInTableIM(p *parser) bool {
|
|||
return inSelectIM(p)
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.18.
|
||||
// Section 12.2.6.4.18.
|
||||
func inTemplateIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken, CommentToken, DoctypeToken:
|
||||
return inBodyIM(p)
|
||||
case StartTagToken:
|
||||
switch p.tok.DataAtom {
|
||||
case a.Base, a.Basefont, a.Bgsound, a.Link, a.Meta, a.Noframes, a.Script, a.Style, a.Template, a.Title:
|
||||
return inHeadIM(p)
|
||||
case a.Caption, a.Colgroup, a.Tbody, a.Tfoot, a.Thead:
|
||||
p.templateStack.pop()
|
||||
p.templateStack = append(p.templateStack, inTableIM)
|
||||
p.im = inTableIM
|
||||
return false
|
||||
case a.Col:
|
||||
p.templateStack.pop()
|
||||
p.templateStack = append(p.templateStack, inColumnGroupIM)
|
||||
p.im = inColumnGroupIM
|
||||
return false
|
||||
case a.Tr:
|
||||
p.templateStack.pop()
|
||||
p.templateStack = append(p.templateStack, inTableBodyIM)
|
||||
p.im = inTableBodyIM
|
||||
return false
|
||||
case a.Td, a.Th:
|
||||
p.templateStack.pop()
|
||||
p.templateStack = append(p.templateStack, inRowIM)
|
||||
p.im = inRowIM
|
||||
return false
|
||||
default:
|
||||
p.templateStack.pop()
|
||||
p.templateStack = append(p.templateStack, inBodyIM)
|
||||
p.im = inBodyIM
|
||||
return false
|
||||
}
|
||||
case EndTagToken:
|
||||
switch p.tok.DataAtom {
|
||||
case a.Template:
|
||||
return inHeadIM(p)
|
||||
default:
|
||||
// Ignore the token.
|
||||
return true
|
||||
}
|
||||
}
|
||||
if !p.oe.contains(a.Template) {
|
||||
// Ignore the token.
|
||||
return true
|
||||
}
|
||||
p.popUntil(defaultScope, a.Template)
|
||||
p.clearActiveFormattingElements()
|
||||
p.templateStack.pop()
|
||||
p.resetInsertionMode()
|
||||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.6.4.19.
|
||||
func afterBodyIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
|
|
@ -1688,7 +1891,7 @@ func afterBodyIM(p *parser) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.19.
|
||||
// Section 12.2.6.4.20.
|
||||
func inFramesetIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case CommentToken:
|
||||
|
|
@ -1720,6 +1923,11 @@ func inFramesetIM(p *parser) bool {
|
|||
p.acknowledgeSelfClosingTag()
|
||||
case a.Noframes:
|
||||
return inHeadIM(p)
|
||||
case a.Template:
|
||||
// TODO: remove this divergence from the HTML5 spec.
|
||||
//
|
||||
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
|
||||
return inTemplateIM(p)
|
||||
}
|
||||
case EndTagToken:
|
||||
switch p.tok.DataAtom {
|
||||
|
|
@ -1738,7 +1946,7 @@ func inFramesetIM(p *parser) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.20.
|
||||
// Section 12.2.6.4.21.
|
||||
func afterFramesetIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case CommentToken:
|
||||
|
|
@ -1777,7 +1985,7 @@ func afterFramesetIM(p *parser) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.21.
|
||||
// Section 12.2.6.4.22.
|
||||
func afterAfterBodyIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case ErrorToken:
|
||||
|
|
@ -1806,7 +2014,7 @@ func afterAfterBodyIM(p *parser) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.4.22.
|
||||
// Section 12.2.6.4.23.
|
||||
func afterAfterFramesetIM(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case CommentToken:
|
||||
|
|
@ -1844,7 +2052,7 @@ func afterAfterFramesetIM(p *parser) bool {
|
|||
|
||||
const whitespaceOrNUL = whitespace + "\x00"
|
||||
|
||||
// Section 12.2.5.5.
|
||||
// Section 12.2.6.5
|
||||
func parseForeignContent(p *parser) bool {
|
||||
switch p.tok.Type {
|
||||
case TextToken:
|
||||
|
|
@ -1924,7 +2132,7 @@ func parseForeignContent(p *parser) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Section 12.2.5.
|
||||
// Section 12.2.6.
|
||||
func (p *parser) inForeignContent() bool {
|
||||
if len(p.oe) == 0 {
|
||||
return false
|
||||
|
|
@ -2064,6 +2272,9 @@ func ParseFragment(r io.Reader, context *Node) ([]*Node, error) {
|
|||
}
|
||||
p.doc.AppendChild(root)
|
||||
p.oe = nodeStack{root}
|
||||
if context != nil && context.DataAtom == a.Template {
|
||||
p.templateStack = append(p.templateStack, inTemplateIM)
|
||||
}
|
||||
p.resetInsertionMode()
|
||||
|
||||
for n := context; n != nil; n = n.Parent {
|
||||
|
|
|
|||
388
vendor/golang.org/x/net/html/parse_test.go
generated
vendored
388
vendor/golang.org/x/net/html/parse_test.go
generated
vendored
|
|
@ -1,388 +0,0 @@
|
|||
// Copyright 2010 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 html
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/html/atom"
|
||||
)
|
||||
|
||||
// readParseTest reads a single test case from r.
|
||||
func readParseTest(r *bufio.Reader) (text, want, context string, err error) {
|
||||
line, err := r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
var b []byte
|
||||
|
||||
// Read the HTML.
|
||||
if string(line) != "#data\n" {
|
||||
return "", "", "", fmt.Errorf(`got %q want "#data\n"`, line)
|
||||
}
|
||||
for {
|
||||
line, err = r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
if line[0] == '#' {
|
||||
break
|
||||
}
|
||||
b = append(b, line...)
|
||||
}
|
||||
text = strings.TrimSuffix(string(b), "\n")
|
||||
b = b[:0]
|
||||
|
||||
// Skip the error list.
|
||||
if string(line) != "#errors\n" {
|
||||
return "", "", "", fmt.Errorf(`got %q want "#errors\n"`, line)
|
||||
}
|
||||
for {
|
||||
line, err = r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
if line[0] == '#' {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if string(line) == "#document-fragment\n" {
|
||||
line, err = r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
context = strings.TrimSpace(string(line))
|
||||
line, err = r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
}
|
||||
|
||||
// Read the dump of what the parse tree should be.
|
||||
if string(line) != "#document\n" {
|
||||
return "", "", "", fmt.Errorf(`got %q want "#document\n"`, line)
|
||||
}
|
||||
inQuote := false
|
||||
for {
|
||||
line, err = r.ReadSlice('\n')
|
||||
if err != nil && err != io.EOF {
|
||||
return "", "", "", err
|
||||
}
|
||||
trimmed := bytes.Trim(line, "| \n")
|
||||
if len(trimmed) > 0 {
|
||||
if line[0] == '|' && trimmed[0] == '"' {
|
||||
inQuote = true
|
||||
}
|
||||
if trimmed[len(trimmed)-1] == '"' && !(line[0] == '|' && len(trimmed) == 1) {
|
||||
inQuote = false
|
||||
}
|
||||
}
|
||||
if len(line) == 0 || len(line) == 1 && line[0] == '\n' && !inQuote {
|
||||
break
|
||||
}
|
||||
b = append(b, line...)
|
||||
}
|
||||
return text, string(b), context, nil
|
||||
}
|
||||
|
||||
func dumpIndent(w io.Writer, level int) {
|
||||
io.WriteString(w, "| ")
|
||||
for i := 0; i < level; i++ {
|
||||
io.WriteString(w, " ")
|
||||
}
|
||||
}
|
||||
|
||||
type sortedAttributes []Attribute
|
||||
|
||||
func (a sortedAttributes) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func (a sortedAttributes) Less(i, j int) bool {
|
||||
if a[i].Namespace != a[j].Namespace {
|
||||
return a[i].Namespace < a[j].Namespace
|
||||
}
|
||||
return a[i].Key < a[j].Key
|
||||
}
|
||||
|
||||
func (a sortedAttributes) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
|
||||
func dumpLevel(w io.Writer, n *Node, level int) error {
|
||||
dumpIndent(w, level)
|
||||
switch n.Type {
|
||||
case ErrorNode:
|
||||
return errors.New("unexpected ErrorNode")
|
||||
case DocumentNode:
|
||||
return errors.New("unexpected DocumentNode")
|
||||
case ElementNode:
|
||||
if n.Namespace != "" {
|
||||
fmt.Fprintf(w, "<%s %s>", n.Namespace, n.Data)
|
||||
} else {
|
||||
fmt.Fprintf(w, "<%s>", n.Data)
|
||||
}
|
||||
attr := sortedAttributes(n.Attr)
|
||||
sort.Sort(attr)
|
||||
for _, a := range attr {
|
||||
io.WriteString(w, "\n")
|
||||
dumpIndent(w, level+1)
|
||||
if a.Namespace != "" {
|
||||
fmt.Fprintf(w, `%s %s="%s"`, a.Namespace, a.Key, a.Val)
|
||||
} else {
|
||||
fmt.Fprintf(w, `%s="%s"`, a.Key, a.Val)
|
||||
}
|
||||
}
|
||||
case TextNode:
|
||||
fmt.Fprintf(w, `"%s"`, n.Data)
|
||||
case CommentNode:
|
||||
fmt.Fprintf(w, "<!-- %s -->", n.Data)
|
||||
case DoctypeNode:
|
||||
fmt.Fprintf(w, "<!DOCTYPE %s", n.Data)
|
||||
if n.Attr != nil {
|
||||
var p, s string
|
||||
for _, a := range n.Attr {
|
||||
switch a.Key {
|
||||
case "public":
|
||||
p = a.Val
|
||||
case "system":
|
||||
s = a.Val
|
||||
}
|
||||
}
|
||||
if p != "" || s != "" {
|
||||
fmt.Fprintf(w, ` "%s"`, p)
|
||||
fmt.Fprintf(w, ` "%s"`, s)
|
||||
}
|
||||
}
|
||||
io.WriteString(w, ">")
|
||||
case scopeMarkerNode:
|
||||
return errors.New("unexpected scopeMarkerNode")
|
||||
default:
|
||||
return errors.New("unknown node type")
|
||||
}
|
||||
io.WriteString(w, "\n")
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
if err := dumpLevel(w, c, level+1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dump(n *Node) (string, error) {
|
||||
if n == nil || n.FirstChild == nil {
|
||||
return "", nil
|
||||
}
|
||||
var b bytes.Buffer
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
if err := dumpLevel(&b, c, 0); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
const testDataDir = "testdata/webkit/"
|
||||
|
||||
func TestParser(t *testing.T) {
|
||||
testFiles, err := filepath.Glob(testDataDir + "*.dat")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, tf := range testFiles {
|
||||
f, err := os.Open(tf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
r := bufio.NewReader(f)
|
||||
|
||||
for i := 0; ; i++ {
|
||||
text, want, context, err := readParseTest(r)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = testParseCase(text, want, context)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("%s test #%d %q, %s", tf, i, text, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testParseCase tests one test case from the test files. If the test does not
|
||||
// pass, it returns an error that explains the failure.
|
||||
// text is the HTML to be parsed, want is a dump of the correct parse tree,
|
||||
// and context is the name of the context node, if any.
|
||||
func testParseCase(text, want, context string) (err error) {
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
switch e := x.(type) {
|
||||
case error:
|
||||
err = e
|
||||
default:
|
||||
err = fmt.Errorf("%v", e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var doc *Node
|
||||
if context == "" {
|
||||
doc, err = Parse(strings.NewReader(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
contextNode := &Node{
|
||||
Type: ElementNode,
|
||||
DataAtom: atom.Lookup([]byte(context)),
|
||||
Data: context,
|
||||
}
|
||||
nodes, err := ParseFragment(strings.NewReader(text), contextNode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
doc = &Node{
|
||||
Type: DocumentNode,
|
||||
}
|
||||
for _, n := range nodes {
|
||||
doc.AppendChild(n)
|
||||
}
|
||||
}
|
||||
|
||||
if err := checkTreeConsistency(doc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
got, err := dump(doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Compare the parsed tree to the #document section.
|
||||
if got != want {
|
||||
return fmt.Errorf("got vs want:\n----\n%s----\n%s----", got, want)
|
||||
}
|
||||
|
||||
if renderTestBlacklist[text] || context != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check that rendering and re-parsing results in an identical tree.
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
pw.CloseWithError(Render(pw, doc))
|
||||
}()
|
||||
doc1, err := Parse(pr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
got1, err := dump(doc1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if got != got1 {
|
||||
return fmt.Errorf("got vs got1:\n----\n%s----\n%s----", got, got1)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Some test input result in parse trees are not 'well-formed' despite
|
||||
// following the HTML5 recovery algorithms. Rendering and re-parsing such a
|
||||
// tree will not result in an exact clone of that tree. We blacklist such
|
||||
// inputs from the render test.
|
||||
var renderTestBlacklist = map[string]bool{
|
||||
// The second <a> will be reparented to the first <table>'s parent. This
|
||||
// results in an <a> whose parent is an <a>, which is not 'well-formed'.
|
||||
`<a><table><td><a><table></table><a></tr><a></table><b>X</b>C<a>Y`: true,
|
||||
// The same thing with a <p>:
|
||||
`<p><table></p>`: true,
|
||||
// More cases of <a> being reparented:
|
||||
`<a href="blah">aba<table><a href="foo">br<tr><td></td></tr>x</table>aoe`: true,
|
||||
`<a><table><a></table><p><a><div><a>`: true,
|
||||
`<a><table><td><a><table></table><a></tr><a></table><a>`: true,
|
||||
// A similar reparenting situation involving <nobr>:
|
||||
`<!DOCTYPE html><body><b><nobr>1<table><nobr></b><i><nobr>2<nobr></i>3`: true,
|
||||
// A <plaintext> element is reparented, putting it before a table.
|
||||
// A <plaintext> element can't have anything after it in HTML.
|
||||
`<table><plaintext><td>`: true,
|
||||
`<!doctype html><table><plaintext></plaintext>`: true,
|
||||
`<!doctype html><table><tbody><plaintext></plaintext>`: true,
|
||||
`<!doctype html><table><tbody><tr><plaintext></plaintext>`: true,
|
||||
// A form inside a table inside a form doesn't work either.
|
||||
`<!doctype html><form><table></form><form></table></form>`: true,
|
||||
// A script that ends at EOF may escape its own closing tag when rendered.
|
||||
`<!doctype html><script><!--<script `: true,
|
||||
`<!doctype html><script><!--<script <`: true,
|
||||
`<!doctype html><script><!--<script <a`: true,
|
||||
`<!doctype html><script><!--<script </`: true,
|
||||
`<!doctype html><script><!--<script </s`: true,
|
||||
`<!doctype html><script><!--<script </script`: true,
|
||||
`<!doctype html><script><!--<script </scripta`: true,
|
||||
`<!doctype html><script><!--<script -`: true,
|
||||
`<!doctype html><script><!--<script -a`: true,
|
||||
`<!doctype html><script><!--<script -<`: true,
|
||||
`<!doctype html><script><!--<script --`: true,
|
||||
`<!doctype html><script><!--<script --a`: true,
|
||||
`<!doctype html><script><!--<script --<`: true,
|
||||
`<script><!--<script `: true,
|
||||
`<script><!--<script <a`: true,
|
||||
`<script><!--<script </script`: true,
|
||||
`<script><!--<script </scripta`: true,
|
||||
`<script><!--<script -`: true,
|
||||
`<script><!--<script -a`: true,
|
||||
`<script><!--<script --`: true,
|
||||
`<script><!--<script --a`: true,
|
||||
`<script><!--<script <`: true,
|
||||
`<script><!--<script </`: true,
|
||||
`<script><!--<script </s`: true,
|
||||
// Reconstructing the active formatting elements results in a <plaintext>
|
||||
// element that contains an <a> element.
|
||||
`<!doctype html><p><a><plaintext>b`: true,
|
||||
}
|
||||
|
||||
func TestNodeConsistency(t *testing.T) {
|
||||
// inconsistentNode is a Node whose DataAtom and Data do not agree.
|
||||
inconsistentNode := &Node{
|
||||
Type: ElementNode,
|
||||
DataAtom: atom.Frameset,
|
||||
Data: "table",
|
||||
}
|
||||
_, err := ParseFragment(strings.NewReader("<p>hello</p>"), inconsistentNode)
|
||||
if err == nil {
|
||||
t.Errorf("got nil error, want non-nil")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParser(b *testing.B) {
|
||||
buf, err := ioutil.ReadFile("testdata/go1.html")
|
||||
if err != nil {
|
||||
b.Fatalf("could not read testdata/go1.html: %v", err)
|
||||
}
|
||||
b.SetBytes(int64(len(buf)))
|
||||
runtime.GC()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Parse(bytes.NewBuffer(buf))
|
||||
}
|
||||
}
|
||||
156
vendor/golang.org/x/net/html/render_test.go
generated
vendored
156
vendor/golang.org/x/net/html/render_test.go
generated
vendored
|
|
@ -1,156 +0,0 @@
|
|||
// Copyright 2010 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 html
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRenderer(t *testing.T) {
|
||||
nodes := [...]*Node{
|
||||
0: {
|
||||
Type: ElementNode,
|
||||
Data: "html",
|
||||
},
|
||||
1: {
|
||||
Type: ElementNode,
|
||||
Data: "head",
|
||||
},
|
||||
2: {
|
||||
Type: ElementNode,
|
||||
Data: "body",
|
||||
},
|
||||
3: {
|
||||
Type: TextNode,
|
||||
Data: "0<1",
|
||||
},
|
||||
4: {
|
||||
Type: ElementNode,
|
||||
Data: "p",
|
||||
Attr: []Attribute{
|
||||
{
|
||||
Key: "id",
|
||||
Val: "A",
|
||||
},
|
||||
{
|
||||
Key: "foo",
|
||||
Val: `abc"def`,
|
||||
},
|
||||
},
|
||||
},
|
||||
5: {
|
||||
Type: TextNode,
|
||||
Data: "2",
|
||||
},
|
||||
6: {
|
||||
Type: ElementNode,
|
||||
Data: "b",
|
||||
Attr: []Attribute{
|
||||
{
|
||||
Key: "empty",
|
||||
Val: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
7: {
|
||||
Type: TextNode,
|
||||
Data: "3",
|
||||
},
|
||||
8: {
|
||||
Type: ElementNode,
|
||||
Data: "i",
|
||||
Attr: []Attribute{
|
||||
{
|
||||
Key: "backslash",
|
||||
Val: `\`,
|
||||
},
|
||||
},
|
||||
},
|
||||
9: {
|
||||
Type: TextNode,
|
||||
Data: "&4",
|
||||
},
|
||||
10: {
|
||||
Type: TextNode,
|
||||
Data: "5",
|
||||
},
|
||||
11: {
|
||||
Type: ElementNode,
|
||||
Data: "blockquote",
|
||||
},
|
||||
12: {
|
||||
Type: ElementNode,
|
||||
Data: "br",
|
||||
},
|
||||
13: {
|
||||
Type: TextNode,
|
||||
Data: "6",
|
||||
},
|
||||
}
|
||||
|
||||
// Build a tree out of those nodes, based on a textual representation.
|
||||
// Only the ".\t"s are significant. The trailing HTML-like text is
|
||||
// just commentary. The "0:" prefixes are for easy cross-reference with
|
||||
// the nodes array.
|
||||
treeAsText := [...]string{
|
||||
0: `<html>`,
|
||||
1: `. <head>`,
|
||||
2: `. <body>`,
|
||||
3: `. . "0<1"`,
|
||||
4: `. . <p id="A" foo="abc"def">`,
|
||||
5: `. . . "2"`,
|
||||
6: `. . . <b empty="">`,
|
||||
7: `. . . . "3"`,
|
||||
8: `. . . <i backslash="\">`,
|
||||
9: `. . . . "&4"`,
|
||||
10: `. . "5"`,
|
||||
11: `. . <blockquote>`,
|
||||
12: `. . <br>`,
|
||||
13: `. . "6"`,
|
||||
}
|
||||
if len(nodes) != len(treeAsText) {
|
||||
t.Fatal("len(nodes) != len(treeAsText)")
|
||||
}
|
||||
var stack [8]*Node
|
||||
for i, line := range treeAsText {
|
||||
level := 0
|
||||
for line[0] == '.' {
|
||||
// Strip a leading ".\t".
|
||||
line = line[2:]
|
||||
level++
|
||||
}
|
||||
n := nodes[i]
|
||||
if level == 0 {
|
||||
if stack[0] != nil {
|
||||
t.Fatal("multiple root nodes")
|
||||
}
|
||||
stack[0] = n
|
||||
} else {
|
||||
stack[level-1].AppendChild(n)
|
||||
stack[level] = n
|
||||
for i := level + 1; i < len(stack); i++ {
|
||||
stack[i] = nil
|
||||
}
|
||||
}
|
||||
// At each stage of tree construction, we check all nodes for consistency.
|
||||
for j, m := range nodes {
|
||||
if err := checkNodeConsistency(m); err != nil {
|
||||
t.Fatalf("i=%d, j=%d: %v", i, j, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
want := `<html><head></head><body>0<1<p id="A" foo="abc"def">` +
|
||||
`2<b empty="">3</b><i backslash="\">&4</i></p>` +
|
||||
`5<blockquote></blockquote><br/>6</body></html>`
|
||||
b := new(bytes.Buffer)
|
||||
if err := Render(b, nodes[0]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := b.String(); got != want {
|
||||
t.Errorf("got vs want:\n%s\n%s\n", got, want)
|
||||
}
|
||||
}
|
||||
4
vendor/golang.org/x/net/html/token.go
generated
vendored
4
vendor/golang.org/x/net/html/token.go
generated
vendored
|
|
@ -1161,8 +1161,8 @@ func (z *Tokenizer) TagAttr() (key, val []byte, moreAttr bool) {
|
|||
return nil, nil, false
|
||||
}
|
||||
|
||||
// Token returns the next Token. The result's Data and Attr values remain valid
|
||||
// after subsequent Next calls.
|
||||
// Token returns the current Token. The result's Data and Attr values remain
|
||||
// valid after subsequent Next calls.
|
||||
func (z *Tokenizer) Token() Token {
|
||||
t := Token{Type: z.tt}
|
||||
switch z.tt {
|
||||
|
|
|
|||
748
vendor/golang.org/x/net/html/token_test.go
generated
vendored
748
vendor/golang.org/x/net/html/token_test.go
generated
vendored
|
|
@ -1,748 +0,0 @@
|
|||
// Copyright 2010 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 html
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type tokenTest struct {
|
||||
// A short description of the test case.
|
||||
desc string
|
||||
// The HTML to parse.
|
||||
html string
|
||||
// The string representations of the expected tokens, joined by '$'.
|
||||
golden string
|
||||
}
|
||||
|
||||
var tokenTests = []tokenTest{
|
||||
{
|
||||
"empty",
|
||||
"",
|
||||
"",
|
||||
},
|
||||
// A single text node. The tokenizer should not break text nodes on whitespace,
|
||||
// nor should it normalize whitespace within a text node.
|
||||
{
|
||||
"text",
|
||||
"foo bar",
|
||||
"foo bar",
|
||||
},
|
||||
// An entity.
|
||||
{
|
||||
"entity",
|
||||
"one < two",
|
||||
"one < two",
|
||||
},
|
||||
// A start, self-closing and end tag. The tokenizer does not care if the start
|
||||
// and end tokens don't match; that is the job of the parser.
|
||||
{
|
||||
"tags",
|
||||
"<a>b<c/>d</e>",
|
||||
"<a>$b$<c/>$d$</e>",
|
||||
},
|
||||
// Angle brackets that aren't a tag.
|
||||
{
|
||||
"not a tag #0",
|
||||
"<",
|
||||
"<",
|
||||
},
|
||||
{
|
||||
"not a tag #1",
|
||||
"</",
|
||||
"</",
|
||||
},
|
||||
{
|
||||
"not a tag #2",
|
||||
"</>",
|
||||
"<!---->",
|
||||
},
|
||||
{
|
||||
"not a tag #3",
|
||||
"a</>b",
|
||||
"a$<!---->$b",
|
||||
},
|
||||
{
|
||||
"not a tag #4",
|
||||
"</ >",
|
||||
"<!-- -->",
|
||||
},
|
||||
{
|
||||
"not a tag #5",
|
||||
"</.",
|
||||
"<!--.-->",
|
||||
},
|
||||
{
|
||||
"not a tag #6",
|
||||
"</.>",
|
||||
"<!--.-->",
|
||||
},
|
||||
{
|
||||
"not a tag #7",
|
||||
"a < b",
|
||||
"a < b",
|
||||
},
|
||||
{
|
||||
"not a tag #8",
|
||||
"<.>",
|
||||
"<.>",
|
||||
},
|
||||
{
|
||||
"not a tag #9",
|
||||
"a<<<b>>>c",
|
||||
"a<<$<b>$>>c",
|
||||
},
|
||||
{
|
||||
"not a tag #10",
|
||||
"if x<0 and y < 0 then x*y>0",
|
||||
"if x<0 and y < 0 then x*y>0",
|
||||
},
|
||||
{
|
||||
"not a tag #11",
|
||||
"<<p>",
|
||||
"<$<p>",
|
||||
},
|
||||
// EOF in a tag name.
|
||||
{
|
||||
"tag name eof #0",
|
||||
"<a",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"tag name eof #1",
|
||||
"<a ",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"tag name eof #2",
|
||||
"a<b",
|
||||
"a",
|
||||
},
|
||||
{
|
||||
"tag name eof #3",
|
||||
"<a><b",
|
||||
"<a>",
|
||||
},
|
||||
{
|
||||
"tag name eof #4",
|
||||
`<a x`,
|
||||
``,
|
||||
},
|
||||
// Some malformed tags that are missing a '>'.
|
||||
{
|
||||
"malformed tag #0",
|
||||
`<p</p>`,
|
||||
`<p< p="">`,
|
||||
},
|
||||
{
|
||||
"malformed tag #1",
|
||||
`<p </p>`,
|
||||
`<p <="" p="">`,
|
||||
},
|
||||
{
|
||||
"malformed tag #2",
|
||||
`<p id`,
|
||||
``,
|
||||
},
|
||||
{
|
||||
"malformed tag #3",
|
||||
`<p id=`,
|
||||
``,
|
||||
},
|
||||
{
|
||||
"malformed tag #4",
|
||||
`<p id=>`,
|
||||
`<p id="">`,
|
||||
},
|
||||
{
|
||||
"malformed tag #5",
|
||||
`<p id=0`,
|
||||
``,
|
||||
},
|
||||
{
|
||||
"malformed tag #6",
|
||||
`<p id=0</p>`,
|
||||
`<p id="0</p">`,
|
||||
},
|
||||
{
|
||||
"malformed tag #7",
|
||||
`<p id="0</p>`,
|
||||
``,
|
||||
},
|
||||
{
|
||||
"malformed tag #8",
|
||||
`<p id="0"</p>`,
|
||||
`<p id="0" <="" p="">`,
|
||||
},
|
||||
{
|
||||
"malformed tag #9",
|
||||
`<p></p id`,
|
||||
`<p>`,
|
||||
},
|
||||
// Raw text and RCDATA.
|
||||
{
|
||||
"basic raw text",
|
||||
"<script><a></b></script>",
|
||||
"<script>$<a></b>$</script>",
|
||||
},
|
||||
{
|
||||
"unfinished script end tag",
|
||||
"<SCRIPT>a</SCR",
|
||||
"<script>$a</SCR",
|
||||
},
|
||||
{
|
||||
"broken script end tag",
|
||||
"<SCRIPT>a</SCR ipt>",
|
||||
"<script>$a</SCR ipt>",
|
||||
},
|
||||
{
|
||||
"EOF in script end tag",
|
||||
"<SCRIPT>a</SCRipt",
|
||||
"<script>$a</SCRipt",
|
||||
},
|
||||
{
|
||||
"scriptx end tag",
|
||||
"<SCRIPT>a</SCRiptx",
|
||||
"<script>$a</SCRiptx",
|
||||
},
|
||||
{
|
||||
"' ' completes script end tag",
|
||||
"<SCRIPT>a</SCRipt ",
|
||||
"<script>$a",
|
||||
},
|
||||
{
|
||||
"'>' completes script end tag",
|
||||
"<SCRIPT>a</SCRipt>",
|
||||
"<script>$a$</script>",
|
||||
},
|
||||
{
|
||||
"self-closing script end tag",
|
||||
"<SCRIPT>a</SCRipt/>",
|
||||
"<script>$a$</script>",
|
||||
},
|
||||
{
|
||||
"nested script tag",
|
||||
"<SCRIPT>a</SCRipt<script>",
|
||||
"<script>$a</SCRipt<script>",
|
||||
},
|
||||
{
|
||||
"script end tag after unfinished",
|
||||
"<SCRIPT>a</SCRipt</script>",
|
||||
"<script>$a</SCRipt$</script>",
|
||||
},
|
||||
{
|
||||
"script/style mismatched tags",
|
||||
"<script>a</style>",
|
||||
"<script>$a</style>",
|
||||
},
|
||||
{
|
||||
"style element with entity",
|
||||
"<style>'",
|
||||
"<style>$&apos;",
|
||||
},
|
||||
{
|
||||
"textarea with tag",
|
||||
"<textarea><div></textarea>",
|
||||
"<textarea>$<div>$</textarea>",
|
||||
},
|
||||
{
|
||||
"title with tag and entity",
|
||||
"<title><b>K&R C</b></title>",
|
||||
"<title>$<b>K&R C</b>$</title>",
|
||||
},
|
||||
// DOCTYPE tests.
|
||||
{
|
||||
"Proper DOCTYPE",
|
||||
"<!DOCTYPE html>",
|
||||
"<!DOCTYPE html>",
|
||||
},
|
||||
{
|
||||
"DOCTYPE with no space",
|
||||
"<!doctypehtml>",
|
||||
"<!DOCTYPE html>",
|
||||
},
|
||||
{
|
||||
"DOCTYPE with two spaces",
|
||||
"<!doctype html>",
|
||||
"<!DOCTYPE html>",
|
||||
},
|
||||
{
|
||||
"looks like DOCTYPE but isn't",
|
||||
"<!DOCUMENT html>",
|
||||
"<!--DOCUMENT html-->",
|
||||
},
|
||||
{
|
||||
"DOCTYPE at EOF",
|
||||
"<!DOCtype",
|
||||
"<!DOCTYPE >",
|
||||
},
|
||||
// XML processing instructions.
|
||||
{
|
||||
"XML processing instruction",
|
||||
"<?xml?>",
|
||||
"<!--?xml?-->",
|
||||
},
|
||||
// Comments.
|
||||
{
|
||||
"comment0",
|
||||
"abc<b><!-- skipme --></b>def",
|
||||
"abc$<b>$<!-- skipme -->$</b>$def",
|
||||
},
|
||||
{
|
||||
"comment1",
|
||||
"a<!-->z",
|
||||
"a$<!---->$z",
|
||||
},
|
||||
{
|
||||
"comment2",
|
||||
"a<!--->z",
|
||||
"a$<!---->$z",
|
||||
},
|
||||
{
|
||||
"comment3",
|
||||
"a<!--x>-->z",
|
||||
"a$<!--x>-->$z",
|
||||
},
|
||||
{
|
||||
"comment4",
|
||||
"a<!--x->-->z",
|
||||
"a$<!--x->-->$z",
|
||||
},
|
||||
{
|
||||
"comment5",
|
||||
"a<!>z",
|
||||
"a$<!---->$z",
|
||||
},
|
||||
{
|
||||
"comment6",
|
||||
"a<!->z",
|
||||
"a$<!----->$z",
|
||||
},
|
||||
{
|
||||
"comment7",
|
||||
"a<!---<>z",
|
||||
"a$<!---<>z-->",
|
||||
},
|
||||
{
|
||||
"comment8",
|
||||
"a<!--z",
|
||||
"a$<!--z-->",
|
||||
},
|
||||
{
|
||||
"comment9",
|
||||
"a<!--z-",
|
||||
"a$<!--z-->",
|
||||
},
|
||||
{
|
||||
"comment10",
|
||||
"a<!--z--",
|
||||
"a$<!--z-->",
|
||||
},
|
||||
{
|
||||
"comment11",
|
||||
"a<!--z---",
|
||||
"a$<!--z--->",
|
||||
},
|
||||
{
|
||||
"comment12",
|
||||
"a<!--z----",
|
||||
"a$<!--z---->",
|
||||
},
|
||||
{
|
||||
"comment13",
|
||||
"a<!--x--!>z",
|
||||
"a$<!--x-->$z",
|
||||
},
|
||||
// An attribute with a backslash.
|
||||
{
|
||||
"backslash",
|
||||
`<p id="a\"b">`,
|
||||
`<p id="a\" b"="">`,
|
||||
},
|
||||
// Entities, tag name and attribute key lower-casing, and whitespace
|
||||
// normalization within a tag.
|
||||
{
|
||||
"tricky",
|
||||
"<p \t\n iD=\"a"B\" foo=\"bar\"><EM>te<&;xt</em></p>",
|
||||
`<p id="a"B" foo="bar">$<em>$te<&;xt$</em>$</p>`,
|
||||
},
|
||||
// A nonexistent entity. Tokenizing and converting back to a string should
|
||||
// escape the "&" to become "&".
|
||||
{
|
||||
"noSuchEntity",
|
||||
`<a b="c&noSuchEntity;d"><&alsoDoesntExist;&`,
|
||||
`<a b="c&noSuchEntity;d">$<&alsoDoesntExist;&`,
|
||||
},
|
||||
{
|
||||
"entity without semicolon",
|
||||
`¬it;∉<a b="q=z&=5¬ice=hello¬=world">`,
|
||||
`¬it;∉$<a b="q=z&amp=5&notice=hello¬=world">`,
|
||||
},
|
||||
{
|
||||
"entity with digits",
|
||||
"½",
|
||||
"½",
|
||||
},
|
||||
// Attribute tests:
|
||||
// http://dev.w3.org/html5/pf-summary/Overview.html#attributes
|
||||
{
|
||||
"Empty attribute",
|
||||
`<input disabled FOO>`,
|
||||
`<input disabled="" foo="">`,
|
||||
},
|
||||
{
|
||||
"Empty attribute, whitespace",
|
||||
`<input disabled FOO >`,
|
||||
`<input disabled="" foo="">`,
|
||||
},
|
||||
{
|
||||
"Unquoted attribute value",
|
||||
`<input value=yes FOO=BAR>`,
|
||||
`<input value="yes" foo="BAR">`,
|
||||
},
|
||||
{
|
||||
"Unquoted attribute value, spaces",
|
||||
`<input value = yes FOO = BAR>`,
|
||||
`<input value="yes" foo="BAR">`,
|
||||
},
|
||||
{
|
||||
"Unquoted attribute value, trailing space",
|
||||
`<input value=yes FOO=BAR >`,
|
||||
`<input value="yes" foo="BAR">`,
|
||||
},
|
||||
{
|
||||
"Single-quoted attribute value",
|
||||
`<input value='yes' FOO='BAR'>`,
|
||||
`<input value="yes" foo="BAR">`,
|
||||
},
|
||||
{
|
||||
"Single-quoted attribute value, trailing space",
|
||||
`<input value='yes' FOO='BAR' >`,
|
||||
`<input value="yes" foo="BAR">`,
|
||||
},
|
||||
{
|
||||
"Double-quoted attribute value",
|
||||
`<input value="I'm an attribute" FOO="BAR">`,
|
||||
`<input value="I'm an attribute" foo="BAR">`,
|
||||
},
|
||||
{
|
||||
"Attribute name characters",
|
||||
`<meta http-equiv="content-type">`,
|
||||
`<meta http-equiv="content-type">`,
|
||||
},
|
||||
{
|
||||
"Mixed attributes",
|
||||
`a<P V="0 1" w='2' X=3 y>z`,
|
||||
`a$<p v="0 1" w="2" x="3" y="">$z`,
|
||||
},
|
||||
{
|
||||
"Attributes with a solitary single quote",
|
||||
`<p id=can't><p id=won't>`,
|
||||
`<p id="can't">$<p id="won't">`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestTokenizer(t *testing.T) {
|
||||
loop:
|
||||
for _, tt := range tokenTests {
|
||||
z := NewTokenizer(strings.NewReader(tt.html))
|
||||
if tt.golden != "" {
|
||||
for i, s := range strings.Split(tt.golden, "$") {
|
||||
if z.Next() == ErrorToken {
|
||||
t.Errorf("%s token %d: want %q got error %v", tt.desc, i, s, z.Err())
|
||||
continue loop
|
||||
}
|
||||
actual := z.Token().String()
|
||||
if s != actual {
|
||||
t.Errorf("%s token %d: want %q got %q", tt.desc, i, s, actual)
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
}
|
||||
z.Next()
|
||||
if z.Err() != io.EOF {
|
||||
t.Errorf("%s: want EOF got %q", tt.desc, z.Err())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxBuffer(t *testing.T) {
|
||||
// Exceeding the maximum buffer size generates ErrBufferExceeded.
|
||||
z := NewTokenizer(strings.NewReader("<" + strings.Repeat("t", 10)))
|
||||
z.SetMaxBuf(5)
|
||||
tt := z.Next()
|
||||
if got, want := tt, ErrorToken; got != want {
|
||||
t.Fatalf("token type: got: %v want: %v", got, want)
|
||||
}
|
||||
if got, want := z.Err(), ErrBufferExceeded; got != want {
|
||||
t.Errorf("error type: got: %v want: %v", got, want)
|
||||
}
|
||||
if got, want := string(z.Raw()), "<tttt"; got != want {
|
||||
t.Fatalf("buffered before overflow: got: %q want: %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxBufferReconstruction(t *testing.T) {
|
||||
// Exceeding the maximum buffer size at any point while tokenizing permits
|
||||
// reconstructing the original input.
|
||||
tests:
|
||||
for _, test := range tokenTests {
|
||||
for maxBuf := 1; ; maxBuf++ {
|
||||
r := strings.NewReader(test.html)
|
||||
z := NewTokenizer(r)
|
||||
z.SetMaxBuf(maxBuf)
|
||||
var tokenized bytes.Buffer
|
||||
for {
|
||||
tt := z.Next()
|
||||
tokenized.Write(z.Raw())
|
||||
if tt == ErrorToken {
|
||||
if err := z.Err(); err != io.EOF && err != ErrBufferExceeded {
|
||||
t.Errorf("%s: unexpected error: %v", test.desc, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
// Anything tokenized along with untokenized input or data left in the reader.
|
||||
assembled, err := ioutil.ReadAll(io.MultiReader(&tokenized, bytes.NewReader(z.Buffered()), r))
|
||||
if err != nil {
|
||||
t.Errorf("%s: ReadAll: %v", test.desc, err)
|
||||
continue tests
|
||||
}
|
||||
if got, want := string(assembled), test.html; got != want {
|
||||
t.Errorf("%s: reassembled html:\n got: %q\nwant: %q", test.desc, got, want)
|
||||
continue tests
|
||||
}
|
||||
// EOF indicates that we completed tokenization and hence found the max
|
||||
// maxBuf that generates ErrBufferExceeded, so continue to the next test.
|
||||
if z.Err() == io.EOF {
|
||||
break
|
||||
}
|
||||
} // buffer sizes
|
||||
} // tests
|
||||
}
|
||||
|
||||
func TestPassthrough(t *testing.T) {
|
||||
// Accumulating the raw output for each parse event should reconstruct the
|
||||
// original input.
|
||||
for _, test := range tokenTests {
|
||||
z := NewTokenizer(strings.NewReader(test.html))
|
||||
var parsed bytes.Buffer
|
||||
for {
|
||||
tt := z.Next()
|
||||
parsed.Write(z.Raw())
|
||||
if tt == ErrorToken {
|
||||
break
|
||||
}
|
||||
}
|
||||
if got, want := parsed.String(), test.html; got != want {
|
||||
t.Errorf("%s: parsed output:\n got: %q\nwant: %q", test.desc, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBufAPI(t *testing.T) {
|
||||
s := "0<a>1</a>2<b>3<a>4<a>5</a>6</b>7</a>8<a/>9"
|
||||
z := NewTokenizer(bytes.NewBufferString(s))
|
||||
var result bytes.Buffer
|
||||
depth := 0
|
||||
loop:
|
||||
for {
|
||||
tt := z.Next()
|
||||
switch tt {
|
||||
case ErrorToken:
|
||||
if z.Err() != io.EOF {
|
||||
t.Error(z.Err())
|
||||
}
|
||||
break loop
|
||||
case TextToken:
|
||||
if depth > 0 {
|
||||
result.Write(z.Text())
|
||||
}
|
||||
case StartTagToken, EndTagToken:
|
||||
tn, _ := z.TagName()
|
||||
if len(tn) == 1 && tn[0] == 'a' {
|
||||
if tt == StartTagToken {
|
||||
depth++
|
||||
} else {
|
||||
depth--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
u := "14567"
|
||||
v := string(result.Bytes())
|
||||
if u != v {
|
||||
t.Errorf("TestBufAPI: want %q got %q", u, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertNewlines(t *testing.T) {
|
||||
testCases := map[string]string{
|
||||
"Mac\rDOS\r\nUnix\n": "Mac\nDOS\nUnix\n",
|
||||
"Unix\nMac\rDOS\r\n": "Unix\nMac\nDOS\n",
|
||||
"DOS\r\nDOS\r\nDOS\r\n": "DOS\nDOS\nDOS\n",
|
||||
"": "",
|
||||
"\n": "\n",
|
||||
"\n\r": "\n\n",
|
||||
"\r": "\n",
|
||||
"\r\n": "\n",
|
||||
"\r\n\n": "\n\n",
|
||||
"\r\n\r": "\n\n",
|
||||
"\r\n\r\n": "\n\n",
|
||||
"\r\r": "\n\n",
|
||||
"\r\r\n": "\n\n",
|
||||
"\r\r\n\n": "\n\n\n",
|
||||
"\r\r\r\n": "\n\n\n",
|
||||
"\r \n": "\n \n",
|
||||
"xyz": "xyz",
|
||||
}
|
||||
for in, want := range testCases {
|
||||
if got := string(convertNewlines([]byte(in))); got != want {
|
||||
t.Errorf("input %q: got %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReaderEdgeCases(t *testing.T) {
|
||||
const s = "<p>An io.Reader can return (0, nil) or (n, io.EOF).</p>"
|
||||
testCases := []io.Reader{
|
||||
&zeroOneByteReader{s: s},
|
||||
&eofStringsReader{s: s},
|
||||
&stuckReader{},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
got := []TokenType{}
|
||||
z := NewTokenizer(tc)
|
||||
for {
|
||||
tt := z.Next()
|
||||
if tt == ErrorToken {
|
||||
break
|
||||
}
|
||||
got = append(got, tt)
|
||||
}
|
||||
if err := z.Err(); err != nil && err != io.EOF {
|
||||
if err != io.ErrNoProgress {
|
||||
t.Errorf("i=%d: %v", i, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
want := []TokenType{
|
||||
StartTagToken,
|
||||
TextToken,
|
||||
EndTagToken,
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("i=%d: got %v, want %v", i, got, want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// zeroOneByteReader is like a strings.Reader that alternates between
|
||||
// returning 0 bytes and 1 byte at a time.
|
||||
type zeroOneByteReader struct {
|
||||
s string
|
||||
n int
|
||||
}
|
||||
|
||||
func (r *zeroOneByteReader) Read(p []byte) (int, error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if len(r.s) == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
r.n++
|
||||
if r.n%2 != 0 {
|
||||
return 0, nil
|
||||
}
|
||||
p[0], r.s = r.s[0], r.s[1:]
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
// eofStringsReader is like a strings.Reader but can return an (n, err) where
|
||||
// n > 0 && err != nil.
|
||||
type eofStringsReader struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (r *eofStringsReader) Read(p []byte) (int, error) {
|
||||
n := copy(p, r.s)
|
||||
r.s = r.s[n:]
|
||||
if r.s != "" {
|
||||
return n, nil
|
||||
}
|
||||
return n, io.EOF
|
||||
}
|
||||
|
||||
// stuckReader is an io.Reader that always returns no data and no error.
|
||||
type stuckReader struct{}
|
||||
|
||||
func (*stuckReader) Read(p []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
const (
|
||||
rawLevel = iota
|
||||
lowLevel
|
||||
highLevel
|
||||
)
|
||||
|
||||
func benchmarkTokenizer(b *testing.B, level int) {
|
||||
buf, err := ioutil.ReadFile("testdata/go1.html")
|
||||
if err != nil {
|
||||
b.Fatalf("could not read testdata/go1.html: %v", err)
|
||||
}
|
||||
b.SetBytes(int64(len(buf)))
|
||||
runtime.GC()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
z := NewTokenizer(bytes.NewBuffer(buf))
|
||||
for {
|
||||
tt := z.Next()
|
||||
if tt == ErrorToken {
|
||||
if err := z.Err(); err != nil && err != io.EOF {
|
||||
b.Fatalf("tokenizer error: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
switch level {
|
||||
case rawLevel:
|
||||
// Calling z.Raw just returns the raw bytes of the token. It does
|
||||
// not unescape < to <, or lower-case tag names and attribute keys.
|
||||
z.Raw()
|
||||
case lowLevel:
|
||||
// Caling z.Text, z.TagName and z.TagAttr returns []byte values
|
||||
// whose contents may change on the next call to z.Next.
|
||||
switch tt {
|
||||
case TextToken, CommentToken, DoctypeToken:
|
||||
z.Text()
|
||||
case StartTagToken, SelfClosingTagToken:
|
||||
_, more := z.TagName()
|
||||
for more {
|
||||
_, _, more = z.TagAttr()
|
||||
}
|
||||
case EndTagToken:
|
||||
z.TagName()
|
||||
}
|
||||
case highLevel:
|
||||
// Calling z.Token converts []byte values to strings whose validity
|
||||
// extend beyond the next call to z.Next.
|
||||
z.Token()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRawLevelTokenizer(b *testing.B) { benchmarkTokenizer(b, rawLevel) }
|
||||
func BenchmarkLowLevelTokenizer(b *testing.B) { benchmarkTokenizer(b, lowLevel) }
|
||||
func BenchmarkHighLevelTokenizer(b *testing.B) { benchmarkTokenizer(b, highLevel) }
|
||||
65
vendor/golang.org/x/net/http/httpguts/guts.go
generated
vendored
Normal file
65
vendor/golang.org/x/net/http/httpguts/guts.go
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2018 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 httpguts provides functions implementing various details
|
||||
// of the HTTP specification.
|
||||
//
|
||||
// This package is shared by the standard library (which vendors it)
|
||||
// and x/net/http2. It comes with no API stability promise.
|
||||
package httpguts
|
||||
|
||||
import (
|
||||
"net/textproto"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SniffedContentType reports whether ct is a Content-Type that is known
|
||||
// to cause client-side content sniffing.
|
||||
//
|
||||
// This provides just a partial implementation of mime.ParseMediaType
|
||||
// with the assumption that the Content-Type is not attacker controlled.
|
||||
func SniffedContentType(ct string) bool {
|
||||
if i := strings.Index(ct, ";"); i != -1 {
|
||||
ct = ct[:i]
|
||||
}
|
||||
ct = strings.ToLower(strings.TrimSpace(ct))
|
||||
return ct == "text/plain" || ct == "application/octet-stream" ||
|
||||
ct == "application/unknown" || ct == "unknown/unknown" || ct == "*/*" ||
|
||||
!strings.Contains(ct, "/")
|
||||
}
|
||||
|
||||
// ValidTrailerHeader reports whether name is a valid header field name to appear
|
||||
// in trailers.
|
||||
// See RFC 7230, Section 4.1.2
|
||||
func ValidTrailerHeader(name string) bool {
|
||||
name = textproto.CanonicalMIMEHeaderKey(name)
|
||||
if strings.HasPrefix(name, "If-") || badTrailer[name] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var badTrailer = map[string]bool{
|
||||
"Authorization": true,
|
||||
"Cache-Control": true,
|
||||
"Connection": true,
|
||||
"Content-Encoding": true,
|
||||
"Content-Length": true,
|
||||
"Content-Range": true,
|
||||
"Content-Type": true,
|
||||
"Expect": true,
|
||||
"Host": true,
|
||||
"Keep-Alive": true,
|
||||
"Max-Forwards": true,
|
||||
"Pragma": true,
|
||||
"Proxy-Authenticate": true,
|
||||
"Proxy-Authorization": true,
|
||||
"Proxy-Connection": true,
|
||||
"Range": true,
|
||||
"Realm": true,
|
||||
"Te": true,
|
||||
"Trailer": true,
|
||||
"Transfer-Encoding": true,
|
||||
"Www-Authenticate": true,
|
||||
}
|
||||
|
|
@ -2,12 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package httplex contains rules around lexical matters of various
|
||||
// HTTP-related specifications.
|
||||
//
|
||||
// This package is shared by the standard library (which vendors it)
|
||||
// and x/net/http2. It comes with no API stability promise.
|
||||
package httplex
|
||||
package httpguts
|
||||
|
||||
import (
|
||||
"net"
|
||||
2
vendor/golang.org/x/net/http2/ciphers.go
generated
vendored
2
vendor/golang.org/x/net/http2/ciphers.go
generated
vendored
|
|
@ -5,7 +5,7 @@
|
|||
package http2
|
||||
|
||||
// A list of the possible cipher suite ids. Taken from
|
||||
// http://www.iana.org/assignments/tls-parameters/tls-parameters.txt
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.txt
|
||||
|
||||
const (
|
||||
cipher_TLS_NULL_WITH_NULL_NULL uint16 = 0x0000
|
||||
|
|
|
|||
309
vendor/golang.org/x/net/http2/ciphers_test.go
generated
vendored
309
vendor/golang.org/x/net/http2/ciphers_test.go
generated
vendored
|
|
@ -1,309 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestIsBadCipherBad(t *testing.T) {
|
||||
for _, c := range badCiphers {
|
||||
if !isBadCipher(c) {
|
||||
t.Errorf("Wrong result for isBadCipher(%d), want true", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// verify we don't give false positives on ciphers not on blacklist
|
||||
func TestIsBadCipherGood(t *testing.T) {
|
||||
goodCiphers := map[uint16]string{
|
||||
cipher_TLS_DHE_RSA_WITH_AES_256_CCM: "cipher_TLS_DHE_RSA_WITH_AES_256_CCM",
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM: "cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM",
|
||||
cipher_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256: "cipher_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
|
||||
}
|
||||
for c, name := range goodCiphers {
|
||||
if isBadCipher(c) {
|
||||
t.Errorf("Wrong result for isBadCipher(%d) %s, want false", c, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copied from https://http2.github.io/http2-spec/#BadCipherSuites,
|
||||
var badCiphers = []uint16{
|
||||
cipher_TLS_NULL_WITH_NULL_NULL,
|
||||
cipher_TLS_RSA_WITH_NULL_MD5,
|
||||
cipher_TLS_RSA_WITH_NULL_SHA,
|
||||
cipher_TLS_RSA_EXPORT_WITH_RC4_40_MD5,
|
||||
cipher_TLS_RSA_WITH_RC4_128_MD5,
|
||||
cipher_TLS_RSA_WITH_RC4_128_SHA,
|
||||
cipher_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
|
||||
cipher_TLS_RSA_WITH_IDEA_CBC_SHA,
|
||||
cipher_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,
|
||||
cipher_TLS_RSA_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5,
|
||||
cipher_TLS_DH_anon_WITH_RC4_128_MD5,
|
||||
cipher_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_KRB5_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_KRB5_WITH_RC4_128_SHA,
|
||||
cipher_TLS_KRB5_WITH_IDEA_CBC_SHA,
|
||||
cipher_TLS_KRB5_WITH_DES_CBC_MD5,
|
||||
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_MD5,
|
||||
cipher_TLS_KRB5_WITH_RC4_128_MD5,
|
||||
cipher_TLS_KRB5_WITH_IDEA_CBC_MD5,
|
||||
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA,
|
||||
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA,
|
||||
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_SHA,
|
||||
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5,
|
||||
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5,
|
||||
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_MD5,
|
||||
cipher_TLS_PSK_WITH_NULL_SHA,
|
||||
cipher_TLS_DHE_PSK_WITH_NULL_SHA,
|
||||
cipher_TLS_RSA_PSK_WITH_NULL_SHA,
|
||||
cipher_TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_RSA_WITH_NULL_SHA256,
|
||||
cipher_TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_WITH_AES_256_CBC_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA256,
|
||||
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
|
||||
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA256,
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA,
|
||||
cipher_TLS_PSK_WITH_RC4_128_SHA,
|
||||
cipher_TLS_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_PSK_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_PSK_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_DHE_PSK_WITH_RC4_128_SHA,
|
||||
cipher_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_RSA_PSK_WITH_RC4_128_SHA,
|
||||
cipher_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_RSA_WITH_SEED_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_SEED_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_SEED_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_SEED_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_SEED_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_SEED_CBC_SHA,
|
||||
cipher_TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_DH_RSA_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_DH_DSS_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_DH_anon_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_PSK_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_PSK_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_PSK_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_PSK_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_PSK_WITH_NULL_SHA256,
|
||||
cipher_TLS_PSK_WITH_NULL_SHA384,
|
||||
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_DHE_PSK_WITH_NULL_SHA256,
|
||||
cipher_TLS_DHE_PSK_WITH_NULL_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_NULL_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_NULL_SHA384,
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256,
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256,
|
||||
cipher_TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_NULL_SHA,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_NULL_SHA,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_ECDH_RSA_WITH_NULL_SHA,
|
||||
cipher_TLS_ECDH_RSA_WITH_RC4_128_SHA,
|
||||
cipher_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_ECDHE_RSA_WITH_NULL_SHA,
|
||||
cipher_TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
cipher_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_ECDH_anon_WITH_NULL_SHA,
|
||||
cipher_TLS_ECDH_anon_WITH_RC4_128_SHA,
|
||||
cipher_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_ECDHE_PSK_WITH_RC4_128_SHA,
|
||||
cipher_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA,
|
||||
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA256,
|
||||
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA384,
|
||||
cipher_TLS_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_DH_anon_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_DH_anon_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_PSK_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_RSA_WITH_AES_128_CCM,
|
||||
cipher_TLS_RSA_WITH_AES_256_CCM,
|
||||
cipher_TLS_RSA_WITH_AES_128_CCM_8,
|
||||
cipher_TLS_RSA_WITH_AES_256_CCM_8,
|
||||
cipher_TLS_PSK_WITH_AES_128_CCM,
|
||||
cipher_TLS_PSK_WITH_AES_256_CCM,
|
||||
cipher_TLS_PSK_WITH_AES_128_CCM_8,
|
||||
cipher_TLS_PSK_WITH_AES_256_CCM_8,
|
||||
}
|
||||
2
vendor/golang.org/x/net/http2/configure_transport.go
generated
vendored
2
vendor/golang.org/x/net/http2/configure_transport.go
generated
vendored
|
|
@ -73,7 +73,7 @@ type noDialH2RoundTripper struct{ t *Transport }
|
|||
|
||||
func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
res, err := rt.t.RoundTrip(req)
|
||||
if err == ErrNoCachedConn {
|
||||
if isNoCachedConnError(err) {
|
||||
return nil, http.ErrSkipAltProtocol
|
||||
}
|
||||
return res, err
|
||||
|
|
|
|||
157
vendor/golang.org/x/net/http2/databuffer_test.go
generated
vendored
157
vendor/golang.org/x/net/http2/databuffer_test.go
generated
vendored
|
|
@ -1,157 +0,0 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// +build go1.7
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func fmtDataChunk(chunk []byte) string {
|
||||
out := ""
|
||||
var last byte
|
||||
var count int
|
||||
for _, c := range chunk {
|
||||
if c != last {
|
||||
if count > 0 {
|
||||
out += fmt.Sprintf(" x %d ", count)
|
||||
count = 0
|
||||
}
|
||||
out += string([]byte{c})
|
||||
last = c
|
||||
}
|
||||
count++
|
||||
}
|
||||
if count > 0 {
|
||||
out += fmt.Sprintf(" x %d", count)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func fmtDataChunks(chunks [][]byte) string {
|
||||
var out string
|
||||
for _, chunk := range chunks {
|
||||
out += fmt.Sprintf("{%q}", fmtDataChunk(chunk))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func testDataBuffer(t *testing.T, wantBytes []byte, setup func(t *testing.T) *dataBuffer) {
|
||||
// Run setup, then read the remaining bytes from the dataBuffer and check
|
||||
// that they match wantBytes. We use different read sizes to check corner
|
||||
// cases in Read.
|
||||
for _, readSize := range []int{1, 2, 1 * 1024, 32 * 1024} {
|
||||
t.Run(fmt.Sprintf("ReadSize=%d", readSize), func(t *testing.T) {
|
||||
b := setup(t)
|
||||
buf := make([]byte, readSize)
|
||||
var gotRead bytes.Buffer
|
||||
for {
|
||||
n, err := b.Read(buf)
|
||||
gotRead.Write(buf[:n])
|
||||
if err == errReadEmpty {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("error after %v bytes: %v", gotRead.Len(), err)
|
||||
}
|
||||
}
|
||||
if got, want := gotRead.Bytes(), wantBytes; !bytes.Equal(got, want) {
|
||||
t.Errorf("FinalRead=%q, want %q", fmtDataChunk(got), fmtDataChunk(want))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDataBufferAllocation(t *testing.T) {
|
||||
writes := [][]byte{
|
||||
bytes.Repeat([]byte("a"), 1*1024-1),
|
||||
[]byte("a"),
|
||||
bytes.Repeat([]byte("b"), 4*1024-1),
|
||||
[]byte("b"),
|
||||
bytes.Repeat([]byte("c"), 8*1024-1),
|
||||
[]byte("c"),
|
||||
bytes.Repeat([]byte("d"), 16*1024-1),
|
||||
[]byte("d"),
|
||||
bytes.Repeat([]byte("e"), 32*1024),
|
||||
}
|
||||
var wantRead bytes.Buffer
|
||||
for _, p := range writes {
|
||||
wantRead.Write(p)
|
||||
}
|
||||
|
||||
testDataBuffer(t, wantRead.Bytes(), func(t *testing.T) *dataBuffer {
|
||||
b := &dataBuffer{}
|
||||
for _, p := range writes {
|
||||
if n, err := b.Write(p); n != len(p) || err != nil {
|
||||
t.Fatalf("Write(%q x %d)=%v,%v want %v,nil", p[:1], len(p), n, err, len(p))
|
||||
}
|
||||
}
|
||||
want := [][]byte{
|
||||
bytes.Repeat([]byte("a"), 1*1024),
|
||||
bytes.Repeat([]byte("b"), 4*1024),
|
||||
bytes.Repeat([]byte("c"), 8*1024),
|
||||
bytes.Repeat([]byte("d"), 16*1024),
|
||||
bytes.Repeat([]byte("e"), 16*1024),
|
||||
bytes.Repeat([]byte("e"), 16*1024),
|
||||
}
|
||||
if !reflect.DeepEqual(b.chunks, want) {
|
||||
t.Errorf("dataBuffer.chunks\ngot: %s\nwant: %s", fmtDataChunks(b.chunks), fmtDataChunks(want))
|
||||
}
|
||||
return b
|
||||
})
|
||||
}
|
||||
|
||||
func TestDataBufferAllocationWithExpected(t *testing.T) {
|
||||
writes := [][]byte{
|
||||
bytes.Repeat([]byte("a"), 1*1024), // allocates 16KB
|
||||
bytes.Repeat([]byte("b"), 14*1024),
|
||||
bytes.Repeat([]byte("c"), 15*1024), // allocates 16KB more
|
||||
bytes.Repeat([]byte("d"), 2*1024),
|
||||
bytes.Repeat([]byte("e"), 1*1024), // overflows 32KB expectation, allocates just 1KB
|
||||
}
|
||||
var wantRead bytes.Buffer
|
||||
for _, p := range writes {
|
||||
wantRead.Write(p)
|
||||
}
|
||||
|
||||
testDataBuffer(t, wantRead.Bytes(), func(t *testing.T) *dataBuffer {
|
||||
b := &dataBuffer{expected: 32 * 1024}
|
||||
for _, p := range writes {
|
||||
if n, err := b.Write(p); n != len(p) || err != nil {
|
||||
t.Fatalf("Write(%q x %d)=%v,%v want %v,nil", p[:1], len(p), n, err, len(p))
|
||||
}
|
||||
}
|
||||
want := [][]byte{
|
||||
append(bytes.Repeat([]byte("a"), 1*1024), append(bytes.Repeat([]byte("b"), 14*1024), bytes.Repeat([]byte("c"), 1*1024)...)...),
|
||||
append(bytes.Repeat([]byte("c"), 14*1024), bytes.Repeat([]byte("d"), 2*1024)...),
|
||||
bytes.Repeat([]byte("e"), 1*1024),
|
||||
}
|
||||
if !reflect.DeepEqual(b.chunks, want) {
|
||||
t.Errorf("dataBuffer.chunks\ngot: %s\nwant: %s", fmtDataChunks(b.chunks), fmtDataChunks(want))
|
||||
}
|
||||
return b
|
||||
})
|
||||
}
|
||||
|
||||
func TestDataBufferWriteAfterPartialRead(t *testing.T) {
|
||||
testDataBuffer(t, []byte("cdxyz"), func(t *testing.T) *dataBuffer {
|
||||
b := &dataBuffer{}
|
||||
if n, err := b.Write([]byte("abcd")); n != 4 || err != nil {
|
||||
t.Fatalf("Write(\"abcd\")=%v,%v want 4,nil", n, err)
|
||||
}
|
||||
p := make([]byte, 2)
|
||||
if n, err := b.Read(p); n != 2 || err != nil || !bytes.Equal(p, []byte("ab")) {
|
||||
t.Fatalf("Read()=%q,%v,%v want \"ab\",2,nil", p, n, err)
|
||||
}
|
||||
if n, err := b.Write([]byte("xyz")); n != 3 || err != nil {
|
||||
t.Fatalf("Write(\"xyz\")=%v,%v want 3,nil", n, err)
|
||||
}
|
||||
return b
|
||||
})
|
||||
}
|
||||
24
vendor/golang.org/x/net/http2/errors_test.go
generated
vendored
24
vendor/golang.org/x/net/http2/errors_test.go
generated
vendored
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestErrCodeString(t *testing.T) {
|
||||
tests := []struct {
|
||||
err ErrCode
|
||||
want string
|
||||
}{
|
||||
{ErrCodeProtocol, "PROTOCOL_ERROR"},
|
||||
{0xd, "HTTP_1_1_REQUIRED"},
|
||||
{0xf, "unknown error code 0xf"},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
got := tt.err.String()
|
||||
if got != tt.want {
|
||||
t.Errorf("%d. Error = %q; want %q", i, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
53
vendor/golang.org/x/net/http2/flow_test.go
generated
vendored
53
vendor/golang.org/x/net/http2/flow_test.go
generated
vendored
|
|
@ -1,53 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFlow(t *testing.T) {
|
||||
var st flow
|
||||
var conn flow
|
||||
st.add(3)
|
||||
conn.add(2)
|
||||
|
||||
if got, want := st.available(), int32(3); got != want {
|
||||
t.Errorf("available = %d; want %d", got, want)
|
||||
}
|
||||
st.setConnFlow(&conn)
|
||||
if got, want := st.available(), int32(2); got != want {
|
||||
t.Errorf("after parent setup, available = %d; want %d", got, want)
|
||||
}
|
||||
|
||||
st.take(2)
|
||||
if got, want := conn.available(), int32(0); got != want {
|
||||
t.Errorf("after taking 2, conn = %d; want %d", got, want)
|
||||
}
|
||||
if got, want := st.available(), int32(0); got != want {
|
||||
t.Errorf("after taking 2, stream = %d; want %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlowAdd(t *testing.T) {
|
||||
var f flow
|
||||
if !f.add(1) {
|
||||
t.Fatal("failed to add 1")
|
||||
}
|
||||
if !f.add(-1) {
|
||||
t.Fatal("failed to add -1")
|
||||
}
|
||||
if got, want := f.available(), int32(0); got != want {
|
||||
t.Fatalf("size = %d; want %d", got, want)
|
||||
}
|
||||
if !f.add(1<<31 - 1) {
|
||||
t.Fatal("failed to add 2^31-1")
|
||||
}
|
||||
if got, want := f.available(), int32(1<<31-1); got != want {
|
||||
t.Fatalf("size = %d; want %d", got, want)
|
||||
}
|
||||
if f.add(1) {
|
||||
t.Fatal("adding 1 to max shouldn't be allowed")
|
||||
}
|
||||
|
||||
}
|
||||
4
vendor/golang.org/x/net/http2/frame.go
generated
vendored
4
vendor/golang.org/x/net/http2/frame.go
generated
vendored
|
|
@ -14,8 +14,8 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
const frameHeaderLen = 9
|
||||
|
|
@ -1462,7 +1462,7 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
|
|||
if VerboseLogs && fr.logReads {
|
||||
fr.debugReadLoggerf("http2: decoded hpack field %+v", hf)
|
||||
}
|
||||
if !httplex.ValidHeaderFieldValue(hf.Value) {
|
||||
if !httpguts.ValidHeaderFieldValue(hf.Value) {
|
||||
invalid = headerFieldValueError(hf.Value)
|
||||
}
|
||||
isPseudo := strings.HasPrefix(hf.Name, ":")
|
||||
|
|
|
|||
1191
vendor/golang.org/x/net/http2/frame_test.go
generated
vendored
1191
vendor/golang.org/x/net/http2/frame_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
79
vendor/golang.org/x/net/http2/go18_test.go
generated
vendored
79
vendor/golang.org/x/net/http2/go18_test.go
generated
vendored
|
|
@ -1,79 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Tests that http2.Server.IdleTimeout is initialized from
|
||||
// http.Server.{Idle,Read}Timeout. http.Server.IdleTimeout was
|
||||
// added in Go 1.8.
|
||||
func TestConfigureServerIdleTimeout_Go18(t *testing.T) {
|
||||
const timeout = 5 * time.Second
|
||||
const notThisOne = 1 * time.Second
|
||||
|
||||
// With a zero http2.Server, verify that it copies IdleTimeout:
|
||||
{
|
||||
s1 := &http.Server{
|
||||
IdleTimeout: timeout,
|
||||
ReadTimeout: notThisOne,
|
||||
}
|
||||
s2 := &Server{}
|
||||
if err := ConfigureServer(s1, s2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s2.IdleTimeout != timeout {
|
||||
t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
// And that it falls back to ReadTimeout:
|
||||
{
|
||||
s1 := &http.Server{
|
||||
ReadTimeout: timeout,
|
||||
}
|
||||
s2 := &Server{}
|
||||
if err := ConfigureServer(s1, s2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s2.IdleTimeout != timeout {
|
||||
t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that s1's IdleTimeout doesn't overwrite an existing setting:
|
||||
{
|
||||
s1 := &http.Server{
|
||||
IdleTimeout: notThisOne,
|
||||
}
|
||||
s2 := &Server{
|
||||
IdleTimeout: timeout,
|
||||
}
|
||||
if err := ConfigureServer(s1, s2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if s2.IdleTimeout != timeout {
|
||||
t.Errorf("s2.IdleTimeout = %v; want %v", s2.IdleTimeout, timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertClone(t *testing.T) {
|
||||
c := &tls.Config{
|
||||
GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||
panic("shouldn't be called")
|
||||
},
|
||||
}
|
||||
c2 := cloneTLSConfig(c)
|
||||
if c2.GetClientCertificate == nil {
|
||||
t.Error("GetClientCertificate is nil")
|
||||
}
|
||||
}
|
||||
60
vendor/golang.org/x/net/http2/go19_test.go
generated
vendored
60
vendor/golang.org/x/net/http2/go19_test.go
generated
vendored
|
|
@ -1,60 +0,0 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
// +build go1.9
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestServerGracefulShutdown(t *testing.T) {
|
||||
var st *serverTester
|
||||
handlerDone := make(chan struct{})
|
||||
st = newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
defer close(handlerDone)
|
||||
go st.ts.Config.Shutdown(context.Background())
|
||||
|
||||
ga := st.wantGoAway()
|
||||
if ga.ErrCode != ErrCodeNo {
|
||||
t.Errorf("GOAWAY error = %v; want ErrCodeNo", ga.ErrCode)
|
||||
}
|
||||
if ga.LastStreamID != 1 {
|
||||
t.Errorf("GOAWAY LastStreamID = %v; want 1", ga.LastStreamID)
|
||||
}
|
||||
|
||||
w.Header().Set("x-foo", "bar")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
st.greet()
|
||||
st.bodylessReq1()
|
||||
|
||||
select {
|
||||
case <-handlerDone:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatalf("server did not shutdown?")
|
||||
}
|
||||
hf := st.wantHeaders()
|
||||
goth := st.decodeHeader(hf.HeaderBlockFragment())
|
||||
wanth := [][2]string{
|
||||
{":status", "200"},
|
||||
{"x-foo", "bar"},
|
||||
{"content-type", "text/plain; charset=utf-8"},
|
||||
{"content-length", "0"},
|
||||
}
|
||||
if !reflect.DeepEqual(goth, wanth) {
|
||||
t.Errorf("Got headers %v; want %v", goth, wanth)
|
||||
}
|
||||
|
||||
n, err := st.cc.Read([]byte{0})
|
||||
if n != 0 || err == nil {
|
||||
t.Errorf("Read = %v, %v; want 0, non-nil", n, err)
|
||||
}
|
||||
}
|
||||
33
vendor/golang.org/x/net/http2/gotrack_test.go
generated
vendored
33
vendor/golang.org/x/net/http2/gotrack_test.go
generated
vendored
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGoroutineLock(t *testing.T) {
|
||||
oldDebug := DebugGoroutines
|
||||
DebugGoroutines = true
|
||||
defer func() { DebugGoroutines = oldDebug }()
|
||||
|
||||
g := newGoroutineLock()
|
||||
g.check()
|
||||
|
||||
sawPanic := make(chan interface{})
|
||||
go func() {
|
||||
defer func() { sawPanic <- recover() }()
|
||||
g.check() // should panic
|
||||
}()
|
||||
e := <-sawPanic
|
||||
if e == nil {
|
||||
t.Fatal("did not see panic from check in other goroutine")
|
||||
}
|
||||
if !strings.Contains(fmt.Sprint(e), "wrong goroutine") {
|
||||
t.Errorf("expected on see panic about running on the wrong goroutine; got %v", e)
|
||||
}
|
||||
}
|
||||
2
vendor/golang.org/x/net/http2/hpack/encode.go
generated
vendored
2
vendor/golang.org/x/net/http2/hpack/encode.go
generated
vendored
|
|
@ -206,7 +206,7 @@ func appendVarInt(dst []byte, n byte, i uint64) []byte {
|
|||
}
|
||||
|
||||
// appendHpackString appends s, as encoded in "String Literal"
|
||||
// representation, to dst and returns the the extended buffer.
|
||||
// representation, to dst and returns the extended buffer.
|
||||
//
|
||||
// s will be encoded in Huffman codes only when it produces strictly
|
||||
// shorter byte string.
|
||||
|
|
|
|||
386
vendor/golang.org/x/net/http2/hpack/encode_test.go
generated
vendored
386
vendor/golang.org/x/net/http2/hpack/encode_test.go
generated
vendored
|
|
@ -1,386 +0,0 @@
|
|||
// Copyright 2014 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 hpack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEncoderTableSizeUpdate(t *testing.T) {
|
||||
tests := []struct {
|
||||
size1, size2 uint32
|
||||
wantHex string
|
||||
}{
|
||||
// Should emit 2 table size updates (2048 and 4096)
|
||||
{2048, 4096, "3fe10f 3fe11f 82"},
|
||||
|
||||
// Should emit 1 table size update (2048)
|
||||
{16384, 2048, "3fe10f 82"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
var buf bytes.Buffer
|
||||
e := NewEncoder(&buf)
|
||||
e.SetMaxDynamicTableSize(tt.size1)
|
||||
e.SetMaxDynamicTableSize(tt.size2)
|
||||
if err := e.WriteField(pair(":method", "GET")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := removeSpace(tt.wantHex)
|
||||
if got := hex.EncodeToString(buf.Bytes()); got != want {
|
||||
t.Errorf("e.SetDynamicTableSize %v, %v = %q; want %q", tt.size1, tt.size2, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncoderWriteField(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
e := NewEncoder(&buf)
|
||||
var got []HeaderField
|
||||
d := NewDecoder(4<<10, func(f HeaderField) {
|
||||
got = append(got, f)
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
hdrs []HeaderField
|
||||
}{
|
||||
{[]HeaderField{
|
||||
pair(":method", "GET"),
|
||||
pair(":scheme", "http"),
|
||||
pair(":path", "/"),
|
||||
pair(":authority", "www.example.com"),
|
||||
}},
|
||||
{[]HeaderField{
|
||||
pair(":method", "GET"),
|
||||
pair(":scheme", "http"),
|
||||
pair(":path", "/"),
|
||||
pair(":authority", "www.example.com"),
|
||||
pair("cache-control", "no-cache"),
|
||||
}},
|
||||
{[]HeaderField{
|
||||
pair(":method", "GET"),
|
||||
pair(":scheme", "https"),
|
||||
pair(":path", "/index.html"),
|
||||
pair(":authority", "www.example.com"),
|
||||
pair("custom-key", "custom-value"),
|
||||
}},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
buf.Reset()
|
||||
got = got[:0]
|
||||
for _, hf := range tt.hdrs {
|
||||
if err := e.WriteField(hf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
_, err := d.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
t.Errorf("%d. Decoder Write = %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.hdrs) {
|
||||
t.Errorf("%d. Decoded %+v; want %+v", i, got, tt.hdrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncoderSearchTable(t *testing.T) {
|
||||
e := NewEncoder(nil)
|
||||
|
||||
e.dynTab.add(pair("foo", "bar"))
|
||||
e.dynTab.add(pair("blake", "miz"))
|
||||
e.dynTab.add(pair(":method", "GET"))
|
||||
|
||||
tests := []struct {
|
||||
hf HeaderField
|
||||
wantI uint64
|
||||
wantMatch bool
|
||||
}{
|
||||
// Name and Value match
|
||||
{pair("foo", "bar"), uint64(staticTable.len()) + 3, true},
|
||||
{pair("blake", "miz"), uint64(staticTable.len()) + 2, true},
|
||||
{pair(":method", "GET"), 2, true},
|
||||
|
||||
// Only name match because Sensitive == true. This is allowed to match
|
||||
// any ":method" entry. The current implementation uses the last entry
|
||||
// added in newStaticTable.
|
||||
{HeaderField{":method", "GET", true}, 3, false},
|
||||
|
||||
// Only Name matches
|
||||
{pair("foo", "..."), uint64(staticTable.len()) + 3, false},
|
||||
{pair("blake", "..."), uint64(staticTable.len()) + 2, false},
|
||||
// As before, this is allowed to match any ":method" entry.
|
||||
{pair(":method", "..."), 3, false},
|
||||
|
||||
// None match
|
||||
{pair("foo-", "bar"), 0, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if gotI, gotMatch := e.searchTable(tt.hf); gotI != tt.wantI || gotMatch != tt.wantMatch {
|
||||
t.Errorf("d.search(%+v) = %v, %v; want %v, %v", tt.hf, gotI, gotMatch, tt.wantI, tt.wantMatch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendVarInt(t *testing.T) {
|
||||
tests := []struct {
|
||||
n byte
|
||||
i uint64
|
||||
want []byte
|
||||
}{
|
||||
// Fits in a byte:
|
||||
{1, 0, []byte{0}},
|
||||
{2, 2, []byte{2}},
|
||||
{3, 6, []byte{6}},
|
||||
{4, 14, []byte{14}},
|
||||
{5, 30, []byte{30}},
|
||||
{6, 62, []byte{62}},
|
||||
{7, 126, []byte{126}},
|
||||
{8, 254, []byte{254}},
|
||||
|
||||
// Multiple bytes:
|
||||
{5, 1337, []byte{31, 154, 10}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := appendVarInt(nil, tt.n, tt.i)
|
||||
if !bytes.Equal(got, tt.want) {
|
||||
t.Errorf("appendVarInt(nil, %v, %v) = %v; want %v", tt.n, tt.i, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendHpackString(t *testing.T) {
|
||||
tests := []struct {
|
||||
s, wantHex string
|
||||
}{
|
||||
// Huffman encoded
|
||||
{"www.example.com", "8c f1e3 c2e5 f23a 6ba0 ab90 f4ff"},
|
||||
|
||||
// Not Huffman encoded
|
||||
{"a", "01 61"},
|
||||
|
||||
// zero length
|
||||
{"", "00"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
want := removeSpace(tt.wantHex)
|
||||
buf := appendHpackString(nil, tt.s)
|
||||
if got := hex.EncodeToString(buf); want != got {
|
||||
t.Errorf("appendHpackString(nil, %q) = %q; want %q", tt.s, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendIndexed(t *testing.T) {
|
||||
tests := []struct {
|
||||
i uint64
|
||||
wantHex string
|
||||
}{
|
||||
// 1 byte
|
||||
{1, "81"},
|
||||
{126, "fe"},
|
||||
|
||||
// 2 bytes
|
||||
{127, "ff00"},
|
||||
{128, "ff01"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
want := removeSpace(tt.wantHex)
|
||||
buf := appendIndexed(nil, tt.i)
|
||||
if got := hex.EncodeToString(buf); want != got {
|
||||
t.Errorf("appendIndex(nil, %v) = %q; want %q", tt.i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendNewName(t *testing.T) {
|
||||
tests := []struct {
|
||||
f HeaderField
|
||||
indexing bool
|
||||
wantHex string
|
||||
}{
|
||||
// Incremental indexing
|
||||
{HeaderField{"custom-key", "custom-value", false}, true, "40 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"},
|
||||
|
||||
// Without indexing
|
||||
{HeaderField{"custom-key", "custom-value", false}, false, "00 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"},
|
||||
|
||||
// Never indexed
|
||||
{HeaderField{"custom-key", "custom-value", true}, true, "10 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"},
|
||||
{HeaderField{"custom-key", "custom-value", true}, false, "10 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
want := removeSpace(tt.wantHex)
|
||||
buf := appendNewName(nil, tt.f, tt.indexing)
|
||||
if got := hex.EncodeToString(buf); want != got {
|
||||
t.Errorf("appendNewName(nil, %+v, %v) = %q; want %q", tt.f, tt.indexing, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendIndexedName(t *testing.T) {
|
||||
tests := []struct {
|
||||
f HeaderField
|
||||
i uint64
|
||||
indexing bool
|
||||
wantHex string
|
||||
}{
|
||||
// Incremental indexing
|
||||
{HeaderField{":status", "302", false}, 8, true, "48 82 6402"},
|
||||
|
||||
// Without indexing
|
||||
{HeaderField{":status", "302", false}, 8, false, "08 82 6402"},
|
||||
|
||||
// Never indexed
|
||||
{HeaderField{":status", "302", true}, 8, true, "18 82 6402"},
|
||||
{HeaderField{":status", "302", true}, 8, false, "18 82 6402"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
want := removeSpace(tt.wantHex)
|
||||
buf := appendIndexedName(nil, tt.f, tt.i, tt.indexing)
|
||||
if got := hex.EncodeToString(buf); want != got {
|
||||
t.Errorf("appendIndexedName(nil, %+v, %v) = %q; want %q", tt.f, tt.indexing, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendTableSize(t *testing.T) {
|
||||
tests := []struct {
|
||||
i uint32
|
||||
wantHex string
|
||||
}{
|
||||
// Fits into 1 byte
|
||||
{30, "3e"},
|
||||
|
||||
// Extra byte
|
||||
{31, "3f00"},
|
||||
{32, "3f01"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
want := removeSpace(tt.wantHex)
|
||||
buf := appendTableSize(nil, tt.i)
|
||||
if got := hex.EncodeToString(buf); want != got {
|
||||
t.Errorf("appendTableSize(nil, %v) = %q; want %q", tt.i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncoderSetMaxDynamicTableSize(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
e := NewEncoder(&buf)
|
||||
tests := []struct {
|
||||
v uint32
|
||||
wantUpdate bool
|
||||
wantMinSize uint32
|
||||
wantMaxSize uint32
|
||||
}{
|
||||
// Set new table size to 2048
|
||||
{2048, true, 2048, 2048},
|
||||
|
||||
// Set new table size to 16384, but still limited to
|
||||
// 4096
|
||||
{16384, true, 2048, 4096},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
e.SetMaxDynamicTableSize(tt.v)
|
||||
if got := e.tableSizeUpdate; tt.wantUpdate != got {
|
||||
t.Errorf("e.tableSizeUpdate = %v; want %v", got, tt.wantUpdate)
|
||||
}
|
||||
if got := e.minSize; tt.wantMinSize != got {
|
||||
t.Errorf("e.minSize = %v; want %v", got, tt.wantMinSize)
|
||||
}
|
||||
if got := e.dynTab.maxSize; tt.wantMaxSize != got {
|
||||
t.Errorf("e.maxSize = %v; want %v", got, tt.wantMaxSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncoderSetMaxDynamicTableSizeLimit(t *testing.T) {
|
||||
e := NewEncoder(nil)
|
||||
// 4095 < initialHeaderTableSize means maxSize is truncated to
|
||||
// 4095.
|
||||
e.SetMaxDynamicTableSizeLimit(4095)
|
||||
if got, want := e.dynTab.maxSize, uint32(4095); got != want {
|
||||
t.Errorf("e.dynTab.maxSize = %v; want %v", got, want)
|
||||
}
|
||||
if got, want := e.maxSizeLimit, uint32(4095); got != want {
|
||||
t.Errorf("e.maxSizeLimit = %v; want %v", got, want)
|
||||
}
|
||||
if got, want := e.tableSizeUpdate, true; got != want {
|
||||
t.Errorf("e.tableSizeUpdate = %v; want %v", got, want)
|
||||
}
|
||||
// maxSize will be truncated to maxSizeLimit
|
||||
e.SetMaxDynamicTableSize(16384)
|
||||
if got, want := e.dynTab.maxSize, uint32(4095); got != want {
|
||||
t.Errorf("e.dynTab.maxSize = %v; want %v", got, want)
|
||||
}
|
||||
// 8192 > current maxSizeLimit, so maxSize does not change.
|
||||
e.SetMaxDynamicTableSizeLimit(8192)
|
||||
if got, want := e.dynTab.maxSize, uint32(4095); got != want {
|
||||
t.Errorf("e.dynTab.maxSize = %v; want %v", got, want)
|
||||
}
|
||||
if got, want := e.maxSizeLimit, uint32(8192); got != want {
|
||||
t.Errorf("e.maxSizeLimit = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func removeSpace(s string) string {
|
||||
return strings.Replace(s, " ", "", -1)
|
||||
}
|
||||
|
||||
func BenchmarkEncoderSearchTable(b *testing.B) {
|
||||
e := NewEncoder(nil)
|
||||
|
||||
// A sample of possible header fields.
|
||||
// This is not based on any actual data from HTTP/2 traces.
|
||||
var possible []HeaderField
|
||||
for _, f := range staticTable.ents {
|
||||
if f.Value == "" {
|
||||
possible = append(possible, f)
|
||||
continue
|
||||
}
|
||||
// Generate 5 random values, except for cookie and set-cookie,
|
||||
// which we know can have many values in practice.
|
||||
num := 5
|
||||
if f.Name == "cookie" || f.Name == "set-cookie" {
|
||||
num = 25
|
||||
}
|
||||
for i := 0; i < num; i++ {
|
||||
f.Value = fmt.Sprintf("%s-%d", f.Name, i)
|
||||
possible = append(possible, f)
|
||||
}
|
||||
}
|
||||
for k := 0; k < 10; k++ {
|
||||
f := HeaderField{
|
||||
Name: fmt.Sprintf("x-header-%d", k),
|
||||
Sensitive: rand.Int()%2 == 0,
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
f.Value = fmt.Sprintf("%s-%d", f.Name, i)
|
||||
possible = append(possible, f)
|
||||
}
|
||||
}
|
||||
|
||||
// Add a random sample to the dynamic table. This very loosely simulates
|
||||
// a history of 100 requests with 20 header fields per request.
|
||||
for r := 0; r < 100*20; r++ {
|
||||
f := possible[rand.Int31n(int32(len(possible)))]
|
||||
// Skip if this is in the staticTable verbatim.
|
||||
if _, has := staticTable.search(f); !has {
|
||||
e.dynTab.add(f)
|
||||
}
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
for _, f := range possible {
|
||||
e.searchTable(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
6
vendor/golang.org/x/net/http2/hpack/hpack.go
generated
vendored
6
vendor/golang.org/x/net/http2/hpack/hpack.go
generated
vendored
|
|
@ -389,6 +389,12 @@ func (d *Decoder) callEmit(hf HeaderField) error {
|
|||
|
||||
// (same invariants and behavior as parseHeaderFieldRepr)
|
||||
func (d *Decoder) parseDynamicTableSizeUpdate() error {
|
||||
// RFC 7541, sec 4.2: This dynamic table size update MUST occur at the
|
||||
// beginning of the first header block following the change to the dynamic table size.
|
||||
if d.dynTab.size > 0 {
|
||||
return DecodingError{errors.New("dynamic table size update MUST occur at the beginning of a header block")}
|
||||
}
|
||||
|
||||
buf := d.buf
|
||||
size, buf, err := readVarInt(5, buf)
|
||||
if err != nil {
|
||||
|
|
|
|||
722
vendor/golang.org/x/net/http2/hpack/hpack_test.go
generated
vendored
722
vendor/golang.org/x/net/http2/hpack/hpack_test.go
generated
vendored
|
|
@ -1,722 +0,0 @@
|
|||
// Copyright 2014 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 hpack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (d *Decoder) mustAt(idx int) HeaderField {
|
||||
if hf, ok := d.at(uint64(idx)); !ok {
|
||||
panic(fmt.Sprintf("bogus index %d", idx))
|
||||
} else {
|
||||
return hf
|
||||
}
|
||||
}
|
||||
|
||||
func TestDynamicTableAt(t *testing.T) {
|
||||
d := NewDecoder(4096, nil)
|
||||
at := d.mustAt
|
||||
if got, want := at(2), (pair(":method", "GET")); got != want {
|
||||
t.Errorf("at(2) = %v; want %v", got, want)
|
||||
}
|
||||
d.dynTab.add(pair("foo", "bar"))
|
||||
d.dynTab.add(pair("blake", "miz"))
|
||||
if got, want := at(staticTable.len()+1), (pair("blake", "miz")); got != want {
|
||||
t.Errorf("at(dyn 1) = %v; want %v", got, want)
|
||||
}
|
||||
if got, want := at(staticTable.len()+2), (pair("foo", "bar")); got != want {
|
||||
t.Errorf("at(dyn 2) = %v; want %v", got, want)
|
||||
}
|
||||
if got, want := at(3), (pair(":method", "POST")); got != want {
|
||||
t.Errorf("at(3) = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDynamicTableSizeEvict(t *testing.T) {
|
||||
d := NewDecoder(4096, nil)
|
||||
if want := uint32(0); d.dynTab.size != want {
|
||||
t.Fatalf("size = %d; want %d", d.dynTab.size, want)
|
||||
}
|
||||
add := d.dynTab.add
|
||||
add(pair("blake", "eats pizza"))
|
||||
if want := uint32(15 + 32); d.dynTab.size != want {
|
||||
t.Fatalf("after pizza, size = %d; want %d", d.dynTab.size, want)
|
||||
}
|
||||
add(pair("foo", "bar"))
|
||||
if want := uint32(15 + 32 + 6 + 32); d.dynTab.size != want {
|
||||
t.Fatalf("after foo bar, size = %d; want %d", d.dynTab.size, want)
|
||||
}
|
||||
d.dynTab.setMaxSize(15 + 32 + 1 /* slop */)
|
||||
if want := uint32(6 + 32); d.dynTab.size != want {
|
||||
t.Fatalf("after setMaxSize, size = %d; want %d", d.dynTab.size, want)
|
||||
}
|
||||
if got, want := d.mustAt(staticTable.len()+1), (pair("foo", "bar")); got != want {
|
||||
t.Errorf("at(dyn 1) = %v; want %v", got, want)
|
||||
}
|
||||
add(pair("long", strings.Repeat("x", 500)))
|
||||
if want := uint32(0); d.dynTab.size != want {
|
||||
t.Fatalf("after big one, size = %d; want %d", d.dynTab.size, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoderDecode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in []byte
|
||||
want []HeaderField
|
||||
wantDynTab []HeaderField // newest entry first
|
||||
}{
|
||||
// C.2.1 Literal Header Field with Indexing
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.1
|
||||
{"C.2.1", dehex("400a 6375 7374 6f6d 2d6b 6579 0d63 7573 746f 6d2d 6865 6164 6572"),
|
||||
[]HeaderField{pair("custom-key", "custom-header")},
|
||||
[]HeaderField{pair("custom-key", "custom-header")},
|
||||
},
|
||||
|
||||
// C.2.2 Literal Header Field without Indexing
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.2
|
||||
{"C.2.2", dehex("040c 2f73 616d 706c 652f 7061 7468"),
|
||||
[]HeaderField{pair(":path", "/sample/path")},
|
||||
[]HeaderField{}},
|
||||
|
||||
// C.2.3 Literal Header Field never Indexed
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.3
|
||||
{"C.2.3", dehex("1008 7061 7373 776f 7264 0673 6563 7265 74"),
|
||||
[]HeaderField{{"password", "secret", true}},
|
||||
[]HeaderField{}},
|
||||
|
||||
// C.2.4 Indexed Header Field
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.4
|
||||
{"C.2.4", []byte("\x82"),
|
||||
[]HeaderField{pair(":method", "GET")},
|
||||
[]HeaderField{}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
d := NewDecoder(4096, nil)
|
||||
hf, err := d.DecodeFull(tt.in)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", tt.name, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(hf, tt.want) {
|
||||
t.Errorf("%s: Got %v; want %v", tt.name, hf, tt.want)
|
||||
}
|
||||
gotDynTab := d.dynTab.reverseCopy()
|
||||
if !reflect.DeepEqual(gotDynTab, tt.wantDynTab) {
|
||||
t.Errorf("%s: dynamic table after = %v; want %v", tt.name, gotDynTab, tt.wantDynTab)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dt *dynamicTable) reverseCopy() (hf []HeaderField) {
|
||||
hf = make([]HeaderField, len(dt.table.ents))
|
||||
for i := range hf {
|
||||
hf[i] = dt.table.ents[len(dt.table.ents)-1-i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type encAndWant struct {
|
||||
enc []byte
|
||||
want []HeaderField
|
||||
wantDynTab []HeaderField
|
||||
wantDynSize uint32
|
||||
}
|
||||
|
||||
// C.3 Request Examples without Huffman Coding
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.C.3
|
||||
func TestDecodeC3_NoHuffman(t *testing.T) {
|
||||
testDecodeSeries(t, 4096, []encAndWant{
|
||||
{dehex("8286 8441 0f77 7777 2e65 7861 6d70 6c65 2e63 6f6d"),
|
||||
[]HeaderField{
|
||||
pair(":method", "GET"),
|
||||
pair(":scheme", "http"),
|
||||
pair(":path", "/"),
|
||||
pair(":authority", "www.example.com"),
|
||||
},
|
||||
[]HeaderField{
|
||||
pair(":authority", "www.example.com"),
|
||||
},
|
||||
57,
|
||||
},
|
||||
{dehex("8286 84be 5808 6e6f 2d63 6163 6865"),
|
||||
[]HeaderField{
|
||||
pair(":method", "GET"),
|
||||
pair(":scheme", "http"),
|
||||
pair(":path", "/"),
|
||||
pair(":authority", "www.example.com"),
|
||||
pair("cache-control", "no-cache"),
|
||||
},
|
||||
[]HeaderField{
|
||||
pair("cache-control", "no-cache"),
|
||||
pair(":authority", "www.example.com"),
|
||||
},
|
||||
110,
|
||||
},
|
||||
{dehex("8287 85bf 400a 6375 7374 6f6d 2d6b 6579 0c63 7573 746f 6d2d 7661 6c75 65"),
|
||||
[]HeaderField{
|
||||
pair(":method", "GET"),
|
||||
pair(":scheme", "https"),
|
||||
pair(":path", "/index.html"),
|
||||
pair(":authority", "www.example.com"),
|
||||
pair("custom-key", "custom-value"),
|
||||
},
|
||||
[]HeaderField{
|
||||
pair("custom-key", "custom-value"),
|
||||
pair("cache-control", "no-cache"),
|
||||
pair(":authority", "www.example.com"),
|
||||
},
|
||||
164,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// C.4 Request Examples with Huffman Coding
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.C.4
|
||||
func TestDecodeC4_Huffman(t *testing.T) {
|
||||
testDecodeSeries(t, 4096, []encAndWant{
|
||||
{dehex("8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff"),
|
||||
[]HeaderField{
|
||||
pair(":method", "GET"),
|
||||
pair(":scheme", "http"),
|
||||
pair(":path", "/"),
|
||||
pair(":authority", "www.example.com"),
|
||||
},
|
||||
[]HeaderField{
|
||||
pair(":authority", "www.example.com"),
|
||||
},
|
||||
57,
|
||||
},
|
||||
{dehex("8286 84be 5886 a8eb 1064 9cbf"),
|
||||
[]HeaderField{
|
||||
pair(":method", "GET"),
|
||||
pair(":scheme", "http"),
|
||||
pair(":path", "/"),
|
||||
pair(":authority", "www.example.com"),
|
||||
pair("cache-control", "no-cache"),
|
||||
},
|
||||
[]HeaderField{
|
||||
pair("cache-control", "no-cache"),
|
||||
pair(":authority", "www.example.com"),
|
||||
},
|
||||
110,
|
||||
},
|
||||
{dehex("8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925 a849 e95b b8e8 b4bf"),
|
||||
[]HeaderField{
|
||||
pair(":method", "GET"),
|
||||
pair(":scheme", "https"),
|
||||
pair(":path", "/index.html"),
|
||||
pair(":authority", "www.example.com"),
|
||||
pair("custom-key", "custom-value"),
|
||||
},
|
||||
[]HeaderField{
|
||||
pair("custom-key", "custom-value"),
|
||||
pair("cache-control", "no-cache"),
|
||||
pair(":authority", "www.example.com"),
|
||||
},
|
||||
164,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.C.5
|
||||
// "This section shows several consecutive header lists, corresponding
|
||||
// to HTTP responses, on the same connection. The HTTP/2 setting
|
||||
// parameter SETTINGS_HEADER_TABLE_SIZE is set to the value of 256
|
||||
// octets, causing some evictions to occur."
|
||||
func TestDecodeC5_ResponsesNoHuff(t *testing.T) {
|
||||
testDecodeSeries(t, 256, []encAndWant{
|
||||
{dehex(`
|
||||
4803 3330 3258 0770 7269 7661 7465 611d
|
||||
4d6f 6e2c 2032 3120 4f63 7420 3230 3133
|
||||
2032 303a 3133 3a32 3120 474d 546e 1768
|
||||
7474 7073 3a2f 2f77 7777 2e65 7861 6d70
|
||||
6c65 2e63 6f6d
|
||||
`),
|
||||
[]HeaderField{
|
||||
pair(":status", "302"),
|
||||
pair("cache-control", "private"),
|
||||
pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
|
||||
pair("location", "https://www.example.com"),
|
||||
},
|
||||
[]HeaderField{
|
||||
pair("location", "https://www.example.com"),
|
||||
pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
|
||||
pair("cache-control", "private"),
|
||||
pair(":status", "302"),
|
||||
},
|
||||
222,
|
||||
},
|
||||
{dehex("4803 3330 37c1 c0bf"),
|
||||
[]HeaderField{
|
||||
pair(":status", "307"),
|
||||
pair("cache-control", "private"),
|
||||
pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
|
||||
pair("location", "https://www.example.com"),
|
||||
},
|
||||
[]HeaderField{
|
||||
pair(":status", "307"),
|
||||
pair("location", "https://www.example.com"),
|
||||
pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
|
||||
pair("cache-control", "private"),
|
||||
},
|
||||
222,
|
||||
},
|
||||
{dehex(`
|
||||
88c1 611d 4d6f 6e2c 2032 3120 4f63 7420
|
||||
3230 3133 2032 303a 3133 3a32 3220 474d
|
||||
54c0 5a04 677a 6970 7738 666f 6f3d 4153
|
||||
444a 4b48 514b 425a 584f 5157 454f 5049
|
||||
5541 5851 5745 4f49 553b 206d 6178 2d61
|
||||
6765 3d33 3630 303b 2076 6572 7369 6f6e
|
||||
3d31
|
||||
`),
|
||||
[]HeaderField{
|
||||
pair(":status", "200"),
|
||||
pair("cache-control", "private"),
|
||||
pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
|
||||
pair("location", "https://www.example.com"),
|
||||
pair("content-encoding", "gzip"),
|
||||
pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
|
||||
},
|
||||
[]HeaderField{
|
||||
pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
|
||||
pair("content-encoding", "gzip"),
|
||||
pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
|
||||
},
|
||||
215,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.C.6
|
||||
// "This section shows the same examples as the previous section, but
|
||||
// using Huffman encoding for the literal values. The HTTP/2 setting
|
||||
// parameter SETTINGS_HEADER_TABLE_SIZE is set to the value of 256
|
||||
// octets, causing some evictions to occur. The eviction mechanism
|
||||
// uses the length of the decoded literal values, so the same
|
||||
// evictions occurs as in the previous section."
|
||||
func TestDecodeC6_ResponsesHuffman(t *testing.T) {
|
||||
testDecodeSeries(t, 256, []encAndWant{
|
||||
{dehex(`
|
||||
4882 6402 5885 aec3 771a 4b61 96d0 7abe
|
||||
9410 54d4 44a8 2005 9504 0b81 66e0 82a6
|
||||
2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8
|
||||
e9ae 82ae 43d3
|
||||
`),
|
||||
[]HeaderField{
|
||||
pair(":status", "302"),
|
||||
pair("cache-control", "private"),
|
||||
pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
|
||||
pair("location", "https://www.example.com"),
|
||||
},
|
||||
[]HeaderField{
|
||||
pair("location", "https://www.example.com"),
|
||||
pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
|
||||
pair("cache-control", "private"),
|
||||
pair(":status", "302"),
|
||||
},
|
||||
222,
|
||||
},
|
||||
{dehex("4883 640e ffc1 c0bf"),
|
||||
[]HeaderField{
|
||||
pair(":status", "307"),
|
||||
pair("cache-control", "private"),
|
||||
pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
|
||||
pair("location", "https://www.example.com"),
|
||||
},
|
||||
[]HeaderField{
|
||||
pair(":status", "307"),
|
||||
pair("location", "https://www.example.com"),
|
||||
pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
|
||||
pair("cache-control", "private"),
|
||||
},
|
||||
222,
|
||||
},
|
||||
{dehex(`
|
||||
88c1 6196 d07a be94 1054 d444 a820 0595
|
||||
040b 8166 e084 a62d 1bff c05a 839b d9ab
|
||||
77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b
|
||||
3960 d5af 2708 7f36 72c1 ab27 0fb5 291f
|
||||
9587 3160 65c0 03ed 4ee5 b106 3d50 07
|
||||
`),
|
||||
[]HeaderField{
|
||||
pair(":status", "200"),
|
||||
pair("cache-control", "private"),
|
||||
pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
|
||||
pair("location", "https://www.example.com"),
|
||||
pair("content-encoding", "gzip"),
|
||||
pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
|
||||
},
|
||||
[]HeaderField{
|
||||
pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
|
||||
pair("content-encoding", "gzip"),
|
||||
pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
|
||||
},
|
||||
215,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testDecodeSeries(t *testing.T, size uint32, steps []encAndWant) {
|
||||
d := NewDecoder(size, nil)
|
||||
for i, step := range steps {
|
||||
hf, err := d.DecodeFull(step.enc)
|
||||
if err != nil {
|
||||
t.Fatalf("Error at step index %d: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(hf, step.want) {
|
||||
t.Fatalf("At step index %d: Got headers %v; want %v", i, hf, step.want)
|
||||
}
|
||||
gotDynTab := d.dynTab.reverseCopy()
|
||||
if !reflect.DeepEqual(gotDynTab, step.wantDynTab) {
|
||||
t.Errorf("After step index %d, dynamic table = %v; want %v", i, gotDynTab, step.wantDynTab)
|
||||
}
|
||||
if d.dynTab.size != step.wantDynSize {
|
||||
t.Errorf("After step index %d, dynamic table size = %v; want %v", i, d.dynTab.size, step.wantDynSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHuffmanDecodeExcessPadding(t *testing.T) {
|
||||
tests := [][]byte{
|
||||
{0xff}, // Padding Exceeds 7 bits
|
||||
{0x1f, 0xff}, // {"a", 1 byte excess padding}
|
||||
{0x1f, 0xff, 0xff}, // {"a", 2 byte excess padding}
|
||||
{0x1f, 0xff, 0xff, 0xff}, // {"a", 3 byte excess padding}
|
||||
{0xff, 0x9f, 0xff, 0xff, 0xff}, // {"a", 29 bit excess padding}
|
||||
{'R', 0xbc, '0', 0xff, 0xff, 0xff, 0xff}, // Padding ends on partial symbol.
|
||||
}
|
||||
for i, in := range tests {
|
||||
var buf bytes.Buffer
|
||||
if _, err := HuffmanDecode(&buf, in); err != ErrInvalidHuffman {
|
||||
t.Errorf("test-%d: decode(%q) = %v; want ErrInvalidHuffman", i, in, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHuffmanDecodeEOS(t *testing.T) {
|
||||
in := []byte{0xff, 0xff, 0xff, 0xff, 0xfc} // {EOS, "?"}
|
||||
var buf bytes.Buffer
|
||||
if _, err := HuffmanDecode(&buf, in); err != ErrInvalidHuffman {
|
||||
t.Errorf("error = %v; want ErrInvalidHuffman", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHuffmanDecodeMaxLengthOnTrailingByte(t *testing.T) {
|
||||
in := []byte{0x00, 0x01} // {"0", "0", "0"}
|
||||
var buf bytes.Buffer
|
||||
if err := huffmanDecode(&buf, 2, in); err != ErrStringLength {
|
||||
t.Errorf("error = %v; want ErrStringLength", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHuffmanDecodeCorruptPadding(t *testing.T) {
|
||||
in := []byte{0x00}
|
||||
var buf bytes.Buffer
|
||||
if _, err := HuffmanDecode(&buf, in); err != ErrInvalidHuffman {
|
||||
t.Errorf("error = %v; want ErrInvalidHuffman", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHuffmanDecode(t *testing.T) {
|
||||
tests := []struct {
|
||||
inHex, want string
|
||||
}{
|
||||
{"f1e3 c2e5 f23a 6ba0 ab90 f4ff", "www.example.com"},
|
||||
{"a8eb 1064 9cbf", "no-cache"},
|
||||
{"25a8 49e9 5ba9 7d7f", "custom-key"},
|
||||
{"25a8 49e9 5bb8 e8b4 bf", "custom-value"},
|
||||
{"6402", "302"},
|
||||
{"aec3 771a 4b", "private"},
|
||||
{"d07a be94 1054 d444 a820 0595 040b 8166 e082 a62d 1bff", "Mon, 21 Oct 2013 20:13:21 GMT"},
|
||||
{"9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 d3", "https://www.example.com"},
|
||||
{"9bd9 ab", "gzip"},
|
||||
{"94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07",
|
||||
"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
var buf bytes.Buffer
|
||||
in, err := hex.DecodeString(strings.Replace(tt.inHex, " ", "", -1))
|
||||
if err != nil {
|
||||
t.Errorf("%d. hex input error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if _, err := HuffmanDecode(&buf, in); err != nil {
|
||||
t.Errorf("%d. decode error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if got := buf.String(); tt.want != got {
|
||||
t.Errorf("%d. decode = %q; want %q", i, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendHuffmanString(t *testing.T) {
|
||||
tests := []struct {
|
||||
in, want string
|
||||
}{
|
||||
{"www.example.com", "f1e3 c2e5 f23a 6ba0 ab90 f4ff"},
|
||||
{"no-cache", "a8eb 1064 9cbf"},
|
||||
{"custom-key", "25a8 49e9 5ba9 7d7f"},
|
||||
{"custom-value", "25a8 49e9 5bb8 e8b4 bf"},
|
||||
{"302", "6402"},
|
||||
{"private", "aec3 771a 4b"},
|
||||
{"Mon, 21 Oct 2013 20:13:21 GMT", "d07a be94 1054 d444 a820 0595 040b 8166 e082 a62d 1bff"},
|
||||
{"https://www.example.com", "9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 d3"},
|
||||
{"gzip", "9bd9 ab"},
|
||||
{"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
|
||||
"94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07"},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
buf := []byte{}
|
||||
want := strings.Replace(tt.want, " ", "", -1)
|
||||
buf = AppendHuffmanString(buf, tt.in)
|
||||
if got := hex.EncodeToString(buf); want != got {
|
||||
t.Errorf("%d. encode = %q; want %q", i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHuffmanMaxStrLen(t *testing.T) {
|
||||
const msg = "Some string"
|
||||
huff := AppendHuffmanString(nil, msg)
|
||||
|
||||
testGood := func(max int) {
|
||||
var out bytes.Buffer
|
||||
if err := huffmanDecode(&out, max, huff); err != nil {
|
||||
t.Errorf("For maxLen=%d, unexpected error: %v", max, err)
|
||||
}
|
||||
if out.String() != msg {
|
||||
t.Errorf("For maxLen=%d, out = %q; want %q", max, out.String(), msg)
|
||||
}
|
||||
}
|
||||
testGood(0)
|
||||
testGood(len(msg))
|
||||
testGood(len(msg) + 1)
|
||||
|
||||
var out bytes.Buffer
|
||||
if err := huffmanDecode(&out, len(msg)-1, huff); err != ErrStringLength {
|
||||
t.Errorf("err = %v; want ErrStringLength", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHuffmanRoundtripStress(t *testing.T) {
|
||||
const Len = 50 // of uncompressed string
|
||||
input := make([]byte, Len)
|
||||
var output bytes.Buffer
|
||||
var huff []byte
|
||||
|
||||
n := 5000
|
||||
if testing.Short() {
|
||||
n = 100
|
||||
}
|
||||
seed := time.Now().UnixNano()
|
||||
t.Logf("Seed = %v", seed)
|
||||
src := rand.New(rand.NewSource(seed))
|
||||
var encSize int64
|
||||
for i := 0; i < n; i++ {
|
||||
for l := range input {
|
||||
input[l] = byte(src.Intn(256))
|
||||
}
|
||||
huff = AppendHuffmanString(huff[:0], string(input))
|
||||
encSize += int64(len(huff))
|
||||
output.Reset()
|
||||
if err := huffmanDecode(&output, 0, huff); err != nil {
|
||||
t.Errorf("Failed to decode %q -> %q -> error %v", input, huff, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(output.Bytes(), input) {
|
||||
t.Errorf("Roundtrip failure on %q -> %q -> %q", input, huff, output.Bytes())
|
||||
}
|
||||
}
|
||||
t.Logf("Compressed size of original: %0.02f%% (%v -> %v)", 100*(float64(encSize)/(Len*float64(n))), Len*n, encSize)
|
||||
}
|
||||
|
||||
func TestHuffmanDecodeFuzz(t *testing.T) {
|
||||
const Len = 50 // of compressed
|
||||
var buf, zbuf bytes.Buffer
|
||||
|
||||
n := 5000
|
||||
if testing.Short() {
|
||||
n = 100
|
||||
}
|
||||
seed := time.Now().UnixNano()
|
||||
t.Logf("Seed = %v", seed)
|
||||
src := rand.New(rand.NewSource(seed))
|
||||
numFail := 0
|
||||
for i := 0; i < n; i++ {
|
||||
zbuf.Reset()
|
||||
if i == 0 {
|
||||
// Start with at least one invalid one.
|
||||
zbuf.WriteString("00\x91\xff\xff\xff\xff\xc8")
|
||||
} else {
|
||||
for l := 0; l < Len; l++ {
|
||||
zbuf.WriteByte(byte(src.Intn(256)))
|
||||
}
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
if err := huffmanDecode(&buf, 0, zbuf.Bytes()); err != nil {
|
||||
if err == ErrInvalidHuffman {
|
||||
numFail++
|
||||
continue
|
||||
}
|
||||
t.Errorf("Failed to decode %q: %v", zbuf.Bytes(), err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
t.Logf("%0.02f%% are invalid (%d / %d)", 100*float64(numFail)/float64(n), numFail, n)
|
||||
if numFail < 1 {
|
||||
t.Error("expected at least one invalid huffman encoding (test starts with one)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadVarInt(t *testing.T) {
|
||||
type res struct {
|
||||
i uint64
|
||||
consumed int
|
||||
err error
|
||||
}
|
||||
tests := []struct {
|
||||
n byte
|
||||
p []byte
|
||||
want res
|
||||
}{
|
||||
// Fits in a byte:
|
||||
{1, []byte{0}, res{0, 1, nil}},
|
||||
{2, []byte{2}, res{2, 1, nil}},
|
||||
{3, []byte{6}, res{6, 1, nil}},
|
||||
{4, []byte{14}, res{14, 1, nil}},
|
||||
{5, []byte{30}, res{30, 1, nil}},
|
||||
{6, []byte{62}, res{62, 1, nil}},
|
||||
{7, []byte{126}, res{126, 1, nil}},
|
||||
{8, []byte{254}, res{254, 1, nil}},
|
||||
|
||||
// Doesn't fit in a byte:
|
||||
{1, []byte{1}, res{0, 0, errNeedMore}},
|
||||
{2, []byte{3}, res{0, 0, errNeedMore}},
|
||||
{3, []byte{7}, res{0, 0, errNeedMore}},
|
||||
{4, []byte{15}, res{0, 0, errNeedMore}},
|
||||
{5, []byte{31}, res{0, 0, errNeedMore}},
|
||||
{6, []byte{63}, res{0, 0, errNeedMore}},
|
||||
{7, []byte{127}, res{0, 0, errNeedMore}},
|
||||
{8, []byte{255}, res{0, 0, errNeedMore}},
|
||||
|
||||
// Ignoring top bits:
|
||||
{5, []byte{255, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 111
|
||||
{5, []byte{159, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 100
|
||||
{5, []byte{191, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 101
|
||||
|
||||
// Extra byte:
|
||||
{5, []byte{191, 154, 10, 2}, res{1337, 3, nil}}, // extra byte
|
||||
|
||||
// Short a byte:
|
||||
{5, []byte{191, 154}, res{0, 0, errNeedMore}},
|
||||
|
||||
// integer overflow:
|
||||
{1, []byte{255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, res{0, 0, errVarintOverflow}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
i, remain, err := readVarInt(tt.n, tt.p)
|
||||
consumed := len(tt.p) - len(remain)
|
||||
got := res{i, consumed, err}
|
||||
if got != tt.want {
|
||||
t.Errorf("readVarInt(%d, %v ~ %x) = %+v; want %+v", tt.n, tt.p, tt.p, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fuzz crash, originally reported at https://github.com/bradfitz/http2/issues/56
|
||||
func TestHuffmanFuzzCrash(t *testing.T) {
|
||||
got, err := HuffmanDecodeToString([]byte("00\x91\xff\xff\xff\xff\xc8"))
|
||||
if got != "" {
|
||||
t.Errorf("Got %q; want empty string", got)
|
||||
}
|
||||
if err != ErrInvalidHuffman {
|
||||
t.Errorf("Err = %v; want ErrInvalidHuffman", err)
|
||||
}
|
||||
}
|
||||
|
||||
func pair(name, value string) HeaderField {
|
||||
return HeaderField{Name: name, Value: value}
|
||||
}
|
||||
|
||||
func dehex(s string) []byte {
|
||||
s = strings.Replace(s, " ", "", -1)
|
||||
s = strings.Replace(s, "\n", "", -1)
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func TestEmitEnabled(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
enc := NewEncoder(&buf)
|
||||
enc.WriteField(HeaderField{Name: "foo", Value: "bar"})
|
||||
enc.WriteField(HeaderField{Name: "foo", Value: "bar"})
|
||||
|
||||
numCallback := 0
|
||||
var dec *Decoder
|
||||
dec = NewDecoder(8<<20, func(HeaderField) {
|
||||
numCallback++
|
||||
dec.SetEmitEnabled(false)
|
||||
})
|
||||
if !dec.EmitEnabled() {
|
||||
t.Errorf("initial emit enabled = false; want true")
|
||||
}
|
||||
if _, err := dec.Write(buf.Bytes()); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if numCallback != 1 {
|
||||
t.Errorf("num callbacks = %d; want 1", numCallback)
|
||||
}
|
||||
if dec.EmitEnabled() {
|
||||
t.Errorf("emit enabled = true; want false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveBufLimit(t *testing.T) {
|
||||
const maxStr = 1 << 10
|
||||
var got []HeaderField
|
||||
dec := NewDecoder(initialHeaderTableSize, func(hf HeaderField) {
|
||||
got = append(got, hf)
|
||||
})
|
||||
dec.SetMaxStringLength(maxStr)
|
||||
var frag []byte
|
||||
frag = append(frag[:0], encodeTypeByte(false, false))
|
||||
frag = appendVarInt(frag, 7, 3)
|
||||
frag = append(frag, "foo"...)
|
||||
frag = appendVarInt(frag, 7, 3)
|
||||
frag = append(frag, "bar"...)
|
||||
|
||||
if _, err := dec.Write(frag); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := []HeaderField{{Name: "foo", Value: "bar"}}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("After small writes, got %v; want %v", got, want)
|
||||
}
|
||||
|
||||
frag = append(frag[:0], encodeTypeByte(false, false))
|
||||
frag = appendVarInt(frag, 7, maxStr*3)
|
||||
frag = append(frag, make([]byte, maxStr*3)...)
|
||||
|
||||
_, err := dec.Write(frag)
|
||||
if err != ErrStringLength {
|
||||
t.Fatalf("Write error = %v; want ErrStringLength", err)
|
||||
}
|
||||
}
|
||||
214
vendor/golang.org/x/net/http2/hpack/tables_test.go
generated
vendored
214
vendor/golang.org/x/net/http2/hpack/tables_test.go
generated
vendored
|
|
@ -1,214 +0,0 @@
|
|||
// Copyright 2017 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 hpack
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHeaderFieldTable(t *testing.T) {
|
||||
table := &headerFieldTable{}
|
||||
table.init()
|
||||
table.addEntry(pair("key1", "value1-1"))
|
||||
table.addEntry(pair("key2", "value2-1"))
|
||||
table.addEntry(pair("key1", "value1-2"))
|
||||
table.addEntry(pair("key3", "value3-1"))
|
||||
table.addEntry(pair("key4", "value4-1"))
|
||||
table.addEntry(pair("key2", "value2-2"))
|
||||
|
||||
// Tests will be run twice: once before evicting anything, and
|
||||
// again after evicting the three oldest entries.
|
||||
tests := []struct {
|
||||
f HeaderField
|
||||
beforeWantStaticI uint64
|
||||
beforeWantMatch bool
|
||||
afterWantStaticI uint64
|
||||
afterWantMatch bool
|
||||
}{
|
||||
{HeaderField{"key1", "value1-1", false}, 1, true, 0, false},
|
||||
{HeaderField{"key1", "value1-2", false}, 3, true, 0, false},
|
||||
{HeaderField{"key1", "value1-3", false}, 3, false, 0, false},
|
||||
{HeaderField{"key2", "value2-1", false}, 2, true, 3, false},
|
||||
{HeaderField{"key2", "value2-2", false}, 6, true, 3, true},
|
||||
{HeaderField{"key2", "value2-3", false}, 6, false, 3, false},
|
||||
{HeaderField{"key4", "value4-1", false}, 5, true, 2, true},
|
||||
// Name match only, because sensitive.
|
||||
{HeaderField{"key4", "value4-1", true}, 5, false, 2, false},
|
||||
// Key not found.
|
||||
{HeaderField{"key5", "value5-x", false}, 0, false, 0, false},
|
||||
}
|
||||
|
||||
staticToDynamic := func(i uint64) uint64 {
|
||||
if i == 0 {
|
||||
return 0
|
||||
}
|
||||
return uint64(table.len()) - i + 1 // dynamic is the reversed table
|
||||
}
|
||||
|
||||
searchStatic := func(f HeaderField) (uint64, bool) {
|
||||
old := staticTable
|
||||
staticTable = table
|
||||
defer func() { staticTable = old }()
|
||||
return staticTable.search(f)
|
||||
}
|
||||
|
||||
searchDynamic := func(f HeaderField) (uint64, bool) {
|
||||
return table.search(f)
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
gotI, gotMatch := searchStatic(test.f)
|
||||
if wantI, wantMatch := test.beforeWantStaticI, test.beforeWantMatch; gotI != wantI || gotMatch != wantMatch {
|
||||
t.Errorf("before evictions: searchStatic(%+v)=%v,%v want %v,%v", test.f, gotI, gotMatch, wantI, wantMatch)
|
||||
}
|
||||
gotI, gotMatch = searchDynamic(test.f)
|
||||
wantDynamicI := staticToDynamic(test.beforeWantStaticI)
|
||||
if wantI, wantMatch := wantDynamicI, test.beforeWantMatch; gotI != wantI || gotMatch != wantMatch {
|
||||
t.Errorf("before evictions: searchDynamic(%+v)=%v,%v want %v,%v", test.f, gotI, gotMatch, wantI, wantMatch)
|
||||
}
|
||||
}
|
||||
|
||||
table.evictOldest(3)
|
||||
|
||||
for _, test := range tests {
|
||||
gotI, gotMatch := searchStatic(test.f)
|
||||
if wantI, wantMatch := test.afterWantStaticI, test.afterWantMatch; gotI != wantI || gotMatch != wantMatch {
|
||||
t.Errorf("after evictions: searchStatic(%+v)=%v,%v want %v,%v", test.f, gotI, gotMatch, wantI, wantMatch)
|
||||
}
|
||||
gotI, gotMatch = searchDynamic(test.f)
|
||||
wantDynamicI := staticToDynamic(test.afterWantStaticI)
|
||||
if wantI, wantMatch := wantDynamicI, test.afterWantMatch; gotI != wantI || gotMatch != wantMatch {
|
||||
t.Errorf("after evictions: searchDynamic(%+v)=%v,%v want %v,%v", test.f, gotI, gotMatch, wantI, wantMatch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeaderFieldTable_LookupMapEviction(t *testing.T) {
|
||||
table := &headerFieldTable{}
|
||||
table.init()
|
||||
table.addEntry(pair("key1", "value1-1"))
|
||||
table.addEntry(pair("key2", "value2-1"))
|
||||
table.addEntry(pair("key1", "value1-2"))
|
||||
table.addEntry(pair("key3", "value3-1"))
|
||||
table.addEntry(pair("key4", "value4-1"))
|
||||
table.addEntry(pair("key2", "value2-2"))
|
||||
|
||||
// evict all pairs
|
||||
table.evictOldest(table.len())
|
||||
|
||||
if l := table.len(); l > 0 {
|
||||
t.Errorf("table.len() = %d, want 0", l)
|
||||
}
|
||||
|
||||
if l := len(table.byName); l > 0 {
|
||||
t.Errorf("len(table.byName) = %d, want 0", l)
|
||||
}
|
||||
|
||||
if l := len(table.byNameValue); l > 0 {
|
||||
t.Errorf("len(table.byNameValue) = %d, want 0", l)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStaticTable(t *testing.T) {
|
||||
fromSpec := `
|
||||
+-------+-----------------------------+---------------+
|
||||
| 1 | :authority | |
|
||||
| 2 | :method | GET |
|
||||
| 3 | :method | POST |
|
||||
| 4 | :path | / |
|
||||
| 5 | :path | /index.html |
|
||||
| 6 | :scheme | http |
|
||||
| 7 | :scheme | https |
|
||||
| 8 | :status | 200 |
|
||||
| 9 | :status | 204 |
|
||||
| 10 | :status | 206 |
|
||||
| 11 | :status | 304 |
|
||||
| 12 | :status | 400 |
|
||||
| 13 | :status | 404 |
|
||||
| 14 | :status | 500 |
|
||||
| 15 | accept-charset | |
|
||||
| 16 | accept-encoding | gzip, deflate |
|
||||
| 17 | accept-language | |
|
||||
| 18 | accept-ranges | |
|
||||
| 19 | accept | |
|
||||
| 20 | access-control-allow-origin | |
|
||||
| 21 | age | |
|
||||
| 22 | allow | |
|
||||
| 23 | authorization | |
|
||||
| 24 | cache-control | |
|
||||
| 25 | content-disposition | |
|
||||
| 26 | content-encoding | |
|
||||
| 27 | content-language | |
|
||||
| 28 | content-length | |
|
||||
| 29 | content-location | |
|
||||
| 30 | content-range | |
|
||||
| 31 | content-type | |
|
||||
| 32 | cookie | |
|
||||
| 33 | date | |
|
||||
| 34 | etag | |
|
||||
| 35 | expect | |
|
||||
| 36 | expires | |
|
||||
| 37 | from | |
|
||||
| 38 | host | |
|
||||
| 39 | if-match | |
|
||||
| 40 | if-modified-since | |
|
||||
| 41 | if-none-match | |
|
||||
| 42 | if-range | |
|
||||
| 43 | if-unmodified-since | |
|
||||
| 44 | last-modified | |
|
||||
| 45 | link | |
|
||||
| 46 | location | |
|
||||
| 47 | max-forwards | |
|
||||
| 48 | proxy-authenticate | |
|
||||
| 49 | proxy-authorization | |
|
||||
| 50 | range | |
|
||||
| 51 | referer | |
|
||||
| 52 | refresh | |
|
||||
| 53 | retry-after | |
|
||||
| 54 | server | |
|
||||
| 55 | set-cookie | |
|
||||
| 56 | strict-transport-security | |
|
||||
| 57 | transfer-encoding | |
|
||||
| 58 | user-agent | |
|
||||
| 59 | vary | |
|
||||
| 60 | via | |
|
||||
| 61 | www-authenticate | |
|
||||
+-------+-----------------------------+---------------+
|
||||
`
|
||||
bs := bufio.NewScanner(strings.NewReader(fromSpec))
|
||||
re := regexp.MustCompile(`\| (\d+)\s+\| (\S+)\s*\| (\S(.*\S)?)?\s+\|`)
|
||||
for bs.Scan() {
|
||||
l := bs.Text()
|
||||
if !strings.Contains(l, "|") {
|
||||
continue
|
||||
}
|
||||
m := re.FindStringSubmatch(l)
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
i, err := strconv.Atoi(m[1])
|
||||
if err != nil {
|
||||
t.Errorf("Bogus integer on line %q", l)
|
||||
continue
|
||||
}
|
||||
if i < 1 || i > staticTable.len() {
|
||||
t.Errorf("Bogus index %d on line %q", i, l)
|
||||
continue
|
||||
}
|
||||
if got, want := staticTable.ents[i-1].Name, m[2]; got != want {
|
||||
t.Errorf("header index %d name = %q; want %q", i, got, want)
|
||||
}
|
||||
if got, want := staticTable.ents[i-1].Value, m[3]; got != want {
|
||||
t.Errorf("header index %d value = %q; want %q", i, got, want)
|
||||
}
|
||||
}
|
||||
if err := bs.Err(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
8
vendor/golang.org/x/net/http2/http2.go
generated
vendored
8
vendor/golang.org/x/net/http2/http2.go
generated
vendored
|
|
@ -29,7 +29,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/lex/httplex"
|
||||
"golang.org/x/net/http/httpguts"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -179,7 +179,7 @@ var (
|
|||
)
|
||||
|
||||
// validWireHeaderFieldName reports whether v is a valid header field
|
||||
// name (key). See httplex.ValidHeaderName for the base rules.
|
||||
// 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
|
||||
|
|
@ -191,7 +191,7 @@ func validWireHeaderFieldName(v string) bool {
|
|||
return false
|
||||
}
|
||||
for _, r := range v {
|
||||
if !httplex.IsTokenRune(r) {
|
||||
if !httpguts.IsTokenRune(r) {
|
||||
return false
|
||||
}
|
||||
if 'A' <= r && r <= 'Z' {
|
||||
|
|
@ -312,7 +312,7 @@ func mustUint31(v int32) uint32 {
|
|||
}
|
||||
|
||||
// bodyAllowedForStatus reports whether a given response status code
|
||||
// permits a body. See RFC 2616, section 4.4.
|
||||
// permits a body. See RFC 7230, section 3.3.
|
||||
func bodyAllowedForStatus(status int) bool {
|
||||
switch {
|
||||
case status >= 100 && status <= 199:
|
||||
|
|
|
|||
199
vendor/golang.org/x/net/http2/http2_test.go
generated
vendored
199
vendor/golang.org/x/net/http2/http2_test.go
generated
vendored
|
|
@ -1,199 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/http2/hpack"
|
||||
)
|
||||
|
||||
var knownFailing = flag.Bool("known_failing", false, "Run known-failing tests.")
|
||||
|
||||
func condSkipFailingTest(t *testing.T) {
|
||||
if !*knownFailing {
|
||||
t.Skip("Skipping known-failing test without --known_failing")
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
inTests = true
|
||||
DebugGoroutines = true
|
||||
flag.BoolVar(&VerboseLogs, "verboseh2", VerboseLogs, "Verbose HTTP/2 debug logging")
|
||||
}
|
||||
|
||||
func TestSettingString(t *testing.T) {
|
||||
tests := []struct {
|
||||
s Setting
|
||||
want string
|
||||
}{
|
||||
{Setting{SettingMaxFrameSize, 123}, "[MAX_FRAME_SIZE = 123]"},
|
||||
{Setting{1<<16 - 1, 123}, "[UNKNOWN_SETTING_65535 = 123]"},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
got := fmt.Sprint(tt.s)
|
||||
if got != tt.want {
|
||||
t.Errorf("%d. for %#v, string = %q; want %q", i, tt.s, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type twriter struct {
|
||||
t testing.TB
|
||||
st *serverTester // optional
|
||||
}
|
||||
|
||||
func (w twriter) Write(p []byte) (n int, err error) {
|
||||
if w.st != nil {
|
||||
ps := string(p)
|
||||
for _, phrase := range w.st.logFilter {
|
||||
if strings.Contains(ps, phrase) {
|
||||
return len(p), nil // no logging
|
||||
}
|
||||
}
|
||||
}
|
||||
w.t.Logf("%s", p)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// like encodeHeader, but don't add implicit pseudo headers.
|
||||
func encodeHeaderNoImplicit(t *testing.T, headers ...string) []byte {
|
||||
var buf bytes.Buffer
|
||||
enc := hpack.NewEncoder(&buf)
|
||||
for len(headers) > 0 {
|
||||
k, v := headers[0], headers[1]
|
||||
headers = headers[2:]
|
||||
if err := enc.WriteField(hpack.HeaderField{Name: k, Value: v}); err != nil {
|
||||
t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err)
|
||||
}
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// Verify that curl has http2.
|
||||
func requireCurl(t *testing.T) {
|
||||
out, err := dockerLogs(curl(t, "--version"))
|
||||
if err != nil {
|
||||
t.Skipf("failed to determine curl features; skipping test")
|
||||
}
|
||||
if !strings.Contains(string(out), "HTTP2") {
|
||||
t.Skip("curl doesn't support HTTP2; skipping test")
|
||||
}
|
||||
}
|
||||
|
||||
func curl(t *testing.T, args ...string) (container string) {
|
||||
out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "gohttp2/curl"}, args...)...).Output()
|
||||
if err != nil {
|
||||
t.Skipf("Failed to run curl in docker: %v, %s", err, out)
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
// Verify that h2load exists.
|
||||
func requireH2load(t *testing.T) {
|
||||
out, err := dockerLogs(h2load(t, "--version"))
|
||||
if err != nil {
|
||||
t.Skipf("failed to probe h2load; skipping test: %s", out)
|
||||
}
|
||||
if !strings.Contains(string(out), "h2load nghttp2/") {
|
||||
t.Skipf("h2load not present; skipping test. (Output=%q)", out)
|
||||
}
|
||||
}
|
||||
|
||||
func h2load(t *testing.T, args ...string) (container string) {
|
||||
out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "--entrypoint=/usr/local/bin/h2load", "gohttp2/curl"}, args...)...).Output()
|
||||
if err != nil {
|
||||
t.Skipf("Failed to run h2load in docker: %v, %s", err, out)
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
type puppetCommand struct {
|
||||
fn func(w http.ResponseWriter, r *http.Request)
|
||||
done chan<- bool
|
||||
}
|
||||
|
||||
type handlerPuppet struct {
|
||||
ch chan puppetCommand
|
||||
}
|
||||
|
||||
func newHandlerPuppet() *handlerPuppet {
|
||||
return &handlerPuppet{
|
||||
ch: make(chan puppetCommand),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *handlerPuppet) act(w http.ResponseWriter, r *http.Request) {
|
||||
for cmd := range p.ch {
|
||||
cmd.fn(w, r)
|
||||
cmd.done <- true
|
||||
}
|
||||
}
|
||||
|
||||
func (p *handlerPuppet) done() { close(p.ch) }
|
||||
func (p *handlerPuppet) do(fn func(http.ResponseWriter, *http.Request)) {
|
||||
done := make(chan bool)
|
||||
p.ch <- puppetCommand{fn, done}
|
||||
<-done
|
||||
}
|
||||
func dockerLogs(container string) ([]byte, error) {
|
||||
out, err := exec.Command("docker", "wait", container).CombinedOutput()
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
exitStatus, err := strconv.Atoi(strings.TrimSpace(string(out)))
|
||||
if err != nil {
|
||||
return out, errors.New("unexpected exit status from docker wait")
|
||||
}
|
||||
out, err = exec.Command("docker", "logs", container).CombinedOutput()
|
||||
exec.Command("docker", "rm", container).Run()
|
||||
if err == nil && exitStatus != 0 {
|
||||
err = fmt.Errorf("exit status %d: %s", exitStatus, out)
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
func kill(container string) {
|
||||
exec.Command("docker", "kill", container).Run()
|
||||
exec.Command("docker", "rm", container).Run()
|
||||
}
|
||||
|
||||
func cleanDate(res *http.Response) {
|
||||
if d := res.Header["Date"]; len(d) == 1 {
|
||||
d[0] = "XXX"
|
||||
}
|
||||
}
|
||||
|
||||
func TestSorterPoolAllocs(t *testing.T) {
|
||||
ss := []string{"a", "b", "c"}
|
||||
h := http.Header{
|
||||
"a": nil,
|
||||
"b": nil,
|
||||
"c": nil,
|
||||
}
|
||||
sorter := new(sorter)
|
||||
|
||||
if allocs := testing.AllocsPerRun(100, func() {
|
||||
sorter.SortStrings(ss)
|
||||
}); allocs >= 1 {
|
||||
t.Logf("SortStrings allocs = %v; want <1", allocs)
|
||||
}
|
||||
|
||||
if allocs := testing.AllocsPerRun(5, func() {
|
||||
if len(sorter.Keys(h)) != 3 {
|
||||
t.Fatal("wrong result")
|
||||
}
|
||||
}); allocs > 0 {
|
||||
t.Logf("Keys allocs = %v; want <1", allocs)
|
||||
}
|
||||
}
|
||||
130
vendor/golang.org/x/net/http2/pipe_test.go
generated
vendored
130
vendor/golang.org/x/net/http2/pipe_test.go
generated
vendored
|
|
@ -1,130 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPipeClose(t *testing.T) {
|
||||
var p pipe
|
||||
p.b = new(bytes.Buffer)
|
||||
a := errors.New("a")
|
||||
b := errors.New("b")
|
||||
p.CloseWithError(a)
|
||||
p.CloseWithError(b)
|
||||
_, err := p.Read(make([]byte, 1))
|
||||
if err != a {
|
||||
t.Errorf("err = %v want %v", err, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipeDoneChan(t *testing.T) {
|
||||
var p pipe
|
||||
done := p.Done()
|
||||
select {
|
||||
case <-done:
|
||||
t.Fatal("done too soon")
|
||||
default:
|
||||
}
|
||||
p.CloseWithError(io.EOF)
|
||||
select {
|
||||
case <-done:
|
||||
default:
|
||||
t.Fatal("should be done")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipeDoneChan_ErrFirst(t *testing.T) {
|
||||
var p pipe
|
||||
p.CloseWithError(io.EOF)
|
||||
done := p.Done()
|
||||
select {
|
||||
case <-done:
|
||||
default:
|
||||
t.Fatal("should be done")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipeDoneChan_Break(t *testing.T) {
|
||||
var p pipe
|
||||
done := p.Done()
|
||||
select {
|
||||
case <-done:
|
||||
t.Fatal("done too soon")
|
||||
default:
|
||||
}
|
||||
p.BreakWithError(io.EOF)
|
||||
select {
|
||||
case <-done:
|
||||
default:
|
||||
t.Fatal("should be done")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipeDoneChan_Break_ErrFirst(t *testing.T) {
|
||||
var p pipe
|
||||
p.BreakWithError(io.EOF)
|
||||
done := p.Done()
|
||||
select {
|
||||
case <-done:
|
||||
default:
|
||||
t.Fatal("should be done")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipeCloseWithError(t *testing.T) {
|
||||
p := &pipe{b: new(bytes.Buffer)}
|
||||
const body = "foo"
|
||||
io.WriteString(p, body)
|
||||
a := errors.New("test error")
|
||||
p.CloseWithError(a)
|
||||
all, err := ioutil.ReadAll(p)
|
||||
if string(all) != body {
|
||||
t.Errorf("read bytes = %q; want %q", all, body)
|
||||
}
|
||||
if err != a {
|
||||
t.Logf("read error = %v, %v", err, a)
|
||||
}
|
||||
// Read and Write should fail.
|
||||
if n, err := p.Write([]byte("abc")); err != errClosedPipeWrite || n != 0 {
|
||||
t.Errorf("Write(abc) after close\ngot %v, %v\nwant 0, %v", n, err, errClosedPipeWrite)
|
||||
}
|
||||
if n, err := p.Read(make([]byte, 1)); err == nil || n != 0 {
|
||||
t.Errorf("Read() after close\ngot %v, nil\nwant 0, %v", n, errClosedPipeWrite)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipeBreakWithError(t *testing.T) {
|
||||
p := &pipe{b: new(bytes.Buffer)}
|
||||
io.WriteString(p, "foo")
|
||||
a := errors.New("test err")
|
||||
p.BreakWithError(a)
|
||||
all, err := ioutil.ReadAll(p)
|
||||
if string(all) != "" {
|
||||
t.Errorf("read bytes = %q; want empty string", all)
|
||||
}
|
||||
if err != a {
|
||||
t.Logf("read error = %v, %v", err, a)
|
||||
}
|
||||
if p.b != nil {
|
||||
t.Errorf("buffer should be nil after BreakWithError")
|
||||
}
|
||||
// Write should succeed silently.
|
||||
if n, err := p.Write([]byte("abc")); err != nil || n != 3 {
|
||||
t.Errorf("Write(abc) after break\ngot %v, %v\nwant 0, nil", n, err)
|
||||
}
|
||||
if p.b != nil {
|
||||
t.Errorf("buffer should be nil after Write")
|
||||
}
|
||||
// Read should fail.
|
||||
if n, err := p.Read(make([]byte, 1)); err == nil || n != 0 {
|
||||
t.Errorf("Read() after close\ngot %v, nil\nwant 0, not nil", n)
|
||||
}
|
||||
}
|
||||
112
vendor/golang.org/x/net/http2/server.go
generated
vendored
112
vendor/golang.org/x/net/http2/server.go
generated
vendored
|
|
@ -46,6 +46,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
)
|
||||
|
||||
|
|
@ -406,7 +407,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
|||
// addresses during development.
|
||||
//
|
||||
// TODO: optionally enforce? Or enforce at the time we receive
|
||||
// a new request, and verify the the ServerName matches the :authority?
|
||||
// a new request, and verify the ServerName matches the :authority?
|
||||
// But that precludes proxy situations, perhaps.
|
||||
//
|
||||
// So for now, do nothing here again.
|
||||
|
|
@ -652,7 +653,7 @@ func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
|
|||
if err == nil {
|
||||
return
|
||||
}
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF || isClosedConnError(err) {
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF || isClosedConnError(err) || err == errPrefaceTimeout {
|
||||
// Boring, expected errors.
|
||||
sc.vlogf(format, args...)
|
||||
} else {
|
||||
|
|
@ -897,8 +898,11 @@ func (sc *serverConn) sendServeMsg(msg interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
// readPreface reads the ClientPreface greeting from the peer
|
||||
// or returns an error on timeout or an invalid greeting.
|
||||
var errPrefaceTimeout = errors.New("timeout waiting for client preface")
|
||||
|
||||
// readPreface reads the ClientPreface greeting from the peer or
|
||||
// returns errPrefaceTimeout on timeout, or an error if the greeting
|
||||
// is invalid.
|
||||
func (sc *serverConn) readPreface() error {
|
||||
errc := make(chan error, 1)
|
||||
go func() {
|
||||
|
|
@ -916,7 +920,7 @@ func (sc *serverConn) readPreface() error {
|
|||
defer timer.Stop()
|
||||
select {
|
||||
case <-timer.C:
|
||||
return errors.New("timeout waiting for client preface")
|
||||
return errPrefaceTimeout
|
||||
case err := <-errc:
|
||||
if err == nil {
|
||||
if VerboseLogs {
|
||||
|
|
@ -1604,7 +1608,10 @@ 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 {
|
||||
st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
|
||||
return streamError(id, ErrCodeStreamClosed)
|
||||
// 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
|
||||
// DATA frame payload lengths that form the body.
|
||||
return streamError(id, ErrCodeProtocol)
|
||||
}
|
||||
if f.Length > 0 {
|
||||
// Check whether the client has flow control quota.
|
||||
|
|
@ -1814,7 +1821,7 @@ func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
|||
if st.trailer != nil {
|
||||
for _, hf := range f.RegularFields() {
|
||||
key := sc.canonicalHeader(hf.Name)
|
||||
if !ValidTrailerHeader(key) {
|
||||
if !httpguts.ValidTrailerHeader(key) {
|
||||
// TODO: send more details to the peer somehow. But http2 has
|
||||
// no way to send debug data at a stream level. Discuss with
|
||||
// HTTP folk.
|
||||
|
|
@ -2281,8 +2288,8 @@ func (rws *responseWriterState) hasTrailers() bool { return len(rws.trailers) !=
|
|||
// written in the trailers at the end of the response.
|
||||
func (rws *responseWriterState) declareTrailer(k string) {
|
||||
k = http.CanonicalHeaderKey(k)
|
||||
if !ValidTrailerHeader(k) {
|
||||
// Forbidden by RFC 2616 14.40.
|
||||
if !httpguts.ValidTrailerHeader(k) {
|
||||
// Forbidden by RFC 7230, section 4.1.2.
|
||||
rws.conn.logf("ignoring invalid trailer %q", k)
|
||||
return
|
||||
}
|
||||
|
|
@ -2305,6 +2312,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
|||
isHeadResp := rws.req.Method == "HEAD"
|
||||
if !rws.sentHeader {
|
||||
rws.sentHeader = true
|
||||
|
||||
var ctype, clen string
|
||||
if clen = rws.snapHeader.Get("Content-Length"); clen != "" {
|
||||
rws.snapHeader.Del("Content-Length")
|
||||
|
|
@ -2318,10 +2326,33 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
|||
if clen == "" && rws.handlerDone && bodyAllowedForStatus(rws.status) && (len(p) > 0 || !isHeadResp) {
|
||||
clen = strconv.Itoa(len(p))
|
||||
}
|
||||
|
||||
_, hasContentType := rws.snapHeader["Content-Type"]
|
||||
if !hasContentType && bodyAllowedForStatus(rws.status) {
|
||||
ctype = http.DetectContentType(p)
|
||||
if !hasContentType && bodyAllowedForStatus(rws.status) && len(p) > 0 {
|
||||
if cto := rws.snapHeader.Get("X-Content-Type-Options"); strings.EqualFold("nosniff", cto) {
|
||||
// nosniff is an explicit directive not to guess a content-type.
|
||||
// Content-sniffing is no less susceptible to polyglot attacks via
|
||||
// hosted content when done on the server.
|
||||
ctype = "application/octet-stream"
|
||||
rws.conn.logf("http2: WriteHeader called with X-Content-Type-Options:nosniff but no Content-Type")
|
||||
} else {
|
||||
ctype = http.DetectContentType(p)
|
||||
}
|
||||
}
|
||||
|
||||
var noSniff bool
|
||||
if bodyAllowedForStatus(rws.status) && (rws.sentContentLen > 0 || len(p) > 0) {
|
||||
// If the content type triggers client-side sniffing on old browsers,
|
||||
// attach a X-Content-Type-Options header if not present (or explicitly nil).
|
||||
if _, ok := rws.snapHeader["X-Content-Type-Options"]; !ok {
|
||||
if hasContentType {
|
||||
noSniff = httpguts.SniffedContentType(rws.snapHeader.Get("Content-Type"))
|
||||
} else if ctype != "" {
|
||||
noSniff = httpguts.SniffedContentType(ctype)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var date string
|
||||
if _, ok := rws.snapHeader["Date"]; !ok {
|
||||
// TODO(bradfitz): be faster here, like net/http? measure.
|
||||
|
|
@ -2340,6 +2371,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
|
|||
endStream: endStream,
|
||||
contentType: ctype,
|
||||
contentLength: clen,
|
||||
noSniff: noSniff,
|
||||
date: date,
|
||||
})
|
||||
if err != nil {
|
||||
|
|
@ -2403,7 +2435,7 @@ const TrailerPrefix = "Trailer:"
|
|||
// after the header has already been flushed. Because the Go
|
||||
// ResponseWriter interface has no way to set Trailers (only the
|
||||
// Header), and because we didn't want to expand the ResponseWriter
|
||||
// interface, and because nobody used trailers, and because RFC 2616
|
||||
// interface, and because nobody used trailers, and because RFC 7230
|
||||
// says you SHOULD (but not must) predeclare any trailers in the
|
||||
// header, the official ResponseWriter rules said trailers in Go must
|
||||
// be predeclared, and then we reuse the same ResponseWriter.Header()
|
||||
|
|
@ -2487,6 +2519,24 @@ func (w *responseWriter) Header() http.Header {
|
|||
return rws.handlerHeader
|
||||
}
|
||||
|
||||
// checkWriteHeaderCode is a copy of net/http's checkWriteHeaderCode.
|
||||
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).
|
||||
// But for now any three digits.
|
||||
//
|
||||
// We used to send "HTTP/1.1 000 0" on the wire in responses but there's
|
||||
// no equivalent bogus thing we can realistically send in HTTP/2,
|
||||
// so we'll consistently panic instead and help people find their bugs
|
||||
// early. (We can't return an error from WriteHeader even if we wanted to.)
|
||||
if code < 100 || code > 999 {
|
||||
panic(fmt.Sprintf("invalid WriteHeader code %v", code))
|
||||
}
|
||||
}
|
||||
|
||||
func (w *responseWriter) WriteHeader(code int) {
|
||||
rws := w.rws
|
||||
if rws == nil {
|
||||
|
|
@ -2497,6 +2547,7 @@ 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 {
|
||||
|
|
@ -2768,7 +2819,7 @@ func (sc *serverConn) startPush(msg *startPushRequest) {
|
|||
}
|
||||
|
||||
// foreachHeaderElement splits v according to the "#rule" construction
|
||||
// in RFC 2616 section 2.1 and calls fn for each non-empty element.
|
||||
// in RFC 7230 section 7 and calls fn for each non-empty element.
|
||||
func foreachHeaderElement(v string, fn func(string)) {
|
||||
v = textproto.TrimString(v)
|
||||
if v == "" {
|
||||
|
|
@ -2816,41 +2867,6 @@ func new400Handler(err error) http.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// ValidTrailerHeader reports whether name is a valid header field name to appear
|
||||
// in trailers.
|
||||
// See: http://tools.ietf.org/html/rfc7230#section-4.1.2
|
||||
func ValidTrailerHeader(name string) bool {
|
||||
name = http.CanonicalHeaderKey(name)
|
||||
if strings.HasPrefix(name, "If-") || badTrailer[name] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var badTrailer = map[string]bool{
|
||||
"Authorization": true,
|
||||
"Cache-Control": true,
|
||||
"Connection": true,
|
||||
"Content-Encoding": true,
|
||||
"Content-Length": true,
|
||||
"Content-Range": true,
|
||||
"Content-Type": true,
|
||||
"Expect": true,
|
||||
"Host": true,
|
||||
"Keep-Alive": true,
|
||||
"Max-Forwards": true,
|
||||
"Pragma": true,
|
||||
"Proxy-Authenticate": true,
|
||||
"Proxy-Authorization": true,
|
||||
"Proxy-Connection": true,
|
||||
"Range": true,
|
||||
"Realm": true,
|
||||
"Te": true,
|
||||
"Trailer": true,
|
||||
"Transfer-Encoding": true,
|
||||
"Www-Authenticate": true,
|
||||
}
|
||||
|
||||
// h1ServerKeepAlivesDisabled reports whether hs has its keep-alives
|
||||
// disabled. See comments on h1ServerShutdownChan above for why
|
||||
// the code is written this way.
|
||||
|
|
|
|||
521
vendor/golang.org/x/net/http2/server_push_test.go
generated
vendored
521
vendor/golang.org/x/net/http2/server_push_test.go
generated
vendored
|
|
@ -1,521 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestServer_Push_Success(t *testing.T) {
|
||||
const (
|
||||
mainBody = "<html>index page</html>"
|
||||
pushedBody = "<html>pushed page</html>"
|
||||
userAgent = "testagent"
|
||||
cookie = "testcookie"
|
||||
)
|
||||
|
||||
var stURL string
|
||||
checkPromisedReq := func(r *http.Request, wantMethod string, wantH http.Header) error {
|
||||
if got, want := r.Method, wantMethod; got != want {
|
||||
return fmt.Errorf("promised Req.Method=%q, want %q", got, want)
|
||||
}
|
||||
if got, want := r.Header, wantH; !reflect.DeepEqual(got, want) {
|
||||
return fmt.Errorf("promised Req.Header=%q, want %q", got, want)
|
||||
}
|
||||
if got, want := "https://"+r.Host, stURL; got != want {
|
||||
return fmt.Errorf("promised Req.Host=%q, want %q", got, want)
|
||||
}
|
||||
if r.Body == nil {
|
||||
return fmt.Errorf("nil Body")
|
||||
}
|
||||
if buf, err := ioutil.ReadAll(r.Body); err != nil || len(buf) != 0 {
|
||||
return fmt.Errorf("ReadAll(Body)=%q,%v, want '',nil", buf, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
errc := make(chan error, 3)
|
||||
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.RequestURI() {
|
||||
case "/":
|
||||
// Push "/pushed?get" as a GET request, using an absolute URL.
|
||||
opt := &http.PushOptions{
|
||||
Header: http.Header{
|
||||
"User-Agent": {userAgent},
|
||||
},
|
||||
}
|
||||
if err := w.(http.Pusher).Push(stURL+"/pushed?get", opt); err != nil {
|
||||
errc <- fmt.Errorf("error pushing /pushed?get: %v", err)
|
||||
return
|
||||
}
|
||||
// Push "/pushed?head" as a HEAD request, using a path.
|
||||
opt = &http.PushOptions{
|
||||
Method: "HEAD",
|
||||
Header: http.Header{
|
||||
"User-Agent": {userAgent},
|
||||
"Cookie": {cookie},
|
||||
},
|
||||
}
|
||||
if err := w.(http.Pusher).Push("/pushed?head", opt); err != nil {
|
||||
errc <- fmt.Errorf("error pushing /pushed?head: %v", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(mainBody)))
|
||||
w.WriteHeader(200)
|
||||
io.WriteString(w, mainBody)
|
||||
errc <- nil
|
||||
|
||||
case "/pushed?get":
|
||||
wantH := http.Header{}
|
||||
wantH.Set("User-Agent", userAgent)
|
||||
if err := checkPromisedReq(r, "GET", wantH); err != nil {
|
||||
errc <- fmt.Errorf("/pushed?get: %v", err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(pushedBody)))
|
||||
w.WriteHeader(200)
|
||||
io.WriteString(w, pushedBody)
|
||||
errc <- nil
|
||||
|
||||
case "/pushed?head":
|
||||
wantH := http.Header{}
|
||||
wantH.Set("User-Agent", userAgent)
|
||||
wantH.Set("Cookie", cookie)
|
||||
if err := checkPromisedReq(r, "HEAD", wantH); err != nil {
|
||||
errc <- fmt.Errorf("/pushed?head: %v", err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(204)
|
||||
errc <- nil
|
||||
|
||||
default:
|
||||
errc <- fmt.Errorf("unknown RequestURL %q", r.URL.RequestURI())
|
||||
}
|
||||
})
|
||||
stURL = st.ts.URL
|
||||
|
||||
// Send one request, which should push two responses.
|
||||
st.greet()
|
||||
getSlash(st)
|
||||
for k := 0; k < 3; k++ {
|
||||
select {
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Errorf("timeout waiting for handler %d to finish", k)
|
||||
case err := <-errc:
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkPushPromise := func(f Frame, promiseID uint32, wantH [][2]string) error {
|
||||
pp, ok := f.(*PushPromiseFrame)
|
||||
if !ok {
|
||||
return fmt.Errorf("got a %T; want *PushPromiseFrame", f)
|
||||
}
|
||||
if !pp.HeadersEnded() {
|
||||
return fmt.Errorf("want END_HEADERS flag in PushPromiseFrame")
|
||||
}
|
||||
if got, want := pp.PromiseID, promiseID; got != want {
|
||||
return fmt.Errorf("got PromiseID %v; want %v", got, want)
|
||||
}
|
||||
gotH := st.decodeHeader(pp.HeaderBlockFragment())
|
||||
if !reflect.DeepEqual(gotH, wantH) {
|
||||
return fmt.Errorf("got promised headers %v; want %v", gotH, wantH)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
checkHeaders := func(f Frame, wantH [][2]string) error {
|
||||
hf, ok := f.(*HeadersFrame)
|
||||
if !ok {
|
||||
return fmt.Errorf("got a %T; want *HeadersFrame", f)
|
||||
}
|
||||
gotH := st.decodeHeader(hf.HeaderBlockFragment())
|
||||
if !reflect.DeepEqual(gotH, wantH) {
|
||||
return fmt.Errorf("got response headers %v; want %v", gotH, wantH)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
checkData := func(f Frame, wantData string) error {
|
||||
df, ok := f.(*DataFrame)
|
||||
if !ok {
|
||||
return fmt.Errorf("got a %T; want *DataFrame", f)
|
||||
}
|
||||
if gotData := string(df.Data()); gotData != wantData {
|
||||
return fmt.Errorf("got response data %q; want %q", gotData, wantData)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stream 1 has 2 PUSH_PROMISE + HEADERS + DATA
|
||||
// Stream 2 has HEADERS + DATA
|
||||
// Stream 4 has HEADERS
|
||||
expected := map[uint32][]func(Frame) error{
|
||||
1: {
|
||||
func(f Frame) error {
|
||||
return checkPushPromise(f, 2, [][2]string{
|
||||
{":method", "GET"},
|
||||
{":scheme", "https"},
|
||||
{":authority", st.ts.Listener.Addr().String()},
|
||||
{":path", "/pushed?get"},
|
||||
{"user-agent", userAgent},
|
||||
})
|
||||
},
|
||||
func(f Frame) error {
|
||||
return checkPushPromise(f, 4, [][2]string{
|
||||
{":method", "HEAD"},
|
||||
{":scheme", "https"},
|
||||
{":authority", st.ts.Listener.Addr().String()},
|
||||
{":path", "/pushed?head"},
|
||||
{"cookie", cookie},
|
||||
{"user-agent", userAgent},
|
||||
})
|
||||
},
|
||||
func(f Frame) error {
|
||||
return checkHeaders(f, [][2]string{
|
||||
{":status", "200"},
|
||||
{"content-type", "text/html"},
|
||||
{"content-length", strconv.Itoa(len(mainBody))},
|
||||
})
|
||||
},
|
||||
func(f Frame) error {
|
||||
return checkData(f, mainBody)
|
||||
},
|
||||
},
|
||||
2: {
|
||||
func(f Frame) error {
|
||||
return checkHeaders(f, [][2]string{
|
||||
{":status", "200"},
|
||||
{"content-type", "text/html"},
|
||||
{"content-length", strconv.Itoa(len(pushedBody))},
|
||||
})
|
||||
},
|
||||
func(f Frame) error {
|
||||
return checkData(f, pushedBody)
|
||||
},
|
||||
},
|
||||
4: {
|
||||
func(f Frame) error {
|
||||
return checkHeaders(f, [][2]string{
|
||||
{":status", "204"},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
consumed := map[uint32]int{}
|
||||
for k := 0; len(expected) > 0; k++ {
|
||||
f, err := st.readFrame()
|
||||
if err != nil {
|
||||
for id, left := range expected {
|
||||
t.Errorf("stream %d: missing %d frames", id, len(left))
|
||||
}
|
||||
t.Fatalf("readFrame %d: %v", k, err)
|
||||
}
|
||||
id := f.Header().StreamID
|
||||
label := fmt.Sprintf("stream %d, frame %d", id, consumed[id])
|
||||
if len(expected[id]) == 0 {
|
||||
t.Fatalf("%s: unexpected frame %#+v", label, f)
|
||||
}
|
||||
check := expected[id][0]
|
||||
expected[id] = expected[id][1:]
|
||||
if len(expected[id]) == 0 {
|
||||
delete(expected, id)
|
||||
}
|
||||
if err := check(f); err != nil {
|
||||
t.Fatalf("%s: %v", label, err)
|
||||
}
|
||||
consumed[id]++
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Push_SuccessNoRace(t *testing.T) {
|
||||
// Regression test for issue #18326. Ensure the request handler can mutate
|
||||
// pushed request headers without racing with the PUSH_PROMISE write.
|
||||
errc := make(chan error, 2)
|
||||
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.RequestURI() {
|
||||
case "/":
|
||||
opt := &http.PushOptions{
|
||||
Header: http.Header{"User-Agent": {"testagent"}},
|
||||
}
|
||||
if err := w.(http.Pusher).Push("/pushed", opt); err != nil {
|
||||
errc <- fmt.Errorf("error pushing: %v", err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(200)
|
||||
errc <- nil
|
||||
|
||||
case "/pushed":
|
||||
// Update request header, ensure there is no race.
|
||||
r.Header.Set("User-Agent", "newagent")
|
||||
r.Header.Set("Cookie", "cookie")
|
||||
w.WriteHeader(200)
|
||||
errc <- nil
|
||||
|
||||
default:
|
||||
errc <- fmt.Errorf("unknown RequestURL %q", r.URL.RequestURI())
|
||||
}
|
||||
})
|
||||
|
||||
// Send one request, which should push one response.
|
||||
st.greet()
|
||||
getSlash(st)
|
||||
for k := 0; k < 2; k++ {
|
||||
select {
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Errorf("timeout waiting for handler %d to finish", k)
|
||||
case err := <-errc:
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Push_RejectRecursivePush(t *testing.T) {
|
||||
// Expect two requests, but might get three if there's a bug and the second push succeeds.
|
||||
errc := make(chan error, 3)
|
||||
handler := func(w http.ResponseWriter, r *http.Request) error {
|
||||
baseURL := "https://" + r.Host
|
||||
switch r.URL.Path {
|
||||
case "/":
|
||||
if err := w.(http.Pusher).Push(baseURL+"/push1", nil); err != nil {
|
||||
return fmt.Errorf("first Push()=%v, want nil", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
case "/push1":
|
||||
if got, want := w.(http.Pusher).Push(baseURL+"/push2", nil), ErrRecursivePush; got != want {
|
||||
return fmt.Errorf("Push()=%v, want %v", got, want)
|
||||
}
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unexpected path: %q", r.URL.Path)
|
||||
}
|
||||
}
|
||||
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
errc <- handler(w, r)
|
||||
})
|
||||
defer st.Close()
|
||||
st.greet()
|
||||
getSlash(st)
|
||||
if err := <-errc; err != nil {
|
||||
t.Errorf("First request failed: %v", err)
|
||||
}
|
||||
if err := <-errc; err != nil {
|
||||
t.Errorf("Second request failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func testServer_Push_RejectSingleRequest(t *testing.T, doPush func(http.Pusher, *http.Request) error, settings ...Setting) {
|
||||
// Expect one request, but might get two if there's a bug and the push succeeds.
|
||||
errc := make(chan error, 2)
|
||||
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
errc <- doPush(w.(http.Pusher), r)
|
||||
})
|
||||
defer st.Close()
|
||||
st.greet()
|
||||
if err := st.fr.WriteSettings(settings...); err != nil {
|
||||
st.t.Fatalf("WriteSettings: %v", err)
|
||||
}
|
||||
st.wantSettingsAck()
|
||||
getSlash(st)
|
||||
if err := <-errc; err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// Should not get a PUSH_PROMISE frame.
|
||||
hf := st.wantHeaders()
|
||||
if !hf.StreamEnded() {
|
||||
t.Error("stream should end after headers")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_Push_RejectIfDisabled(t *testing.T) {
|
||||
testServer_Push_RejectSingleRequest(t,
|
||||
func(p http.Pusher, r *http.Request) error {
|
||||
if got, want := p.Push("https://"+r.Host+"/pushed", nil), http.ErrNotSupported; got != want {
|
||||
return fmt.Errorf("Push()=%v, want %v", got, want)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Setting{SettingEnablePush, 0})
|
||||
}
|
||||
|
||||
func TestServer_Push_RejectWhenNoConcurrentStreams(t *testing.T) {
|
||||
testServer_Push_RejectSingleRequest(t,
|
||||
func(p http.Pusher, r *http.Request) error {
|
||||
if got, want := p.Push("https://"+r.Host+"/pushed", nil), ErrPushLimitReached; got != want {
|
||||
return fmt.Errorf("Push()=%v, want %v", got, want)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Setting{SettingMaxConcurrentStreams, 0})
|
||||
}
|
||||
|
||||
func TestServer_Push_RejectWrongScheme(t *testing.T) {
|
||||
testServer_Push_RejectSingleRequest(t,
|
||||
func(p http.Pusher, r *http.Request) error {
|
||||
if err := p.Push("http://"+r.Host+"/pushed", nil); err == nil {
|
||||
return errors.New("Push() should have failed (push target URL is http)")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_Push_RejectMissingHost(t *testing.T) {
|
||||
testServer_Push_RejectSingleRequest(t,
|
||||
func(p http.Pusher, r *http.Request) error {
|
||||
if err := p.Push("https:pushed", nil); err == nil {
|
||||
return errors.New("Push() should have failed (push target URL missing host)")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_Push_RejectRelativePath(t *testing.T) {
|
||||
testServer_Push_RejectSingleRequest(t,
|
||||
func(p http.Pusher, r *http.Request) error {
|
||||
if err := p.Push("../test", nil); err == nil {
|
||||
return errors.New("Push() should have failed (push target is a relative path)")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_Push_RejectForbiddenMethod(t *testing.T) {
|
||||
testServer_Push_RejectSingleRequest(t,
|
||||
func(p http.Pusher, r *http.Request) error {
|
||||
if err := p.Push("https://"+r.Host+"/pushed", &http.PushOptions{Method: "POST"}); err == nil {
|
||||
return errors.New("Push() should have failed (cannot promise a POST)")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_Push_RejectForbiddenHeader(t *testing.T) {
|
||||
testServer_Push_RejectSingleRequest(t,
|
||||
func(p http.Pusher, r *http.Request) error {
|
||||
header := http.Header{
|
||||
"Content-Length": {"10"},
|
||||
"Content-Encoding": {"gzip"},
|
||||
"Trailer": {"Foo"},
|
||||
"Te": {"trailers"},
|
||||
"Host": {"test.com"},
|
||||
":authority": {"test.com"},
|
||||
}
|
||||
if err := p.Push("https://"+r.Host+"/pushed", &http.PushOptions{Header: header}); err == nil {
|
||||
return errors.New("Push() should have failed (forbidden headers)")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_Push_StateTransitions(t *testing.T) {
|
||||
const body = "foo"
|
||||
|
||||
gotPromise := make(chan bool)
|
||||
finishedPush := make(chan bool)
|
||||
|
||||
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.RequestURI() {
|
||||
case "/":
|
||||
if err := w.(http.Pusher).Push("/pushed", nil); err != nil {
|
||||
t.Errorf("Push error: %v", err)
|
||||
}
|
||||
// Don't finish this request until the push finishes so we don't
|
||||
// nondeterministically interleave output frames with the push.
|
||||
<-finishedPush
|
||||
case "/pushed":
|
||||
<-gotPromise
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(body)))
|
||||
w.WriteHeader(200)
|
||||
io.WriteString(w, body)
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
st.greet()
|
||||
if st.stream(2) != nil {
|
||||
t.Fatal("stream 2 should be empty")
|
||||
}
|
||||
if got, want := st.streamState(2), stateIdle; got != want {
|
||||
t.Fatalf("streamState(2)=%v, want %v", got, want)
|
||||
}
|
||||
getSlash(st)
|
||||
// After the PUSH_PROMISE is sent, the stream should be stateHalfClosedRemote.
|
||||
st.wantPushPromise()
|
||||
if got, want := st.streamState(2), stateHalfClosedRemote; got != want {
|
||||
t.Fatalf("streamState(2)=%v, want %v", got, want)
|
||||
}
|
||||
// We stall the HTTP handler for "/pushed" until the above check. If we don't
|
||||
// stall the handler, then the handler might write HEADERS and DATA and finish
|
||||
// the stream before we check st.streamState(2) -- should that happen, we'll
|
||||
// see stateClosed and fail the above check.
|
||||
close(gotPromise)
|
||||
st.wantHeaders()
|
||||
if df := st.wantData(); !df.StreamEnded() {
|
||||
t.Fatal("expected END_STREAM flag on DATA")
|
||||
}
|
||||
if got, want := st.streamState(2), stateClosed; got != want {
|
||||
t.Fatalf("streamState(2)=%v, want %v", got, want)
|
||||
}
|
||||
close(finishedPush)
|
||||
}
|
||||
|
||||
func TestServer_Push_RejectAfterGoAway(t *testing.T) {
|
||||
var readyOnce sync.Once
|
||||
ready := make(chan struct{})
|
||||
errc := make(chan error, 2)
|
||||
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
select {
|
||||
case <-ready:
|
||||
case <-time.After(5 * time.Second):
|
||||
errc <- fmt.Errorf("timeout waiting for GOAWAY to be processed")
|
||||
}
|
||||
if got, want := w.(http.Pusher).Push("https://"+r.Host+"/pushed", nil), http.ErrNotSupported; got != want {
|
||||
errc <- fmt.Errorf("Push()=%v, want %v", got, want)
|
||||
}
|
||||
errc <- nil
|
||||
})
|
||||
defer st.Close()
|
||||
st.greet()
|
||||
getSlash(st)
|
||||
|
||||
// Send GOAWAY and wait for it to be processed.
|
||||
st.fr.WriteGoAway(1, ErrCodeNo, nil)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ready:
|
||||
return
|
||||
default:
|
||||
}
|
||||
st.sc.serveMsgCh <- func(loopNum int) {
|
||||
if !st.sc.pushEnabled {
|
||||
readyOnce.Do(func() { close(ready) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
if err := <-errc; err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
3728
vendor/golang.org/x/net/http2/server_test.go
generated
vendored
3728
vendor/golang.org/x/net/http2/server_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
102
vendor/golang.org/x/net/http2/transport.go
generated
vendored
102
vendor/golang.org/x/net/http2/transport.go
generated
vendored
|
|
@ -27,9 +27,9 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/idna"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -306,7 +306,26 @@ func (sew stickyErrWriter) Write(p []byte) (n int, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
var ErrNoCachedConn = errors.New("http2: no cached connection was available")
|
||||
// noCachedConnError is the concrete type of ErrNoCachedConn, which
|
||||
// needs to be detected by net/http regardless of whether it's its
|
||||
// bundled version (in h2_bundle.go with a rewritten type name) or
|
||||
// from a user's x/net/http2. As such, as it has a unique method name
|
||||
// (IsHTTP2NoCachedConnError) that net/http sniffs for via func
|
||||
// isNoCachedConnError.
|
||||
type noCachedConnError struct{}
|
||||
|
||||
func (noCachedConnError) IsHTTP2NoCachedConnError() {}
|
||||
func (noCachedConnError) Error() string { return "http2: no cached connection was available" }
|
||||
|
||||
// isNoCachedConnError reports whether err is of type noCachedConnError
|
||||
// or its equivalent renamed type in net/http2's h2_bundle.go. Both types
|
||||
// may coexist in the same running program.
|
||||
func isNoCachedConnError(err error) bool {
|
||||
_, ok := err.(interface{ IsHTTP2NoCachedConnError() })
|
||||
return ok
|
||||
}
|
||||
|
||||
var ErrNoCachedConn error = noCachedConnError{}
|
||||
|
||||
// RoundTripOpt are options for the Transport.RoundTripOpt method.
|
||||
type RoundTripOpt struct {
|
||||
|
|
@ -548,6 +567,10 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
|
|||
// henc in response to SETTINGS frames?
|
||||
cc.henc = hpack.NewEncoder(&cc.hbuf)
|
||||
|
||||
if t.AllowHTTP {
|
||||
cc.nextStreamID = 3
|
||||
}
|
||||
|
||||
if cs, ok := c.(connectionStater); ok {
|
||||
state := cs.ConnectionState()
|
||||
cc.tlsState = &state
|
||||
|
|
@ -811,7 +834,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|||
|
||||
cc.wmu.Lock()
|
||||
endStream := !hasBody && !hasTrailers
|
||||
werr := cc.writeHeaders(cs.ID, endStream, hdrs)
|
||||
werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs)
|
||||
cc.wmu.Unlock()
|
||||
traceWroteHeaders(cs.trace)
|
||||
cc.mu.Unlock()
|
||||
|
|
@ -932,6 +955,9 @@ func (cc *ClientConn) awaitOpenSlotForRequest(req *http.Request) error {
|
|||
for {
|
||||
cc.lastActive = time.Now()
|
||||
if cc.closed || !cc.canTakeNewRequestLocked() {
|
||||
if waitingForConn != nil {
|
||||
close(waitingForConn)
|
||||
}
|
||||
return errClientConnUnusable
|
||||
}
|
||||
if int64(len(cc.streams))+1 <= int64(cc.maxConcurrentStreams) {
|
||||
|
|
@ -964,13 +990,12 @@ func (cc *ClientConn) awaitOpenSlotForRequest(req *http.Request) error {
|
|||
}
|
||||
|
||||
// requires cc.wmu be held
|
||||
func (cc *ClientConn) writeHeaders(streamID uint32, endStream bool, hdrs []byte) error {
|
||||
func (cc *ClientConn) writeHeaders(streamID uint32, endStream bool, maxFrameSize int, hdrs []byte) error {
|
||||
first := true // first frame written (HEADERS is first, then CONTINUATION)
|
||||
frameSize := int(cc.maxFrameSize)
|
||||
for len(hdrs) > 0 && cc.werr == nil {
|
||||
chunk := hdrs
|
||||
if len(chunk) > frameSize {
|
||||
chunk = chunk[:frameSize]
|
||||
if len(chunk) > maxFrameSize {
|
||||
chunk = chunk[:maxFrameSize]
|
||||
}
|
||||
hdrs = hdrs[len(chunk):]
|
||||
endHeaders := len(hdrs) == 0
|
||||
|
|
@ -1087,13 +1112,17 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
|
|||
}
|
||||
}
|
||||
|
||||
cc.mu.Lock()
|
||||
maxFrameSize := int(cc.maxFrameSize)
|
||||
cc.mu.Unlock()
|
||||
|
||||
cc.wmu.Lock()
|
||||
defer cc.wmu.Unlock()
|
||||
|
||||
// Two ways to send END_STREAM: either with trailers, or
|
||||
// with an empty DATA frame.
|
||||
if len(trls) > 0 {
|
||||
err = cc.writeHeaders(cs.ID, true, trls)
|
||||
err = cc.writeHeaders(cs.ID, true, maxFrameSize, trls)
|
||||
} else {
|
||||
err = cc.fr.WriteData(cs.ID, true, nil)
|
||||
}
|
||||
|
|
@ -1152,7 +1181,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
|||
if host == "" {
|
||||
host = req.URL.Host
|
||||
}
|
||||
host, err := httplex.PunycodeHostPort(host)
|
||||
host, err := httpguts.PunycodeHostPort(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1177,11 +1206,11 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
|
|||
// potentially pollute our hpack state. (We want to be able to
|
||||
// continue to reuse the hpack encoder for future requests)
|
||||
for k, vv := range req.Header {
|
||||
if !httplex.ValidHeaderFieldName(k) {
|
||||
if !httpguts.ValidHeaderFieldName(k) {
|
||||
return nil, fmt.Errorf("invalid HTTP header name %q", k)
|
||||
}
|
||||
for _, v := range vv {
|
||||
if !httplex.ValidHeaderFieldValue(v) {
|
||||
if !httpguts.ValidHeaderFieldValue(v) {
|
||||
return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k)
|
||||
}
|
||||
}
|
||||
|
|
@ -1373,17 +1402,12 @@ func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream {
|
|||
// clientConnReadLoop is the state owned by the clientConn's frame-reading readLoop.
|
||||
type clientConnReadLoop struct {
|
||||
cc *ClientConn
|
||||
activeRes map[uint32]*clientStream // keyed by streamID
|
||||
closeWhenIdle bool
|
||||
}
|
||||
|
||||
// readLoop runs in its own goroutine and reads and dispatches frames.
|
||||
func (cc *ClientConn) readLoop() {
|
||||
rl := &clientConnReadLoop{
|
||||
cc: cc,
|
||||
activeRes: make(map[uint32]*clientStream),
|
||||
}
|
||||
|
||||
rl := &clientConnReadLoop{cc: cc}
|
||||
defer rl.cleanup()
|
||||
cc.readerErr = rl.run()
|
||||
if ce, ok := cc.readerErr.(ConnectionError); ok {
|
||||
|
|
@ -1438,10 +1462,8 @@ func (rl *clientConnReadLoop) cleanup() {
|
|||
} else if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
for _, cs := range rl.activeRes {
|
||||
cs.bufPipe.CloseWithError(err)
|
||||
}
|
||||
for _, cs := range cc.streams {
|
||||
cs.bufPipe.CloseWithError(err) // no-op if already closed
|
||||
select {
|
||||
case cs.resc <- resAndError{err: err}:
|
||||
default:
|
||||
|
|
@ -1519,7 +1541,7 @@ func (rl *clientConnReadLoop) run() error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
if rl.closeWhenIdle && gotReply && maybeIdle && len(rl.activeRes) == 0 {
|
||||
if rl.closeWhenIdle && gotReply && maybeIdle {
|
||||
cc.closeIfIdle()
|
||||
}
|
||||
}
|
||||
|
|
@ -1527,6 +1549,13 @@ func (rl *clientConnReadLoop) run() error {
|
|||
|
||||
func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
|
||||
cc := rl.cc
|
||||
cs := cc.streamByID(f.StreamID, false)
|
||||
if cs == nil {
|
||||
// We'd get here if we canceled a request while the
|
||||
// server had its response still in flight. So if this
|
||||
// was just something we canceled, ignore it.
|
||||
return nil
|
||||
}
|
||||
if f.StreamEnded() {
|
||||
// Issue 20521: If the stream has ended, streamByID() causes
|
||||
// clientStream.done to be closed, which causes the request's bodyWriter
|
||||
|
|
@ -1535,14 +1564,15 @@ func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
|
|||
// Deferring stream closure allows the header processing to occur first.
|
||||
// clientConn.RoundTrip may still receive the bodyWriter error first, but
|
||||
// the fix for issue 16102 prioritises any response.
|
||||
defer cc.streamByID(f.StreamID, true)
|
||||
}
|
||||
cs := cc.streamByID(f.StreamID, false)
|
||||
if cs == nil {
|
||||
// We'd get here if we canceled a request while the
|
||||
// server had its response still in flight. So if this
|
||||
// was just something we canceled, ignore it.
|
||||
return nil
|
||||
//
|
||||
// Issue 22413: If there is no request body, we should close the
|
||||
// stream before writing to cs.resc so that the stream is closed
|
||||
// immediately once RoundTrip returns.
|
||||
if cs.req.Body != nil {
|
||||
defer cc.forgetStreamID(f.StreamID)
|
||||
} else {
|
||||
cc.forgetStreamID(f.StreamID)
|
||||
}
|
||||
}
|
||||
if !cs.firstByte {
|
||||
if cs.trace != nil {
|
||||
|
|
@ -1567,6 +1597,7 @@ func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
|
|||
}
|
||||
// Any other error type is a stream error.
|
||||
cs.cc.writeStreamReset(f.StreamID, ErrCodeProtocol, err)
|
||||
cc.forgetStreamID(cs.ID)
|
||||
cs.resc <- resAndError{err: err}
|
||||
return nil // return nil from process* funcs to keep conn alive
|
||||
}
|
||||
|
|
@ -1574,9 +1605,6 @@ func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
|
|||
// (nil, nil) special case. See handleResponse docs.
|
||||
return nil
|
||||
}
|
||||
if res.Body != noBody {
|
||||
rl.activeRes[cs.ID] = cs
|
||||
}
|
||||
cs.resTrailer = &res.Trailer
|
||||
cs.resc <- resAndError{res: res}
|
||||
return nil
|
||||
|
|
@ -1596,11 +1624,11 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
|||
|
||||
status := f.PseudoValue("status")
|
||||
if status == "" {
|
||||
return nil, errors.New("missing status pseudo header")
|
||||
return nil, errors.New("malformed response from server: missing status pseudo header")
|
||||
}
|
||||
statusCode, err := strconv.Atoi(status)
|
||||
if err != nil {
|
||||
return nil, errors.New("malformed non-numeric status pseudo header")
|
||||
return nil, errors.New("malformed response from server: malformed non-numeric status pseudo header")
|
||||
}
|
||||
|
||||
if statusCode == 100 {
|
||||
|
|
@ -1915,7 +1943,6 @@ func (rl *clientConnReadLoop) endStreamError(cs *clientStream, err error) {
|
|||
rl.closeWhenIdle = true
|
||||
}
|
||||
cs.bufPipe.closeWithErrorAndCode(err, code)
|
||||
delete(rl.activeRes, cs.ID)
|
||||
|
||||
select {
|
||||
case cs.resc <- resAndError{err: err}:
|
||||
|
|
@ -2042,7 +2069,6 @@ func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error {
|
|||
cs.bufPipe.CloseWithError(err)
|
||||
cs.cc.cond.Broadcast() // wake up checkResetOrDone via clientStream.awaitFlowControl
|
||||
}
|
||||
delete(rl.activeRes, cs.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -2225,7 +2251,7 @@ func (t *Transport) getBodyWriterState(cs *clientStream, body io.Reader) (s body
|
|||
}
|
||||
s.delay = t.expectContinueTimeout()
|
||||
if s.delay == 0 ||
|
||||
!httplex.HeaderValuesContainsToken(
|
||||
!httpguts.HeaderValuesContainsToken(
|
||||
cs.req.Header["Expect"],
|
||||
"100-continue") {
|
||||
return
|
||||
|
|
@ -2280,5 +2306,5 @@ func (s bodyWriterState) scheduleBodyWrite() {
|
|||
// isConnectionCloseRequest reports whether req should use its own
|
||||
// connection for a single request and then close the connection.
|
||||
func isConnectionCloseRequest(req *http.Request) bool {
|
||||
return req.Close || httplex.HeaderValuesContainsToken(req.Header["Connection"], "close")
|
||||
return req.Close || httpguts.HeaderValuesContainsToken(req.Header["Connection"], "close")
|
||||
}
|
||||
|
|
|
|||
3796
vendor/golang.org/x/net/http2/transport_test.go
generated
vendored
3796
vendor/golang.org/x/net/http2/transport_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
8
vendor/golang.org/x/net/http2/write.go
generated
vendored
8
vendor/golang.org/x/net/http2/write.go
generated
vendored
|
|
@ -11,8 +11,8 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
// writeFramer is implemented by any type that is used to write frames.
|
||||
|
|
@ -186,6 +186,7 @@ type writeResHeaders struct {
|
|||
date string
|
||||
contentType string
|
||||
contentLength string
|
||||
noSniff bool
|
||||
}
|
||||
|
||||
func encKV(enc *hpack.Encoder, k, v string) {
|
||||
|
|
@ -222,6 +223,9 @@ func (w *writeResHeaders) writeFrame(ctx writeContext) error {
|
|||
if w.contentLength != "" {
|
||||
encKV(enc, "content-length", w.contentLength)
|
||||
}
|
||||
if w.noSniff {
|
||||
encKV(enc, "x-content-type-options", "nosniff")
|
||||
}
|
||||
if w.date != "" {
|
||||
encKV(enc, "date", w.date)
|
||||
}
|
||||
|
|
@ -350,7 +354,7 @@ func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
|
|||
}
|
||||
isTE := k == "transfer-encoding"
|
||||
for _, v := range vv {
|
||||
if !httplex.ValidHeaderFieldValue(v) {
|
||||
if !httpguts.ValidHeaderFieldValue(v) {
|
||||
// TODO: return an error? golang.org/issue/14048
|
||||
// For now just omit it.
|
||||
continue
|
||||
|
|
|
|||
541
vendor/golang.org/x/net/http2/writesched_priority_test.go
generated
vendored
541
vendor/golang.org/x/net/http2/writesched_priority_test.go
generated
vendored
|
|
@ -1,541 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func defaultPriorityWriteScheduler() *priorityWriteScheduler {
|
||||
return NewPriorityWriteScheduler(nil).(*priorityWriteScheduler)
|
||||
}
|
||||
|
||||
func checkPriorityWellFormed(ws *priorityWriteScheduler) error {
|
||||
for id, n := range ws.nodes {
|
||||
if id != n.id {
|
||||
return fmt.Errorf("bad ws.nodes: ws.nodes[%d] = %d", id, n.id)
|
||||
}
|
||||
if n.parent == nil {
|
||||
if n.next != nil || n.prev != nil {
|
||||
return fmt.Errorf("bad node %d: nil parent but prev/next not nil", id)
|
||||
}
|
||||
continue
|
||||
}
|
||||
found := false
|
||||
for k := n.parent.kids; k != nil; k = k.next {
|
||||
if k.id == id {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("bad node %d: not found in parent %d kids list", id, n.parent.id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fmtTree(ws *priorityWriteScheduler, fmtNode func(*priorityNode) string) string {
|
||||
var ids []int
|
||||
for _, n := range ws.nodes {
|
||||
ids = append(ids, int(n.id))
|
||||
}
|
||||
sort.Ints(ids)
|
||||
|
||||
var buf bytes.Buffer
|
||||
for _, id := range ids {
|
||||
if buf.Len() != 0 {
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
if id == 0 {
|
||||
buf.WriteString(fmtNode(&ws.root))
|
||||
} else {
|
||||
buf.WriteString(fmtNode(ws.nodes[uint32(id)]))
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func fmtNodeParentSkipRoot(n *priorityNode) string {
|
||||
switch {
|
||||
case n.id == 0:
|
||||
return ""
|
||||
case n.parent == nil:
|
||||
return fmt.Sprintf("%d{parent:nil}", n.id)
|
||||
default:
|
||||
return fmt.Sprintf("%d{parent:%d}", n.id, n.parent.id)
|
||||
}
|
||||
}
|
||||
|
||||
func fmtNodeWeightParentSkipRoot(n *priorityNode) string {
|
||||
switch {
|
||||
case n.id == 0:
|
||||
return ""
|
||||
case n.parent == nil:
|
||||
return fmt.Sprintf("%d{weight:%d,parent:nil}", n.id, n.weight)
|
||||
default:
|
||||
return fmt.Sprintf("%d{weight:%d,parent:%d}", n.id, n.weight, n.parent.id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityTwoStreams(t *testing.T) {
|
||||
ws := defaultPriorityWriteScheduler()
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{})
|
||||
|
||||
want := "1{weight:15,parent:0} 2{weight:15,parent:0}"
|
||||
if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
|
||||
t.Errorf("After open\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
|
||||
// Move 1's parent to 2.
|
||||
ws.AdjustStream(1, PriorityParam{
|
||||
StreamDep: 2,
|
||||
Weight: 32,
|
||||
Exclusive: false,
|
||||
})
|
||||
want = "1{weight:32,parent:2} 2{weight:15,parent:0}"
|
||||
if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
|
||||
t.Errorf("After adjust\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
|
||||
if err := checkPriorityWellFormed(ws); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityAdjustExclusiveZero(t *testing.T) {
|
||||
// 1, 2, and 3 are all children of the 0 stream.
|
||||
// Exclusive reprioritization to any of the streams should bring
|
||||
// the rest of the streams under the reprioritized stream.
|
||||
ws := defaultPriorityWriteScheduler()
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{})
|
||||
ws.OpenStream(3, OpenStreamOptions{})
|
||||
|
||||
want := "1{weight:15,parent:0} 2{weight:15,parent:0} 3{weight:15,parent:0}"
|
||||
if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
|
||||
t.Errorf("After open\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
|
||||
ws.AdjustStream(2, PriorityParam{
|
||||
StreamDep: 0,
|
||||
Weight: 20,
|
||||
Exclusive: true,
|
||||
})
|
||||
want = "1{weight:15,parent:2} 2{weight:20,parent:0} 3{weight:15,parent:2}"
|
||||
if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
|
||||
t.Errorf("After adjust\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
|
||||
if err := checkPriorityWellFormed(ws); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityAdjustOwnParent(t *testing.T) {
|
||||
// Assigning a node as its own parent should have no effect.
|
||||
ws := defaultPriorityWriteScheduler()
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{})
|
||||
ws.AdjustStream(2, PriorityParam{
|
||||
StreamDep: 2,
|
||||
Weight: 20,
|
||||
Exclusive: true,
|
||||
})
|
||||
want := "1{weight:15,parent:0} 2{weight:15,parent:0}"
|
||||
if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
|
||||
t.Errorf("After adjust\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
if err := checkPriorityWellFormed(ws); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityClosedStreams(t *testing.T) {
|
||||
ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{MaxClosedNodesInTree: 2}).(*priorityWriteScheduler)
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
|
||||
ws.OpenStream(3, OpenStreamOptions{PusherID: 2})
|
||||
ws.OpenStream(4, OpenStreamOptions{PusherID: 3})
|
||||
|
||||
// Close the first three streams. We lose 1, but keep 2 and 3.
|
||||
ws.CloseStream(1)
|
||||
ws.CloseStream(2)
|
||||
ws.CloseStream(3)
|
||||
|
||||
want := "2{weight:15,parent:0} 3{weight:15,parent:2} 4{weight:15,parent:3}"
|
||||
if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
|
||||
t.Errorf("After close\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
if err := checkPriorityWellFormed(ws); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Adding a stream as an exclusive child of 1 gives it default
|
||||
// priorities, since 1 is gone.
|
||||
ws.OpenStream(5, OpenStreamOptions{})
|
||||
ws.AdjustStream(5, PriorityParam{StreamDep: 1, Weight: 15, Exclusive: true})
|
||||
|
||||
// Adding a stream as an exclusive child of 2 should work, since 2 is not gone.
|
||||
ws.OpenStream(6, OpenStreamOptions{})
|
||||
ws.AdjustStream(6, PriorityParam{StreamDep: 2, Weight: 15, Exclusive: true})
|
||||
|
||||
want = "2{weight:15,parent:0} 3{weight:15,parent:6} 4{weight:15,parent:3} 5{weight:15,parent:0} 6{weight:15,parent:2}"
|
||||
if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
|
||||
t.Errorf("After add streams\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
if err := checkPriorityWellFormed(ws); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityClosedStreamsDisabled(t *testing.T) {
|
||||
ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{}).(*priorityWriteScheduler)
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
|
||||
ws.OpenStream(3, OpenStreamOptions{PusherID: 2})
|
||||
|
||||
// Close the first two streams. We keep only 3.
|
||||
ws.CloseStream(1)
|
||||
ws.CloseStream(2)
|
||||
|
||||
want := "3{weight:15,parent:0}"
|
||||
if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
|
||||
t.Errorf("After close\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
if err := checkPriorityWellFormed(ws); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityIdleStreams(t *testing.T) {
|
||||
ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{MaxIdleNodesInTree: 2}).(*priorityWriteScheduler)
|
||||
ws.AdjustStream(1, PriorityParam{StreamDep: 0, Weight: 15}) // idle
|
||||
ws.AdjustStream(2, PriorityParam{StreamDep: 0, Weight: 15}) // idle
|
||||
ws.AdjustStream(3, PriorityParam{StreamDep: 2, Weight: 20}) // idle
|
||||
ws.OpenStream(4, OpenStreamOptions{})
|
||||
ws.OpenStream(5, OpenStreamOptions{})
|
||||
ws.OpenStream(6, OpenStreamOptions{})
|
||||
ws.AdjustStream(4, PriorityParam{StreamDep: 1, Weight: 15})
|
||||
ws.AdjustStream(5, PriorityParam{StreamDep: 2, Weight: 15})
|
||||
ws.AdjustStream(6, PriorityParam{StreamDep: 3, Weight: 15})
|
||||
|
||||
want := "2{weight:15,parent:0} 3{weight:20,parent:2} 4{weight:15,parent:0} 5{weight:15,parent:2} 6{weight:15,parent:3}"
|
||||
if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
|
||||
t.Errorf("After open\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
if err := checkPriorityWellFormed(ws); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityIdleStreamsDisabled(t *testing.T) {
|
||||
ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{}).(*priorityWriteScheduler)
|
||||
ws.AdjustStream(1, PriorityParam{StreamDep: 0, Weight: 15}) // idle
|
||||
ws.AdjustStream(2, PriorityParam{StreamDep: 0, Weight: 15}) // idle
|
||||
ws.AdjustStream(3, PriorityParam{StreamDep: 2, Weight: 20}) // idle
|
||||
ws.OpenStream(4, OpenStreamOptions{})
|
||||
|
||||
want := "4{weight:15,parent:0}"
|
||||
if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want {
|
||||
t.Errorf("After open\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
if err := checkPriorityWellFormed(ws); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrioritySection531NonExclusive(t *testing.T) {
|
||||
// Example from RFC 7540 Section 5.3.1.
|
||||
// A,B,C,D = 1,2,3,4
|
||||
ws := defaultPriorityWriteScheduler()
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
|
||||
ws.OpenStream(3, OpenStreamOptions{PusherID: 1})
|
||||
ws.OpenStream(4, OpenStreamOptions{})
|
||||
ws.AdjustStream(4, PriorityParam{
|
||||
StreamDep: 1,
|
||||
Weight: 15,
|
||||
Exclusive: false,
|
||||
})
|
||||
want := "1{parent:0} 2{parent:1} 3{parent:1} 4{parent:1}"
|
||||
if got := fmtTree(ws, fmtNodeParentSkipRoot); got != want {
|
||||
t.Errorf("After adjust\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
if err := checkPriorityWellFormed(ws); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrioritySection531Exclusive(t *testing.T) {
|
||||
// Example from RFC 7540 Section 5.3.1.
|
||||
// A,B,C,D = 1,2,3,4
|
||||
ws := defaultPriorityWriteScheduler()
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
|
||||
ws.OpenStream(3, OpenStreamOptions{PusherID: 1})
|
||||
ws.OpenStream(4, OpenStreamOptions{})
|
||||
ws.AdjustStream(4, PriorityParam{
|
||||
StreamDep: 1,
|
||||
Weight: 15,
|
||||
Exclusive: true,
|
||||
})
|
||||
want := "1{parent:0} 2{parent:4} 3{parent:4} 4{parent:1}"
|
||||
if got := fmtTree(ws, fmtNodeParentSkipRoot); got != want {
|
||||
t.Errorf("After adjust\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
if err := checkPriorityWellFormed(ws); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func makeSection533Tree() *priorityWriteScheduler {
|
||||
// Initial tree from RFC 7540 Section 5.3.3.
|
||||
// A,B,C,D,E,F = 1,2,3,4,5,6
|
||||
ws := defaultPriorityWriteScheduler()
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
|
||||
ws.OpenStream(3, OpenStreamOptions{PusherID: 1})
|
||||
ws.OpenStream(4, OpenStreamOptions{PusherID: 3})
|
||||
ws.OpenStream(5, OpenStreamOptions{PusherID: 3})
|
||||
ws.OpenStream(6, OpenStreamOptions{PusherID: 4})
|
||||
return ws
|
||||
}
|
||||
|
||||
func TestPrioritySection533NonExclusive(t *testing.T) {
|
||||
// Example from RFC 7540 Section 5.3.3.
|
||||
// A,B,C,D,E,F = 1,2,3,4,5,6
|
||||
ws := defaultPriorityWriteScheduler()
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
|
||||
ws.OpenStream(3, OpenStreamOptions{PusherID: 1})
|
||||
ws.OpenStream(4, OpenStreamOptions{PusherID: 3})
|
||||
ws.OpenStream(5, OpenStreamOptions{PusherID: 3})
|
||||
ws.OpenStream(6, OpenStreamOptions{PusherID: 4})
|
||||
ws.AdjustStream(1, PriorityParam{
|
||||
StreamDep: 4,
|
||||
Weight: 15,
|
||||
Exclusive: false,
|
||||
})
|
||||
want := "1{parent:4} 2{parent:1} 3{parent:1} 4{parent:0} 5{parent:3} 6{parent:4}"
|
||||
if got := fmtTree(ws, fmtNodeParentSkipRoot); got != want {
|
||||
t.Errorf("After adjust\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
if err := checkPriorityWellFormed(ws); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrioritySection533Exclusive(t *testing.T) {
|
||||
// Example from RFC 7540 Section 5.3.3.
|
||||
// A,B,C,D,E,F = 1,2,3,4,5,6
|
||||
ws := defaultPriorityWriteScheduler()
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
|
||||
ws.OpenStream(3, OpenStreamOptions{PusherID: 1})
|
||||
ws.OpenStream(4, OpenStreamOptions{PusherID: 3})
|
||||
ws.OpenStream(5, OpenStreamOptions{PusherID: 3})
|
||||
ws.OpenStream(6, OpenStreamOptions{PusherID: 4})
|
||||
ws.AdjustStream(1, PriorityParam{
|
||||
StreamDep: 4,
|
||||
Weight: 15,
|
||||
Exclusive: true,
|
||||
})
|
||||
want := "1{parent:4} 2{parent:1} 3{parent:1} 4{parent:0} 5{parent:3} 6{parent:1}"
|
||||
if got := fmtTree(ws, fmtNodeParentSkipRoot); got != want {
|
||||
t.Errorf("After adjust\ngot %q\nwant %q", got, want)
|
||||
}
|
||||
if err := checkPriorityWellFormed(ws); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func checkPopAll(ws WriteScheduler, order []uint32) error {
|
||||
for k, id := range order {
|
||||
wr, ok := ws.Pop()
|
||||
if !ok {
|
||||
return fmt.Errorf("Pop[%d]: got ok=false, want %d (order=%v)", k, id, order)
|
||||
}
|
||||
if got := wr.StreamID(); got != id {
|
||||
return fmt.Errorf("Pop[%d]: got %v, want %d (order=%v)", k, got, id, order)
|
||||
}
|
||||
}
|
||||
wr, ok := ws.Pop()
|
||||
if ok {
|
||||
return fmt.Errorf("Pop[%d]: got %v, want ok=false (order=%v)", len(order), wr.StreamID(), order)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestPriorityPopFrom533Tree(t *testing.T) {
|
||||
ws := makeSection533Tree()
|
||||
|
||||
ws.Push(makeWriteHeadersRequest(3 /*C*/))
|
||||
ws.Push(makeWriteNonStreamRequest())
|
||||
ws.Push(makeWriteHeadersRequest(5 /*E*/))
|
||||
ws.Push(makeWriteHeadersRequest(1 /*A*/))
|
||||
t.Log("tree:", fmtTree(ws, fmtNodeParentSkipRoot))
|
||||
|
||||
if err := checkPopAll(ws, []uint32{0 /*NonStream*/, 1, 3, 5}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityPopFromLinearTree(t *testing.T) {
|
||||
ws := defaultPriorityWriteScheduler()
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
|
||||
ws.OpenStream(3, OpenStreamOptions{PusherID: 2})
|
||||
ws.OpenStream(4, OpenStreamOptions{PusherID: 3})
|
||||
|
||||
ws.Push(makeWriteHeadersRequest(3))
|
||||
ws.Push(makeWriteHeadersRequest(4))
|
||||
ws.Push(makeWriteHeadersRequest(1))
|
||||
ws.Push(makeWriteHeadersRequest(2))
|
||||
ws.Push(makeWriteNonStreamRequest())
|
||||
ws.Push(makeWriteNonStreamRequest())
|
||||
t.Log("tree:", fmtTree(ws, fmtNodeParentSkipRoot))
|
||||
|
||||
if err := checkPopAll(ws, []uint32{0, 0 /*NonStreams*/, 1, 2, 3, 4}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityFlowControl(t *testing.T) {
|
||||
ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{ThrottleOutOfOrderWrites: false})
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
|
||||
|
||||
sc := &serverConn{maxFrameSize: 16}
|
||||
st1 := &stream{id: 1, sc: sc}
|
||||
st2 := &stream{id: 2, sc: sc}
|
||||
|
||||
ws.Push(FrameWriteRequest{&writeData{1, make([]byte, 16), false}, st1, nil})
|
||||
ws.Push(FrameWriteRequest{&writeData{2, make([]byte, 16), false}, st2, nil})
|
||||
ws.AdjustStream(2, PriorityParam{StreamDep: 1})
|
||||
|
||||
// No flow-control bytes available.
|
||||
if wr, ok := ws.Pop(); ok {
|
||||
t.Fatalf("Pop(limited by flow control)=%v,true, want false", wr)
|
||||
}
|
||||
|
||||
// Add enough flow-control bytes to write st2 in two Pop calls.
|
||||
// Should write data from st2 even though it's lower priority than st1.
|
||||
for i := 1; i <= 2; i++ {
|
||||
st2.flow.add(8)
|
||||
wr, ok := ws.Pop()
|
||||
if !ok {
|
||||
t.Fatalf("Pop(%d)=false, want true", i)
|
||||
}
|
||||
if got, want := wr.DataSize(), 8; got != want {
|
||||
t.Fatalf("Pop(%d)=%d bytes, want %d bytes", i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityThrottleOutOfOrderWrites(t *testing.T) {
|
||||
ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{ThrottleOutOfOrderWrites: true})
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{PusherID: 1})
|
||||
|
||||
sc := &serverConn{maxFrameSize: 4096}
|
||||
st1 := &stream{id: 1, sc: sc}
|
||||
st2 := &stream{id: 2, sc: sc}
|
||||
st1.flow.add(4096)
|
||||
st2.flow.add(4096)
|
||||
ws.Push(FrameWriteRequest{&writeData{2, make([]byte, 4096), false}, st2, nil})
|
||||
ws.AdjustStream(2, PriorityParam{StreamDep: 1})
|
||||
|
||||
// We have enough flow-control bytes to write st2 in a single Pop call.
|
||||
// However, due to out-of-order write throttling, the first call should
|
||||
// only write 1KB.
|
||||
wr, ok := ws.Pop()
|
||||
if !ok {
|
||||
t.Fatalf("Pop(st2.first)=false, want true")
|
||||
}
|
||||
if got, want := wr.StreamID(), uint32(2); got != want {
|
||||
t.Fatalf("Pop(st2.first)=stream %d, want stream %d", got, want)
|
||||
}
|
||||
if got, want := wr.DataSize(), 1024; got != want {
|
||||
t.Fatalf("Pop(st2.first)=%d bytes, want %d bytes", got, want)
|
||||
}
|
||||
|
||||
// Now add data on st1. This should take precedence.
|
||||
ws.Push(FrameWriteRequest{&writeData{1, make([]byte, 4096), false}, st1, nil})
|
||||
wr, ok = ws.Pop()
|
||||
if !ok {
|
||||
t.Fatalf("Pop(st1)=false, want true")
|
||||
}
|
||||
if got, want := wr.StreamID(), uint32(1); got != want {
|
||||
t.Fatalf("Pop(st1)=stream %d, want stream %d", got, want)
|
||||
}
|
||||
if got, want := wr.DataSize(), 4096; got != want {
|
||||
t.Fatalf("Pop(st1)=%d bytes, want %d bytes", got, want)
|
||||
}
|
||||
|
||||
// Should go back to writing 1KB from st2.
|
||||
wr, ok = ws.Pop()
|
||||
if !ok {
|
||||
t.Fatalf("Pop(st2.last)=false, want true")
|
||||
}
|
||||
if got, want := wr.StreamID(), uint32(2); got != want {
|
||||
t.Fatalf("Pop(st2.last)=stream %d, want stream %d", got, want)
|
||||
}
|
||||
if got, want := wr.DataSize(), 1024; got != want {
|
||||
t.Fatalf("Pop(st2.last)=%d bytes, want %d bytes", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityWeights(t *testing.T) {
|
||||
ws := defaultPriorityWriteScheduler()
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.OpenStream(2, OpenStreamOptions{})
|
||||
|
||||
sc := &serverConn{maxFrameSize: 8}
|
||||
st1 := &stream{id: 1, sc: sc}
|
||||
st2 := &stream{id: 2, sc: sc}
|
||||
st1.flow.add(40)
|
||||
st2.flow.add(40)
|
||||
|
||||
ws.Push(FrameWriteRequest{&writeData{1, make([]byte, 40), false}, st1, nil})
|
||||
ws.Push(FrameWriteRequest{&writeData{2, make([]byte, 40), false}, st2, nil})
|
||||
ws.AdjustStream(1, PriorityParam{StreamDep: 0, Weight: 34})
|
||||
ws.AdjustStream(2, PriorityParam{StreamDep: 0, Weight: 9})
|
||||
|
||||
// st1 gets 3.5x the bandwidth of st2 (3.5 = (34+1)/(9+1)).
|
||||
// The maximum frame size is 8 bytes. The write sequence should be:
|
||||
// st1, total bytes so far is (st1=8, st=0)
|
||||
// st2, total bytes so far is (st1=8, st=8)
|
||||
// st1, total bytes so far is (st1=16, st=8)
|
||||
// st1, total bytes so far is (st1=24, st=8) // 3x bandwidth
|
||||
// st1, total bytes so far is (st1=32, st=8) // 4x bandwidth
|
||||
// st2, total bytes so far is (st1=32, st=16) // 2x bandwidth
|
||||
// st1, total bytes so far is (st1=40, st=16)
|
||||
// st2, total bytes so far is (st1=40, st=24)
|
||||
// st2, total bytes so far is (st1=40, st=32)
|
||||
// st2, total bytes so far is (st1=40, st=40)
|
||||
if err := checkPopAll(ws, []uint32{1, 2, 1, 1, 1, 2, 1, 2, 2, 2}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPriorityRstStreamOnNonOpenStreams(t *testing.T) {
|
||||
ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{
|
||||
MaxClosedNodesInTree: 0,
|
||||
MaxIdleNodesInTree: 0,
|
||||
})
|
||||
ws.OpenStream(1, OpenStreamOptions{})
|
||||
ws.CloseStream(1)
|
||||
ws.Push(FrameWriteRequest{write: streamError(1, ErrCodeProtocol)})
|
||||
ws.Push(FrameWriteRequest{write: streamError(2, ErrCodeProtocol)})
|
||||
|
||||
if err := checkPopAll(ws, []uint32{1, 2}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
44
vendor/golang.org/x/net/http2/writesched_random_test.go
generated
vendored
44
vendor/golang.org/x/net/http2/writesched_random_test.go
generated
vendored
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRandomScheduler(t *testing.T) {
|
||||
ws := NewRandomWriteScheduler()
|
||||
ws.Push(makeWriteHeadersRequest(3))
|
||||
ws.Push(makeWriteHeadersRequest(4))
|
||||
ws.Push(makeWriteHeadersRequest(1))
|
||||
ws.Push(makeWriteHeadersRequest(2))
|
||||
ws.Push(makeWriteNonStreamRequest())
|
||||
ws.Push(makeWriteNonStreamRequest())
|
||||
|
||||
// Pop all frames. Should get the non-stream requests first,
|
||||
// followed by the stream requests in any order.
|
||||
var order []FrameWriteRequest
|
||||
for {
|
||||
wr, ok := ws.Pop()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
order = append(order, wr)
|
||||
}
|
||||
t.Logf("got frames: %v", order)
|
||||
if len(order) != 6 {
|
||||
t.Fatalf("got %d frames, expected 6", len(order))
|
||||
}
|
||||
if order[0].StreamID() != 0 || order[1].StreamID() != 0 {
|
||||
t.Fatal("expected non-stream frames first", order[0], order[1])
|
||||
}
|
||||
got := make(map[uint32]bool)
|
||||
for _, wr := range order[2:] {
|
||||
got[wr.StreamID()] = true
|
||||
}
|
||||
for id := uint32(1); id <= 4; id++ {
|
||||
if !got[id] {
|
||||
t.Errorf("frame not found for stream %d", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
125
vendor/golang.org/x/net/http2/writesched_test.go
generated
vendored
125
vendor/golang.org/x/net/http2/writesched_test.go
generated
vendored
|
|
@ -1,125 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func makeWriteNonStreamRequest() FrameWriteRequest {
|
||||
return FrameWriteRequest{writeSettingsAck{}, nil, nil}
|
||||
}
|
||||
|
||||
func makeWriteHeadersRequest(streamID uint32) FrameWriteRequest {
|
||||
st := &stream{id: streamID}
|
||||
return FrameWriteRequest{&writeResHeaders{streamID: streamID, httpResCode: 200}, st, nil}
|
||||
}
|
||||
|
||||
func checkConsume(wr FrameWriteRequest, nbytes int32, want []FrameWriteRequest) error {
|
||||
consumed, rest, n := wr.Consume(nbytes)
|
||||
var wantConsumed, wantRest FrameWriteRequest
|
||||
switch len(want) {
|
||||
case 0:
|
||||
case 1:
|
||||
wantConsumed = want[0]
|
||||
case 2:
|
||||
wantConsumed = want[0]
|
||||
wantRest = want[1]
|
||||
}
|
||||
if !reflect.DeepEqual(consumed, wantConsumed) || !reflect.DeepEqual(rest, wantRest) || n != len(want) {
|
||||
return fmt.Errorf("got %v, %v, %v\nwant %v, %v, %v", consumed, rest, n, wantConsumed, wantRest, len(want))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestFrameWriteRequestNonData(t *testing.T) {
|
||||
wr := makeWriteNonStreamRequest()
|
||||
if got, want := wr.DataSize(), 0; got != want {
|
||||
t.Errorf("DataSize: got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
// Non-DATA frames are always consumed whole.
|
||||
if err := checkConsume(wr, 0, []FrameWriteRequest{wr}); err != nil {
|
||||
t.Errorf("Consume:\n%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFrameWriteRequestData(t *testing.T) {
|
||||
st := &stream{
|
||||
id: 1,
|
||||
sc: &serverConn{maxFrameSize: 16},
|
||||
}
|
||||
const size = 32
|
||||
wr := FrameWriteRequest{&writeData{st.id, make([]byte, size), true}, st, make(chan error)}
|
||||
if got, want := wr.DataSize(), size; got != want {
|
||||
t.Errorf("DataSize: got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
// No flow-control bytes available: cannot consume anything.
|
||||
if err := checkConsume(wr, math.MaxInt32, []FrameWriteRequest{}); err != nil {
|
||||
t.Errorf("Consume(limited by flow control):\n%v", err)
|
||||
}
|
||||
|
||||
// Add enough flow-control bytes to consume the entire frame,
|
||||
// but we're now restricted by st.sc.maxFrameSize.
|
||||
st.flow.add(size)
|
||||
want := []FrameWriteRequest{
|
||||
{
|
||||
write: &writeData{st.id, make([]byte, st.sc.maxFrameSize), false},
|
||||
stream: st,
|
||||
done: nil,
|
||||
},
|
||||
{
|
||||
write: &writeData{st.id, make([]byte, size-st.sc.maxFrameSize), true},
|
||||
stream: st,
|
||||
done: wr.done,
|
||||
},
|
||||
}
|
||||
if err := checkConsume(wr, math.MaxInt32, want); err != nil {
|
||||
t.Errorf("Consume(limited by maxFrameSize):\n%v", err)
|
||||
}
|
||||
rest := want[1]
|
||||
|
||||
// Consume 8 bytes from the remaining frame.
|
||||
want = []FrameWriteRequest{
|
||||
{
|
||||
write: &writeData{st.id, make([]byte, 8), false},
|
||||
stream: st,
|
||||
done: nil,
|
||||
},
|
||||
{
|
||||
write: &writeData{st.id, make([]byte, size-st.sc.maxFrameSize-8), true},
|
||||
stream: st,
|
||||
done: wr.done,
|
||||
},
|
||||
}
|
||||
if err := checkConsume(rest, 8, want); err != nil {
|
||||
t.Errorf("Consume(8):\n%v", err)
|
||||
}
|
||||
rest = want[1]
|
||||
|
||||
// Consume all remaining bytes.
|
||||
want = []FrameWriteRequest{
|
||||
{
|
||||
write: &writeData{st.id, make([]byte, size-st.sc.maxFrameSize-8), true},
|
||||
stream: st,
|
||||
done: wr.done,
|
||||
},
|
||||
}
|
||||
if err := checkConsume(rest, math.MaxInt32, want); err != nil {
|
||||
t.Errorf("Consume(remainder):\n%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFrameWriteRequest_StreamID(t *testing.T) {
|
||||
const streamID = 123
|
||||
wr := FrameWriteRequest{write: streamError(streamID, ErrCodeNo)}
|
||||
if got := wr.StreamID(); got != streamID {
|
||||
t.Errorf("FrameWriteRequest(StreamError) = %v; want %v", got, streamID)
|
||||
}
|
||||
}
|
||||
356
vendor/golang.org/x/net/http2/z_spec_test.go
generated
vendored
356
vendor/golang.org/x/net/http2/z_spec_test.go
generated
vendored
|
|
@ -1,356 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var coverSpec = flag.Bool("coverspec", false, "Run spec coverage tests")
|
||||
|
||||
// The global map of sentence coverage for the http2 spec.
|
||||
var defaultSpecCoverage specCoverage
|
||||
|
||||
var loadSpecOnce sync.Once
|
||||
|
||||
func loadSpec() {
|
||||
if f, err := os.Open("testdata/draft-ietf-httpbis-http2.xml"); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
defaultSpecCoverage = readSpecCov(f)
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// covers marks all sentences for section sec in defaultSpecCoverage. Sentences not
|
||||
// "covered" will be included in report outputted by TestSpecCoverage.
|
||||
func covers(sec, sentences string) {
|
||||
loadSpecOnce.Do(loadSpec)
|
||||
defaultSpecCoverage.cover(sec, sentences)
|
||||
}
|
||||
|
||||
type specPart struct {
|
||||
section string
|
||||
sentence string
|
||||
}
|
||||
|
||||
func (ss specPart) Less(oo specPart) bool {
|
||||
atoi := func(s string) int {
|
||||
n, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return n
|
||||
}
|
||||
a := strings.Split(ss.section, ".")
|
||||
b := strings.Split(oo.section, ".")
|
||||
for len(a) > 0 {
|
||||
if len(b) == 0 {
|
||||
return false
|
||||
}
|
||||
x, y := atoi(a[0]), atoi(b[0])
|
||||
if x == y {
|
||||
a, b = a[1:], b[1:]
|
||||
continue
|
||||
}
|
||||
return x < y
|
||||
}
|
||||
if len(b) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type bySpecSection []specPart
|
||||
|
||||
func (a bySpecSection) Len() int { return len(a) }
|
||||
func (a bySpecSection) Less(i, j int) bool { return a[i].Less(a[j]) }
|
||||
func (a bySpecSection) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
type specCoverage struct {
|
||||
coverage map[specPart]bool
|
||||
d *xml.Decoder
|
||||
}
|
||||
|
||||
func joinSection(sec []int) string {
|
||||
s := fmt.Sprintf("%d", sec[0])
|
||||
for _, n := range sec[1:] {
|
||||
s = fmt.Sprintf("%s.%d", s, n)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (sc specCoverage) readSection(sec []int) {
|
||||
var (
|
||||
buf = new(bytes.Buffer)
|
||||
sub = 0
|
||||
)
|
||||
for {
|
||||
tk, err := sc.d.Token()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
switch v := tk.(type) {
|
||||
case xml.StartElement:
|
||||
if skipElement(v) {
|
||||
if err := sc.d.Skip(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if v.Name.Local == "section" {
|
||||
sub++
|
||||
}
|
||||
break
|
||||
}
|
||||
switch v.Name.Local {
|
||||
case "section":
|
||||
sub++
|
||||
sc.readSection(append(sec, sub))
|
||||
case "xref":
|
||||
buf.Write(sc.readXRef(v))
|
||||
}
|
||||
case xml.CharData:
|
||||
if len(sec) == 0 {
|
||||
break
|
||||
}
|
||||
buf.Write(v)
|
||||
case xml.EndElement:
|
||||
if v.Name.Local == "section" {
|
||||
sc.addSentences(joinSection(sec), buf.String())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sc specCoverage) readXRef(se xml.StartElement) []byte {
|
||||
var b []byte
|
||||
for {
|
||||
tk, err := sc.d.Token()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
switch v := tk.(type) {
|
||||
case xml.CharData:
|
||||
if b != nil {
|
||||
panic("unexpected CharData")
|
||||
}
|
||||
b = []byte(string(v))
|
||||
case xml.EndElement:
|
||||
if v.Name.Local != "xref" {
|
||||
panic("expected </xref>")
|
||||
}
|
||||
if b != nil {
|
||||
return b
|
||||
}
|
||||
sig := attrSig(se)
|
||||
switch sig {
|
||||
case "target":
|
||||
return []byte(fmt.Sprintf("[%s]", attrValue(se, "target")))
|
||||
case "fmt-of,rel,target", "fmt-,,rel,target":
|
||||
return []byte(fmt.Sprintf("[%s, %s]", attrValue(se, "target"), attrValue(se, "rel")))
|
||||
case "fmt-of,sec,target", "fmt-,,sec,target":
|
||||
return []byte(fmt.Sprintf("[section %s of %s]", attrValue(se, "sec"), attrValue(se, "target")))
|
||||
case "fmt-of,rel,sec,target":
|
||||
return []byte(fmt.Sprintf("[section %s of %s, %s]", attrValue(se, "sec"), attrValue(se, "target"), attrValue(se, "rel")))
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown attribute signature %q in %#v", sig, fmt.Sprintf("%#v", se)))
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected tag %q", v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var skipAnchor = map[string]bool{
|
||||
"intro": true,
|
||||
"Overview": true,
|
||||
}
|
||||
|
||||
var skipTitle = map[string]bool{
|
||||
"Acknowledgements": true,
|
||||
"Change Log": true,
|
||||
"Document Organization": true,
|
||||
"Conventions and Terminology": true,
|
||||
}
|
||||
|
||||
func skipElement(s xml.StartElement) bool {
|
||||
switch s.Name.Local {
|
||||
case "artwork":
|
||||
return true
|
||||
case "section":
|
||||
for _, attr := range s.Attr {
|
||||
switch attr.Name.Local {
|
||||
case "anchor":
|
||||
if skipAnchor[attr.Value] || strings.HasPrefix(attr.Value, "changes.since.") {
|
||||
return true
|
||||
}
|
||||
case "title":
|
||||
if skipTitle[attr.Value] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func readSpecCov(r io.Reader) specCoverage {
|
||||
sc := specCoverage{
|
||||
coverage: map[specPart]bool{},
|
||||
d: xml.NewDecoder(r)}
|
||||
sc.readSection(nil)
|
||||
return sc
|
||||
}
|
||||
|
||||
func (sc specCoverage) addSentences(sec string, sentence string) {
|
||||
for _, s := range parseSentences(sentence) {
|
||||
sc.coverage[specPart{sec, s}] = false
|
||||
}
|
||||
}
|
||||
|
||||
func (sc specCoverage) cover(sec string, sentence string) {
|
||||
for _, s := range parseSentences(sentence) {
|
||||
p := specPart{sec, s}
|
||||
if _, ok := sc.coverage[p]; !ok {
|
||||
panic(fmt.Sprintf("Not found in spec: %q, %q", sec, s))
|
||||
}
|
||||
sc.coverage[specPart{sec, s}] = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var whitespaceRx = regexp.MustCompile(`\s+`)
|
||||
|
||||
func parseSentences(sens string) []string {
|
||||
sens = strings.TrimSpace(sens)
|
||||
if sens == "" {
|
||||
return nil
|
||||
}
|
||||
ss := strings.Split(whitespaceRx.ReplaceAllString(sens, " "), ". ")
|
||||
for i, s := range ss {
|
||||
s = strings.TrimSpace(s)
|
||||
if !strings.HasSuffix(s, ".") {
|
||||
s += "."
|
||||
}
|
||||
ss[i] = s
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
func TestSpecParseSentences(t *testing.T) {
|
||||
tests := []struct {
|
||||
ss string
|
||||
want []string
|
||||
}{
|
||||
{"Sentence 1. Sentence 2.",
|
||||
[]string{
|
||||
"Sentence 1.",
|
||||
"Sentence 2.",
|
||||
}},
|
||||
{"Sentence 1. \nSentence 2.\tSentence 3.",
|
||||
[]string{
|
||||
"Sentence 1.",
|
||||
"Sentence 2.",
|
||||
"Sentence 3.",
|
||||
}},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
got := parseSentences(tt.ss)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("%d: got = %q, want %q", i, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpecCoverage(t *testing.T) {
|
||||
if !*coverSpec {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
loadSpecOnce.Do(loadSpec)
|
||||
|
||||
var (
|
||||
list []specPart
|
||||
cv = defaultSpecCoverage.coverage
|
||||
total = len(cv)
|
||||
complete = 0
|
||||
)
|
||||
|
||||
for sp, touched := range defaultSpecCoverage.coverage {
|
||||
if touched {
|
||||
complete++
|
||||
} else {
|
||||
list = append(list, sp)
|
||||
}
|
||||
}
|
||||
sort.Stable(bySpecSection(list))
|
||||
|
||||
if testing.Short() && len(list) > 5 {
|
||||
list = list[:5]
|
||||
}
|
||||
|
||||
for _, p := range list {
|
||||
t.Errorf("\tSECTION %s: %s", p.section, p.sentence)
|
||||
}
|
||||
|
||||
t.Logf("%d/%d (%d%%) sentences covered", complete, total, (complete/total)*100)
|
||||
}
|
||||
|
||||
func attrSig(se xml.StartElement) string {
|
||||
var names []string
|
||||
for _, attr := range se.Attr {
|
||||
if attr.Name.Local == "fmt" {
|
||||
names = append(names, "fmt-"+attr.Value)
|
||||
} else {
|
||||
names = append(names, attr.Name.Local)
|
||||
}
|
||||
}
|
||||
sort.Strings(names)
|
||||
return strings.Join(names, ",")
|
||||
}
|
||||
|
||||
func attrValue(se xml.StartElement, attr string) string {
|
||||
for _, a := range se.Attr {
|
||||
if a.Name.Local == attr {
|
||||
return a.Value
|
||||
}
|
||||
}
|
||||
panic("unknown attribute " + attr)
|
||||
}
|
||||
|
||||
func TestSpecPartLess(t *testing.T) {
|
||||
tests := []struct {
|
||||
sec1, sec2 string
|
||||
want bool
|
||||
}{
|
||||
{"6.2.1", "6.2", false},
|
||||
{"6.2", "6.2.1", true},
|
||||
{"6.10", "6.10.1", true},
|
||||
{"6.10", "6.1.1", false}, // 10, not 1
|
||||
{"6.1", "6.1", false}, // equal, so not less
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := (specPart{tt.sec1, "foo"}).Less(specPart{tt.sec2, "foo"})
|
||||
if got != tt.want {
|
||||
t.Errorf("Less(%q, %q) = %v; want %v", tt.sec1, tt.sec2, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
70
vendor/golang.org/x/net/idna/example_test.go
generated
vendored
70
vendor/golang.org/x/net/idna/example_test.go
generated
vendored
|
|
@ -1,70 +0,0 @@
|
|||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
// Copyright 2017 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 idna_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
func ExampleProfile() {
|
||||
// Raw Punycode has no restrictions and does no mappings.
|
||||
fmt.Println(idna.ToASCII(""))
|
||||
fmt.Println(idna.ToASCII("*.faß.com"))
|
||||
fmt.Println(idna.Punycode.ToASCII("*.faß.com"))
|
||||
|
||||
// Rewrite IDN for lookup. This (currently) uses transitional mappings to
|
||||
// find a balance between IDNA2003 and IDNA2008 compatibility.
|
||||
fmt.Println(idna.Lookup.ToASCII(""))
|
||||
fmt.Println(idna.Lookup.ToASCII("www.faß.com"))
|
||||
|
||||
// Convert an IDN to ASCII for registration purposes. This changes the
|
||||
// encoding, but reports an error if the input was illformed.
|
||||
fmt.Println(idna.Registration.ToASCII(""))
|
||||
fmt.Println(idna.Registration.ToASCII("www.faß.com"))
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
// *.xn--fa-hia.com <nil>
|
||||
// *.xn--fa-hia.com <nil>
|
||||
// <nil>
|
||||
// www.fass.com <nil>
|
||||
// idna: invalid label ""
|
||||
// www.xn--fa-hia.com <nil>
|
||||
}
|
||||
|
||||
func ExampleNew() {
|
||||
var p *idna.Profile
|
||||
|
||||
// Raw Punycode has no restrictions and does no mappings.
|
||||
p = idna.New()
|
||||
fmt.Println(p.ToASCII("*.faß.com"))
|
||||
|
||||
// Do mappings. Note that star is not allowed in a DNS lookup.
|
||||
p = idna.New(
|
||||
idna.MapForLookup(),
|
||||
idna.Transitional(true)) // Map ß -> ss
|
||||
fmt.Println(p.ToASCII("*.faß.com"))
|
||||
|
||||
// Lookup for registration. Also does not allow '*'.
|
||||
p = idna.New(idna.ValidateForRegistration())
|
||||
fmt.Println(p.ToUnicode("*.faß.com"))
|
||||
|
||||
// Set up a profile maps for lookup, but allows wild cards.
|
||||
p = idna.New(
|
||||
idna.MapForLookup(),
|
||||
idna.Transitional(true), // Map ß -> ss
|
||||
idna.StrictDomainName(false)) // Set more permissive ASCII rules.
|
||||
fmt.Println(p.ToASCII("*.faß.com"))
|
||||
|
||||
// Output:
|
||||
// *.xn--fa-hia.com <nil>
|
||||
// *.fass.com idna: disallowed rune U+002A
|
||||
// *.faß.com idna: disallowed rune U+002A
|
||||
// *.fass.com <nil>
|
||||
}
|
||||
108
vendor/golang.org/x/net/idna/idna_test.go
generated
vendored
108
vendor/golang.org/x/net/idna/idna_test.go
generated
vendored
|
|
@ -1,108 +0,0 @@
|
|||
// Copyright 2012 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 idna
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var idnaTestCases = [...]struct {
|
||||
ascii, unicode string
|
||||
}{
|
||||
// Labels.
|
||||
{"books", "books"},
|
||||
{"xn--bcher-kva", "bücher"},
|
||||
|
||||
// Domains.
|
||||
{"foo--xn--bar.org", "foo--xn--bar.org"},
|
||||
{"golang.org", "golang.org"},
|
||||
{"example.xn--p1ai", "example.рф"},
|
||||
{"xn--czrw28b.tw", "商業.tw"},
|
||||
{"www.xn--mller-kva.de", "www.müller.de"},
|
||||
}
|
||||
|
||||
func TestIDNA(t *testing.T) {
|
||||
for _, tc := range idnaTestCases {
|
||||
if a, err := ToASCII(tc.unicode); err != nil {
|
||||
t.Errorf("ToASCII(%q): %v", tc.unicode, err)
|
||||
} else if a != tc.ascii {
|
||||
t.Errorf("ToASCII(%q): got %q, want %q", tc.unicode, a, tc.ascii)
|
||||
}
|
||||
|
||||
if u, err := ToUnicode(tc.ascii); err != nil {
|
||||
t.Errorf("ToUnicode(%q): %v", tc.ascii, err)
|
||||
} else if u != tc.unicode {
|
||||
t.Errorf("ToUnicode(%q): got %q, want %q", tc.ascii, u, tc.unicode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIDNASeparators(t *testing.T) {
|
||||
type subCase struct {
|
||||
unicode string
|
||||
wantASCII string
|
||||
wantErr bool
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
profile *Profile
|
||||
subCases []subCase
|
||||
}{
|
||||
{
|
||||
name: "Punycode", profile: Punycode,
|
||||
subCases: []subCase{
|
||||
{"example\u3002jp", "xn--examplejp-ck3h", false},
|
||||
{"東京\uFF0Ejp", "xn--jp-l92cn98g071o", false},
|
||||
{"大阪\uFF61jp", "xn--jp-ku9cz72u463f", false},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Lookup", profile: Lookup,
|
||||
subCases: []subCase{
|
||||
{"example\u3002jp", "example.jp", false},
|
||||
{"東京\uFF0Ejp", "xn--1lqs71d.jp", false},
|
||||
{"大阪\uFF61jp", "xn--pssu33l.jp", false},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Display", profile: Display,
|
||||
subCases: []subCase{
|
||||
{"example\u3002jp", "example.jp", false},
|
||||
{"東京\uFF0Ejp", "xn--1lqs71d.jp", false},
|
||||
{"大阪\uFF61jp", "xn--pssu33l.jp", false},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Registration", profile: Registration,
|
||||
subCases: []subCase{
|
||||
{"example\u3002jp", "", true},
|
||||
{"東京\uFF0Ejp", "", true},
|
||||
{"大阪\uFF61jp", "", true},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
for _, c := range tc.subCases {
|
||||
gotA, err := tc.profile.ToASCII(c.unicode)
|
||||
if c.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("ToASCII(%q): got no error, but an error expected", c.unicode)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("ToASCII(%q): got err=%v, but no error expected", c.unicode, err)
|
||||
} else if gotA != c.wantASCII {
|
||||
t.Errorf("ToASCII(%q): got %q, want %q", c.unicode, gotA, c.wantASCII)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(nigeltao): test errors, once we've specified when ToASCII and ToUnicode
|
||||
// return errors.
|
||||
198
vendor/golang.org/x/net/idna/punycode_test.go
generated
vendored
198
vendor/golang.org/x/net/idna/punycode_test.go
generated
vendored
|
|
@ -1,198 +0,0 @@
|
|||
// Copyright 2012 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 idna
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var punycodeTestCases = [...]struct {
|
||||
s, encoded string
|
||||
}{
|
||||
{"", ""},
|
||||
{"-", "--"},
|
||||
{"-a", "-a-"},
|
||||
{"-a-", "-a--"},
|
||||
{"a", "a-"},
|
||||
{"a-", "a--"},
|
||||
{"a-b", "a-b-"},
|
||||
{"books", "books-"},
|
||||
{"bücher", "bcher-kva"},
|
||||
{"Hello世界", "Hello-ck1hg65u"},
|
||||
{"ü", "tda"},
|
||||
{"üý", "tdac"},
|
||||
|
||||
// The test cases below come from RFC 3492 section 7.1 with Errata 3026.
|
||||
{
|
||||
// (A) Arabic (Egyptian).
|
||||
"\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644" +
|
||||
"\u0645\u0648\u0634\u0639\u0631\u0628\u064A\u061F",
|
||||
"egbpdaj6bu4bxfgehfvwxn",
|
||||
},
|
||||
{
|
||||
// (B) Chinese (simplified).
|
||||
"\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2D\u6587",
|
||||
"ihqwcrb4cv8a8dqg056pqjye",
|
||||
},
|
||||
{
|
||||
// (C) Chinese (traditional).
|
||||
"\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587",
|
||||
"ihqwctvzc91f659drss3x8bo0yb",
|
||||
},
|
||||
{
|
||||
// (D) Czech.
|
||||
"\u0050\u0072\u006F\u010D\u0070\u0072\u006F\u0073\u0074" +
|
||||
"\u011B\u006E\u0065\u006D\u006C\u0075\u0076\u00ED\u010D" +
|
||||
"\u0065\u0073\u006B\u0079",
|
||||
"Proprostnemluvesky-uyb24dma41a",
|
||||
},
|
||||
{
|
||||
// (E) Hebrew.
|
||||
"\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8" +
|
||||
"\u05DC\u05D0\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2" +
|
||||
"\u05D1\u05E8\u05D9\u05EA",
|
||||
"4dbcagdahymbxekheh6e0a7fei0b",
|
||||
},
|
||||
{
|
||||
// (F) Hindi (Devanagari).
|
||||
"\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D" +
|
||||
"\u0926\u0940\u0915\u094D\u092F\u094B\u0902\u0928\u0939" +
|
||||
"\u0940\u0902\u092C\u094B\u0932\u0938\u0915\u0924\u0947" +
|
||||
"\u0939\u0948\u0902",
|
||||
"i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd",
|
||||
},
|
||||
{
|
||||
// (G) Japanese (kanji and hiragana).
|
||||
"\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092" +
|
||||
"\u8A71\u3057\u3066\u304F\u308C\u306A\u3044\u306E\u304B",
|
||||
"n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa",
|
||||
},
|
||||
{
|
||||
// (H) Korean (Hangul syllables).
|
||||
"\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774" +
|
||||
"\uD55C\uAD6D\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74" +
|
||||
"\uC5BC\uB9C8\uB098\uC88B\uC744\uAE4C",
|
||||
"989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5j" +
|
||||
"psd879ccm6fea98c",
|
||||
},
|
||||
{
|
||||
// (I) Russian (Cyrillic).
|
||||
"\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E" +
|
||||
"\u043D\u0438\u043D\u0435\u0433\u043E\u0432\u043E\u0440" +
|
||||
"\u044F\u0442\u043F\u043E\u0440\u0443\u0441\u0441\u043A" +
|
||||
"\u0438",
|
||||
"b1abfaaepdrnnbgefbadotcwatmq2g4l",
|
||||
},
|
||||
{
|
||||
// (J) Spanish.
|
||||
"\u0050\u006F\u0072\u0071\u0075\u00E9\u006E\u006F\u0070" +
|
||||
"\u0075\u0065\u0064\u0065\u006E\u0073\u0069\u006D\u0070" +
|
||||
"\u006C\u0065\u006D\u0065\u006E\u0074\u0065\u0068\u0061" +
|
||||
"\u0062\u006C\u0061\u0072\u0065\u006E\u0045\u0073\u0070" +
|
||||
"\u0061\u00F1\u006F\u006C",
|
||||
"PorqunopuedensimplementehablarenEspaol-fmd56a",
|
||||
},
|
||||
{
|
||||
// (K) Vietnamese.
|
||||
"\u0054\u1EA1\u0069\u0073\u0061\u006F\u0068\u1ECD\u006B" +
|
||||
"\u0068\u00F4\u006E\u0067\u0074\u0068\u1EC3\u0063\u0068" +
|
||||
"\u1EC9\u006E\u00F3\u0069\u0074\u0069\u1EBF\u006E\u0067" +
|
||||
"\u0056\u0069\u1EC7\u0074",
|
||||
"TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g",
|
||||
},
|
||||
{
|
||||
// (L) 3<nen>B<gumi><kinpachi><sensei>.
|
||||
"\u0033\u5E74\u0042\u7D44\u91D1\u516B\u5148\u751F",
|
||||
"3B-ww4c5e180e575a65lsy2b",
|
||||
},
|
||||
{
|
||||
// (M) <amuro><namie>-with-SUPER-MONKEYS.
|
||||
"\u5B89\u5BA4\u5948\u7F8E\u6075\u002D\u0077\u0069\u0074" +
|
||||
"\u0068\u002D\u0053\u0055\u0050\u0045\u0052\u002D\u004D" +
|
||||
"\u004F\u004E\u004B\u0045\u0059\u0053",
|
||||
"-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n",
|
||||
},
|
||||
{
|
||||
// (N) Hello-Another-Way-<sorezore><no><basho>.
|
||||
"\u0048\u0065\u006C\u006C\u006F\u002D\u0041\u006E\u006F" +
|
||||
"\u0074\u0068\u0065\u0072\u002D\u0057\u0061\u0079\u002D" +
|
||||
"\u305D\u308C\u305E\u308C\u306E\u5834\u6240",
|
||||
"Hello-Another-Way--fc4qua05auwb3674vfr0b",
|
||||
},
|
||||
{
|
||||
// (O) <hitotsu><yane><no><shita>2.
|
||||
"\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B\u0032",
|
||||
"2-u9tlzr9756bt3uc0v",
|
||||
},
|
||||
{
|
||||
// (P) Maji<de>Koi<suru>5<byou><mae>
|
||||
"\u004D\u0061\u006A\u0069\u3067\u004B\u006F\u0069\u3059" +
|
||||
"\u308B\u0035\u79D2\u524D",
|
||||
"MajiKoi5-783gue6qz075azm5e",
|
||||
},
|
||||
{
|
||||
// (Q) <pafii>de<runba>
|
||||
"\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0",
|
||||
"de-jg4avhby1noc0d",
|
||||
},
|
||||
{
|
||||
// (R) <sono><supiido><de>
|
||||
"\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067",
|
||||
"d9juau41awczczp",
|
||||
},
|
||||
{
|
||||
// (S) -> $1.00 <-
|
||||
"\u002D\u003E\u0020\u0024\u0031\u002E\u0030\u0030\u0020" +
|
||||
"\u003C\u002D",
|
||||
"-> $1.00 <--",
|
||||
},
|
||||
}
|
||||
|
||||
func TestPunycode(t *testing.T) {
|
||||
for _, tc := range punycodeTestCases {
|
||||
if got, err := decode(tc.encoded); err != nil {
|
||||
t.Errorf("decode(%q): %v", tc.encoded, err)
|
||||
} else if got != tc.s {
|
||||
t.Errorf("decode(%q): got %q, want %q", tc.encoded, got, tc.s)
|
||||
}
|
||||
|
||||
if got, err := encode("", tc.s); err != nil {
|
||||
t.Errorf(`encode("", %q): %v`, tc.s, err)
|
||||
} else if got != tc.encoded {
|
||||
t.Errorf(`encode("", %q): got %q, want %q`, tc.s, got, tc.encoded)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var punycodeErrorTestCases = [...]string{
|
||||
"decode -", // A sole '-' is invalid.
|
||||
"decode foo\x00bar", // '\x00' is not in [0-9A-Za-z].
|
||||
"decode foo#bar", // '#' is not in [0-9A-Za-z].
|
||||
"decode foo\u00A3bar", // '\u00A3' is not in [0-9A-Za-z].
|
||||
"decode 9", // "9a" decodes to codepoint \u00A3; "9" is truncated.
|
||||
"decode 99999a", // "99999a" decodes to codepoint \U0048A3C1, which is > \U0010FFFF.
|
||||
"decode 9999999999a", // "9999999999a" overflows the int32 calculation.
|
||||
|
||||
"encode " + strings.Repeat("x", 65536) + "\uff00", // int32 overflow.
|
||||
}
|
||||
|
||||
func TestPunycodeErrors(t *testing.T) {
|
||||
for _, tc := range punycodeErrorTestCases {
|
||||
var err error
|
||||
switch {
|
||||
case strings.HasPrefix(tc, "decode "):
|
||||
_, err = decode(tc[7:])
|
||||
case strings.HasPrefix(tc, "encode "):
|
||||
_, err = encode("", tc[7:])
|
||||
}
|
||||
if err == nil {
|
||||
if len(tc) > 256 {
|
||||
tc = tc[:100] + "..." + tc[len(tc)-100:]
|
||||
}
|
||||
t.Errorf("no error for %s", tc)
|
||||
}
|
||||
}
|
||||
}
|
||||
170
vendor/golang.org/x/net/internal/timeseries/timeseries_test.go
generated
vendored
170
vendor/golang.org/x/net/internal/timeseries/timeseries_test.go
generated
vendored
|
|
@ -1,170 +0,0 @@
|
|||
// Copyright 2015 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 timeseries
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func isNear(x *Float, y float64, tolerance float64) bool {
|
||||
return math.Abs(x.Value()-y) < tolerance
|
||||
}
|
||||
|
||||
func isApproximate(x *Float, y float64) bool {
|
||||
return isNear(x, y, 1e-2)
|
||||
}
|
||||
|
||||
func checkApproximate(t *testing.T, o Observable, y float64) {
|
||||
x := o.(*Float)
|
||||
if !isApproximate(x, y) {
|
||||
t.Errorf("Wanted %g, got %g", y, x.Value())
|
||||
}
|
||||
}
|
||||
|
||||
func checkNear(t *testing.T, o Observable, y, tolerance float64) {
|
||||
x := o.(*Float)
|
||||
if !isNear(x, y, tolerance) {
|
||||
t.Errorf("Wanted %g +- %g, got %g", y, tolerance, x.Value())
|
||||
}
|
||||
}
|
||||
|
||||
var baseTime = time.Date(2013, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
func tu(s int64) time.Time {
|
||||
return baseTime.Add(time.Duration(s) * time.Second)
|
||||
}
|
||||
|
||||
func tu2(s int64, ns int64) time.Time {
|
||||
return baseTime.Add(time.Duration(s)*time.Second + time.Duration(ns)*time.Nanosecond)
|
||||
}
|
||||
|
||||
func TestBasicTimeSeries(t *testing.T) {
|
||||
ts := NewTimeSeries(NewFloat)
|
||||
fo := new(Float)
|
||||
*fo = Float(10)
|
||||
ts.AddWithTime(fo, tu(1))
|
||||
ts.AddWithTime(fo, tu(1))
|
||||
ts.AddWithTime(fo, tu(1))
|
||||
ts.AddWithTime(fo, tu(1))
|
||||
checkApproximate(t, ts.Range(tu(0), tu(1)), 40)
|
||||
checkApproximate(t, ts.Total(), 40)
|
||||
ts.AddWithTime(fo, tu(3))
|
||||
ts.AddWithTime(fo, tu(3))
|
||||
ts.AddWithTime(fo, tu(3))
|
||||
checkApproximate(t, ts.Range(tu(0), tu(2)), 40)
|
||||
checkApproximate(t, ts.Range(tu(2), tu(4)), 30)
|
||||
checkApproximate(t, ts.Total(), 70)
|
||||
ts.AddWithTime(fo, tu(1))
|
||||
ts.AddWithTime(fo, tu(1))
|
||||
checkApproximate(t, ts.Range(tu(0), tu(2)), 60)
|
||||
checkApproximate(t, ts.Range(tu(2), tu(4)), 30)
|
||||
checkApproximate(t, ts.Total(), 90)
|
||||
*fo = Float(100)
|
||||
ts.AddWithTime(fo, tu(100))
|
||||
checkApproximate(t, ts.Range(tu(99), tu(100)), 100)
|
||||
checkApproximate(t, ts.Range(tu(0), tu(4)), 36)
|
||||
checkApproximate(t, ts.Total(), 190)
|
||||
*fo = Float(10)
|
||||
ts.AddWithTime(fo, tu(1))
|
||||
ts.AddWithTime(fo, tu(1))
|
||||
checkApproximate(t, ts.Range(tu(0), tu(4)), 44)
|
||||
checkApproximate(t, ts.Range(tu(37), tu2(100, 100e6)), 100)
|
||||
checkApproximate(t, ts.Range(tu(50), tu2(100, 100e6)), 100)
|
||||
checkApproximate(t, ts.Range(tu(99), tu2(100, 100e6)), 100)
|
||||
checkApproximate(t, ts.Total(), 210)
|
||||
|
||||
for i, l := range ts.ComputeRange(tu(36), tu(100), 64) {
|
||||
if i == 63 {
|
||||
checkApproximate(t, l, 100)
|
||||
} else {
|
||||
checkApproximate(t, l, 0)
|
||||
}
|
||||
}
|
||||
|
||||
checkApproximate(t, ts.Range(tu(0), tu(100)), 210)
|
||||
checkApproximate(t, ts.Range(tu(10), tu(100)), 100)
|
||||
|
||||
for i, l := range ts.ComputeRange(tu(0), tu(100), 100) {
|
||||
if i < 10 {
|
||||
checkApproximate(t, l, 11)
|
||||
} else if i >= 90 {
|
||||
checkApproximate(t, l, 10)
|
||||
} else {
|
||||
checkApproximate(t, l, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloat(t *testing.T) {
|
||||
f := Float(1)
|
||||
if g, w := f.String(), "1"; g != w {
|
||||
t.Errorf("Float(1).String = %q; want %q", g, w)
|
||||
}
|
||||
f2 := Float(2)
|
||||
var o Observable = &f2
|
||||
f.Add(o)
|
||||
if g, w := f.Value(), 3.0; g != w {
|
||||
t.Errorf("Float post-add = %v; want %v", g, w)
|
||||
}
|
||||
f.Multiply(2)
|
||||
if g, w := f.Value(), 6.0; g != w {
|
||||
t.Errorf("Float post-multiply = %v; want %v", g, w)
|
||||
}
|
||||
f.Clear()
|
||||
if g, w := f.Value(), 0.0; g != w {
|
||||
t.Errorf("Float post-clear = %v; want %v", g, w)
|
||||
}
|
||||
f.CopyFrom(&f2)
|
||||
if g, w := f.Value(), 2.0; g != w {
|
||||
t.Errorf("Float post-CopyFrom = %v; want %v", g, w)
|
||||
}
|
||||
}
|
||||
|
||||
type mockClock struct {
|
||||
time time.Time
|
||||
}
|
||||
|
||||
func (m *mockClock) Time() time.Time { return m.time }
|
||||
func (m *mockClock) Set(t time.Time) { m.time = t }
|
||||
|
||||
const buckets = 6
|
||||
|
||||
var testResolutions = []time.Duration{
|
||||
10 * time.Second, // level holds one minute of observations
|
||||
100 * time.Second, // level holds ten minutes of observations
|
||||
10 * time.Minute, // level holds one hour of observations
|
||||
}
|
||||
|
||||
// TestTimeSeries uses a small number of buckets to force a higher
|
||||
// error rate on approximations from the timeseries.
|
||||
type TestTimeSeries struct {
|
||||
timeSeries
|
||||
}
|
||||
|
||||
func TestExpectedErrorRate(t *testing.T) {
|
||||
ts := new(TestTimeSeries)
|
||||
fake := new(mockClock)
|
||||
fake.Set(time.Now())
|
||||
ts.timeSeries.init(testResolutions, NewFloat, buckets, fake)
|
||||
for i := 1; i <= 61*61; i++ {
|
||||
fake.Set(fake.Time().Add(1 * time.Second))
|
||||
ob := Float(1)
|
||||
ts.AddWithTime(&ob, fake.Time())
|
||||
|
||||
// The results should be accurate within one missing bucket (1/6) of the observations recorded.
|
||||
checkNear(t, ts.Latest(0, buckets), min(float64(i), 60), 10)
|
||||
checkNear(t, ts.Latest(1, buckets), min(float64(i), 600), 100)
|
||||
checkNear(t, ts.Latest(2, buckets), min(float64(i), 3600), 600)
|
||||
}
|
||||
}
|
||||
|
||||
func min(a, b float64) float64 {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
119
vendor/golang.org/x/net/lex/httplex/httplex_test.go
generated
vendored
119
vendor/golang.org/x/net/lex/httplex/httplex_test.go
generated
vendored
|
|
@ -1,119 +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.
|
||||
|
||||
package httplex
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func isChar(c rune) bool { return c <= 127 }
|
||||
|
||||
func isCtl(c rune) bool { return c <= 31 || c == 127 }
|
||||
|
||||
func isSeparator(c rune) bool {
|
||||
switch c {
|
||||
case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestIsToken(t *testing.T) {
|
||||
for i := 0; i <= 130; i++ {
|
||||
r := rune(i)
|
||||
expected := isChar(r) && !isCtl(r) && !isSeparator(r)
|
||||
if IsTokenRune(r) != expected {
|
||||
t.Errorf("isToken(0x%x) = %v", r, !expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeaderValuesContainsToken(t *testing.T) {
|
||||
tests := []struct {
|
||||
vals []string
|
||||
token string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
vals: []string{"foo"},
|
||||
token: "foo",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
vals: []string{"bar", "foo"},
|
||||
token: "foo",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
vals: []string{"foo"},
|
||||
token: "FOO",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
vals: []string{"foo"},
|
||||
token: "bar",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
vals: []string{" foo "},
|
||||
token: "FOO",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
vals: []string{"foo,bar"},
|
||||
token: "FOO",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
vals: []string{"bar,foo,bar"},
|
||||
token: "FOO",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
vals: []string{"bar , foo"},
|
||||
token: "FOO",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
vals: []string{"foo ,bar "},
|
||||
token: "FOO",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
vals: []string{"bar, foo ,bar"},
|
||||
token: "FOO",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
vals: []string{"bar , foo"},
|
||||
token: "FOO",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := HeaderValuesContainsToken(tt.vals, tt.token)
|
||||
if got != tt.want {
|
||||
t.Errorf("headerValuesContainsToken(%q, %q) = %v; want %v", tt.vals, tt.token, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPunycodeHostPort(t *testing.T) {
|
||||
tests := []struct {
|
||||
in, want string
|
||||
}{
|
||||
{"www.google.com", "www.google.com"},
|
||||
{"гофер.рф", "xn--c1ae0ajs.xn--p1ai"},
|
||||
{"bücher.de", "xn--bcher-kva.de"},
|
||||
{"bücher.de:8080", "xn--bcher-kva.de:8080"},
|
||||
{"[1::6]:8080", "[1::6]:8080"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got, err := PunycodeHostPort(tt.in)
|
||||
if tt.want != got || err != nil {
|
||||
t.Errorf("PunycodeHostPort(%q) = %q, %v, want %q, nil", tt.in, got, err, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
416
vendor/golang.org/x/net/publicsuffix/list_test.go
generated
vendored
416
vendor/golang.org/x/net/publicsuffix/list_test.go
generated
vendored
|
|
@ -1,416 +0,0 @@
|
|||
// Copyright 2012 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 publicsuffix
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNodeLabel(t *testing.T) {
|
||||
for i, want := range nodeLabels {
|
||||
got := nodeLabel(uint32(i))
|
||||
if got != want {
|
||||
t.Errorf("%d: got %q, want %q", i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFind(t *testing.T) {
|
||||
testCases := []string{
|
||||
"",
|
||||
"a",
|
||||
"a0",
|
||||
"aaaa",
|
||||
"ao",
|
||||
"ap",
|
||||
"ar",
|
||||
"aro",
|
||||
"arp",
|
||||
"arpa",
|
||||
"arpaa",
|
||||
"arpb",
|
||||
"az",
|
||||
"b",
|
||||
"b0",
|
||||
"ba",
|
||||
"z",
|
||||
"zu",
|
||||
"zv",
|
||||
"zw",
|
||||
"zx",
|
||||
"zy",
|
||||
"zz",
|
||||
"zzzz",
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
got := find(tc, 0, numTLD)
|
||||
want := notFound
|
||||
for i := uint32(0); i < numTLD; i++ {
|
||||
if tc == nodeLabel(i) {
|
||||
want = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("%q: got %d, want %d", tc, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestICANN(t *testing.T) {
|
||||
testCases := map[string]bool{
|
||||
"foo.org": true,
|
||||
"foo.co.uk": true,
|
||||
"foo.dyndns.org": false,
|
||||
"foo.go.dyndns.org": false,
|
||||
"foo.blogspot.co.uk": false,
|
||||
"foo.intranet": false,
|
||||
}
|
||||
for domain, want := range testCases {
|
||||
_, got := PublicSuffix(domain)
|
||||
if got != want {
|
||||
t.Errorf("%q: got %v, want %v", domain, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var publicSuffixTestCases = []struct {
|
||||
domain, want string
|
||||
}{
|
||||
// Empty string.
|
||||
{"", ""},
|
||||
|
||||
// The .ao rules are:
|
||||
// ao
|
||||
// ed.ao
|
||||
// gv.ao
|
||||
// og.ao
|
||||
// co.ao
|
||||
// pb.ao
|
||||
// it.ao
|
||||
{"ao", "ao"},
|
||||
{"www.ao", "ao"},
|
||||
{"pb.ao", "pb.ao"},
|
||||
{"www.pb.ao", "pb.ao"},
|
||||
{"www.xxx.yyy.zzz.pb.ao", "pb.ao"},
|
||||
|
||||
// The .ar rules are:
|
||||
// ar
|
||||
// com.ar
|
||||
// edu.ar
|
||||
// gob.ar
|
||||
// gov.ar
|
||||
// int.ar
|
||||
// mil.ar
|
||||
// net.ar
|
||||
// org.ar
|
||||
// tur.ar
|
||||
// blogspot.com.ar
|
||||
{"ar", "ar"},
|
||||
{"www.ar", "ar"},
|
||||
{"nic.ar", "ar"},
|
||||
{"www.nic.ar", "ar"},
|
||||
{"com.ar", "com.ar"},
|
||||
{"www.com.ar", "com.ar"},
|
||||
{"blogspot.com.ar", "blogspot.com.ar"},
|
||||
{"www.blogspot.com.ar", "blogspot.com.ar"},
|
||||
{"www.xxx.yyy.zzz.blogspot.com.ar", "blogspot.com.ar"},
|
||||
{"logspot.com.ar", "com.ar"},
|
||||
{"zlogspot.com.ar", "com.ar"},
|
||||
{"zblogspot.com.ar", "com.ar"},
|
||||
|
||||
// The .arpa rules are:
|
||||
// arpa
|
||||
// e164.arpa
|
||||
// in-addr.arpa
|
||||
// ip6.arpa
|
||||
// iris.arpa
|
||||
// uri.arpa
|
||||
// urn.arpa
|
||||
{"arpa", "arpa"},
|
||||
{"www.arpa", "arpa"},
|
||||
{"urn.arpa", "urn.arpa"},
|
||||
{"www.urn.arpa", "urn.arpa"},
|
||||
{"www.xxx.yyy.zzz.urn.arpa", "urn.arpa"},
|
||||
|
||||
// The relevant {kobe,kyoto}.jp rules are:
|
||||
// jp
|
||||
// *.kobe.jp
|
||||
// !city.kobe.jp
|
||||
// kyoto.jp
|
||||
// ide.kyoto.jp
|
||||
{"jp", "jp"},
|
||||
{"kobe.jp", "jp"},
|
||||
{"c.kobe.jp", "c.kobe.jp"},
|
||||
{"b.c.kobe.jp", "c.kobe.jp"},
|
||||
{"a.b.c.kobe.jp", "c.kobe.jp"},
|
||||
{"city.kobe.jp", "kobe.jp"},
|
||||
{"www.city.kobe.jp", "kobe.jp"},
|
||||
{"kyoto.jp", "kyoto.jp"},
|
||||
{"test.kyoto.jp", "kyoto.jp"},
|
||||
{"ide.kyoto.jp", "ide.kyoto.jp"},
|
||||
{"b.ide.kyoto.jp", "ide.kyoto.jp"},
|
||||
{"a.b.ide.kyoto.jp", "ide.kyoto.jp"},
|
||||
|
||||
// The .tw rules are:
|
||||
// tw
|
||||
// edu.tw
|
||||
// gov.tw
|
||||
// mil.tw
|
||||
// com.tw
|
||||
// net.tw
|
||||
// org.tw
|
||||
// idv.tw
|
||||
// game.tw
|
||||
// ebiz.tw
|
||||
// club.tw
|
||||
// 網路.tw (xn--zf0ao64a.tw)
|
||||
// 組織.tw (xn--uc0atv.tw)
|
||||
// 商業.tw (xn--czrw28b.tw)
|
||||
// blogspot.tw
|
||||
{"tw", "tw"},
|
||||
{"aaa.tw", "tw"},
|
||||
{"www.aaa.tw", "tw"},
|
||||
{"xn--czrw28b.aaa.tw", "tw"},
|
||||
{"edu.tw", "edu.tw"},
|
||||
{"www.edu.tw", "edu.tw"},
|
||||
{"xn--czrw28b.edu.tw", "edu.tw"},
|
||||
{"xn--czrw28b.tw", "xn--czrw28b.tw"},
|
||||
{"www.xn--czrw28b.tw", "xn--czrw28b.tw"},
|
||||
{"xn--uc0atv.xn--czrw28b.tw", "xn--czrw28b.tw"},
|
||||
{"xn--kpry57d.tw", "tw"},
|
||||
|
||||
// The .uk rules are:
|
||||
// uk
|
||||
// ac.uk
|
||||
// co.uk
|
||||
// gov.uk
|
||||
// ltd.uk
|
||||
// me.uk
|
||||
// net.uk
|
||||
// nhs.uk
|
||||
// org.uk
|
||||
// plc.uk
|
||||
// police.uk
|
||||
// *.sch.uk
|
||||
// blogspot.co.uk
|
||||
{"uk", "uk"},
|
||||
{"aaa.uk", "uk"},
|
||||
{"www.aaa.uk", "uk"},
|
||||
{"mod.uk", "uk"},
|
||||
{"www.mod.uk", "uk"},
|
||||
{"sch.uk", "uk"},
|
||||
{"mod.sch.uk", "mod.sch.uk"},
|
||||
{"www.sch.uk", "www.sch.uk"},
|
||||
{"blogspot.co.uk", "blogspot.co.uk"},
|
||||
{"blogspot.nic.uk", "uk"},
|
||||
{"blogspot.sch.uk", "blogspot.sch.uk"},
|
||||
|
||||
// The .рф rules are
|
||||
// рф (xn--p1ai)
|
||||
{"xn--p1ai", "xn--p1ai"},
|
||||
{"aaa.xn--p1ai", "xn--p1ai"},
|
||||
{"www.xxx.yyy.xn--p1ai", "xn--p1ai"},
|
||||
|
||||
// The .bd rules are:
|
||||
// *.bd
|
||||
{"bd", "bd"},
|
||||
{"www.bd", "www.bd"},
|
||||
{"zzz.bd", "zzz.bd"},
|
||||
{"www.zzz.bd", "zzz.bd"},
|
||||
{"www.xxx.yyy.zzz.bd", "zzz.bd"},
|
||||
|
||||
// There are no .nosuchtld rules.
|
||||
{"nosuchtld", "nosuchtld"},
|
||||
{"foo.nosuchtld", "nosuchtld"},
|
||||
{"bar.foo.nosuchtld", "nosuchtld"},
|
||||
}
|
||||
|
||||
func BenchmarkPublicSuffix(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, tc := range publicSuffixTestCases {
|
||||
List.PublicSuffix(tc.domain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicSuffix(t *testing.T) {
|
||||
for _, tc := range publicSuffixTestCases {
|
||||
got := List.PublicSuffix(tc.domain)
|
||||
if got != tc.want {
|
||||
t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSlowPublicSuffix(t *testing.T) {
|
||||
for _, tc := range publicSuffixTestCases {
|
||||
got := slowPublicSuffix(tc.domain)
|
||||
if got != tc.want {
|
||||
t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// slowPublicSuffix implements the canonical (but O(number of rules)) public
|
||||
// suffix algorithm described at http://publicsuffix.org/list/.
|
||||
//
|
||||
// 1. Match domain against all rules and take note of the matching ones.
|
||||
// 2. If no rules match, the prevailing rule is "*".
|
||||
// 3. If more than one rule matches, the prevailing rule is the one which is an exception rule.
|
||||
// 4. If there is no matching exception rule, the prevailing rule is the one with the most labels.
|
||||
// 5. If the prevailing rule is a exception rule, modify it by removing the leftmost label.
|
||||
// 6. The public suffix is the set of labels from the domain which directly match the labels of the prevailing rule (joined by dots).
|
||||
// 7. The registered or registrable domain is the public suffix plus one additional label.
|
||||
//
|
||||
// This function returns the public suffix, not the registrable domain, and so
|
||||
// it stops after step 6.
|
||||
func slowPublicSuffix(domain string) string {
|
||||
match := func(rulePart, domainPart string) bool {
|
||||
switch rulePart[0] {
|
||||
case '*':
|
||||
return true
|
||||
case '!':
|
||||
return rulePart[1:] == domainPart
|
||||
}
|
||||
return rulePart == domainPart
|
||||
}
|
||||
|
||||
domainParts := strings.Split(domain, ".")
|
||||
var matchingRules [][]string
|
||||
|
||||
loop:
|
||||
for _, rule := range rules {
|
||||
ruleParts := strings.Split(rule, ".")
|
||||
if len(domainParts) < len(ruleParts) {
|
||||
continue
|
||||
}
|
||||
for i := range ruleParts {
|
||||
rulePart := ruleParts[len(ruleParts)-1-i]
|
||||
domainPart := domainParts[len(domainParts)-1-i]
|
||||
if !match(rulePart, domainPart) {
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
matchingRules = append(matchingRules, ruleParts)
|
||||
}
|
||||
if len(matchingRules) == 0 {
|
||||
matchingRules = append(matchingRules, []string{"*"})
|
||||
} else {
|
||||
sort.Sort(byPriority(matchingRules))
|
||||
}
|
||||
prevailing := matchingRules[0]
|
||||
if prevailing[0][0] == '!' {
|
||||
prevailing = prevailing[1:]
|
||||
}
|
||||
if prevailing[0][0] == '*' {
|
||||
replaced := domainParts[len(domainParts)-len(prevailing)]
|
||||
prevailing = append([]string{replaced}, prevailing[1:]...)
|
||||
}
|
||||
return strings.Join(prevailing, ".")
|
||||
}
|
||||
|
||||
type byPriority [][]string
|
||||
|
||||
func (b byPriority) Len() int { return len(b) }
|
||||
func (b byPriority) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||
func (b byPriority) Less(i, j int) bool {
|
||||
if b[i][0][0] == '!' {
|
||||
return true
|
||||
}
|
||||
if b[j][0][0] == '!' {
|
||||
return false
|
||||
}
|
||||
return len(b[i]) > len(b[j])
|
||||
}
|
||||
|
||||
// eTLDPlusOneTestCases come from
|
||||
// https://github.com/publicsuffix/list/blob/master/tests/test_psl.txt
|
||||
var eTLDPlusOneTestCases = []struct {
|
||||
domain, want string
|
||||
}{
|
||||
// Empty input.
|
||||
{"", ""},
|
||||
// Unlisted TLD.
|
||||
{"example", ""},
|
||||
{"example.example", "example.example"},
|
||||
{"b.example.example", "example.example"},
|
||||
{"a.b.example.example", "example.example"},
|
||||
// TLD with only 1 rule.
|
||||
{"biz", ""},
|
||||
{"domain.biz", "domain.biz"},
|
||||
{"b.domain.biz", "domain.biz"},
|
||||
{"a.b.domain.biz", "domain.biz"},
|
||||
// TLD with some 2-level rules.
|
||||
{"com", ""},
|
||||
{"example.com", "example.com"},
|
||||
{"b.example.com", "example.com"},
|
||||
{"a.b.example.com", "example.com"},
|
||||
{"uk.com", ""},
|
||||
{"example.uk.com", "example.uk.com"},
|
||||
{"b.example.uk.com", "example.uk.com"},
|
||||
{"a.b.example.uk.com", "example.uk.com"},
|
||||
{"test.ac", "test.ac"},
|
||||
// TLD with only 1 (wildcard) rule.
|
||||
{"mm", ""},
|
||||
{"c.mm", ""},
|
||||
{"b.c.mm", "b.c.mm"},
|
||||
{"a.b.c.mm", "b.c.mm"},
|
||||
// More complex TLD.
|
||||
{"jp", ""},
|
||||
{"test.jp", "test.jp"},
|
||||
{"www.test.jp", "test.jp"},
|
||||
{"ac.jp", ""},
|
||||
{"test.ac.jp", "test.ac.jp"},
|
||||
{"www.test.ac.jp", "test.ac.jp"},
|
||||
{"kyoto.jp", ""},
|
||||
{"test.kyoto.jp", "test.kyoto.jp"},
|
||||
{"ide.kyoto.jp", ""},
|
||||
{"b.ide.kyoto.jp", "b.ide.kyoto.jp"},
|
||||
{"a.b.ide.kyoto.jp", "b.ide.kyoto.jp"},
|
||||
{"c.kobe.jp", ""},
|
||||
{"b.c.kobe.jp", "b.c.kobe.jp"},
|
||||
{"a.b.c.kobe.jp", "b.c.kobe.jp"},
|
||||
{"city.kobe.jp", "city.kobe.jp"},
|
||||
{"www.city.kobe.jp", "city.kobe.jp"},
|
||||
// TLD with a wildcard rule and exceptions.
|
||||
{"ck", ""},
|
||||
{"test.ck", ""},
|
||||
{"b.test.ck", "b.test.ck"},
|
||||
{"a.b.test.ck", "b.test.ck"},
|
||||
{"www.ck", "www.ck"},
|
||||
{"www.www.ck", "www.ck"},
|
||||
// US K12.
|
||||
{"us", ""},
|
||||
{"test.us", "test.us"},
|
||||
{"www.test.us", "test.us"},
|
||||
{"ak.us", ""},
|
||||
{"test.ak.us", "test.ak.us"},
|
||||
{"www.test.ak.us", "test.ak.us"},
|
||||
{"k12.ak.us", ""},
|
||||
{"test.k12.ak.us", "test.k12.ak.us"},
|
||||
{"www.test.k12.ak.us", "test.k12.ak.us"},
|
||||
// Punycoded IDN labels
|
||||
{"xn--85x722f.com.cn", "xn--85x722f.com.cn"},
|
||||
{"xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn"},
|
||||
{"www.xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn"},
|
||||
{"shishi.xn--55qx5d.cn", "shishi.xn--55qx5d.cn"},
|
||||
{"xn--55qx5d.cn", ""},
|
||||
{"xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s"},
|
||||
{"www.xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s"},
|
||||
{"shishi.xn--fiqs8s", "shishi.xn--fiqs8s"},
|
||||
{"xn--fiqs8s", ""},
|
||||
}
|
||||
|
||||
func TestEffectiveTLDPlusOne(t *testing.T) {
|
||||
for _, tc := range eTLDPlusOneTestCases {
|
||||
got, _ := EffectiveTLDPlusOne(tc.domain)
|
||||
if got != tc.want {
|
||||
t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
18783
vendor/golang.org/x/net/publicsuffix/table.go
generated
vendored
18783
vendor/golang.org/x/net/publicsuffix/table.go
generated
vendored
File diff suppressed because it is too large
Load diff
16756
vendor/golang.org/x/net/publicsuffix/table_test.go
generated
vendored
16756
vendor/golang.org/x/net/publicsuffix/table_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
325
vendor/golang.org/x/net/trace/histogram_test.go
generated
vendored
325
vendor/golang.org/x/net/trace/histogram_test.go
generated
vendored
|
|
@ -1,325 +0,0 @@
|
|||
// Copyright 2015 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 trace
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type sumTest struct {
|
||||
value int64
|
||||
sum int64
|
||||
sumOfSquares float64
|
||||
total int64
|
||||
}
|
||||
|
||||
var sumTests = []sumTest{
|
||||
{100, 100, 10000, 1},
|
||||
{50, 150, 12500, 2},
|
||||
{50, 200, 15000, 3},
|
||||
{50, 250, 17500, 4},
|
||||
}
|
||||
|
||||
type bucketingTest struct {
|
||||
in int64
|
||||
log int
|
||||
bucket int
|
||||
}
|
||||
|
||||
var bucketingTests = []bucketingTest{
|
||||
{0, 0, 0},
|
||||
{1, 1, 0},
|
||||
{2, 2, 1},
|
||||
{3, 2, 1},
|
||||
{4, 3, 2},
|
||||
{1000, 10, 9},
|
||||
{1023, 10, 9},
|
||||
{1024, 11, 10},
|
||||
{1000000, 20, 19},
|
||||
}
|
||||
|
||||
type multiplyTest struct {
|
||||
in int64
|
||||
ratio float64
|
||||
expectedSum int64
|
||||
expectedTotal int64
|
||||
expectedSumOfSquares float64
|
||||
}
|
||||
|
||||
var multiplyTests = []multiplyTest{
|
||||
{15, 2.5, 37, 2, 562.5},
|
||||
{128, 4.6, 758, 13, 77953.9},
|
||||
}
|
||||
|
||||
type percentileTest struct {
|
||||
fraction float64
|
||||
expected int64
|
||||
}
|
||||
|
||||
var percentileTests = []percentileTest{
|
||||
{0.25, 48},
|
||||
{0.5, 96},
|
||||
{0.6, 109},
|
||||
{0.75, 128},
|
||||
{0.90, 205},
|
||||
{0.95, 230},
|
||||
{0.99, 256},
|
||||
}
|
||||
|
||||
func TestSum(t *testing.T) {
|
||||
var h histogram
|
||||
|
||||
for _, test := range sumTests {
|
||||
h.addMeasurement(test.value)
|
||||
sum := h.sum
|
||||
if sum != test.sum {
|
||||
t.Errorf("h.Sum = %v WANT: %v", sum, test.sum)
|
||||
}
|
||||
|
||||
sumOfSquares := h.sumOfSquares
|
||||
if sumOfSquares != test.sumOfSquares {
|
||||
t.Errorf("h.SumOfSquares = %v WANT: %v", sumOfSquares, test.sumOfSquares)
|
||||
}
|
||||
|
||||
total := h.total()
|
||||
if total != test.total {
|
||||
t.Errorf("h.Total = %v WANT: %v", total, test.total)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiply(t *testing.T) {
|
||||
var h histogram
|
||||
for i, test := range multiplyTests {
|
||||
h.addMeasurement(test.in)
|
||||
h.Multiply(test.ratio)
|
||||
if h.sum != test.expectedSum {
|
||||
t.Errorf("#%v: h.sum = %v WANT: %v", i, h.sum, test.expectedSum)
|
||||
}
|
||||
if h.total() != test.expectedTotal {
|
||||
t.Errorf("#%v: h.total = %v WANT: %v", i, h.total(), test.expectedTotal)
|
||||
}
|
||||
if h.sumOfSquares != test.expectedSumOfSquares {
|
||||
t.Errorf("#%v: h.SumOfSquares = %v WANT: %v", i, test.expectedSumOfSquares, h.sumOfSquares)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBucketingFunctions(t *testing.T) {
|
||||
for _, test := range bucketingTests {
|
||||
log := log2(test.in)
|
||||
if log != test.log {
|
||||
t.Errorf("log2 = %v WANT: %v", log, test.log)
|
||||
}
|
||||
|
||||
bucket := getBucket(test.in)
|
||||
if bucket != test.bucket {
|
||||
t.Errorf("getBucket = %v WANT: %v", bucket, test.bucket)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAverage(t *testing.T) {
|
||||
a := new(histogram)
|
||||
average := a.average()
|
||||
if average != 0 {
|
||||
t.Errorf("Average of empty histogram was %v WANT: 0", average)
|
||||
}
|
||||
|
||||
a.addMeasurement(1)
|
||||
a.addMeasurement(1)
|
||||
a.addMeasurement(3)
|
||||
const expected = float64(5) / float64(3)
|
||||
average = a.average()
|
||||
|
||||
if !isApproximate(average, expected) {
|
||||
t.Errorf("Average = %g WANT: %v", average, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStandardDeviation(t *testing.T) {
|
||||
a := new(histogram)
|
||||
add(a, 10, 1<<4)
|
||||
add(a, 10, 1<<5)
|
||||
add(a, 10, 1<<6)
|
||||
stdDev := a.standardDeviation()
|
||||
const expected = 19.95
|
||||
|
||||
if !isApproximate(stdDev, expected) {
|
||||
t.Errorf("StandardDeviation = %v WANT: %v", stdDev, expected)
|
||||
}
|
||||
|
||||
// No values
|
||||
a = new(histogram)
|
||||
stdDev = a.standardDeviation()
|
||||
|
||||
if !isApproximate(stdDev, 0) {
|
||||
t.Errorf("StandardDeviation = %v WANT: 0", stdDev)
|
||||
}
|
||||
|
||||
add(a, 1, 1<<4)
|
||||
if !isApproximate(stdDev, 0) {
|
||||
t.Errorf("StandardDeviation = %v WANT: 0", stdDev)
|
||||
}
|
||||
|
||||
add(a, 10, 1<<4)
|
||||
if !isApproximate(stdDev, 0) {
|
||||
t.Errorf("StandardDeviation = %v WANT: 0", stdDev)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPercentileBoundary(t *testing.T) {
|
||||
a := new(histogram)
|
||||
add(a, 5, 1<<4)
|
||||
add(a, 10, 1<<6)
|
||||
add(a, 5, 1<<7)
|
||||
|
||||
for _, test := range percentileTests {
|
||||
percentile := a.percentileBoundary(test.fraction)
|
||||
if percentile != test.expected {
|
||||
t.Errorf("h.PercentileBoundary (fraction=%v) = %v WANT: %v", test.fraction, percentile, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyFrom(t *testing.T) {
|
||||
a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
||||
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1}
|
||||
b := histogram{6, 36, []int64{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}, 5, -1}
|
||||
|
||||
a.CopyFrom(&b)
|
||||
|
||||
if a.String() != b.String() {
|
||||
t.Errorf("a.String = %s WANT: %s", a.String(), b.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestClear(t *testing.T) {
|
||||
a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
||||
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1}
|
||||
|
||||
a.Clear()
|
||||
|
||||
expected := "0, 0.000000, 0, 0, []"
|
||||
if a.String() != expected {
|
||||
t.Errorf("a.String = %s WANT %s", a.String(), expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
||||
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1}
|
||||
b := a.New()
|
||||
|
||||
expected := "0, 0.000000, 0, 0, []"
|
||||
if b.(*histogram).String() != expected {
|
||||
t.Errorf("b.(*histogram).String = %s WANT: %s", b.(*histogram).String(), expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
// The tests here depend on the associativity of addMeasurement and Add.
|
||||
// Add empty observation
|
||||
a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
||||
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1}
|
||||
b := a.New()
|
||||
|
||||
expected := a.String()
|
||||
a.Add(b)
|
||||
if a.String() != expected {
|
||||
t.Errorf("a.String = %s WANT: %s", a.String(), expected)
|
||||
}
|
||||
|
||||
// Add same bucketed value, no new buckets
|
||||
c := new(histogram)
|
||||
d := new(histogram)
|
||||
e := new(histogram)
|
||||
c.addMeasurement(12)
|
||||
d.addMeasurement(11)
|
||||
e.addMeasurement(12)
|
||||
e.addMeasurement(11)
|
||||
c.Add(d)
|
||||
if c.String() != e.String() {
|
||||
t.Errorf("c.String = %s WANT: %s", c.String(), e.String())
|
||||
}
|
||||
|
||||
// Add bucketed values
|
||||
f := new(histogram)
|
||||
g := new(histogram)
|
||||
h := new(histogram)
|
||||
f.addMeasurement(4)
|
||||
f.addMeasurement(12)
|
||||
f.addMeasurement(100)
|
||||
g.addMeasurement(18)
|
||||
g.addMeasurement(36)
|
||||
g.addMeasurement(255)
|
||||
h.addMeasurement(4)
|
||||
h.addMeasurement(12)
|
||||
h.addMeasurement(100)
|
||||
h.addMeasurement(18)
|
||||
h.addMeasurement(36)
|
||||
h.addMeasurement(255)
|
||||
f.Add(g)
|
||||
if f.String() != h.String() {
|
||||
t.Errorf("f.String = %q WANT: %q", f.String(), h.String())
|
||||
}
|
||||
|
||||
// add buckets to no buckets
|
||||
i := new(histogram)
|
||||
j := new(histogram)
|
||||
k := new(histogram)
|
||||
j.addMeasurement(18)
|
||||
j.addMeasurement(36)
|
||||
j.addMeasurement(255)
|
||||
k.addMeasurement(18)
|
||||
k.addMeasurement(36)
|
||||
k.addMeasurement(255)
|
||||
i.Add(j)
|
||||
if i.String() != k.String() {
|
||||
t.Errorf("i.String = %q WANT: %q", i.String(), k.String())
|
||||
}
|
||||
|
||||
// add buckets to single value (no overlap)
|
||||
l := new(histogram)
|
||||
m := new(histogram)
|
||||
n := new(histogram)
|
||||
l.addMeasurement(0)
|
||||
m.addMeasurement(18)
|
||||
m.addMeasurement(36)
|
||||
m.addMeasurement(255)
|
||||
n.addMeasurement(0)
|
||||
n.addMeasurement(18)
|
||||
n.addMeasurement(36)
|
||||
n.addMeasurement(255)
|
||||
l.Add(m)
|
||||
if l.String() != n.String() {
|
||||
t.Errorf("l.String = %q WANT: %q", l.String(), n.String())
|
||||
}
|
||||
|
||||
// mixed order
|
||||
o := new(histogram)
|
||||
p := new(histogram)
|
||||
o.addMeasurement(0)
|
||||
o.addMeasurement(2)
|
||||
o.addMeasurement(0)
|
||||
p.addMeasurement(0)
|
||||
p.addMeasurement(0)
|
||||
p.addMeasurement(2)
|
||||
if o.String() != p.String() {
|
||||
t.Errorf("o.String = %q WANT: %q", o.String(), p.String())
|
||||
}
|
||||
}
|
||||
|
||||
func add(h *histogram, times int, val int64) {
|
||||
for i := 0; i < times; i++ {
|
||||
h.addMeasurement(val)
|
||||
}
|
||||
}
|
||||
|
||||
func isApproximate(x, y float64) bool {
|
||||
return math.Abs(x-y) < 1e-2
|
||||
}
|
||||
59
vendor/golang.org/x/net/trace/trace.go
generated
vendored
59
vendor/golang.org/x/net/trace/trace.go
generated
vendored
|
|
@ -368,7 +368,11 @@ func New(family, title string) Trace {
|
|||
}
|
||||
|
||||
func (tr *trace) Finish() {
|
||||
tr.Elapsed = time.Now().Sub(tr.Start)
|
||||
elapsed := time.Now().Sub(tr.Start)
|
||||
tr.mu.Lock()
|
||||
tr.Elapsed = elapsed
|
||||
tr.mu.Unlock()
|
||||
|
||||
if DebugUseAfterFinish {
|
||||
buf := make([]byte, 4<<10) // 4 KB should be enough
|
||||
n := runtime.Stack(buf, false)
|
||||
|
|
@ -381,14 +385,17 @@ func (tr *trace) Finish() {
|
|||
m.Remove(tr)
|
||||
|
||||
f := getFamily(tr.Family, true)
|
||||
tr.mu.RLock() // protects tr fields in Cond.match calls
|
||||
for _, b := range f.Buckets {
|
||||
if b.Cond.match(tr) {
|
||||
b.Add(tr)
|
||||
}
|
||||
}
|
||||
tr.mu.RUnlock()
|
||||
|
||||
// Add a sample of elapsed time as microseconds to the family's timeseries
|
||||
h := new(histogram)
|
||||
h.addMeasurement(tr.Elapsed.Nanoseconds() / 1e3)
|
||||
h.addMeasurement(elapsed.Nanoseconds() / 1e3)
|
||||
f.LatencyMu.Lock()
|
||||
f.Latency.Add(h)
|
||||
f.LatencyMu.Unlock()
|
||||
|
|
@ -684,25 +691,20 @@ type trace struct {
|
|||
// Title is the title of this trace.
|
||||
Title string
|
||||
|
||||
// Timing information.
|
||||
Start time.Time
|
||||
Elapsed time.Duration // zero while active
|
||||
// Start time of the this trace.
|
||||
Start time.Time
|
||||
|
||||
// Trace information if non-zero.
|
||||
traceID uint64
|
||||
spanID uint64
|
||||
|
||||
// Whether this trace resulted in an error.
|
||||
IsError bool
|
||||
|
||||
// Append-only sequence of events (modulo discards).
|
||||
mu sync.RWMutex
|
||||
events []event
|
||||
events []event // Append-only sequence of events (modulo discards).
|
||||
maxEvents int
|
||||
recycler func(interface{})
|
||||
IsError bool // Whether this trace resulted in an error.
|
||||
Elapsed time.Duration // Elapsed time for this trace, zero while active.
|
||||
traceID uint64 // Trace information if non-zero.
|
||||
spanID uint64
|
||||
|
||||
refs int32 // how many buckets this is in
|
||||
recycler func(interface{})
|
||||
disc discarded // scratch space to avoid allocation
|
||||
refs int32 // how many buckets this is in
|
||||
disc discarded // scratch space to avoid allocation
|
||||
|
||||
finishStack []byte // where finish was called, if DebugUseAfterFinish is set
|
||||
|
||||
|
|
@ -714,14 +716,18 @@ func (tr *trace) reset() {
|
|||
tr.Family = ""
|
||||
tr.Title = ""
|
||||
tr.Start = time.Time{}
|
||||
|
||||
tr.mu.Lock()
|
||||
tr.Elapsed = 0
|
||||
tr.traceID = 0
|
||||
tr.spanID = 0
|
||||
tr.IsError = false
|
||||
tr.maxEvents = 0
|
||||
tr.events = nil
|
||||
tr.refs = 0
|
||||
tr.recycler = nil
|
||||
tr.mu.Unlock()
|
||||
|
||||
tr.refs = 0
|
||||
tr.disc = 0
|
||||
tr.finishStack = nil
|
||||
for i := range tr.eventsBuf {
|
||||
|
|
@ -801,21 +807,31 @@ func (tr *trace) LazyPrintf(format string, a ...interface{}) {
|
|||
tr.addEvent(&lazySprintf{format, a}, false, false)
|
||||
}
|
||||
|
||||
func (tr *trace) SetError() { tr.IsError = true }
|
||||
func (tr *trace) SetError() {
|
||||
tr.mu.Lock()
|
||||
tr.IsError = true
|
||||
tr.mu.Unlock()
|
||||
}
|
||||
|
||||
func (tr *trace) SetRecycler(f func(interface{})) {
|
||||
tr.mu.Lock()
|
||||
tr.recycler = f
|
||||
tr.mu.Unlock()
|
||||
}
|
||||
|
||||
func (tr *trace) SetTraceInfo(traceID, spanID uint64) {
|
||||
tr.mu.Lock()
|
||||
tr.traceID, tr.spanID = traceID, spanID
|
||||
tr.mu.Unlock()
|
||||
}
|
||||
|
||||
func (tr *trace) SetMaxEvents(m int) {
|
||||
tr.mu.Lock()
|
||||
// Always keep at least three events: first, discarded count, last.
|
||||
if len(tr.events) == 0 && m > 3 {
|
||||
tr.maxEvents = m
|
||||
}
|
||||
tr.mu.Unlock()
|
||||
}
|
||||
|
||||
func (tr *trace) ref() {
|
||||
|
|
@ -824,6 +840,7 @@ func (tr *trace) ref() {
|
|||
|
||||
func (tr *trace) unref() {
|
||||
if atomic.AddInt32(&tr.refs, -1) == 0 {
|
||||
tr.mu.RLock()
|
||||
if tr.recycler != nil {
|
||||
// freeTrace clears tr, so we hold tr.recycler and tr.events here.
|
||||
go func(f func(interface{}), es []event) {
|
||||
|
|
@ -834,6 +851,7 @@ func (tr *trace) unref() {
|
|||
}
|
||||
}(tr.recycler, tr.events)
|
||||
}
|
||||
tr.mu.RUnlock()
|
||||
|
||||
freeTrace(tr)
|
||||
}
|
||||
|
|
@ -844,7 +862,10 @@ func (tr *trace) When() string {
|
|||
}
|
||||
|
||||
func (tr *trace) ElapsedTime() string {
|
||||
tr.mu.RLock()
|
||||
t := tr.Elapsed
|
||||
tr.mu.RUnlock()
|
||||
|
||||
if t == 0 {
|
||||
// Active trace.
|
||||
t = time.Since(tr.Start)
|
||||
|
|
|
|||
178
vendor/golang.org/x/net/trace/trace_test.go
generated
vendored
178
vendor/golang.org/x/net/trace/trace_test.go
generated
vendored
|
|
@ -1,178 +0,0 @@
|
|||
// Copyright 2015 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 trace
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type s struct{}
|
||||
|
||||
func (s) String() string { return "lazy string" }
|
||||
|
||||
// TestReset checks whether all the fields are zeroed after reset.
|
||||
func TestReset(t *testing.T) {
|
||||
tr := New("foo", "bar")
|
||||
tr.LazyLog(s{}, false)
|
||||
tr.LazyPrintf("%d", 1)
|
||||
tr.SetRecycler(func(_ interface{}) {})
|
||||
tr.SetTraceInfo(3, 4)
|
||||
tr.SetMaxEvents(100)
|
||||
tr.SetError()
|
||||
tr.Finish()
|
||||
|
||||
tr.(*trace).reset()
|
||||
|
||||
if !reflect.DeepEqual(tr, new(trace)) {
|
||||
t.Errorf("reset didn't clear all fields: %+v", tr)
|
||||
}
|
||||
}
|
||||
|
||||
// TestResetLog checks whether all the fields are zeroed after reset.
|
||||
func TestResetLog(t *testing.T) {
|
||||
el := NewEventLog("foo", "bar")
|
||||
el.Printf("message")
|
||||
el.Errorf("error")
|
||||
el.Finish()
|
||||
|
||||
el.(*eventLog).reset()
|
||||
|
||||
if !reflect.DeepEqual(el, new(eventLog)) {
|
||||
t.Errorf("reset didn't clear all fields: %+v", el)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthRequest(t *testing.T) {
|
||||
testCases := []struct {
|
||||
host string
|
||||
want bool
|
||||
}{
|
||||
{host: "192.168.23.1", want: false},
|
||||
{host: "192.168.23.1:8080", want: false},
|
||||
{host: "malformed remote addr", want: false},
|
||||
{host: "localhost", want: true},
|
||||
{host: "localhost:8080", want: true},
|
||||
{host: "127.0.0.1", want: true},
|
||||
{host: "127.0.0.1:8080", want: true},
|
||||
{host: "::1", want: true},
|
||||
{host: "[::1]:8080", want: true},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
req := &http.Request{RemoteAddr: tt.host}
|
||||
any, sensitive := AuthRequest(req)
|
||||
if any != tt.want || sensitive != tt.want {
|
||||
t.Errorf("AuthRequest(%q) = %t, %t; want %t, %t", tt.host, any, sensitive, tt.want, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseTemplate checks that all templates used by this package are valid
|
||||
// as they are parsed on first usage
|
||||
func TestParseTemplate(t *testing.T) {
|
||||
if tmpl := distTmpl(); tmpl == nil {
|
||||
t.Error("invalid template returned from distTmpl()")
|
||||
}
|
||||
if tmpl := pageTmpl(); tmpl == nil {
|
||||
t.Error("invalid template returned from pageTmpl()")
|
||||
}
|
||||
if tmpl := eventsTmpl(); tmpl == nil {
|
||||
t.Error("invalid template returned from eventsTmpl()")
|
||||
}
|
||||
}
|
||||
|
||||
func benchmarkTrace(b *testing.B, maxEvents, numEvents int) {
|
||||
numSpans := (b.N + numEvents + 1) / numEvents
|
||||
|
||||
for i := 0; i < numSpans; i++ {
|
||||
tr := New("test", "test")
|
||||
tr.SetMaxEvents(maxEvents)
|
||||
for j := 0; j < numEvents; j++ {
|
||||
tr.LazyPrintf("%d", j)
|
||||
}
|
||||
tr.Finish()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTrace_Default_2(b *testing.B) {
|
||||
benchmarkTrace(b, 0, 2)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_Default_10(b *testing.B) {
|
||||
benchmarkTrace(b, 0, 10)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_Default_100(b *testing.B) {
|
||||
benchmarkTrace(b, 0, 100)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_Default_1000(b *testing.B) {
|
||||
benchmarkTrace(b, 0, 1000)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_Default_10000(b *testing.B) {
|
||||
benchmarkTrace(b, 0, 10000)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_10_2(b *testing.B) {
|
||||
benchmarkTrace(b, 10, 2)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_10_10(b *testing.B) {
|
||||
benchmarkTrace(b, 10, 10)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_10_100(b *testing.B) {
|
||||
benchmarkTrace(b, 10, 100)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_10_1000(b *testing.B) {
|
||||
benchmarkTrace(b, 10, 1000)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_10_10000(b *testing.B) {
|
||||
benchmarkTrace(b, 10, 10000)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_100_2(b *testing.B) {
|
||||
benchmarkTrace(b, 100, 2)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_100_10(b *testing.B) {
|
||||
benchmarkTrace(b, 100, 10)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_100_100(b *testing.B) {
|
||||
benchmarkTrace(b, 100, 100)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_100_1000(b *testing.B) {
|
||||
benchmarkTrace(b, 100, 1000)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_100_10000(b *testing.B) {
|
||||
benchmarkTrace(b, 100, 10000)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_1000_2(b *testing.B) {
|
||||
benchmarkTrace(b, 1000, 2)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_1000_10(b *testing.B) {
|
||||
benchmarkTrace(b, 1000, 10)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_1000_100(b *testing.B) {
|
||||
benchmarkTrace(b, 1000, 100)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_1000_1000(b *testing.B) {
|
||||
benchmarkTrace(b, 1000, 1000)
|
||||
}
|
||||
|
||||
func BenchmarkTrace_1000_10000(b *testing.B) {
|
||||
benchmarkTrace(b, 1000, 10000)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue