Bumping k8s dependencies to 1.13

This commit is contained in:
Cheng Xing
2018-11-16 14:08:25 -08:00
parent 305407125c
commit b4c0b68ec7
8002 changed files with 884099 additions and 276228 deletions

View File

@@ -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.

View File

@@ -38,6 +38,7 @@ const (
type JumpTest uint16
// Supported operators for conditional jumps.
// K can be RegX for JumpIfX
const (
// K == A
JumpEqual JumpTest = iota
@@ -134,12 +135,9 @@ const (
opMaskLoadDest = 0x01
opMaskLoadWidth = 0x18
opMaskLoadMode = 0xe0
// opClsALU
opMaskOperandSrc = 0x08
opMaskOperator = 0xf0
// opClsJump
opMaskJumpConst = 0x0f
opMaskJumpCond = 0xf0
// opClsALU & opClsJump
opMaskOperand = 0x08
opMaskOperator = 0xf0
)
const (
@@ -192,15 +190,21 @@ const (
opLoadWidth1
)
// Operator defined by ALUOp*
// Operand for ALU and Jump instructions
type opOperand uint16
// Supported operand sources.
const (
opALUSrcConstant uint16 = iota << 3
opALUSrcX
opOperandConstant opOperand = iota << 3
opOperandX
)
// An jumpOp is a conditional jump condition.
type jumpOp uint16
// Supported jump conditions.
const (
opJumpAlways = iota << 4
opJumpAlways jumpOp = iota << 4
opJumpEqual
opJumpGT
opJumpGE

View File

@@ -89,10 +89,14 @@ func (ri RawInstruction) Disassemble() Instruction {
case opClsALU:
switch op := ALUOp(ri.Op & opMaskOperator); op {
case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor:
if ri.Op&opMaskOperandSrc != 0 {
switch operand := opOperand(ri.Op & opMaskOperand); operand {
case opOperandX:
return ALUOpX{Op: op}
case opOperandConstant:
return ALUOpConstant{Op: op, Val: ri.K}
default:
return ri
}
return ALUOpConstant{Op: op, Val: ri.K}
case aluOpNeg:
return NegateA{}
default:
@@ -100,63 +104,18 @@ func (ri RawInstruction) Disassemble() Instruction {
}
case opClsJump:
if ri.Op&opMaskJumpConst != opClsJump {
return ri
}
switch ri.Op & opMaskJumpCond {
switch op := jumpOp(ri.Op & opMaskOperator); op {
case opJumpAlways:
return Jump{Skip: ri.K}
case opJumpEqual:
if ri.Jt == 0 {
return JumpIf{
Cond: JumpNotEqual,
Val: ri.K,
SkipTrue: ri.Jf,
SkipFalse: 0,
}
}
return JumpIf{
Cond: JumpEqual,
Val: ri.K,
SkipTrue: ri.Jt,
SkipFalse: ri.Jf,
}
case opJumpGT:
if ri.Jt == 0 {
return JumpIf{
Cond: JumpLessOrEqual,
Val: ri.K,
SkipTrue: ri.Jf,
SkipFalse: 0,
}
}
return JumpIf{
Cond: JumpGreaterThan,
Val: ri.K,
SkipTrue: ri.Jt,
SkipFalse: ri.Jf,
}
case opJumpGE:
if ri.Jt == 0 {
return JumpIf{
Cond: JumpLessThan,
Val: ri.K,
SkipTrue: ri.Jf,
SkipFalse: 0,
}
}
return JumpIf{
Cond: JumpGreaterOrEqual,
Val: ri.K,
SkipTrue: ri.Jt,
SkipFalse: ri.Jf,
}
case opJumpSet:
return JumpIf{
Cond: JumpBitsSet,
Val: ri.K,
SkipTrue: ri.Jt,
SkipFalse: ri.Jf,
case opJumpEqual, opJumpGT, opJumpGE, opJumpSet:
cond, skipTrue, skipFalse := jumpOpToTest(op, ri.Jt, ri.Jf)
switch operand := opOperand(ri.Op & opMaskOperand); operand {
case opOperandX:
return JumpIfX{Cond: cond, SkipTrue: skipTrue, SkipFalse: skipFalse}
case opOperandConstant:
return JumpIf{Cond: cond, Val: ri.K, SkipTrue: skipTrue, SkipFalse: skipFalse}
default:
return ri
}
default:
return ri
@@ -187,6 +146,41 @@ func (ri RawInstruction) Disassemble() Instruction {
}
}
func jumpOpToTest(op jumpOp, skipTrue uint8, skipFalse uint8) (JumpTest, uint8, uint8) {
var test JumpTest
// Decode "fake" jump conditions that don't appear in machine code
// Ensures the Assemble -> Disassemble stage recreates the same instructions
// See https://github.com/golang/go/issues/18470
if skipTrue == 0 {
switch op {
case opJumpEqual:
test = JumpNotEqual
case opJumpGT:
test = JumpLessOrEqual
case opJumpGE:
test = JumpLessThan
case opJumpSet:
test = JumpBitsNotSet
}
return test, skipFalse, 0
}
switch op {
case opJumpEqual:
test = JumpEqual
case opJumpGT:
test = JumpGreaterThan
case opJumpGE:
test = JumpGreaterOrEqual
case opJumpSet:
test = JumpBitsSet
}
return test, skipTrue, skipFalse
}
// LoadConstant loads Val into register Dst.
type LoadConstant struct {
Dst Register
@@ -413,7 +407,7 @@ type ALUOpConstant struct {
// Assemble implements the Instruction Assemble method.
func (a ALUOpConstant) Assemble() (RawInstruction, error) {
return RawInstruction{
Op: opClsALU | opALUSrcConstant | uint16(a.Op),
Op: opClsALU | uint16(opOperandConstant) | uint16(a.Op),
K: a.Val,
}, nil
}
@@ -454,7 +448,7 @@ type ALUOpX struct {
// Assemble implements the Instruction Assemble method.
func (a ALUOpX) Assemble() (RawInstruction, error) {
return RawInstruction{
Op: opClsALU | opALUSrcX | uint16(a.Op),
Op: opClsALU | uint16(opOperandX) | uint16(a.Op),
}, nil
}
@@ -509,7 +503,7 @@ type Jump struct {
// Assemble implements the Instruction Assemble method.
func (a Jump) Assemble() (RawInstruction, error) {
return RawInstruction{
Op: opClsJump | opJumpAlways,
Op: opClsJump | uint16(opJumpAlways),
K: a.Skip,
}, nil
}
@@ -530,11 +524,39 @@ type JumpIf struct {
// Assemble implements the Instruction Assemble method.
func (a JumpIf) Assemble() (RawInstruction, error) {
return jumpToRaw(a.Cond, opOperandConstant, a.Val, a.SkipTrue, a.SkipFalse)
}
// String returns the instruction in assembler notation.
func (a JumpIf) String() string {
return jumpToString(a.Cond, fmt.Sprintf("#%d", a.Val), a.SkipTrue, a.SkipFalse)
}
// JumpIfX skips the following Skip instructions in the program if A
// <Cond> X is true.
type JumpIfX struct {
Cond JumpTest
SkipTrue uint8
SkipFalse uint8
}
// Assemble implements the Instruction Assemble method.
func (a JumpIfX) Assemble() (RawInstruction, error) {
return jumpToRaw(a.Cond, opOperandX, 0, a.SkipTrue, a.SkipFalse)
}
// String returns the instruction in assembler notation.
func (a JumpIfX) String() string {
return jumpToString(a.Cond, "x", a.SkipTrue, a.SkipFalse)
}
// jumpToRaw assembles a jump instruction into a RawInstruction
func jumpToRaw(test JumpTest, operand opOperand, k uint32, skipTrue, skipFalse uint8) (RawInstruction, error) {
var (
cond uint16
cond jumpOp
flip bool
)
switch a.Cond {
switch test {
case JumpEqual:
cond = opJumpEqual
case JumpNotEqual:
@@ -552,63 +574,63 @@ func (a JumpIf) Assemble() (RawInstruction, error) {
case JumpBitsNotSet:
cond, flip = opJumpSet, true
default:
return RawInstruction{}, fmt.Errorf("unknown JumpTest %v", a.Cond)
return RawInstruction{}, fmt.Errorf("unknown JumpTest %v", test)
}
jt, jf := a.SkipTrue, a.SkipFalse
jt, jf := skipTrue, skipFalse
if flip {
jt, jf = jf, jt
}
return RawInstruction{
Op: opClsJump | cond,
Op: opClsJump | uint16(cond) | uint16(operand),
Jt: jt,
Jf: jf,
K: a.Val,
K: k,
}, nil
}
// String returns the instruction in assembler notation.
func (a JumpIf) String() string {
switch a.Cond {
// jumpToString converts a jump instruction to assembler notation
func jumpToString(cond JumpTest, operand string, skipTrue, skipFalse uint8) string {
switch cond {
// K == A
case JumpEqual:
return conditionalJump(a, "jeq", "jneq")
return conditionalJump(operand, skipTrue, skipFalse, "jeq", "jneq")
// K != A
case JumpNotEqual:
return fmt.Sprintf("jneq #%d,%d", a.Val, a.SkipTrue)
return fmt.Sprintf("jneq %s,%d", operand, skipTrue)
// K > A
case JumpGreaterThan:
return conditionalJump(a, "jgt", "jle")
return conditionalJump(operand, skipTrue, skipFalse, "jgt", "jle")
// K < A
case JumpLessThan:
return fmt.Sprintf("jlt #%d,%d", a.Val, a.SkipTrue)
return fmt.Sprintf("jlt %s,%d", operand, skipTrue)
// K >= A
case JumpGreaterOrEqual:
return conditionalJump(a, "jge", "jlt")
return conditionalJump(operand, skipTrue, skipFalse, "jge", "jlt")
// K <= A
case JumpLessOrEqual:
return fmt.Sprintf("jle #%d,%d", a.Val, a.SkipTrue)
return fmt.Sprintf("jle %s,%d", operand, skipTrue)
// K & A != 0
case JumpBitsSet:
if a.SkipFalse > 0 {
return fmt.Sprintf("jset #%d,%d,%d", a.Val, a.SkipTrue, a.SkipFalse)
if skipFalse > 0 {
return fmt.Sprintf("jset %s,%d,%d", operand, skipTrue, skipFalse)
}
return fmt.Sprintf("jset #%d,%d", a.Val, a.SkipTrue)
return fmt.Sprintf("jset %s,%d", operand, skipTrue)
// K & A == 0, there is no assembler instruction for JumpBitNotSet, use JumpBitSet and invert skips
case JumpBitsNotSet:
return JumpIf{Cond: JumpBitsSet, SkipTrue: a.SkipFalse, SkipFalse: a.SkipTrue, Val: a.Val}.String()
return jumpToString(JumpBitsSet, operand, skipFalse, skipTrue)
default:
return fmt.Sprintf("unknown instruction: %#v", a)
return fmt.Sprintf("unknown JumpTest %#v", cond)
}
}
func conditionalJump(inst JumpIf, positiveJump, negativeJump string) string {
if inst.SkipTrue > 0 {
if inst.SkipFalse > 0 {
return fmt.Sprintf("%s #%d,%d,%d", positiveJump, inst.Val, inst.SkipTrue, inst.SkipFalse)
func conditionalJump(operand string, skipTrue, skipFalse uint8, positiveJump, negativeJump string) string {
if skipTrue > 0 {
if skipFalse > 0 {
return fmt.Sprintf("%s %s,%d,%d", positiveJump, operand, skipTrue, skipFalse)
}
return fmt.Sprintf("%s #%d,%d", positiveJump, inst.Val, inst.SkipTrue)
return fmt.Sprintf("%s %s,%d", positiveJump, operand, skipTrue)
}
return fmt.Sprintf("%s #%d,%d", negativeJump, inst.Val, inst.SkipFalse)
return fmt.Sprintf("%s %s,%d", negativeJump, operand, skipFalse)
}
// RetA exits the BPF program, returning the value of register A.

View File

@@ -64,14 +64,22 @@ var allInstructions = []Instruction{
NegateA{},
Jump{Skip: 10},
JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 8, SkipFalse: 9},
JumpIf{Cond: JumpNotEqual, Val: 42, SkipTrue: 8},
JumpIf{Cond: JumpLessThan, Val: 42, SkipTrue: 7},
JumpIf{Cond: JumpLessOrEqual, Val: 42, SkipTrue: 6},
JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 4, SkipFalse: 5},
JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 3, SkipFalse: 4},
JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 2, SkipFalse: 3},
Jump{Skip: 17},
JumpIf{Cond: JumpEqual, Val: 42, SkipTrue: 15, SkipFalse: 16},
JumpIf{Cond: JumpNotEqual, Val: 42, SkipTrue: 15},
JumpIf{Cond: JumpLessThan, Val: 42, SkipTrue: 14},
JumpIf{Cond: JumpLessOrEqual, Val: 42, SkipTrue: 13},
JumpIf{Cond: JumpGreaterThan, Val: 42, SkipTrue: 11, SkipFalse: 12},
JumpIf{Cond: JumpGreaterOrEqual, Val: 42, SkipTrue: 10, SkipFalse: 11},
JumpIf{Cond: JumpBitsSet, Val: 42, SkipTrue: 9, SkipFalse: 10},
JumpIfX{Cond: JumpEqual, SkipTrue: 8, SkipFalse: 9},
JumpIfX{Cond: JumpNotEqual, SkipTrue: 8},
JumpIfX{Cond: JumpLessThan, SkipTrue: 7},
JumpIfX{Cond: JumpLessOrEqual, SkipTrue: 6},
JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4, SkipFalse: 5},
JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3, SkipFalse: 4},
JumpIfX{Cond: JumpBitsSet, SkipTrue: 2, SkipFalse: 3},
TAX{},
TXA{},
@@ -487,7 +495,67 @@ func TestString(t *testing.T) {
},
{
instruction: JumpIf{Cond: 0xffff, Val: 42, SkipTrue: 1, SkipFalse: 2},
assembler: "unknown instruction: bpf.JumpIf{Cond:0xffff, Val:0x2a, SkipTrue:0x1, SkipFalse:0x2}",
assembler: "unknown JumpTest 0xffff",
},
{
instruction: JumpIfX{Cond: JumpEqual, SkipTrue: 8, SkipFalse: 9},
assembler: "jeq x,8,9",
},
{
instruction: JumpIfX{Cond: JumpEqual, SkipTrue: 8},
assembler: "jeq x,8",
},
{
instruction: JumpIfX{Cond: JumpEqual, SkipFalse: 8},
assembler: "jneq x,8",
},
{
instruction: JumpIfX{Cond: JumpNotEqual, SkipTrue: 8},
assembler: "jneq x,8",
},
{
instruction: JumpIfX{Cond: JumpLessThan, SkipTrue: 7},
assembler: "jlt x,7",
},
{
instruction: JumpIfX{Cond: JumpLessOrEqual, SkipTrue: 6},
assembler: "jle x,6",
},
{
instruction: JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4, SkipFalse: 5},
assembler: "jgt x,4,5",
},
{
instruction: JumpIfX{Cond: JumpGreaterThan, SkipTrue: 4},
assembler: "jgt x,4",
},
{
instruction: JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3, SkipFalse: 4},
assembler: "jge x,3,4",
},
{
instruction: JumpIfX{Cond: JumpGreaterOrEqual, SkipTrue: 3},
assembler: "jge x,3",
},
{
instruction: JumpIfX{Cond: JumpBitsSet, SkipTrue: 2, SkipFalse: 3},
assembler: "jset x,2,3",
},
{
instruction: JumpIfX{Cond: JumpBitsSet, SkipTrue: 2},
assembler: "jset x,2",
},
{
instruction: JumpIfX{Cond: JumpBitsNotSet, SkipTrue: 2, SkipFalse: 3},
assembler: "jset x,3,2",
},
{
instruction: JumpIfX{Cond: JumpBitsNotSet, SkipTrue: 2},
assembler: "jset x,0,2",
},
{
instruction: JumpIfX{Cond: 0xffff, SkipTrue: 1, SkipFalse: 2},
assembler: "unknown JumpTest 0xffff",
},
{
instruction: TAX{},

View File

@@ -1 +1 @@
50,0 0 0 42,1 0 0 42,96 0 0 3,97 0 0 3,48 0 0 42,40 0 0 42,32 0 0 42,80 0 0 42,72 0 0 42,64 0 0 42,177 0 0 42,128 0 0 0,32 0 0 4294963200,32 0 0 4294963204,32 0 0 4294963256,2 0 0 3,3 0 0 3,4 0 0 42,20 0 0 42,36 0 0 42,52 0 0 42,68 0 0 42,84 0 0 42,100 0 0 42,116 0 0 42,148 0 0 42,164 0 0 42,12 0 0 0,28 0 0 0,44 0 0 0,60 0 0 0,76 0 0 0,92 0 0 0,108 0 0 0,124 0 0 0,156 0 0 0,172 0 0 0,132 0 0 0,5 0 0 10,21 8 9 42,21 0 8 42,53 0 7 42,37 0 6 42,37 4 5 42,53 3 4 42,69 2 3 42,7 0 0 0,135 0 0 0,22 0 0 0,6 0 0 0,
57,0 0 0 42,1 0 0 42,96 0 0 3,97 0 0 3,48 0 0 42,40 0 0 42,32 0 0 42,80 0 0 42,72 0 0 42,64 0 0 42,177 0 0 42,128 0 0 0,32 0 0 4294963200,32 0 0 4294963204,32 0 0 4294963256,2 0 0 3,3 0 0 3,4 0 0 42,20 0 0 42,36 0 0 42,52 0 0 42,68 0 0 42,84 0 0 42,100 0 0 42,116 0 0 42,148 0 0 42,164 0 0 42,12 0 0 0,28 0 0 0,44 0 0 0,60 0 0 0,76 0 0 0,92 0 0 0,108 0 0 0,124 0 0 0,156 0 0 0,172 0 0 0,132 0 0 0,5 0 0 17,21 15 16 42,21 0 15 42,53 0 14 42,37 0 13 42,37 11 12 42,53 10 11 42,69 9 10 42,29 8 9 0,29 0 8 0,61 0 7 0,45 0 6 0,45 4 5 0,61 3 4 0,77 2 3 0,7 0 0 0,135 0 0 0,22 0 0 0,6 0 0 42,

View File

@@ -1,6 +1,6 @@
# This filter is compiled to all_instructions.bpf by the `bpf_asm`
# tool, which can be found in the linux kernel source tree under
# tools/net.
# tools/bpf.
# Load immediate
ld #42
@@ -60,7 +60,7 @@ xor x
# !A
neg
# Jumps
# Jump A <op> constant
ja end
jeq #42,prev,end
jne #42,end
@@ -70,6 +70,15 @@ jgt #42,prev,end
jge #42,prev,end
jset #42,prev,end
# Jump A <op> X
jeq x,prev,end
jne x,end
jlt x,end
jle x,end
jgt x,prev,end
jge x,prev,end
jset x,prev,end
# Register transfers
tax
txa

10
vendor/golang.org/x/net/bpf/vm.go generated vendored
View File

@@ -35,6 +35,13 @@ func NewVM(filter []Instruction) (*VM, error) {
if check <= int(ins.SkipFalse) {
return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
}
case JumpIfX:
if check <= int(ins.SkipTrue) {
return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
}
if check <= int(ins.SkipFalse) {
return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
}
// Check for division or modulus by zero
case ALUOpConstant:
if ins.Val != 0 {
@@ -109,6 +116,9 @@ func (v *VM) Run(in []byte) (int, error) {
case JumpIf:
jump := jumpIf(ins, regA)
i += jump
case JumpIfX:
jump := jumpIfX(ins, regA, regX)
i += jump
case LoadAbsolute:
regA, ok = loadAbsolute(ins, in)
case LoadConstant:

View File

@@ -55,34 +55,41 @@ func aluOpCommon(op ALUOp, regA uint32, value uint32) uint32 {
}
}
func jumpIf(ins JumpIf, value uint32) int {
var ok bool
inV := uint32(ins.Val)
func jumpIf(ins JumpIf, regA uint32) int {
return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, ins.Val)
}
switch ins.Cond {
func jumpIfX(ins JumpIfX, regA uint32, regX uint32) int {
return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, regX)
}
func jumpIfCommon(cond JumpTest, skipTrue, skipFalse uint8, regA uint32, value uint32) int {
var ok bool
switch cond {
case JumpEqual:
ok = value == inV
ok = regA == value
case JumpNotEqual:
ok = value != inV
ok = regA != value
case JumpGreaterThan:
ok = value > inV
ok = regA > value
case JumpLessThan:
ok = value < inV
ok = regA < value
case JumpGreaterOrEqual:
ok = value >= inV
ok = regA >= value
case JumpLessOrEqual:
ok = value <= inV
ok = regA <= value
case JumpBitsSet:
ok = (value & inV) != 0
ok = (regA & value) != 0
case JumpBitsNotSet:
ok = (value & inV) == 0
ok = (regA & value) == 0
}
if ok {
return int(ins.SkipTrue)
return int(skipTrue)
}
return int(ins.SkipFalse)
return int(skipFalse)
}
func loadAbsolute(ins LoadAbsolute, in []byte) (uint32, bool) {

View File

@@ -83,6 +83,32 @@ func TestVMJumpIfFalseOutOfProgram(t *testing.T) {
}
}
func TestVMJumpIfXTrueOutOfProgram(t *testing.T) {
_, _, err := testVM(t, []bpf.Instruction{
bpf.JumpIfX{
Cond: bpf.JumpEqual,
SkipTrue: 2,
},
bpf.RetA{},
})
if errStr(err) != "cannot jump 2 instructions in true case; jumping past program bounds" {
t.Fatalf("unexpected error: %v", err)
}
}
func TestVMJumpIfXFalseOutOfProgram(t *testing.T) {
_, _, err := testVM(t, []bpf.Instruction{
bpf.JumpIfX{
Cond: bpf.JumpEqual,
SkipFalse: 3,
},
bpf.RetA{},
})
if errStr(err) != "cannot jump 3 instructions in false case; jumping past program bounds" {
t.Fatalf("unexpected error: %v", err)
}
}
func TestVMJumpIfEqual(t *testing.T) {
vm, done, err := testVM(t, []bpf.Instruction{
bpf.LoadAbsolute{
@@ -378,3 +404,323 @@ func TestVMJumpIfBitsNotSet(t *testing.T) {
want, got)
}
}
func TestVMJumpIfXEqual(t *testing.T) {
vm, done, err := testVM(t, []bpf.Instruction{
bpf.LoadAbsolute{
Off: 8,
Size: 1,
},
bpf.LoadConstant{
Dst: bpf.RegX,
Val: 1,
},
bpf.JumpIfX{
Cond: bpf.JumpEqual,
SkipTrue: 1,
},
bpf.RetConstant{
Val: 0,
},
bpf.RetConstant{
Val: 9,
},
})
if err != nil {
t.Fatalf("failed to load BPF program: %v", err)
}
defer done()
out, err := vm.Run([]byte{
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
1,
})
if err != nil {
t.Fatalf("unexpected error while running program: %v", err)
}
if want, got := 1, out; want != got {
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
want, got)
}
}
func TestVMJumpIfXNotEqual(t *testing.T) {
vm, done, err := testVM(t, []bpf.Instruction{
bpf.LoadAbsolute{
Off: 8,
Size: 1,
},
bpf.LoadConstant{
Dst: bpf.RegX,
Val: 1,
},
bpf.JumpIfX{
Cond: bpf.JumpNotEqual,
SkipFalse: 1,
},
bpf.RetConstant{
Val: 0,
},
bpf.RetConstant{
Val: 9,
},
})
if err != nil {
t.Fatalf("failed to load BPF program: %v", err)
}
defer done()
out, err := vm.Run([]byte{
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
1,
})
if err != nil {
t.Fatalf("unexpected error while running program: %v", err)
}
if want, got := 1, out; want != got {
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
want, got)
}
}
func TestVMJumpIfXGreaterThan(t *testing.T) {
vm, done, err := testVM(t, []bpf.Instruction{
bpf.LoadAbsolute{
Off: 8,
Size: 4,
},
bpf.LoadConstant{
Dst: bpf.RegX,
Val: 0x00010202,
},
bpf.JumpIfX{
Cond: bpf.JumpGreaterThan,
SkipTrue: 1,
},
bpf.RetConstant{
Val: 0,
},
bpf.RetConstant{
Val: 12,
},
})
if err != nil {
t.Fatalf("failed to load BPF program: %v", err)
}
defer done()
out, err := vm.Run([]byte{
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0, 1, 2, 3,
})
if err != nil {
t.Fatalf("unexpected error while running program: %v", err)
}
if want, got := 4, out; want != got {
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
want, got)
}
}
func TestVMJumpIfXLessThan(t *testing.T) {
vm, done, err := testVM(t, []bpf.Instruction{
bpf.LoadAbsolute{
Off: 8,
Size: 4,
},
bpf.LoadConstant{
Dst: bpf.RegX,
Val: 0xff010203,
},
bpf.JumpIfX{
Cond: bpf.JumpLessThan,
SkipTrue: 1,
},
bpf.RetConstant{
Val: 0,
},
bpf.RetConstant{
Val: 12,
},
})
if err != nil {
t.Fatalf("failed to load BPF program: %v", err)
}
defer done()
out, err := vm.Run([]byte{
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0, 1, 2, 3,
})
if err != nil {
t.Fatalf("unexpected error while running program: %v", err)
}
if want, got := 4, out; want != got {
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
want, got)
}
}
func TestVMJumpIfXGreaterOrEqual(t *testing.T) {
vm, done, err := testVM(t, []bpf.Instruction{
bpf.LoadAbsolute{
Off: 8,
Size: 4,
},
bpf.LoadConstant{
Dst: bpf.RegX,
Val: 0x00010203,
},
bpf.JumpIfX{
Cond: bpf.JumpGreaterOrEqual,
SkipTrue: 1,
},
bpf.RetConstant{
Val: 0,
},
bpf.RetConstant{
Val: 12,
},
})
if err != nil {
t.Fatalf("failed to load BPF program: %v", err)
}
defer done()
out, err := vm.Run([]byte{
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0, 1, 2, 3,
})
if err != nil {
t.Fatalf("unexpected error while running program: %v", err)
}
if want, got := 4, out; want != got {
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
want, got)
}
}
func TestVMJumpIfXLessOrEqual(t *testing.T) {
vm, done, err := testVM(t, []bpf.Instruction{
bpf.LoadAbsolute{
Off: 8,
Size: 4,
},
bpf.LoadConstant{
Dst: bpf.RegX,
Val: 0xff010203,
},
bpf.JumpIfX{
Cond: bpf.JumpLessOrEqual,
SkipTrue: 1,
},
bpf.RetConstant{
Val: 0,
},
bpf.RetConstant{
Val: 12,
},
})
if err != nil {
t.Fatalf("failed to load BPF program: %v", err)
}
defer done()
out, err := vm.Run([]byte{
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0, 1, 2, 3,
})
if err != nil {
t.Fatalf("unexpected error while running program: %v", err)
}
if want, got := 4, out; want != got {
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
want, got)
}
}
func TestVMJumpIfXBitsSet(t *testing.T) {
vm, done, err := testVM(t, []bpf.Instruction{
bpf.LoadAbsolute{
Off: 8,
Size: 2,
},
bpf.LoadConstant{
Dst: bpf.RegX,
Val: 0x1122,
},
bpf.JumpIfX{
Cond: bpf.JumpBitsSet,
SkipTrue: 1,
},
bpf.RetConstant{
Val: 0,
},
bpf.RetConstant{
Val: 10,
},
})
if err != nil {
t.Fatalf("failed to load BPF program: %v", err)
}
defer done()
out, err := vm.Run([]byte{
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0x01, 0x02,
})
if err != nil {
t.Fatalf("unexpected error while running program: %v", err)
}
if want, got := 2, out; want != got {
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
want, got)
}
}
func TestVMJumpIfXBitsNotSet(t *testing.T) {
vm, done, err := testVM(t, []bpf.Instruction{
bpf.LoadAbsolute{
Off: 8,
Size: 2,
},
bpf.LoadConstant{
Dst: bpf.RegX,
Val: 0x1221,
},
bpf.JumpIfX{
Cond: bpf.JumpBitsNotSet,
SkipTrue: 1,
},
bpf.RetConstant{
Val: 0,
},
bpf.RetConstant{
Val: 10,
},
})
if err != nil {
t.Fatalf("failed to load BPF program: %v", err)
}
defer done()
out, err := vm.Run([]byte{
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0x01, 0x02,
})
if err != nil {
t.Fatalf("unexpected error while running program: %v", err)
}
if want, got := 2, out; want != got {
t.Fatalf("unexpected number of output bytes:\n- want: %d\n- got: %d",
want, got)
}
}

View File

@@ -2,18 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.7
// Package ctxhttp provides helper functions for performing context-aware HTTP requests.
package ctxhttp // import "golang.org/x/net/context/ctxhttp"
import (
"context"
"io"
"net/http"
"net/url"
"strings"
"golang.org/x/net/context"
)
// Do sends an HTTP request with the provided http.Client and returns

View File

@@ -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()
}

View File

@@ -1,147 +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 !go1.7
package ctxhttp // import "golang.org/x/net/context/ctxhttp"
import (
"io"
"net/http"
"net/url"
"strings"
"golang.org/x/net/context"
)
func nop() {}
var (
testHookContextDoneBeforeHeaders = nop
testHookDoReturned = nop
testHookDidBodyClose = nop
)
// Do sends an HTTP request with the provided http.Client and returns an HTTP response.
// If the client is nil, http.DefaultClient is used.
// If the context is canceled or times out, ctx.Err() will be returned.
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
if client == nil {
client = http.DefaultClient
}
// TODO(djd): Respect any existing value of req.Cancel.
cancel := make(chan struct{})
req.Cancel = cancel
type responseAndError struct {
resp *http.Response
err error
}
result := make(chan responseAndError, 1)
// Make local copies of test hooks closed over by goroutines below.
// Prevents data races in tests.
testHookDoReturned := testHookDoReturned
testHookDidBodyClose := testHookDidBodyClose
go func() {
resp, err := client.Do(req)
testHookDoReturned()
result <- responseAndError{resp, err}
}()
var resp *http.Response
select {
case <-ctx.Done():
testHookContextDoneBeforeHeaders()
close(cancel)
// Clean up after the goroutine calling client.Do:
go func() {
if r := <-result; r.resp != nil {
testHookDidBodyClose()
r.resp.Body.Close()
}
}()
return nil, ctx.Err()
case r := <-result:
var err error
resp, err = r.resp, r.err
if err != nil {
return resp, err
}
}
c := make(chan struct{})
go func() {
select {
case <-ctx.Done():
close(cancel)
case <-c:
// The response's Body is closed.
}
}()
resp.Body = &notifyingReader{resp.Body, c}
return resp, nil
}
// Get issues a GET request via the Do function.
func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
return Do(ctx, client, req)
}
// Head issues a HEAD request via the Do function.
func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
req, err := http.NewRequest("HEAD", url, nil)
if err != nil {
return nil, err
}
return Do(ctx, client, req)
}
// Post issues a POST request via the Do function.
func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequest("POST", url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", bodyType)
return Do(ctx, client, req)
}
// PostForm issues a POST request via the Do function.
func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}
// notifyingReader is an io.ReadCloser that closes the notify channel after
// Close is called or a Read fails on the underlying ReadCloser.
type notifyingReader struct {
io.ReadCloser
notify chan<- struct{}
}
func (r *notifyingReader) Read(p []byte) (int, error) {
n, err := r.ReadCloser.Read(p)
if err != nil && r.notify != nil {
close(r.notify)
r.notify = nil
}
return n, err
}
func (r *notifyingReader) Close() error {
err := r.ReadCloser.Close()
if r.notify != nil {
close(r.notify)
r.notify = nil
}
return err
}

View File

@@ -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()
}

View File

@@ -7,16 +7,28 @@
package ctxhttp
import (
"context"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
"time"
"golang.org/x/net/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()
}
const (
requestDuration = 100 * time.Millisecond
requestBody = "ok"

View File

@@ -37,20 +37,20 @@ func ExampleParser() {
},
Answers: []dnsmessage.Resource{
{
dnsmessage.ResourceHeader{
Header: dnsmessage.ResourceHeader{
Name: mustNewName("foo.bar.example.com."),
Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET,
},
&dnsmessage.AResource{[4]byte{127, 0, 0, 1}},
Body: &dnsmessage.AResource{A: [4]byte{127, 0, 0, 1}},
},
{
dnsmessage.ResourceHeader{
Header: dnsmessage.ResourceHeader{
Name: mustNewName("bar.example.com."),
Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET,
},
&dnsmessage.AResource{[4]byte{127, 0, 0, 2}},
Body: &dnsmessage.AResource{A: [4]byte{127, 0, 0, 2}},
},
},
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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.
@@ -701,6 +701,8 @@ var extra = []string{
"plaintext",
"prompt",
"public",
"rb",
"rtc",
"spacer",
"strike",
"svg",

File diff suppressed because it is too large Load Diff

View File

@@ -296,6 +296,7 @@ var testAtomList = []string{
"public",
"q",
"radiogroup",
"rb",
"readonly",
"referrerpolicy",
"rel",
@@ -305,6 +306,7 @@ var testAtomList = []string{
"rowspan",
"rp",
"rt",
"rtc",
"ruby",
"s",
"samp",

View File

@@ -97,8 +97,16 @@ func isSpecialElement(element *Node) bool {
switch element.Namespace {
case "", "html":
return isSpecialElementMap[element.Data]
case "math":
switch element.Data {
case "mi", "mo", "mn", "ms", "mtext", "annotation-xml":
return true
}
case "svg":
return element.Data == "foreignObject"
switch element.Data {
case "foreignObject", "desc", "title":
return true
}
}
return false
}

4154
vendor/golang.org/x/net/html/entity.go generated vendored

File diff suppressed because it is too large Load Diff

26
vendor/golang.org/x/net/html/node.go generated vendored
View File

@@ -174,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)
@@ -192,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
}

305
vendor/golang.org/x/net/html/parse.go generated vendored
View File

@@ -32,6 +32,8 @@ type parser struct {
head, form *Node
// 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
@@ -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
@@ -236,7 +238,7 @@ func (p *parser) shouldFosterParent() bool {
// fosterParent adds a child node according to the foster parenting rules.
// 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 +247,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]
@@ -415,14 +430,34 @@ func (p *parser) setOriginalIM() {
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,20 +469,36 @@ func (p *parser) resetInsertionMode() {
p.im = inColumnGroupIM
case a.Table:
p.im = inTableIM
case a.Template:
// TODO: remove this divergence from the HTML5 spec.
if n.Namespace != "" {
continue
}
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"
@@ -590,19 +641,41 @@ 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
}
// TODO: remove this divergence from the HTML5 spec.
//
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
p.generateImpliedEndTags()
for i := len(p.oe) - 1; i >= 0; i-- {
if n := p.oe[i]; n.Namespace == "" && n.DataAtom == a.Template {
p.oe = p.oe[:i]
break
}
}
p.clearActiveFormattingElements()
p.templateStack.pop()
p.resetInsertionMode()
return true
default:
// Ignore the token.
return true
@@ -648,7 +721,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 +733,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
@@ -727,10 +802,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,9 +848,13 @@ 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 {
p.popUntil(buttonScope, a.P)
p.addElement()
if p.form != nil && !p.oe.contains(a.Template) {
// Ignore the token
return true
}
p.popUntil(buttonScope, a.P)
p.addElement()
if !p.oe.contains(a.Template) {
p.form = p.top()
}
case a.Li:
@@ -903,6 +988,14 @@ func inBodyIM(p *parser) bool {
p.acknowledgeSelfClosingTag()
p.popUntil(buttonScope, a.P)
p.parseImpliedToken(StartTagToken, a.Form, a.Form.String())
if p.form == nil {
// NOTE: The 'isindex' element has been removed,
// and the 'template' element has not been designed to be
// collaborative with the index element.
//
// Ignore the token.
return true
}
if action != "" {
p.form.Attr = []Attribute{{Key: "action", Val: action}}
}
@@ -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 {
@@ -993,15 +1091,29 @@ 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) {
i := p.indexOfElementInScope(defaultScope, a.Form)
if i == -1 {
// Ignore the token.
return true
}
p.generateImpliedEndTags()
if p.oe[i].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 +1134,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 +1144,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
@@ -1206,9 +1335,6 @@ func textIM(p *parser) bool {
// Section 12.2.6.4.9.
func inTableIM(p *parser) bool {
switch p.tok.Type {
case ErrorToken:
// Stop parsing.
return true
case TextToken:
p.tok.Data = strings.Replace(p.tok.Data, "\x00", "", -1)
switch p.oe.top().DataAtom {
@@ -1249,7 +1375,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 +1387,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 +1417,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{
@@ -1301,6 +1429,8 @@ func inTableIM(p *parser) bool {
case DoctypeToken:
// Ignore the token.
return true
case ErrorToken:
return inBodyIM(p)
}
p.fosterParenting = true
@@ -1386,11 +1516,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,14 +1530,18 @@ func inColumnGroupIM(p *parser) bool {
case a.Col:
// Ignore the token.
return true
case a.Template:
return inHeadIM(p)
}
case ErrorToken:
return inBodyIM(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.6.4.13.
@@ -1563,9 +1699,6 @@ func inCellIM(p *parser) bool {
// Section 12.2.6.4.16.
func inSelectIM(p *parser) bool {
switch p.tok.Type {
case ErrorToken:
// Stop parsing.
return true
case TextToken:
p.addText(strings.Replace(p.tok.Data, "\x00", "", -1))
case StartTagToken:
@@ -1597,7 +1730,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 +1751,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{
@@ -1627,6 +1762,8 @@ func inSelectIM(p *parser) bool {
case DoctypeToken:
// Ignore the token.
return true
case ErrorToken:
return inBodyIM(p)
}
return true
@@ -1650,6 +1787,72 @@ func inSelectInTableIM(p *parser) bool {
return inSelectIM(p)
}
// 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
}
case ErrorToken:
if !p.oe.contains(a.Template) {
// Ignore the token.
return true
}
// TODO: remove this divergence from the HTML5 spec.
//
// See https://bugs.chromium.org/p/chromium/issues/detail?id=829668
p.generateImpliedEndTags()
for i := len(p.oe) - 1; i >= 0; i-- {
if n := p.oe[i]; n.Namespace == "" && n.DataAtom == a.Template {
p.oe = p.oe[:i]
break
}
}
p.clearActiveFormattingElements()
p.templateStack.pop()
p.resetInsertionMode()
return false
}
return false
}
// Section 12.2.6.4.19.
func afterBodyIM(p *parser) bool {
switch p.tok.Type {
@@ -2012,6 +2215,15 @@ func (p *parser) parse() error {
}
// Parse returns the parse tree for the HTML from the given Reader.
//
// It implements the HTML5 parsing algorithm
// (https://html.spec.whatwg.org/multipage/syntax.html#tree-construction),
// which is very complicated. The resultant tree can contain implicitly created
// nodes that have no explicit <tag> listed in r's data, and nodes' parents can
// differ from the nesting implied by a naive processing of start and end
// <tag>s. Conversely, explicit <tag>s in r's data can be silently dropped,
// with no corresponding node in the resulting tree.
//
// The input is assumed to be UTF-8 encoded.
func Parse(r io.Reader) (*Node, error) {
p := &parser{
@@ -2033,6 +2245,8 @@ func Parse(r io.Reader) (*Node, error) {
// ParseFragment parses a fragment of HTML and returns the nodes that were
// found. If the fragment is the InnerHTML for an existing element, pass that
// element in context.
//
// It has the same intricacies as Parse.
func ParseFragment(r io.Reader, context *Node) ([]*Node, error) {
contextTag := ""
if context != nil {
@@ -2064,6 +2278,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 {

View File

@@ -125,6 +125,7 @@ func (a sortedAttributes) Swap(i, j int) {
func dumpLevel(w io.Writer, n *Node, level int) error {
dumpIndent(w, level)
level++
switch n.Type {
case ErrorNode:
return errors.New("unexpected ErrorNode")
@@ -140,13 +141,19 @@ func dumpLevel(w io.Writer, n *Node, level int) error {
sort.Sort(attr)
for _, a := range attr {
io.WriteString(w, "\n")
dumpIndent(w, level+1)
dumpIndent(w, level)
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)
}
}
if n.Namespace == "" && n.DataAtom == atom.Template {
io.WriteString(w, "\n")
dumpIndent(w, level)
level++
io.WriteString(w, "content")
}
case TextNode:
fmt.Fprintf(w, `"%s"`, n.Data)
case CommentNode:
@@ -176,7 +183,7 @@ func dumpLevel(w io.Writer, n *Node, level int) error {
}
io.WriteString(w, "\n")
for c := n.FirstChild; c != nil; c = c.NextSibling {
if err := dumpLevel(w, c, level+1); err != nil {
if err := dumpLevel(w, c, level); err != nil {
return err
}
}
@@ -196,34 +203,36 @@ func dump(n *Node) (string, error) {
return b.String(), nil
}
const testDataDir = "testdata/webkit/"
var testDataDirs = []string{"testdata/webkit/", "testdata/go/"}
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)
for _, testDataDir := range testDataDirs {
testFiles, err := filepath.Glob(testDataDir + "*.dat")
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
}
for _, tf := range testFiles {
f, err := os.Open(tf)
if err != nil {
t.Fatal(err)
}
defer f.Close()
r := bufio.NewReader(f)
err = testParseCase(text, want, context)
for i := 0; ; i++ {
text, want, context, err := readParseTest(r)
if err == io.EOF {
break
}
if err != nil {
t.Fatal(err)
}
if err != nil {
t.Errorf("%s test #%d %q, %s", tf, i, text, err)
err = testParseCase(text, want, context)
if err != nil {
t.Errorf("%s test #%d %q, %s", tf, i, text, err)
}
}
}
}
@@ -320,6 +329,7 @@ var renderTestBlacklist = map[string]bool{
`<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,
`<template><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.
@@ -373,6 +383,11 @@ func TestNodeConsistency(t *testing.T) {
}
}
func TestParseFragmentWithNilContext(t *testing.T) {
// This shouldn't panic.
ParseFragment(strings.NewReader("<p>hello</p>"), nil)
}
func BenchmarkParser(b *testing.B) {
buf, err := ioutil.ReadFile("testdata/go1.html")
if err != nil {

37
vendor/golang.org/x/net/html/testdata/go/template.dat generated vendored Normal file
View File

@@ -0,0 +1,37 @@
#data
<body><template><yt-icon-button></yt-icon-button><form><paper-input></paper-input></form><style></style></template>
#errors
#document
| <html>
| <head>
| <body>
| <template>
| content
| <yt-icon-button>
| <form>
| <paper-input>
| <style>
#data
<template><tBody><isindex/action=0>
#errors
#document
| <html>
| <head>
| <template>
| content
| <tbody>
| <body>
#data
<math><template><mo><template>
#errors
#document
| <html>
| <head>
| <body>
| <math math>
| <math template>
| <math mo>
| <template>
| content

298
vendor/golang.org/x/net/html/testdata/webkit/ruby.dat generated vendored Normal file
View File

@@ -0,0 +1,298 @@
#data
<html><ruby>a<rb>b<rb></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rb>
| "b"
| <rb>
#data
<html><ruby>a<rb>b<rt></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rb>
| "b"
| <rt>
#data
<html><ruby>a<rb>b<rtc></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rb>
| "b"
| <rtc>
#data
<html><ruby>a<rb>b<rp></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rb>
| "b"
| <rp>
#data
<html><ruby>a<rb>b<span></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rb>
| "b"
| <span>
#data
<html><ruby>a<rt>b<rb></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rt>
| "b"
| <rb>
#data
<html><ruby>a<rt>b<rt></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rt>
| "b"
| <rt>
#data
<html><ruby>a<rt>b<rtc></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rt>
| "b"
| <rtc>
#data
<html><ruby>a<rt>b<rp></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rt>
| "b"
| <rp>
#data
<html><ruby>a<rt>b<span></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rt>
| "b"
| <span>
#data
<html><ruby>a<rtc>b<rb></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rtc>
| "b"
| <rb>
#data
<html><ruby>a<rtc>b<rt>c<rt>d</ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rtc>
| "b"
| <rt>
| "c"
| <rt>
| "d"
#data
<html><ruby>a<rtc>b<rtc></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rtc>
| "b"
| <rtc>
#data
<html><ruby>a<rtc>b<rp></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rtc>
| "b"
| <rp>
#data
<html><ruby>a<rtc>b<span></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rtc>
| "b"
| <span>
#data
<html><ruby>a<rp>b<rb></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rp>
| "b"
| <rb>
#data
<html><ruby>a<rp>b<rt></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rp>
| "b"
| <rt>
#data
<html><ruby>a<rp>b<rtc></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rp>
| "b"
| <rtc>
#data
<html><ruby>a<rp>b<rp></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rp>
| "b"
| <rp>
#data
<html><ruby>a<rp>b<span></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| "a"
| <rp>
| "b"
| <span>
#data
<html><ruby><rtc><ruby>a<rb>b<rt></ruby></ruby></html>
#errors
(1,6): expected-doctype-but-got-start-tag
#document
| <html>
| <head>
| <body>
| <ruby>
| <rtc>
| <ruby>
| "a"
| <rb>
| "b"
| <rt>

1326
vendor/golang.org/x/net/html/testdata/webkit/template.dat generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -589,20 +589,20 @@ func TestConvertNewlines(t *testing.T) {
"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",
"": "",
"\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 {

50
vendor/golang.org/x/net/http/httpguts/guts.go generated vendored Normal file
View File

@@ -0,0 +1,50 @@
// 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"
)
// 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,
}

View File

@@ -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"

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package httplex
package httpguts
import (
"testing"

View File

@@ -4,4 +4,10 @@
package httpproxy
var ExportUseProxy = (*Config).useProxy
func ExportUseProxy(cfg *Config, host string) bool {
cfg1 := &config{
Config: *cfg,
}
cfg1.init()
return cfg1.useProxy(host)
}

View File

@@ -37,13 +37,18 @@ type Config struct {
HTTPSProxy string
// NoProxy represents the NO_PROXY or no_proxy environment
// variable. It specifies URLs that should be excluded from
// proxying as a comma-separated list of domain names or a
// single asterisk (*) to indicate that no proxying should be
// done. A domain name matches that name and all subdomains. A
// domain name with a leading "." matches subdomains only. For
// example "foo.com" matches "foo.com" and "bar.foo.com";
// ".y.com" matches "x.y.com" but not "y.com".
// variable. It specifies a string that contains comma-separated values
// specifying hosts that should be excluded from proxying. Each value is
// represented by an IP address prefix (1.2.3.4), an IP address prefix in
// CIDR notation (1.2.3.4/8), a domain name, or a special DNS label (*).
// An IP address prefix and domain name can also include a literal port
// number (1.2.3.4:80).
// A domain name matches that name and all subdomains. A domain name with
// a leading "." matches subdomains only. For example "foo.com" matches
// "foo.com" and "bar.foo.com"; ".y.com" matches "x.y.com" but not "y.com".
// A single asterisk (*) indicates that no proxying should be done.
// A best effort is made to parse the string and errors are
// ignored.
NoProxy string
// CGI holds whether the current process is running
@@ -55,6 +60,26 @@ type Config struct {
CGI bool
}
// config holds the parsed configuration for HTTP proxy settings.
type config struct {
// Config represents the original configuration as defined above.
Config
// httpsProxy is the parsed URL of the HTTPSProxy if defined.
httpsProxy *url.URL
// httpProxy is the parsed URL of the HTTPProxy if defined.
httpProxy *url.URL
// ipMatchers represent all values in the NoProxy that are IP address
// prefixes or an IP address in CIDR notation.
ipMatchers []matcher
// domainMatchers represent all values in the NoProxy that are a domain
// name or hostname & domain name
domainMatchers []matcher
}
// FromEnvironment returns a Config instance populated from the
// environment variables HTTP_PROXY, HTTPS_PROXY and NO_PROXY (or the
// lowercase versions thereof). HTTPS_PROXY takes precedence over
@@ -92,29 +117,40 @@ func getEnvAny(names ...string) string {
// As a special case, if req.URL.Host is "localhost" (with or without a
// port number), then a nil URL and nil error will be returned.
func (cfg *Config) ProxyFunc() func(reqURL *url.URL) (*url.URL, error) {
// Prevent Config changes from affecting the function calculation.
// TODO Preprocess proxy settings for more efficient evaluation.
cfg1 := *cfg
// Preprocess the Config settings for more efficient evaluation.
cfg1 := &config{
Config: *cfg,
}
cfg1.init()
return cfg1.proxyForURL
}
func (cfg *Config) proxyForURL(reqURL *url.URL) (*url.URL, error) {
var proxy string
func (cfg *config) proxyForURL(reqURL *url.URL) (*url.URL, error) {
var proxy *url.URL
if reqURL.Scheme == "https" {
proxy = cfg.HTTPSProxy
proxy = cfg.httpsProxy
}
if proxy == "" {
proxy = cfg.HTTPProxy
if proxy != "" && cfg.CGI {
if proxy == nil {
proxy = cfg.httpProxy
if proxy != nil && cfg.CGI {
return nil, errors.New("refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy")
}
}
if proxy == "" {
if proxy == nil {
return nil, nil
}
if !cfg.useProxy(canonicalAddr(reqURL)) {
return nil, nil
}
return proxy, nil
}
func parseProxy(proxy string) (*url.URL, error) {
if proxy == "" {
return nil, nil
}
proxyURL, err := url.Parse(proxy)
if err != nil ||
(proxyURL.Scheme != "http" &&
@@ -136,60 +172,107 @@ func (cfg *Config) proxyForURL(reqURL *url.URL) (*url.URL, error) {
// useProxy reports whether requests to addr should use a proxy,
// according to the NO_PROXY or no_proxy environment variable.
// addr is always a canonicalAddr with a host and port.
func (cfg *Config) useProxy(addr string) bool {
func (cfg *config) useProxy(addr string) bool {
if len(addr) == 0 {
return true
}
host, _, err := net.SplitHostPort(addr)
host, port, err := net.SplitHostPort(addr)
if err != nil {
return false
}
if host == "localhost" {
return false
}
if ip := net.ParseIP(host); ip != nil {
ip := net.ParseIP(host)
if ip != nil {
if ip.IsLoopback() {
return false
}
}
noProxy := cfg.NoProxy
if noProxy == "*" {
return false
}
addr = strings.ToLower(strings.TrimSpace(host))
addr = strings.ToLower(strings.TrimSpace(addr))
if hasPort(addr) {
addr = addr[:strings.LastIndex(addr, ":")]
if ip != nil {
for _, m := range cfg.ipMatchers {
if m.match(addr, port, ip) {
return false
}
}
}
for _, p := range strings.Split(noProxy, ",") {
p = strings.ToLower(strings.TrimSpace(p))
if len(p) == 0 {
continue
}
if hasPort(p) {
p = p[:strings.LastIndex(p, ":")]
}
if addr == p {
return false
}
if len(p) == 0 {
// There is no host part, likely the entry is malformed; ignore.
continue
}
if p[0] == '.' && (strings.HasSuffix(addr, p) || addr == p[1:]) {
// no_proxy ".foo.com" matches "bar.foo.com" or "foo.com"
return false
}
if p[0] != '.' && strings.HasSuffix(addr, p) && addr[len(addr)-len(p)-1] == '.' {
// no_proxy "foo.com" matches "bar.foo.com"
for _, m := range cfg.domainMatchers {
if m.match(addr, port, ip) {
return false
}
}
return true
}
func (c *config) init() {
if parsed, err := parseProxy(c.HTTPProxy); err == nil {
c.httpProxy = parsed
}
if parsed, err := parseProxy(c.HTTPSProxy); err == nil {
c.httpsProxy = parsed
}
for _, p := range strings.Split(c.NoProxy, ",") {
p = strings.ToLower(strings.TrimSpace(p))
if len(p) == 0 {
continue
}
if p == "*" {
c.ipMatchers = []matcher{allMatch{}}
c.domainMatchers = []matcher{allMatch{}}
return
}
// IPv4/CIDR, IPv6/CIDR
if _, pnet, err := net.ParseCIDR(p); err == nil {
c.ipMatchers = append(c.ipMatchers, cidrMatch{cidr: pnet})
continue
}
// IPv4:port, [IPv6]:port
phost, pport, err := net.SplitHostPort(p)
if err == nil {
if len(phost) == 0 {
// There is no host part, likely the entry is malformed; ignore.
continue
}
if phost[0] == '[' && phost[len(phost)-1] == ']' {
phost = phost[1 : len(phost)-1]
}
} else {
phost = p
}
// IPv4, IPv6
if pip := net.ParseIP(phost); pip != nil {
c.ipMatchers = append(c.ipMatchers, ipMatch{ip: pip, port: pport})
continue
}
if len(phost) == 0 {
// There is no host part, likely the entry is malformed; ignore.
continue
}
// domain.com or domain.com:80
// foo.com matches bar.foo.com
// .domain.com or .domain.com:port
// *.domain.com or *.domain.com:port
if strings.HasPrefix(phost, "*.") {
phost = phost[1:]
}
matchHost := false
if phost[0] != '.' {
matchHost = true
phost = "." + phost
}
c.domainMatchers = append(c.domainMatchers, domainMatch{host: phost, port: pport, matchHost: matchHost})
}
}
var portMap = map[string]string{
"http": "80",
"https": "443",
@@ -237,3 +320,51 @@ func isASCII(s string) bool {
}
return true
}
// matcher represents the matching rule for a given value in the NO_PROXY list
type matcher interface {
// match returns true if the host and optional port or ip and optional port
// are allowed
match(host, port string, ip net.IP) bool
}
// allMatch matches on all possible inputs
type allMatch struct{}
func (a allMatch) match(host, port string, ip net.IP) bool {
return true
}
type cidrMatch struct {
cidr *net.IPNet
}
func (m cidrMatch) match(host, port string, ip net.IP) bool {
return m.cidr.Contains(ip)
}
type ipMatch struct {
ip net.IP
port string
}
func (m ipMatch) match(host, port string, ip net.IP) bool {
if m.ip.Equal(ip) {
return m.port == "" || m.port == port
}
return false
}
type domainMatch struct {
host string
port string
matchHost bool
}
func (m domainMatch) match(host, port string, ip net.IP) bool {
if strings.HasSuffix(host, m.host) || (m.matchHost && host == m.host[1:]) {
return m.port == "" || m.port == port
}
return false
}

View File

@@ -144,7 +144,7 @@ var proxyForURLTests = []proxyForURLTest{{
HTTPProxy: "proxy",
},
req: "http://example.com/",
want: "<nil>",
want: "http://proxy",
}, {
cfg: httpproxy.Config{
NoProxy: "ample.com",
@@ -269,19 +269,36 @@ var UseProxyTests = []struct {
{"[::1]", false},
{"[::2]", true}, // not a loopback address
{"barbaz.net", false}, // match as .barbaz.net
{"foobar.com", false}, // have a port but match
{"foofoobar.com", true}, // not match as a part of foobar.com
{"baz.com", true}, // not match as a part of barbaz.com
{"localhost.net", true}, // not match as suffix of address
{"local.localhost", true}, // not match as prefix as address
{"barbarbaz.net", true}, // not match because NO_PROXY have a '.'
{"www.foobar.com", false}, // match because NO_PROXY includes "foobar.com"
{"192.168.1.1", false}, // matches exact IPv4
{"192.168.1.2", true}, // ports do not match
{"192.168.1.3", false}, // matches exact IPv4:port
{"192.168.1.4", true}, // no match
{"10.0.0.2", false}, // matches IPv4/CIDR
{"[2001:db8::52:0:1]", false}, // matches exact IPv6
{"[2001:db8::52:0:2]", true}, // no match
{"[2001:db8::52:0:3]", false}, // matches exact [IPv6]:port
{"[2002:db8:a::123]", false}, // matches IPv6/CIDR
{"[fe80::424b:c8be:1643:a1b6]", true}, // no match
{"barbaz.net", true}, // does not match as .barbaz.net
{"www.barbaz.net", false}, // does match as .barbaz.net
{"foobar.com", false}, // does match as foobar.com
{"www.foobar.com", false}, // match because NO_PROXY includes "foobar.com"
{"foofoobar.com", true}, // not match as a part of foobar.com
{"baz.com", true}, // not match as a part of barbaz.com
{"localhost.net", true}, // not match as suffix of address
{"local.localhost", true}, // not match as prefix as address
{"barbarbaz.net", true}, // not match, wrong domain
{"wildcard.io", true}, // does not match as *.wildcard.io
{"nested.wildcard.io", false}, // match as *.wildcard.io
{"awildcard.io", true}, // not a match because of '*'
}
var noProxy = "foobar.com, .barbaz.net, *.wildcard.io, 192.168.1.1, 192.168.1.2:81, 192.168.1.3:80, 10.0.0.0/30, 2001:db8::52:0:1, [2001:db8::52:0:2]:443, [2001:db8::52:0:3]:80, 2002:db8:a::45/64"
func TestUseProxy(t *testing.T) {
cfg := &httpproxy.Config{
NoProxy: "foobar.com, .barbaz.net",
NoProxy: noProxy,
}
for _, test := range UseProxyTests {
if httpproxy.ExportUseProxy(cfg, test.host+":80") != test.match {
@@ -299,3 +316,36 @@ func TestInvalidNoProxy(t *testing.T) {
t.Errorf("useProxy unexpected return; got false; want true")
}
}
func TestAllNoProxy(t *testing.T) {
cfg := &httpproxy.Config{
NoProxy: "*",
}
for _, test := range UseProxyTests {
if httpproxy.ExportUseProxy(cfg, test.host+":80") != false {
t.Errorf("useProxy(%v) = true, want false", test.host)
}
}
}
func BenchmarkProxyForURL(b *testing.B) {
cfg := &httpproxy.Config{
HTTPProxy: "http://proxy.example.org",
HTTPSProxy: "https://proxy.example.org",
NoProxy: noProxy,
}
for _, test := range UseProxyTests {
u, err := url.Parse("https://" + test.host + ":80")
if err != nil {
b.Fatalf("parsed failed: %s", test.host)
}
proxyFunc := cfg.ProxyFunc()
b.Run(test.host, func(b *testing.B) {
for n := 0; n < b.N; n++ {
if au, e := proxyFunc(u); e != nil && test.match == (au != nil) {
b.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match)
}
}
})
}
}

View File

@@ -52,9 +52,31 @@ const (
noDialOnMiss = false
)
// shouldTraceGetConn reports whether getClientConn should call any
// ClientTrace.GetConn hook associated with the http.Request.
//
// This complexity is needed to avoid double calls of the GetConn hook
// during the back-and-forth between net/http and x/net/http2 (when the
// net/http.Transport is upgraded to also speak http2), as well as support
// the case where x/net/http2 is being used directly.
func (p *clientConnPool) shouldTraceGetConn(st clientConnIdleState) bool {
// If our Transport wasn't made via ConfigureTransport, always
// trace the GetConn hook if provided, because that means the
// http2 package is being used directly and it's the one
// dialing, as opposed to net/http.
if _, ok := p.t.ConnPool.(noDialClientConnPool); !ok {
return true
}
// Otherwise, only use the GetConn hook if this connection has
// been used previously for other requests. For fresh
// connections, the net/http package does the dialing.
return !st.freshConn
}
func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
if isConnectionCloseRequest(req) && dialOnMiss {
// It gets its own connection.
traceGetConn(req, addr)
const singleUse = true
cc, err := p.t.dialClientConn(addr, singleUse)
if err != nil {
@@ -64,7 +86,10 @@ func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMis
}
p.mu.Lock()
for _, cc := range p.conns[addr] {
if cc.CanTakeNewRequest() {
if st := cc.idleState(); st.canTakeNewRequest {
if p.shouldTraceGetConn(st) {
traceGetConn(req, addr)
}
p.mu.Unlock()
return cc, nil
}
@@ -73,6 +98,7 @@ func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMis
p.mu.Unlock()
return nil, ErrNoCachedConn
}
traceGetConn(req, addr)
call := p.getStartDialLocked(addr)
p.mu.Unlock()
<-call.done

View File

@@ -1,80 +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 go1.6
package http2
import (
"crypto/tls"
"fmt"
"net/http"
)
func configureTransport(t1 *http.Transport) (*Transport, error) {
connPool := new(clientConnPool)
t2 := &Transport{
ConnPool: noDialClientConnPool{connPool},
t1: t1,
}
connPool.t = t2
if err := registerHTTPSProtocol(t1, noDialH2RoundTripper{t2}); err != nil {
return nil, err
}
if t1.TLSClientConfig == nil {
t1.TLSClientConfig = new(tls.Config)
}
if !strSliceContains(t1.TLSClientConfig.NextProtos, "h2") {
t1.TLSClientConfig.NextProtos = append([]string{"h2"}, t1.TLSClientConfig.NextProtos...)
}
if !strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") {
t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1")
}
upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper {
addr := authorityAddr("https", authority)
if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
go c.Close()
return erringRoundTripper{err}
} else if !used {
// Turns out we don't need this c.
// For example, two goroutines made requests to the same host
// at the same time, both kicking off TCP dials. (since protocol
// was unknown)
go c.Close()
}
return t2
}
if m := t1.TLSNextProto; len(m) == 0 {
t1.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{
"h2": upgradeFn,
}
} else {
m["h2"] = upgradeFn
}
return t2, nil
}
// registerHTTPSProtocol calls Transport.RegisterProtocol but
// converting panics into errors.
func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("%v", e)
}
}()
t.RegisterProtocol("https", rt)
return nil
}
// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
// if there's already has a cached connection to the host.
type noDialH2RoundTripper struct{ t *Transport }
func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
res, err := rt.t.RoundTrip(req)
if isNoCachedConnError(err) {
return nil, http.ErrSkipAltProtocol
}
return res, err
}

View File

@@ -2,8 +2,6 @@
// 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 (

View File

@@ -41,10 +41,10 @@ func (f *flow) take(n int32) {
// add adds n bytes (positive or negative) to the flow control window.
// It returns false if the sum would exceed 2^31-1.
func (f *flow) add(n int32) bool {
remain := (1<<31 - 1) - f.n
if n > remain {
return false
sum := f.n + n
if (sum > n) == (f.n > 0) {
f.n = sum
return true
}
f.n += n
return true
return false
}

View File

@@ -49,5 +49,39 @@ func TestFlowAdd(t *testing.T) {
if f.add(1) {
t.Fatal("adding 1 to max shouldn't be allowed")
}
}
func TestFlowAddOverflow(t *testing.T) {
var f flow
if !f.add(0) {
t.Fatal("failed to add 0")
}
if !f.add(-1) {
t.Fatal("failed to add -1")
}
if !f.add(0) {
t.Fatal("failed to add 0")
}
if !f.add(1) {
t.Fatal("failed to add 1")
}
if !f.add(1) {
t.Fatal("failed to add 1")
}
if !f.add(0) {
t.Fatal("failed to add 0")
}
if !f.add(-3) {
t.Fatal("failed to add -3")
}
if got, want := f.available(), int32(-2); 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+-3+(1<<31-1)); got != want {
t.Fatalf("size = %d; want %d", got, want)
}
}

View File

@@ -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
@@ -733,32 +733,67 @@ func (f *SettingsFrame) IsAck() bool {
return f.FrameHeader.Flags.Has(FlagSettingsAck)
}
func (f *SettingsFrame) Value(s SettingID) (v uint32, ok bool) {
func (f *SettingsFrame) Value(id SettingID) (v uint32, ok bool) {
f.checkValid()
buf := f.p
for len(buf) > 0 {
settingID := SettingID(binary.BigEndian.Uint16(buf[:2]))
if settingID == s {
return binary.BigEndian.Uint32(buf[2:6]), true
for i := 0; i < f.NumSettings(); i++ {
if s := f.Setting(i); s.ID == id {
return s.Val, true
}
buf = buf[6:]
}
return 0, false
}
// Setting returns the setting from the frame at the given 0-based index.
// The index must be >= 0 and less than f.NumSettings().
func (f *SettingsFrame) Setting(i int) Setting {
buf := f.p
return Setting{
ID: SettingID(binary.BigEndian.Uint16(buf[i*6 : i*6+2])),
Val: binary.BigEndian.Uint32(buf[i*6+2 : i*6+6]),
}
}
func (f *SettingsFrame) NumSettings() int { return len(f.p) / 6 }
// HasDuplicates reports whether f contains any duplicate setting IDs.
func (f *SettingsFrame) HasDuplicates() bool {
num := f.NumSettings()
if num == 0 {
return false
}
// If it's small enough (the common case), just do the n^2
// thing and avoid a map allocation.
if num < 10 {
for i := 0; i < num; i++ {
idi := f.Setting(i).ID
for j := i + 1; j < num; j++ {
idj := f.Setting(j).ID
if idi == idj {
return true
}
}
}
return false
}
seen := map[SettingID]bool{}
for i := 0; i < num; i++ {
id := f.Setting(i).ID
if seen[id] {
return true
}
seen[id] = true
}
return false
}
// ForeachSetting runs fn for each setting.
// It stops and returns the first error.
func (f *SettingsFrame) ForeachSetting(fn func(Setting) error) error {
f.checkValid()
buf := f.p
for len(buf) > 0 {
if err := fn(Setting{
SettingID(binary.BigEndian.Uint16(buf[:2])),
binary.BigEndian.Uint32(buf[2:6]),
}); err != nil {
for i := 0; i < f.NumSettings(); i++ {
if err := fn(f.Setting(i)); err != nil {
return err
}
buf = buf[6:]
}
return nil
}
@@ -1442,7 +1477,7 @@ func (fr *Framer) maxHeaderStringLen() int {
}
// readMetaFrame returns 0 or more CONTINUATION frames from fr and
// merge them into into the provided hf and returns a MetaHeadersFrame
// merge them into the provided hf and returns a MetaHeadersFrame
// with the decoded hpack values.
func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
if fr.AllowIllegalReads {
@@ -1462,7 +1497,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, ":")

View File

@@ -1189,3 +1189,47 @@ func encodeHeaderRaw(t *testing.T, pairs ...string) []byte {
var he hpackEncoder
return he.encodeHeaderRaw(t, pairs...)
}
func TestSettingsDuplicates(t *testing.T) {
tests := []struct {
settings []Setting
want bool
}{
{nil, false},
{[]Setting{{ID: 1}}, false},
{[]Setting{{ID: 1}, {ID: 2}}, false},
{[]Setting{{ID: 1}, {ID: 2}}, false},
{[]Setting{{ID: 1}, {ID: 2}, {ID: 3}}, false},
{[]Setting{{ID: 1}, {ID: 2}, {ID: 3}}, false},
{[]Setting{{ID: 1}, {ID: 2}, {ID: 3}, {ID: 4}}, false},
{[]Setting{{ID: 1}, {ID: 2}, {ID: 3}, {ID: 2}}, true},
{[]Setting{{ID: 4}, {ID: 2}, {ID: 3}, {ID: 4}}, true},
{[]Setting{
{ID: 1}, {ID: 2}, {ID: 3}, {ID: 4},
{ID: 5}, {ID: 6}, {ID: 7}, {ID: 8},
{ID: 9}, {ID: 10}, {ID: 11}, {ID: 12},
}, false},
{[]Setting{
{ID: 1}, {ID: 2}, {ID: 3}, {ID: 4},
{ID: 5}, {ID: 6}, {ID: 7}, {ID: 8},
{ID: 9}, {ID: 10}, {ID: 11}, {ID: 11},
}, true},
}
for i, tt := range tests {
fr, _ := testFramer()
fr.WriteSettings(tt.settings...)
f, err := fr.ReadFrame()
if err != nil {
t.Fatalf("%d. ReadFrame: %v", i, err)
}
sf := f.(*SettingsFrame)
got := sf.HasDuplicates()
if got != tt.want {
t.Errorf("%d. HasDuplicates = %v; want %v", i, got, tt.want)
}
}
}

29
vendor/golang.org/x/net/http2/go111.go generated vendored Normal file
View File

@@ -0,0 +1,29 @@
// 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.
// +build go1.11
package http2
import (
"net/http/httptrace"
"net/textproto"
)
func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool {
return trace != nil && trace.WroteHeaderField != nil
}
func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {
if trace != nil && trace.WroteHeaderField != nil {
trace.WroteHeaderField(k, []string{v})
}
}
func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
if trace != nil {
return trace.Got1xxResponse
}
return nil
}

View File

@@ -1,16 +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.6
package http2
import (
"net/http"
"time"
)
func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
return t1.ExpectContinueTimeout
}

106
vendor/golang.org/x/net/http2/go17.go generated vendored
View File

@@ -1,106 +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.7
package http2
import (
"context"
"net"
"net/http"
"net/http/httptrace"
"time"
)
type contextContext interface {
context.Context
}
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx contextContext, cancel func()) {
ctx, cancel = context.WithCancel(context.Background())
ctx = context.WithValue(ctx, http.LocalAddrContextKey, c.LocalAddr())
if hs := opts.baseConfig(); hs != nil {
ctx = context.WithValue(ctx, http.ServerContextKey, hs)
}
return
}
func contextWithCancel(ctx contextContext) (_ contextContext, cancel func()) {
return context.WithCancel(ctx)
}
func requestWithContext(req *http.Request, ctx contextContext) *http.Request {
return req.WithContext(ctx)
}
type clientTrace httptrace.ClientTrace
func reqContext(r *http.Request) context.Context { return r.Context() }
func (t *Transport) idleConnTimeout() time.Duration {
if t.t1 != nil {
return t.t1.IdleConnTimeout
}
return 0
}
func setResponseUncompressed(res *http.Response) { res.Uncompressed = true }
func traceGotConn(req *http.Request, cc *ClientConn) {
trace := httptrace.ContextClientTrace(req.Context())
if trace == nil || trace.GotConn == nil {
return
}
ci := httptrace.GotConnInfo{Conn: cc.tconn}
cc.mu.Lock()
ci.Reused = cc.nextStreamID > 1
ci.WasIdle = len(cc.streams) == 0 && ci.Reused
if ci.WasIdle && !cc.lastActive.IsZero() {
ci.IdleTime = time.Now().Sub(cc.lastActive)
}
cc.mu.Unlock()
trace.GotConn(ci)
}
func traceWroteHeaders(trace *clientTrace) {
if trace != nil && trace.WroteHeaders != nil {
trace.WroteHeaders()
}
}
func traceGot100Continue(trace *clientTrace) {
if trace != nil && trace.Got100Continue != nil {
trace.Got100Continue()
}
}
func traceWait100Continue(trace *clientTrace) {
if trace != nil && trace.Wait100Continue != nil {
trace.Wait100Continue()
}
}
func traceWroteRequest(trace *clientTrace, err error) {
if trace != nil && trace.WroteRequest != nil {
trace.WroteRequest(httptrace.WroteRequestInfo{Err: err})
}
}
func traceFirstResponseByte(trace *clientTrace) {
if trace != nil && trace.GotFirstResponseByte != nil {
trace.GotFirstResponseByte()
}
}
func requestTrace(req *http.Request) *clientTrace {
trace := httptrace.ContextClientTrace(req.Context())
return (*clientTrace)(trace)
}
// Ping sends a PING frame to the server and waits for the ack.
func (cc *ClientConn) Ping(ctx context.Context) error {
return cc.ping(ctx)
}

View File

@@ -1,36 +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.7,!go1.8
package http2
import "crypto/tls"
// temporary copy of Go 1.7's private tls.Config.clone:
func cloneTLSConfig(c *tls.Config) *tls.Config {
return &tls.Config{
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
NameToCertificate: c.NameToCertificate,
GetCertificate: c.GetCertificate,
RootCAs: c.RootCAs,
NextProtos: c.NextProtos,
ServerName: c.ServerName,
ClientAuth: c.ClientAuth,
ClientCAs: c.ClientCAs,
InsecureSkipVerify: c.InsecureSkipVerify,
CipherSuites: c.CipherSuites,
PreferServerCipherSuites: c.PreferServerCipherSuites,
SessionTicketsDisabled: c.SessionTicketsDisabled,
SessionTicketKey: c.SessionTicketKey,
ClientSessionCache: c.ClientSessionCache,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
CurvePreferences: c.CurvePreferences,
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
Renegotiation: c.Renegotiation,
}
}

View File

@@ -1,56 +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 go1.8
package http2
import (
"crypto/tls"
"io"
"net/http"
)
func cloneTLSConfig(c *tls.Config) *tls.Config {
c2 := c.Clone()
c2.GetClientCertificate = c.GetClientCertificate // golang.org/issue/19264
return c2
}
var _ http.Pusher = (*responseWriter)(nil)
// Push implements http.Pusher.
func (w *responseWriter) Push(target string, opts *http.PushOptions) error {
internalOpts := pushOptions{}
if opts != nil {
internalOpts.Method = opts.Method
internalOpts.Header = opts.Header
}
return w.push(target, internalOpts)
}
func configureServer18(h1 *http.Server, h2 *Server) error {
if h2.IdleTimeout == 0 {
if h1.IdleTimeout != 0 {
h2.IdleTimeout = h1.IdleTimeout
} else {
h2.IdleTimeout = h1.ReadTimeout
}
}
return nil
}
func shouldLogPanic(panicValue interface{}) bool {
return panicValue != nil && panicValue != http.ErrAbortHandler
}
func reqGetBody(req *http.Request) func() (io.ReadCloser, error) {
return req.GetBody
}
func reqBodyIsNoBody(body io.ReadCloser) bool {
return body == http.NoBody
}
func go18httpNoBody() io.ReadCloser { return http.NoBody } // for tests only

View File

@@ -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")
}
}

View File

@@ -1,16 +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 go1.9
package http2
import (
"net/http"
)
func configureServer19(s *http.Server, conf *Server) error {
s.RegisterOnShutdown(conf.state.startGracefulShutdown)
return nil
}

View File

@@ -1,59 +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-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)
}
}

492
vendor/golang.org/x/net/http2/h2c/h2c.go generated vendored Normal file
View File

@@ -0,0 +1,492 @@
// 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 h2c implements the unencrypted "h2c" form of HTTP/2.
//
// The h2c protocol is the non-TLS version of HTTP/2 which is not available from
// net/http or golang.org/x/net/http2.
package h2c
import (
"bufio"
"bytes"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"io"
"log"
"net"
"net/http"
"net/textproto"
"os"
"strings"
"golang.org/x/net/http/httpguts"
"golang.org/x/net/http2"
"golang.org/x/net/http2/hpack"
)
var (
http2VerboseLogs bool
)
func init() {
e := os.Getenv("GODEBUG")
if strings.Contains(e, "http2debug=1") || strings.Contains(e, "http2debug=2") {
http2VerboseLogs = true
}
}
// h2cHandler is a Handler which implements h2c by hijacking the HTTP/1 traffic
// that should be h2c traffic. There are two ways to begin a h2c connection
// (RFC 7540 Section 3.2 and 3.4): (1) Starting with Prior Knowledge - this
// works by starting an h2c connection with a string of bytes that is valid
// HTTP/1, but unlikely to occur in practice and (2) Upgrading from HTTP/1 to
// h2c - this works by using the HTTP/1 Upgrade header to request an upgrade to
// h2c. When either of those situations occur we hijack the HTTP/1 connection,
// convert it to a HTTP/2 connection and pass the net.Conn to http2.ServeConn.
type h2cHandler struct {
Handler http.Handler
s *http2.Server
}
// NewHandler returns an http.Handler that wraps h, intercepting any h2c
// traffic. If a request is an h2c connection, it's hijacked and redirected to
// s.ServeConn. Otherwise the returned Handler just forwards requests to h. This
// works because h2c is designed to be parseable as valid HTTP/1, but ignored by
// any HTTP server that does not handle h2c. Therefore we leverage the HTTP/1
// compatible parts of the Go http library to parse and recognize h2c requests.
// Once a request is recognized as h2c, we hijack the connection and convert it
// to an HTTP/2 connection which is understandable to s.ServeConn. (s.ServeConn
// understands HTTP/2 except for the h2c part of it.)
func NewHandler(h http.Handler, s *http2.Server) http.Handler {
return &h2cHandler{
Handler: h,
s: s,
}
}
// ServeHTTP implement the h2c support that is enabled by h2c.GetH2CHandler.
func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Handle h2c with prior knowledge (RFC 7540 Section 3.4)
if r.Method == "PRI" && len(r.Header) == 0 && r.URL.Path == "*" && r.Proto == "HTTP/2.0" {
if http2VerboseLogs {
log.Print("h2c: attempting h2c with prior knowledge.")
}
conn, err := initH2CWithPriorKnowledge(w)
if err != nil {
if http2VerboseLogs {
log.Printf("h2c: error h2c with prior knowledge: %v", err)
}
return
}
defer conn.Close()
s.s.ServeConn(conn, &http2.ServeConnOpts{Handler: s.Handler})
return
}
// Handle Upgrade to h2c (RFC 7540 Section 3.2)
if conn, err := h2cUpgrade(w, r); err == nil {
defer conn.Close()
s.s.ServeConn(conn, &http2.ServeConnOpts{Handler: s.Handler})
return
}
s.Handler.ServeHTTP(w, r)
return
}
// initH2CWithPriorKnowledge implements creating a h2c connection with prior
// knowledge (Section 3.4) and creates a net.Conn suitable for http2.ServeConn.
// All we have to do is look for the client preface that is suppose to be part
// of the body, and reforward the client preface on the net.Conn this function
// creates.
func initH2CWithPriorKnowledge(w http.ResponseWriter) (net.Conn, error) {
hijacker, ok := w.(http.Hijacker)
if !ok {
panic("Hijack not supported.")
}
conn, rw, err := hijacker.Hijack()
if err != nil {
panic(fmt.Sprintf("Hijack failed: %v", err))
}
const expectedBody = "SM\r\n\r\n"
buf := make([]byte, len(expectedBody))
n, err := io.ReadFull(rw, buf)
if string(buf[:n]) == expectedBody {
c := &rwConn{
Conn: conn,
Reader: io.MultiReader(strings.NewReader(http2.ClientPreface), rw),
BufWriter: rw.Writer,
}
return c, nil
}
conn.Close()
if http2VerboseLogs {
log.Printf(
"h2c: missing the request body portion of the client preface. Wanted: %v Got: %v",
[]byte(expectedBody),
buf[0:n],
)
}
return nil, errors.New("invalid client preface")
}
// drainClientPreface reads a single instance of the HTTP/2 client preface from
// the supplied reader.
func drainClientPreface(r io.Reader) error {
var buf bytes.Buffer
prefaceLen := int64(len(http2.ClientPreface))
n, err := io.CopyN(&buf, r, prefaceLen)
if err != nil {
return err
}
if n != prefaceLen || buf.String() != http2.ClientPreface {
return fmt.Errorf("Client never sent: %s", http2.ClientPreface)
}
return nil
}
// h2cUpgrade establishes a h2c connection using the HTTP/1 upgrade (Section 3.2).
func h2cUpgrade(w http.ResponseWriter, r *http.Request) (net.Conn, error) {
if !isH2CUpgrade(r.Header) {
return nil, errors.New("non-conforming h2c headers")
}
// Initial bytes we put into conn to fool http2 server
initBytes, _, err := convertH1ReqToH2(r)
if err != nil {
return nil, err
}
hijacker, ok := w.(http.Hijacker)
if !ok {
return nil, errors.New("hijack not supported.")
}
conn, rw, err := hijacker.Hijack()
if err != nil {
return nil, fmt.Errorf("hijack failed: %v", err)
}
rw.Write([]byte("HTTP/1.1 101 Switching Protocols\r\n" +
"Connection: Upgrade\r\n" +
"Upgrade: h2c\r\n\r\n"))
rw.Flush()
// A conforming client will now send an H2 client preface which need to drain
// since we already sent this.
if err := drainClientPreface(rw); err != nil {
return nil, err
}
c := &rwConn{
Conn: conn,
Reader: io.MultiReader(initBytes, rw),
BufWriter: newSettingsAckSwallowWriter(rw.Writer),
}
return c, nil
}
// convert the data contained in the HTTP/1 upgrade request into the HTTP/2
// version in byte form.
func convertH1ReqToH2(r *http.Request) (*bytes.Buffer, []http2.Setting, error) {
h2Bytes := bytes.NewBuffer([]byte((http2.ClientPreface)))
framer := http2.NewFramer(h2Bytes, nil)
settings, err := getH2Settings(r.Header)
if err != nil {
return nil, nil, err
}
if err := framer.WriteSettings(settings...); err != nil {
return nil, nil, err
}
headerBytes, err := getH2HeaderBytes(r, getMaxHeaderTableSize(settings))
if err != nil {
return nil, nil, err
}
maxFrameSize := int(getMaxFrameSize(settings))
needOneHeader := len(headerBytes) < maxFrameSize
err = framer.WriteHeaders(http2.HeadersFrameParam{
StreamID: 1,
BlockFragment: headerBytes,
EndHeaders: needOneHeader,
})
if err != nil {
return nil, nil, err
}
for i := maxFrameSize; i < len(headerBytes); i += maxFrameSize {
if len(headerBytes)-i > maxFrameSize {
if err := framer.WriteContinuation(1,
false, // endHeaders
headerBytes[i:maxFrameSize]); err != nil {
return nil, nil, err
}
} else {
if err := framer.WriteContinuation(1,
true, // endHeaders
headerBytes[i:]); err != nil {
return nil, nil, err
}
}
}
return h2Bytes, settings, nil
}
// getMaxFrameSize returns the SETTINGS_MAX_FRAME_SIZE. If not present default
// value is 16384 as specified by RFC 7540 Section 6.5.2.
func getMaxFrameSize(settings []http2.Setting) uint32 {
for _, setting := range settings {
if setting.ID == http2.SettingMaxFrameSize {
return setting.Val
}
}
return 16384
}
// getMaxHeaderTableSize returns the SETTINGS_HEADER_TABLE_SIZE. If not present
// default value is 4096 as specified by RFC 7540 Section 6.5.2.
func getMaxHeaderTableSize(settings []http2.Setting) uint32 {
for _, setting := range settings {
if setting.ID == http2.SettingHeaderTableSize {
return setting.Val
}
}
return 4096
}
// bufWriter is a Writer interface that also has a Flush method.
type bufWriter interface {
io.Writer
Flush() error
}
// rwConn implements net.Conn but overrides Read and Write so that reads and
// writes are forwarded to the provided io.Reader and bufWriter.
type rwConn struct {
net.Conn
io.Reader
BufWriter bufWriter
}
// Read forwards reads to the underlying Reader.
func (c *rwConn) Read(p []byte) (int, error) {
return c.Reader.Read(p)
}
// Write forwards writes to the underlying bufWriter and immediately flushes.
func (c *rwConn) Write(p []byte) (int, error) {
n, err := c.BufWriter.Write(p)
if err := c.BufWriter.Flush(); err != nil {
return 0, err
}
return n, err
}
// settingsAckSwallowWriter is a writer that normally forwards bytes to its
// underlying Writer, but swallows the first SettingsAck frame that it sees.
type settingsAckSwallowWriter struct {
Writer *bufio.Writer
buf []byte
didSwallow bool
}
// newSettingsAckSwallowWriter returns a new settingsAckSwallowWriter.
func newSettingsAckSwallowWriter(w *bufio.Writer) *settingsAckSwallowWriter {
return &settingsAckSwallowWriter{
Writer: w,
buf: make([]byte, 0),
didSwallow: false,
}
}
// Write implements io.Writer interface. Normally forwards bytes to w.Writer,
// except for the first Settings ACK frame that it sees.
func (w *settingsAckSwallowWriter) Write(p []byte) (int, error) {
if !w.didSwallow {
w.buf = append(w.buf, p...)
// Process all the frames we have collected into w.buf
for {
// Append until we get full frame header which is 9 bytes
if len(w.buf) < 9 {
break
}
// Check if we have collected a whole frame.
fh, err := http2.ReadFrameHeader(bytes.NewBuffer(w.buf))
if err != nil {
// Corrupted frame, fail current Write
return 0, err
}
fSize := fh.Length + 9
if uint32(len(w.buf)) < fSize {
// Have not collected whole frame. Stop processing buf, and withold on
// forward bytes to w.Writer until we get the full frame.
break
}
// We have now collected a whole frame.
if fh.Type == http2.FrameSettings && fh.Flags.Has(http2.FlagSettingsAck) {
// If Settings ACK frame, do not forward to underlying writer, remove
// bytes from w.buf, and record that we have swallowed Settings Ack
// frame.
w.didSwallow = true
w.buf = w.buf[fSize:]
continue
}
// Not settings ack frame. Forward bytes to w.Writer.
if _, err := w.Writer.Write(w.buf[:fSize]); err != nil {
// Couldn't forward bytes. Fail current Write.
return 0, err
}
w.buf = w.buf[fSize:]
}
return len(p), nil
}
return w.Writer.Write(p)
}
// Flush calls w.Writer.Flush.
func (w *settingsAckSwallowWriter) Flush() error {
return w.Writer.Flush()
}
// isH2CUpgrade returns true if the header properly request an upgrade to h2c
// as specified by Section 3.2.
func isH2CUpgrade(h http.Header) bool {
return httpguts.HeaderValuesContainsToken(h[textproto.CanonicalMIMEHeaderKey("Upgrade")], "h2c") &&
httpguts.HeaderValuesContainsToken(h[textproto.CanonicalMIMEHeaderKey("Connection")], "HTTP2-Settings")
}
// getH2Settings returns the []http2.Setting that are encoded in the
// HTTP2-Settings header.
func getH2Settings(h http.Header) ([]http2.Setting, error) {
vals, ok := h[textproto.CanonicalMIMEHeaderKey("HTTP2-Settings")]
if !ok {
return nil, errors.New("missing HTTP2-Settings header")
}
if len(vals) != 1 {
return nil, fmt.Errorf("expected 1 HTTP2-Settings. Got: %v", vals)
}
settings, err := decodeSettings(vals[0])
if err != nil {
return nil, fmt.Errorf("Invalid HTTP2-Settings: %q", vals[0])
}
return settings, nil
}
// decodeSettings decodes the base64url header value of the HTTP2-Settings
// header. RFC 7540 Section 3.2.1.
func decodeSettings(headerVal string) ([]http2.Setting, error) {
b, err := base64.RawURLEncoding.DecodeString(headerVal)
if err != nil {
return nil, err
}
if len(b)%6 != 0 {
return nil, err
}
settings := make([]http2.Setting, 0)
for i := 0; i < len(b)/6; i++ {
settings = append(settings, http2.Setting{
ID: http2.SettingID(binary.BigEndian.Uint16(b[i*6 : i*6+2])),
Val: binary.BigEndian.Uint32(b[i*6+2 : i*6+6]),
})
}
return settings, nil
}
// getH2HeaderBytes return the headers in r a []bytes encoded by HPACK.
func getH2HeaderBytes(r *http.Request, maxHeaderTableSize uint32) ([]byte, error) {
headerBytes := bytes.NewBuffer(nil)
hpackEnc := hpack.NewEncoder(headerBytes)
hpackEnc.SetMaxDynamicTableSize(maxHeaderTableSize)
// Section 8.1.2.3
err := hpackEnc.WriteField(hpack.HeaderField{
Name: ":method",
Value: r.Method,
})
if err != nil {
return nil, err
}
err = hpackEnc.WriteField(hpack.HeaderField{
Name: ":scheme",
Value: "http",
})
if err != nil {
return nil, err
}
err = hpackEnc.WriteField(hpack.HeaderField{
Name: ":authority",
Value: r.Host,
})
if err != nil {
return nil, err
}
path := r.URL.Path
if r.URL.RawQuery != "" {
path = strings.Join([]string{path, r.URL.RawQuery}, "?")
}
err = hpackEnc.WriteField(hpack.HeaderField{
Name: ":path",
Value: path,
})
if err != nil {
return nil, err
}
// TODO Implement Section 8.3
for header, values := range r.Header {
// Skip non h2 headers
if isNonH2Header(header) {
continue
}
for _, v := range values {
err := hpackEnc.WriteField(hpack.HeaderField{
Name: strings.ToLower(header),
Value: v,
})
if err != nil {
return nil, err
}
}
}
return headerBytes.Bytes(), nil
}
// Connection specific headers listed in RFC 7540 Section 8.1.2.2 that are not
// suppose to be transferred to HTTP/2. The Http2-Settings header is skipped
// since already use to create the HTTP/2 SETTINGS frame.
var nonH2Headers = []string{
"Connection",
"Keep-Alive",
"Proxy-Connection",
"Transfer-Encoding",
"Upgrade",
"Http2-Settings",
}
// isNonH2Header returns true if header should not be transferred to HTTP/2.
func isNonH2Header(header string) bool {
for _, nonH2h := range nonH2Headers {
if header == nonH2h {
return true
}
}
return false
}

58
vendor/golang.org/x/net/http2/h2c/h2c_test.go generated vendored Normal file
View File

@@ -0,0 +1,58 @@
// 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 h2c
import (
"bufio"
"bytes"
"fmt"
"log"
"net/http"
"testing"
"golang.org/x/net/http2"
)
func TestSettingsAckSwallowWriter(t *testing.T) {
var buf bytes.Buffer
swallower := newSettingsAckSwallowWriter(bufio.NewWriter(&buf))
fw := http2.NewFramer(swallower, nil)
fw.WriteSettings(http2.Setting{http2.SettingMaxFrameSize, 2})
fw.WriteSettingsAck()
fw.WriteData(1, true, []byte{})
swallower.Flush()
fr := http2.NewFramer(nil, bufio.NewReader(&buf))
f, err := fr.ReadFrame()
if err != nil {
t.Fatal(err)
}
if f.Header().Type != http2.FrameSettings {
t.Fatalf("Expected first frame to be SETTINGS. Got: %v", f.Header().Type)
}
f, err = fr.ReadFrame()
if err != nil {
t.Fatal(err)
}
if f.Header().Type != http2.FrameData {
t.Fatalf("Expected first frame to be DATA. Got: %v", f.Header().Type)
}
}
func ExampleNewHandler() {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello world")
})
h2s := &http2.Server{
// ...
}
h1s := &http.Server{
Addr: ":8080",
Handler: NewHandler(handler, h2s),
}
log.Fatal(h1s.ListenAndServe())
}

View File

@@ -160,6 +160,9 @@ func echoCapitalHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "PUT required.", 400)
return
}
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
io.Copy(flushWriter{w}, capitalizeReader{r.Body})
}

View File

@@ -3,6 +3,7 @@ kind: Service
metadata:
name: h2demo
spec:
externalTrafficPolicy: Local
ports:
- port: 80
targetPort: 80

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !plan9,!solaris
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
/*
The h2i command is an interactive HTTP/2 console.

View File

@@ -7,15 +7,21 @@ package http2
import (
"net/http"
"strings"
"sync"
)
var (
commonLowerHeader = map[string]string{} // Go-Canonical-Case -> lower-case
commonCanonHeader = map[string]string{} // lower-case -> Go-Canonical-Case
commonBuildOnce sync.Once
commonLowerHeader map[string]string // Go-Canonical-Case -> lower-case
commonCanonHeader map[string]string // lower-case -> Go-Canonical-Case
)
func init() {
for _, v := range []string{
func buildCommonHeaderMapsOnce() {
commonBuildOnce.Do(buildCommonHeaderMaps)
}
func buildCommonHeaderMaps() {
common := []string{
"accept",
"accept-charset",
"accept-encoding",
@@ -63,7 +69,10 @@ func init() {
"vary",
"via",
"www-authenticate",
} {
}
commonLowerHeader = make(map[string]string, len(common))
commonCanonHeader = make(map[string]string, len(common))
for _, v := range common {
chk := http.CanonicalHeaderKey(v)
commonLowerHeader[chk] = v
commonCanonHeader[v] = chk
@@ -71,6 +80,7 @@ func init() {
}
func lowerHeader(v string) string {
buildCommonHeaderMapsOnce()
if s, ok := commonLowerHeader[v]; ok {
return s
}

View File

@@ -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 {

View File

@@ -462,6 +462,27 @@ func TestHuffmanDecode(t *testing.T) {
}
}
func BenchmarkHuffmanDecode(b *testing.B) {
b.StopTimer()
enc, err := hex.DecodeString(strings.Replace("94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07",
" ", "", -1))
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.StartTimer()
var buf bytes.Buffer
for i := 0; i < b.N; i++ {
buf.Reset()
if _, err := HuffmanDecode(&buf, enc); err != nil {
b.Fatalf("decode error: %v", err)
}
if string(buf.Bytes()) != "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" {
b.Fatalf("bogus output %q", buf.Bytes())
}
}
}
func TestAppendHuffmanString(t *testing.T) {
tests := []struct {
in, want string
@@ -720,3 +741,22 @@ func TestSaveBufLimit(t *testing.T) {
t.Fatalf("Write error = %v; want ErrStringLength", err)
}
}
func TestDynamicSizeUpdate(t *testing.T) {
var buf bytes.Buffer
enc := NewEncoder(&buf)
enc.SetMaxDynamicTableSize(255)
enc.WriteField(HeaderField{Name: "foo", Value: "bar"})
d := NewDecoder(4096, nil)
_, err := d.DecodeFull(buf.Bytes())
if err != nil {
t.Fatalf("unexpected error: got = %v", err)
}
// must fail since the dynamic table update must be at the beginning
_, err = d.DecodeFull(buf.Bytes())
if err == nil {
t.Fatalf("dynamic table size update not at the beginning of a header block")
}
}

View File

@@ -47,6 +47,7 @@ var ErrInvalidHuffman = errors.New("hpack: invalid Huffman-encoded data")
// If maxLen is greater than 0, attempts to write more to buf than
// maxLen bytes will return ErrStringLength.
func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
rootHuffmanNode := getRootHuffmanNode()
n := rootHuffmanNode
// cur is the bit buffer that has not been fed into n.
// cbits is the number of low order bits in cur that are valid.
@@ -106,7 +107,7 @@ func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
type node struct {
// children is non-nil for internal nodes
children []*node
children *[256]*node
// The following are only valid if children is nil:
codeLen uint8 // number of bits that led to the output of sym
@@ -114,22 +115,31 @@ type node struct {
}
func newInternalNode() *node {
return &node{children: make([]*node, 256)}
return &node{children: new([256]*node)}
}
var rootHuffmanNode = newInternalNode()
var (
buildRootOnce sync.Once
lazyRootHuffmanNode *node
)
func init() {
func getRootHuffmanNode() *node {
buildRootOnce.Do(buildRootHuffmanNode)
return lazyRootHuffmanNode
}
func buildRootHuffmanNode() {
if len(huffmanCodes) != 256 {
panic("unexpected size")
}
lazyRootHuffmanNode = newInternalNode()
for i, code := range huffmanCodes {
addDecoderNode(byte(i), code, huffmanCodeLen[i])
}
}
func addDecoderNode(sym byte, code uint32, codeLen uint8) {
cur := rootHuffmanNode
cur := lazyRootHuffmanNode
for codeLen > 8 {
codeLen -= 8
i := uint8(code >> codeLen)

View File

@@ -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' {
@@ -201,19 +201,12 @@ func validWireHeaderFieldName(v string) bool {
return true
}
var httpCodeStringCommon = map[int]string{} // n -> strconv.Itoa(n)
func init() {
for i := 100; i <= 999; i++ {
if v := http.StatusText(i); v != "" {
httpCodeStringCommon[i] = strconv.Itoa(i)
}
}
}
func httpCodeString(code int) string {
if s, ok := httpCodeStringCommon[code]; ok {
return s
switch code {
case 200:
return "200"
case 404:
return "404"
}
return strconv.Itoa(code)
}

View File

@@ -14,6 +14,7 @@ import (
"strconv"
"strings"
"testing"
"time"
"golang.org/x/net/http2/hpack"
)
@@ -197,3 +198,83 @@ func TestSorterPoolAllocs(t *testing.T) {
t.Logf("Keys allocs = %v; want <1", allocs)
}
}
// waitCondition reports whether fn eventually returned true,
// checking immediately and then every checkEvery amount,
// until waitFor has elapsed, at which point it returns false.
func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
deadline := time.Now().Add(waitFor)
for time.Now().Before(deadline) {
if fn() {
return true
}
time.Sleep(checkEvery)
}
return false
}
// waitErrCondition is like waitCondition but with errors instead of bools.
func waitErrCondition(waitFor, checkEvery time.Duration, fn func() error) error {
deadline := time.Now().Add(waitFor)
var err error
for time.Now().Before(deadline) {
if err = fn(); err == nil {
return nil
}
time.Sleep(checkEvery)
}
return err
}
// 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)
}
}
}

20
vendor/golang.org/x/net/http2/not_go111.go generated vendored Normal file
View File

@@ -0,0 +1,20 @@
// 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.
// +build !go1.11
package http2
import (
"net/http/httptrace"
"net/textproto"
)
func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { return false }
func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {}
func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
return nil
}

View File

@@ -1,21 +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 !go1.6
package http2
import (
"net/http"
"time"
)
func configureTransport(t1 *http.Transport) (*Transport, error) {
return nil, errTransportVersion
}
func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
return 0
}

View File

@@ -1,87 +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.7
package http2
import (
"crypto/tls"
"net"
"net/http"
"time"
)
type contextContext interface {
Done() <-chan struct{}
Err() error
}
type fakeContext struct{}
func (fakeContext) Done() <-chan struct{} { return nil }
func (fakeContext) Err() error { panic("should not be called") }
func reqContext(r *http.Request) fakeContext {
return fakeContext{}
}
func setResponseUncompressed(res *http.Response) {
// Nothing.
}
type clientTrace struct{}
func requestTrace(*http.Request) *clientTrace { return nil }
func traceGotConn(*http.Request, *ClientConn) {}
func traceFirstResponseByte(*clientTrace) {}
func traceWroteHeaders(*clientTrace) {}
func traceWroteRequest(*clientTrace, error) {}
func traceGot100Continue(trace *clientTrace) {}
func traceWait100Continue(trace *clientTrace) {}
func nop() {}
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx contextContext, cancel func()) {
return nil, nop
}
func contextWithCancel(ctx contextContext) (_ contextContext, cancel func()) {
return ctx, nop
}
func requestWithContext(req *http.Request, ctx contextContext) *http.Request {
return req
}
// temporary copy of Go 1.6's private tls.Config.clone:
func cloneTLSConfig(c *tls.Config) *tls.Config {
return &tls.Config{
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
NameToCertificate: c.NameToCertificate,
GetCertificate: c.GetCertificate,
RootCAs: c.RootCAs,
NextProtos: c.NextProtos,
ServerName: c.ServerName,
ClientAuth: c.ClientAuth,
ClientCAs: c.ClientCAs,
InsecureSkipVerify: c.InsecureSkipVerify,
CipherSuites: c.CipherSuites,
PreferServerCipherSuites: c.PreferServerCipherSuites,
SessionTicketsDisabled: c.SessionTicketsDisabled,
SessionTicketKey: c.SessionTicketKey,
ClientSessionCache: c.ClientSessionCache,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
CurvePreferences: c.CurvePreferences,
}
}
func (cc *ClientConn) Ping(ctx contextContext) error {
return cc.ping(ctx)
}
func (t *Transport) idleConnTimeout() time.Duration { return 0 }

View File

@@ -1,29 +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 (
"io"
"net/http"
)
func configureServer18(h1 *http.Server, h2 *Server) error {
// No IdleTimeout to sync prior to Go 1.8.
return nil
}
func shouldLogPanic(panicValue interface{}) bool {
return panicValue != nil
}
func reqGetBody(req *http.Request) func() (io.ReadCloser, error) {
return nil
}
func reqBodyIsNoBody(io.ReadCloser) bool { return false }
func go18httpNoBody() io.ReadCloser { return nil } // for tests only

View File

@@ -1,16 +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.9
package http2
import (
"net/http"
)
func configureServer19(s *http.Server, conf *Server) error {
// not supported prior to go1.9
return nil
}

View File

@@ -28,6 +28,7 @@ package http2
import (
"bufio"
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
@@ -46,6 +47,7 @@ import (
"sync"
"time"
"golang.org/x/net/http/httpguts"
"golang.org/x/net/http2/hpack"
)
@@ -208,12 +210,14 @@ func ConfigureServer(s *http.Server, conf *Server) error {
conf = new(Server)
}
conf.state = &serverInternalState{activeConns: make(map[*serverConn]struct{})}
if err := configureServer18(s, conf); err != nil {
return err
}
if err := configureServer19(s, conf); err != nil {
return err
if h1, h2 := s, conf; h2.IdleTimeout == 0 {
if h1.IdleTimeout != 0 {
h2.IdleTimeout = h1.IdleTimeout
} else {
h2.IdleTimeout = h1.ReadTimeout
}
}
s.RegisterOnShutdown(conf.state.startGracefulShutdown)
if s.TLSConfig == nil {
s.TLSConfig = new(tls.Config)
@@ -434,6 +438,15 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
sc.serve()
}
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx context.Context, cancel func()) {
ctx, cancel = context.WithCancel(context.Background())
ctx = context.WithValue(ctx, http.LocalAddrContextKey, c.LocalAddr())
if hs := opts.baseConfig(); hs != nil {
ctx = context.WithValue(ctx, http.ServerContextKey, hs)
}
return
}
func (sc *serverConn) rejectConn(err ErrCode, debug string) {
sc.vlogf("http2: server rejecting conn: %v, %s", err, debug)
// ignoring errors. hanging up anyway.
@@ -449,7 +462,7 @@ type serverConn struct {
conn net.Conn
bw *bufferedWriter // writing to conn
handler http.Handler
baseCtx contextContext
baseCtx context.Context
framer *Framer
doneServing chan struct{} // closed when serverConn.serve ends
readFrameCh chan readFrameResult // written by serverConn.readFrames
@@ -529,7 +542,7 @@ type stream struct {
id uint32
body *pipe // non-nil if expecting DATA frames
cw closeWaiter // closed wait stream transitions to closed state
ctx contextContext
ctx context.Context
cancelCtx func()
// owned by serverConn's serve loop:
@@ -662,6 +675,7 @@ func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
func (sc *serverConn) canonicalHeader(v string) string {
sc.serveG.check()
buildCommonHeaderMapsOnce()
cv, ok := commonCanonHeader[v]
if ok {
return cv
@@ -1108,7 +1122,7 @@ func (sc *serverConn) startFrameWrite(wr FrameWriteRequest) {
// errHandlerPanicked is the error given to any callers blocked in a read from
// Request.Body when the main goroutine panics. Since most handlers read in the
// the main ServeHTTP goroutine, this will show up rarely.
// main ServeHTTP goroutine, this will show up rarely.
var errHandlerPanicked = errors.New("http2: handler panicked")
// wroteFrame is called on the serve goroutine with the result of
@@ -1486,6 +1500,12 @@ func (sc *serverConn) processSettings(f *SettingsFrame) error {
}
return nil
}
if f.NumSettings() > 100 || f.HasDuplicates() {
// This isn't actually in the spec, but hang up on
// suspiciously large settings frames or those with
// duplicate entries.
return ConnectionError(ErrCodeProtocol)
}
if err := f.ForeachSetting(sc.processSetting); err != nil {
return err
}
@@ -1574,6 +1594,12 @@ func (sc *serverConn) processData(f *DataFrame) error {
// type PROTOCOL_ERROR."
return ConnectionError(ErrCodeProtocol)
}
// RFC 7540, sec 6.1: If a DATA frame is received whose stream is not in
// "open" or "half-closed (local)" state, the recipient MUST respond with a
// stream error (Section 5.4.2) of type STREAM_CLOSED.
if state == stateClosed {
return streamError(id, ErrCodeStreamClosed)
}
if st == nil || state != stateOpen || st.gotTrailerHeader || st.resetQueued {
// This includes sending a RST_STREAM if the stream is
// in stateHalfClosedLocal (which currently means that
@@ -1607,7 +1633,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.
@@ -1717,6 +1746,13 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
// processing this frame.
return nil
}
// RFC 7540, sec 5.1: If an endpoint receives additional frames, other than
// WINDOW_UPDATE, PRIORITY, or RST_STREAM, for a stream that is in
// this state, it MUST respond with a stream error (Section 5.4.2) of
// type STREAM_CLOSED.
if st.state == stateHalfClosedRemote {
return streamError(id, ErrCodeStreamClosed)
}
return st.processTrailerHeaders(f)
}
@@ -1817,7 +1853,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.
@@ -1858,7 +1894,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream
panic("internal error: cannot create stream with id 0")
}
ctx, cancelCtx := contextWithCancel(sc.baseCtx)
ctx, cancelCtx := context.WithCancel(sc.baseCtx)
st := &stream{
sc: sc,
id: id,
@@ -2024,7 +2060,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
Body: body,
Trailer: trailer,
}
req = requestWithContext(req, st.ctx)
req = req.WithContext(st.ctx)
rws := responseWriterStatePool.Get().(*responseWriterState)
bwSave := rws.bw
@@ -2052,7 +2088,7 @@ func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler
stream: rw.rws.stream,
})
// Same as net/http:
if shouldLogPanic(e) {
if e != nil && e != http.ErrAbortHandler {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
@@ -2284,7 +2320,7 @@ 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) {
if !httpguts.ValidTrailerHeader(k) {
// Forbidden by RFC 7230, section 4.1.2.
rws.conn.logf("ignoring invalid trailer %q", k)
return
@@ -2335,6 +2371,19 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
foreachHeaderElement(v, rws.declareTrailer)
}
// "Connection" headers aren't allowed in HTTP/2 (RFC 7540, 8.1.2.2),
// but respect "Connection" == "close" to mean sending a GOAWAY and tearing
// down the TCP connection when idle, like we do for HTTP/1.
// TODO: remove more Connection-specific header fields here, in addition
// to "Connection".
if _, ok := rws.snapHeader["Connection"]; ok {
v := rws.snapHeader.Get("Connection")
delete(rws.snapHeader, "Connection")
if v == "close" {
rws.conn.startGracefulShutdown()
}
}
endStream := (rws.handlerDone && !rws.hasTrailers() && len(p) == 0) || isHeadResp
err = rws.conn.writeHeaders(rws.stream, &writeResHeaders{
streamID: rws.stream.id,
@@ -2601,14 +2650,9 @@ var (
ErrPushLimitReached = errors.New("http2: push would exceed peer's SETTINGS_MAX_CONCURRENT_STREAMS")
)
// pushOptions is the internal version of http.PushOptions, which we
// cannot include here because it's only defined in Go 1.8 and later.
type pushOptions struct {
Method string
Header http.Header
}
var _ http.Pusher = (*responseWriter)(nil)
func (w *responseWriter) push(target string, opts pushOptions) error {
func (w *responseWriter) Push(target string, opts *http.PushOptions) error {
st := w.rws.stream
sc := st.sc
sc.serveG.checkNotOn()
@@ -2619,6 +2663,10 @@ func (w *responseWriter) push(target string, opts pushOptions) error {
return ErrRecursivePush
}
if opts == nil {
opts = new(http.PushOptions)
}
// Default options.
if opts.Method == "" {
opts.Method = "GET"
@@ -2838,41 +2886,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.

View File

@@ -2,8 +2,6 @@
// 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 (

View File

@@ -6,6 +6,7 @@ package http2
import (
"bytes"
"context"
"crypto/tls"
"errors"
"flag"
@@ -2360,9 +2361,6 @@ func TestServer_NoCrash_HandlerClose_Then_ClientClose(t *testing.T) {
// it did before.
st.writeData(1, true, []byte("foo"))
// Get our flow control bytes back, since the handler didn't get them.
st.wantWindowUpdate(0, uint32(len("foo")))
// Sent after a peer sends data anyway (admittedly the
// previous RST_STREAM might've still been in-flight),
// but they'll get the more friendly 'cancel' code
@@ -2418,6 +2416,7 @@ func testRejectTLS(t *testing.T, max uint16) {
func TestServer_Rejects_TLSBadCipher(t *testing.T) {
st := newServerTester(t, nil, func(c *tls.Config) {
c.MaxVersion = tls.VersionTLS12 // workaround for golang.org/issue/28762
// Only list bad ones:
c.CipherSuites = []uint16{
tls.TLS_RSA_WITH_RC4_128_SHA,
@@ -3723,3 +3722,177 @@ func TestIssue20704Race(t *testing.T) {
resp.Body.Close()
}
}
func TestServer_Rejects_TooSmall(t *testing.T) {
testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
ioutil.ReadAll(r.Body)
return nil
}, func(st *serverTester) {
st.writeHeaders(HeadersFrameParam{
StreamID: 1, // clients send odd numbers
BlockFragment: st.encodeHeader(
":method", "POST",
"content-length", "4",
),
EndStream: false, // to say DATA frames are coming
EndHeaders: true,
})
st.writeData(1, true, []byte("12345"))
st.wantRSTStream(1, ErrCodeProtocol)
})
}
// Tests that a handler setting "Connection: close" results in a GOAWAY being sent,
// and the connection still completing.
func TestServerHandlerConnectionClose(t *testing.T) {
unblockHandler := make(chan bool, 1)
defer close(unblockHandler) // backup; in case of errors
testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
w.Header().Set("Connection", "close")
w.Header().Set("Foo", "bar")
w.(http.Flusher).Flush()
<-unblockHandler
return nil
}, func(st *serverTester) {
st.writeHeaders(HeadersFrameParam{
StreamID: 1,
BlockFragment: st.encodeHeader(),
EndStream: true,
EndHeaders: true,
})
var sawGoAway bool
var sawRes bool
for {
f, err := st.readFrame()
if err == io.EOF {
break
}
if err != nil {
t.Fatal(err)
}
switch f := f.(type) {
case *GoAwayFrame:
sawGoAway = true
unblockHandler <- true
if f.LastStreamID != 1 || f.ErrCode != ErrCodeNo {
t.Errorf("unexpected GOAWAY frame: %v", summarizeFrame(f))
}
case *HeadersFrame:
goth := st.decodeHeader(f.HeaderBlockFragment())
wanth := [][2]string{
{":status", "200"},
{"foo", "bar"},
}
if !reflect.DeepEqual(goth, wanth) {
t.Errorf("got headers %v; want %v", goth, wanth)
}
sawRes = true
case *DataFrame:
if f.StreamID != 1 || !f.StreamEnded() || len(f.Data()) != 0 {
t.Errorf("unexpected DATA frame: %v", summarizeFrame(f))
}
default:
t.Logf("unexpected frame: %v", summarizeFrame(f))
}
}
if !sawGoAway {
t.Errorf("didn't see GOAWAY")
}
if !sawRes {
t.Errorf("didn't see response")
}
})
}
func TestServer_Headers_HalfCloseRemote(t *testing.T) {
var st *serverTester
writeData := make(chan bool)
writeHeaders := make(chan bool)
leaveHandler := make(chan bool)
st = newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
if st.stream(1) == nil {
t.Errorf("nil stream 1 in handler")
}
if got, want := st.streamState(1), stateOpen; got != want {
t.Errorf("in handler, state is %v; want %v", got, want)
}
writeData <- true
if n, err := r.Body.Read(make([]byte, 1)); n != 0 || err != io.EOF {
t.Errorf("body read = %d, %v; want 0, EOF", n, err)
}
if got, want := st.streamState(1), stateHalfClosedRemote; got != want {
t.Errorf("in handler, state is %v; want %v", got, want)
}
writeHeaders <- true
<-leaveHandler
})
st.greet()
st.writeHeaders(HeadersFrameParam{
StreamID: 1,
BlockFragment: st.encodeHeader(),
EndStream: false, // keep it open
EndHeaders: true,
})
<-writeData
st.writeData(1, true, nil)
<-writeHeaders
st.writeHeaders(HeadersFrameParam{
StreamID: 1,
BlockFragment: st.encodeHeader(),
EndStream: false, // keep it open
EndHeaders: true,
})
defer close(leaveHandler)
st.wantRSTStream(1, ErrCodeStreamClosed)
}
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-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)
}
}

View File

@@ -10,6 +10,7 @@ import (
"bufio"
"bytes"
"compress/gzip"
"context"
"crypto/rand"
"crypto/tls"
"errors"
@@ -21,15 +22,17 @@ import (
mathrand "math/rand"
"net"
"net/http"
"net/http/httptrace"
"net/textproto"
"sort"
"strconv"
"strings"
"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 (
@@ -117,16 +120,56 @@ func (t *Transport) disableCompression() bool {
return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression)
}
var errTransportVersion = errors.New("http2: ConfigureTransport is only supported starting at Go 1.6")
// ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2.
// It requires Go 1.6 or later and returns an error if the net/http package is too old
// or if t1 has already been HTTP/2-enabled.
// It returns an error if t1 has already been HTTP/2-enabled.
func ConfigureTransport(t1 *http.Transport) error {
_, err := configureTransport(t1) // in configure_transport.go (go1.6) or not_go16.go
_, err := configureTransport(t1)
return err
}
func configureTransport(t1 *http.Transport) (*Transport, error) {
connPool := new(clientConnPool)
t2 := &Transport{
ConnPool: noDialClientConnPool{connPool},
t1: t1,
}
connPool.t = t2
if err := registerHTTPSProtocol(t1, noDialH2RoundTripper{t2}); err != nil {
return nil, err
}
if t1.TLSClientConfig == nil {
t1.TLSClientConfig = new(tls.Config)
}
if !strSliceContains(t1.TLSClientConfig.NextProtos, "h2") {
t1.TLSClientConfig.NextProtos = append([]string{"h2"}, t1.TLSClientConfig.NextProtos...)
}
if !strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") {
t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1")
}
upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper {
addr := authorityAddr("https", authority)
if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
go c.Close()
return erringRoundTripper{err}
} else if !used {
// Turns out we don't need this c.
// For example, two goroutines made requests to the same host
// at the same time, both kicking off TCP dials. (since protocol
// was unknown)
go c.Close()
}
return t2
}
if m := t1.TLSNextProto; len(m) == 0 {
t1.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{
"h2": upgradeFn,
}
} else {
m["h2"] = upgradeFn
}
return t2, nil
}
func (t *Transport) connPool() ClientConnPool {
t.connPoolOnce.Do(t.initConnPool)
return t.connPoolOrDef
@@ -159,6 +202,7 @@ type ClientConn struct {
cond *sync.Cond // hold mu; broadcast on flow/closed changes
flow flow // our conn-level flow control quota (cs.flow is per stream)
inflow flow // peer's conn-level flow control
closing bool
closed bool
wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back
goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received
@@ -190,7 +234,7 @@ type ClientConn struct {
type clientStream struct {
cc *ClientConn
req *http.Request
trace *clientTrace // or nil
trace *httptrace.ClientTrace // or nil
ID uint32
resc chan resAndError
bufPipe pipe // buffered pipe with the flow-controlled response payload
@@ -211,9 +255,10 @@ type clientStream struct {
done chan struct{} // closed when stream remove from cc.streams map; close calls guarded by cc.mu
// owned by clientConnReadLoop:
firstByte bool // got the first response byte
pastHeaders bool // got first MetaHeadersFrame (actual headers)
pastTrailers bool // got optional second MetaHeadersFrame (trailers)
firstByte bool // got the first response byte
pastHeaders bool // got first MetaHeadersFrame (actual headers)
pastTrailers bool // got optional second MetaHeadersFrame (trailers)
num1xx uint8 // number of 1xx responses seen
trailer http.Header // accumulated trailers
resTrailer *http.Header // client's Response.Trailer
@@ -223,7 +268,7 @@ type clientStream struct {
// channel to be signaled. A non-nil error is returned only if the request was
// canceled.
func awaitRequestCancel(req *http.Request, done <-chan struct{}) error {
ctx := reqContext(req)
ctx := req.Context()
if req.Cancel == nil && ctx.Done() == nil {
return nil
}
@@ -237,6 +282,17 @@ func awaitRequestCancel(req *http.Request, done <-chan struct{}) error {
}
}
var got1xxFuncForTests func(int, textproto.MIMEHeader) error
// get1xxTraceFunc returns the value of request's httptrace.ClientTrace.Got1xxResponse func,
// if any. It returns nil if not set or if the Go version is too old.
func (cs *clientStream) get1xxTraceFunc() func(int, textproto.MIMEHeader) error {
if fn := got1xxFuncForTests; fn != nil {
return fn
}
return traceGot1xxResponseFunc(cs.trace)
}
// awaitRequestCancel waits for the user to cancel a request, its context to
// expire, or for the request to be done (any way it might be removed from the
// cc.streams map: peer reset, successful completion, TCP connection breakage,
@@ -387,8 +443,8 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
select {
case <-time.After(time.Second * time.Duration(backoff)):
continue
case <-reqContext(req).Done():
return nil, reqContext(req).Err()
case <-req.Context().Done():
return nil, req.Context().Err()
}
}
}
@@ -423,27 +479,35 @@ func shouldRetryRequest(req *http.Request, err error, afterBodyWrite bool) (*htt
if !canRetryError(err) {
return nil, err
}
// If the Body is nil (or http.NoBody), it's safe to reuse
// this request and its Body.
if req.Body == nil || req.Body == http.NoBody {
return req, nil
}
// If the request body can be reset back to its original
// state via the optional req.GetBody, do that.
if req.GetBody != nil {
// TODO: consider a req.Body.Close here? or audit that all caller paths do?
body, err := req.GetBody()
if err != nil {
return nil, err
}
newReq := *req
newReq.Body = body
return &newReq, nil
}
// The Request.Body can't reset back to the beginning, but we
// don't seem to have started to read from it yet, so reuse
// the request directly. The "afterBodyWrite" means the
// bodyWrite process has started, which becomes true before
// the first Read.
if !afterBodyWrite {
return req, nil
}
// If the Body is nil (or http.NoBody), it's safe to reuse
// this request and its Body.
if req.Body == nil || reqBodyIsNoBody(req.Body) {
return req, nil
}
// Otherwise we depend on the Request having its GetBody
// func defined.
getBody := reqGetBody(req) // Go 1.8: getBody = req.GetBody
if getBody == nil {
return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err)
}
body, err := getBody()
if err != nil {
return nil, err
}
newReq := *req
newReq.Body = body
return &newReq, nil
return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err)
}
func canRetryError(err error) bool {
@@ -471,7 +535,7 @@ func (t *Transport) dialClientConn(addr string, singleUse bool) (*ClientConn, er
func (t *Transport) newTLSConfig(host string) *tls.Config {
cfg := new(tls.Config)
if t.TLSClientConfig != nil {
*cfg = *cloneTLSConfig(t.TLSClientConfig)
*cfg = *t.TLSClientConfig.Clone()
}
if !strSliceContains(cfg.NextProtos, NextProtoTLS) {
cfg.NextProtos = append([]string{NextProtoTLS}, cfg.NextProtos...)
@@ -522,7 +586,7 @@ func (t *Transport) expectContinueTimeout() time.Duration {
if t.t1 == nil {
return 0
}
return transportExpectContinueTimeout(t.t1)
return t.t1.ExpectContinueTimeout
}
func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
@@ -567,6 +631,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
@@ -626,12 +694,32 @@ func (cc *ClientConn) CanTakeNewRequest() bool {
return cc.canTakeNewRequestLocked()
}
func (cc *ClientConn) canTakeNewRequestLocked() bool {
// clientConnIdleState describes the suitability of a client
// connection to initiate a new RoundTrip request.
type clientConnIdleState struct {
canTakeNewRequest bool
freshConn bool // whether it's unused by any previous request
}
func (cc *ClientConn) idleState() clientConnIdleState {
cc.mu.Lock()
defer cc.mu.Unlock()
return cc.idleStateLocked()
}
func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) {
if cc.singleUse && cc.nextStreamID > 1 {
return false
return
}
return cc.goAway == nil && !cc.closed &&
st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing &&
int64(cc.nextStreamID)+int64(cc.pendingRequests) < math.MaxInt32
st.freshConn = cc.nextStreamID == 1 && st.canTakeNewRequest
return
}
func (cc *ClientConn) canTakeNewRequestLocked() bool {
st := cc.idleStateLocked()
return st.canTakeNewRequest
}
// onIdleTimeout is called from a time.AfterFunc goroutine. It will
@@ -661,6 +749,87 @@ func (cc *ClientConn) closeIfIdle() {
cc.tconn.Close()
}
var shutdownEnterWaitStateHook = func() {}
// Shutdown gracefully close the client connection, waiting for running streams to complete.
func (cc *ClientConn) Shutdown(ctx context.Context) error {
if err := cc.sendGoAway(); err != nil {
return err
}
// Wait for all in-flight streams to complete or connection to close
done := make(chan error, 1)
cancelled := false // guarded by cc.mu
go func() {
cc.mu.Lock()
defer cc.mu.Unlock()
for {
if len(cc.streams) == 0 || cc.closed {
cc.closed = true
done <- cc.tconn.Close()
break
}
if cancelled {
break
}
cc.cond.Wait()
}
}()
shutdownEnterWaitStateHook()
select {
case err := <-done:
return err
case <-ctx.Done():
cc.mu.Lock()
// Free the goroutine above
cancelled = true
cc.cond.Broadcast()
cc.mu.Unlock()
return ctx.Err()
}
}
func (cc *ClientConn) sendGoAway() error {
cc.mu.Lock()
defer cc.mu.Unlock()
cc.wmu.Lock()
defer cc.wmu.Unlock()
if cc.closing {
// GOAWAY sent already
return nil
}
// Send a graceful shutdown frame to server
maxStreamID := cc.nextStreamID
if err := cc.fr.WriteGoAway(maxStreamID, ErrCodeNo, nil); err != nil {
return err
}
if err := cc.bw.Flush(); err != nil {
return err
}
// Prevent new requests
cc.closing = true
return nil
}
// Close closes the client connection immediately.
//
// In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead.
func (cc *ClientConn) Close() error {
cc.mu.Lock()
defer cc.cond.Broadcast()
defer cc.mu.Unlock()
err := errors.New("http2: client connection force closed via ClientConn.Close")
for id, cs := range cc.streams {
select {
case cs.resc <- resAndError{err: err}:
default:
}
cs.bufPipe.CloseWithError(err)
delete(cc.streams, id)
}
cc.closed = true
return cc.tconn.Close()
}
const maxAllocFrameSize = 512 << 10
// frameBuffer returns a scratch buffer suitable for writing DATA frames.
@@ -743,7 +912,7 @@ func checkConnHeaders(req *http.Request) error {
if vv := req.Header["Transfer-Encoding"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "chunked") {
return fmt.Errorf("http2: invalid Transfer-Encoding request header: %q", vv)
}
if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "close" && vv[0] != "keep-alive") {
if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && !strings.EqualFold(vv[0], "close") && !strings.EqualFold(vv[0], "keep-alive")) {
return fmt.Errorf("http2: invalid Connection request header: %q", vv)
}
return nil
@@ -753,7 +922,7 @@ func checkConnHeaders(req *http.Request) error {
// req.ContentLength, where 0 actually means zero (not unknown) and -1
// means unknown.
func actualContentLength(req *http.Request) int64 {
if req.Body == nil || reqBodyIsNoBody(req.Body) {
if req.Body == nil || req.Body == http.NoBody {
return 0
}
if req.ContentLength != 0 {
@@ -823,7 +992,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
cs := cc.newStream()
cs.req = req
cs.trace = requestTrace(req)
cs.trace = httptrace.ContextClientTrace(req.Context())
cs.requestedGzip = requestedGzip
bodyWriter := cc.t.getBodyWriterState(cs, body)
cs.on100 = bodyWriter.on100
@@ -861,7 +1030,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
readLoopResCh := cs.resc
bodyWritten := false
ctx := reqContext(req)
ctx := req.Context()
handleReadLoopResponse := func(re resAndError) (*http.Response, bool, error) {
res := re.res
@@ -931,6 +1100,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
default:
}
if err != nil {
cc.forgetStreamID(cs.ID)
return nil, cs.getStartedWrite(), err
}
bodyWritten = true
@@ -951,6 +1121,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) {
@@ -1049,6 +1222,7 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
sawEOF = true
err = nil
} else if err != nil {
cc.writeStreamReset(cs.ID, ErrCodeCancel, err)
return err
}
@@ -1174,7 +1348,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
}
@@ -1199,11 +1373,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)
}
}
@@ -1284,9 +1458,16 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
return nil, errRequestHeaderListSize
}
trace := httptrace.ContextClientTrace(req.Context())
traceHeaders := traceHasWroteHeaderField(trace)
// Header list size is ok. Write the headers.
enumerateHeaders(func(name, value string) {
cc.writeHeader(strings.ToLower(name), value)
name = strings.ToLower(name)
cc.writeHeader(name, value)
if traceHeaders {
traceWroteHeaderField(trace, name, value)
}
})
return cc.hbuf.Bytes(), nil
@@ -1608,8 +1789,7 @@ func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
// is the detail.
//
// As a special case, handleResponse may return (nil, nil) to skip the
// frame (currently only used for 100 expect continue). This special
// case is going away after Issue 13851 is fixed.
// frame (currently only used for 1xx responses).
func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFrame) (*http.Response, error) {
if f.Truncated {
return nil, errResponseHeaderListSize
@@ -1624,15 +1804,6 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
return nil, errors.New("malformed response from server: malformed non-numeric status pseudo header")
}
if statusCode == 100 {
traceGot100Continue(cs.trace)
if cs.on100 != nil {
cs.on100() // forces any write delay timer to fire
}
cs.pastHeaders = false // do it all again
return nil, nil
}
header := make(http.Header)
res := &http.Response{
Proto: "HTTP/2.0",
@@ -1657,6 +1828,27 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
}
}
if statusCode >= 100 && statusCode <= 199 {
cs.num1xx++
const max1xxResponses = 5 // arbitrary bound on number of informational responses, same as net/http
if cs.num1xx > max1xxResponses {
return nil, errors.New("http2: too many 1xx informational responses")
}
if fn := cs.get1xxTraceFunc(); fn != nil {
if err := fn(statusCode, textproto.MIMEHeader(header)); err != nil {
return nil, err
}
}
if statusCode == 100 {
traceGot100Continue(cs.trace)
if cs.on100 != nil {
cs.on100() // forces any write delay timer to fire
}
}
cs.pastHeaders = false // do it all again
return nil, nil
}
streamEnded := f.StreamEnded()
isHead := cs.req.Method == "HEAD"
if !streamEnded || isHead {
@@ -1689,7 +1881,7 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
res.Header.Del("Content-Length")
res.ContentLength = -1
res.Body = &gzipReader{body: res.Body}
setResponseUncompressed(res)
res.Uncompressed = true
}
return res, nil
}
@@ -2066,8 +2258,7 @@ func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error {
}
// Ping sends a PING frame to the server and waits for the ack.
// Public implementation is in go17.go and not_go17.go
func (cc *ClientConn) ping(ctx contextContext) error {
func (cc *ClientConn) Ping(ctx context.Context) error {
c := make(chan struct{})
// Generate a random payload
var p [8]byte
@@ -2244,7 +2435,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
@@ -2299,5 +2490,93 @@ 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")
}
// registerHTTPSProtocol calls Transport.RegisterProtocol but
// converting panics into errors.
func registerHTTPSProtocol(t *http.Transport, rt noDialH2RoundTripper) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("%v", e)
}
}()
t.RegisterProtocol("https", rt)
return nil
}
// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
// if there's already has a cached connection to the host.
// (The field is exported so it can be accessed via reflect from net/http; tested
// by TestNoDialH2RoundTripperType)
type noDialH2RoundTripper struct{ *Transport }
func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
res, err := rt.Transport.RoundTrip(req)
if isNoCachedConnError(err) {
return nil, http.ErrSkipAltProtocol
}
return res, err
}
func (t *Transport) idleConnTimeout() time.Duration {
if t.t1 != nil {
return t.t1.IdleConnTimeout
}
return 0
}
func traceGetConn(req *http.Request, hostPort string) {
trace := httptrace.ContextClientTrace(req.Context())
if trace == nil || trace.GetConn == nil {
return
}
trace.GetConn(hostPort)
}
func traceGotConn(req *http.Request, cc *ClientConn) {
trace := httptrace.ContextClientTrace(req.Context())
if trace == nil || trace.GotConn == nil {
return
}
ci := httptrace.GotConnInfo{Conn: cc.tconn}
cc.mu.Lock()
ci.Reused = cc.nextStreamID > 1
ci.WasIdle = len(cc.streams) == 0 && ci.Reused
if ci.WasIdle && !cc.lastActive.IsZero() {
ci.IdleTime = time.Now().Sub(cc.lastActive)
}
cc.mu.Unlock()
trace.GotConn(ci)
}
func traceWroteHeaders(trace *httptrace.ClientTrace) {
if trace != nil && trace.WroteHeaders != nil {
trace.WroteHeaders()
}
}
func traceGot100Continue(trace *httptrace.ClientTrace) {
if trace != nil && trace.Got100Continue != nil {
trace.Got100Continue()
}
}
func traceWait100Continue(trace *httptrace.ClientTrace) {
if trace != nil && trace.Wait100Continue != nil {
trace.Wait100Continue()
}
}
func traceWroteRequest(trace *httptrace.ClientTrace, err error) {
if trace != nil && trace.WroteRequest != nil {
trace.WroteRequest(httptrace.WroteRequestInfo{Err: err})
}
}
func traceFirstResponseByte(trace *httptrace.ClientTrace) {
if trace != nil && trace.GotFirstResponseByte != nil {
trace.GotFirstResponseByte()
}
}

View File

@@ -7,6 +7,7 @@ package http2
import (
"bufio"
"bytes"
"context"
"crypto/tls"
"errors"
"flag"
@@ -18,6 +19,7 @@ import (
"net"
"net/http"
"net/http/httptest"
"net/textproto"
"net/url"
"os"
"reflect"
@@ -41,12 +43,13 @@ var (
var tlsConfigInsecure = &tls.Config{InsecureSkipVerify: true}
type testContext struct{}
var canceledCtx context.Context
func (testContext) Done() <-chan struct{} { return make(chan struct{}) }
func (testContext) Err() error { panic("should not be called") }
func (testContext) Deadline() (deadline time.Time, ok bool) { return time.Time{}, false }
func (testContext) Value(key interface{}) interface{} { return nil }
func init() {
ctx, cancel := context.WithCancel(context.Background())
cancel()
canceledCtx = ctx
}
func TestTransportExternal(t *testing.T) {
if !*extNet {
@@ -421,7 +424,7 @@ func TestActualContentLength(t *testing.T) {
},
// http.NoBody means 0, not -1.
3: {
req: &http.Request{Body: go18httpNoBody()},
req: &http.Request{Body: http.NoBody},
want: 0,
},
}
@@ -561,9 +564,6 @@ func TestTransportDialTLS(t *testing.T) {
func TestConfigureTransport(t *testing.T) {
t1 := &http.Transport{}
err := ConfigureTransport(t1)
if err == errTransportVersion {
t.Skip(err)
}
if err != nil {
t.Fatal(err)
}
@@ -1190,6 +1190,77 @@ func testTransportResPattern(t *testing.T, expect100Continue, resHeader headerTy
ct.run()
}
// Issue 26189, Issue 17739: ignore unknown 1xx responses
func TestTransportUnknown1xx(t *testing.T) {
var buf bytes.Buffer
defer func() { got1xxFuncForTests = nil }()
got1xxFuncForTests = func(code int, header textproto.MIMEHeader) error {
fmt.Fprintf(&buf, "code=%d header=%v\n", code, header)
return nil
}
ct := newClientTester(t)
ct.client = func() error {
req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
res, err := ct.tr.RoundTrip(req)
if err != nil {
return fmt.Errorf("RoundTrip: %v", err)
}
defer res.Body.Close()
if res.StatusCode != 204 {
return fmt.Errorf("status code = %v; want 204", res.StatusCode)
}
want := `code=110 header=map[Foo-Bar:[110]]
code=111 header=map[Foo-Bar:[111]]
code=112 header=map[Foo-Bar:[112]]
code=113 header=map[Foo-Bar:[113]]
code=114 header=map[Foo-Bar:[114]]
`
if got := buf.String(); got != want {
t.Errorf("Got trace:\n%s\nWant:\n%s", got, want)
}
return nil
}
ct.server = func() error {
ct.greet()
var buf bytes.Buffer
enc := hpack.NewEncoder(&buf)
for {
f, err := ct.fr.ReadFrame()
if err != nil {
return err
}
switch f := f.(type) {
case *WindowUpdateFrame, *SettingsFrame:
case *HeadersFrame:
for i := 110; i <= 114; i++ {
buf.Reset()
enc.WriteField(hpack.HeaderField{Name: ":status", Value: fmt.Sprint(i)})
enc.WriteField(hpack.HeaderField{Name: "foo-bar", Value: fmt.Sprint(i)})
ct.fr.WriteHeaders(HeadersFrameParam{
StreamID: f.StreamID,
EndHeaders: true,
EndStream: false,
BlockFragment: buf.Bytes(),
})
}
buf.Reset()
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "204"})
ct.fr.WriteHeaders(HeadersFrameParam{
StreamID: f.StreamID,
EndHeaders: true,
EndStream: false,
BlockFragment: buf.Bytes(),
})
return nil
}
}
}
ct.run()
}
func TestTransportReceiveUndeclaredTrailer(t *testing.T) {
ct := newClientTester(t)
ct.client = func() error {
@@ -2022,6 +2093,11 @@ func TestTransportRejectsConnHeaders(t *testing.T) {
value: []string{"close"},
want: "Accept-Encoding,User-Agent",
},
{
key: "Connection",
value: []string{"CLoSe"},
want: "Accept-Encoding,User-Agent",
},
{
key: "Connection",
value: []string{"close", "something-else"},
@@ -2032,6 +2108,11 @@ func TestTransportRejectsConnHeaders(t *testing.T) {
value: []string{"keep-alive"},
want: "Accept-Encoding,User-Agent",
},
{
key: "Connection",
value: []string{"Keep-ALIVE"},
want: "Accept-Encoding,User-Agent",
},
{
key: "Proxy-Connection", // just deleted and ignored
value: []string{"keep-alive"},
@@ -2394,11 +2475,12 @@ func TestTransportHandlerBodyClose(t *testing.T) {
}
tr.CloseIdleConnections()
gd := runtime.NumGoroutine() - g0
if gd > numReq/2 {
if !waitCondition(5*time.Second, 100*time.Millisecond, func() bool {
gd := runtime.NumGoroutine() - g0
return gd < numReq/2
}) {
t.Errorf("appeared to leak goroutines")
}
}
// https://golang.org/issue/15930
@@ -3043,7 +3125,7 @@ func TestClientConnPing(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if err = cc.Ping(testContext{}); err != nil {
if err = cc.Ping(context.Background()); err != nil {
t.Fatal(err)
}
}
@@ -3699,7 +3781,7 @@ func TestTransportNoBodyMeansNoDATA(t *testing.T) {
unblockClient := make(chan bool)
ct.client = func() error {
req, _ := http.NewRequest("GET", "https://dummy.tld/", go18httpNoBody())
req, _ := http.NewRequest("GET", "https://dummy.tld/", http.NoBody)
ct.tr.RoundTrip(req)
<-unblockClient
return nil
@@ -3845,3 +3927,352 @@ func BenchmarkClientRequestHeaders(b *testing.B) {
b.Run(" 100 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 100) })
b.Run("1000 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 1000) })
}
func activeStreams(cc *ClientConn) int {
cc.mu.Lock()
defer cc.mu.Unlock()
return len(cc.streams)
}
type closeMode int
const (
closeAtHeaders closeMode = iota
closeAtBody
shutdown
shutdownCancel
)
// See golang.org/issue/17292
func testClientConnClose(t *testing.T, closeMode closeMode) {
clientDone := make(chan struct{})
defer close(clientDone)
handlerDone := make(chan struct{})
closeDone := make(chan struct{})
beforeHeader := func() {}
bodyWrite := func(w http.ResponseWriter) {}
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
defer close(handlerDone)
beforeHeader()
w.WriteHeader(http.StatusOK)
w.(http.Flusher).Flush()
bodyWrite(w)
select {
case <-w.(http.CloseNotifier).CloseNotify():
// client closed connection before completion
if closeMode == shutdown || closeMode == shutdownCancel {
t.Error("expected request to complete")
}
case <-clientDone:
if closeMode == closeAtHeaders || closeMode == closeAtBody {
t.Error("expected connection closed by client")
}
}
}, optOnlyServer)
defer st.Close()
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
defer tr.CloseIdleConnections()
cc, err := tr.dialClientConn(st.ts.Listener.Addr().String(), false)
req, err := http.NewRequest("GET", st.ts.URL, nil)
if err != nil {
t.Fatal(err)
}
if closeMode == closeAtHeaders {
beforeHeader = func() {
if err := cc.Close(); err != nil {
t.Error(err)
}
close(closeDone)
}
}
var sendBody chan struct{}
if closeMode == closeAtBody {
sendBody = make(chan struct{})
bodyWrite = func(w http.ResponseWriter) {
<-sendBody
b := make([]byte, 32)
w.Write(b)
w.(http.Flusher).Flush()
if err := cc.Close(); err != nil {
t.Errorf("unexpected ClientConn close error: %v", err)
}
close(closeDone)
w.Write(b)
w.(http.Flusher).Flush()
}
}
res, err := cc.RoundTrip(req)
if res != nil {
defer res.Body.Close()
}
if closeMode == closeAtHeaders {
got := fmt.Sprint(err)
want := "http2: client connection force closed via ClientConn.Close"
if got != want {
t.Fatalf("RoundTrip error = %v, want %v", got, want)
}
} else {
if err != nil {
t.Fatalf("RoundTrip: %v", err)
}
if got, want := activeStreams(cc), 1; got != want {
t.Errorf("got %d active streams, want %d", got, want)
}
}
switch closeMode {
case shutdownCancel:
if err = cc.Shutdown(canceledCtx); err != context.Canceled {
t.Errorf("got %v, want %v", err, context.Canceled)
}
if cc.closing == false {
t.Error("expected closing to be true")
}
if cc.CanTakeNewRequest() == true {
t.Error("CanTakeNewRequest to return false")
}
if v, want := len(cc.streams), 1; v != want {
t.Errorf("expected %d active streams, got %d", want, v)
}
clientDone <- struct{}{}
<-handlerDone
case shutdown:
wait := make(chan struct{})
shutdownEnterWaitStateHook = func() {
close(wait)
shutdownEnterWaitStateHook = func() {}
}
defer func() { shutdownEnterWaitStateHook = func() {} }()
shutdown := make(chan struct{}, 1)
go func() {
if err = cc.Shutdown(context.Background()); err != nil {
t.Error(err)
}
close(shutdown)
}()
// Let the shutdown to enter wait state
<-wait
cc.mu.Lock()
if cc.closing == false {
t.Error("expected closing to be true")
}
cc.mu.Unlock()
if cc.CanTakeNewRequest() == true {
t.Error("CanTakeNewRequest to return false")
}
if got, want := activeStreams(cc), 1; got != want {
t.Errorf("got %d active streams, want %d", got, want)
}
// Let the active request finish
clientDone <- struct{}{}
// Wait for the shutdown to end
select {
case <-shutdown:
case <-time.After(2 * time.Second):
t.Fatal("expected server connection to close")
}
case closeAtHeaders, closeAtBody:
if closeMode == closeAtBody {
go close(sendBody)
if _, err := io.Copy(ioutil.Discard, res.Body); err == nil {
t.Error("expected a Copy error, got nil")
}
}
<-closeDone
if got, want := activeStreams(cc), 0; got != want {
t.Errorf("got %d active streams, want %d", got, want)
}
// wait for server to get the connection close notice
select {
case <-handlerDone:
case <-time.After(2 * time.Second):
t.Fatal("expected server connection to close")
}
}
}
// The client closes the connection just after the server got the client's HEADERS
// frame, but before the server sends its HEADERS response back. The expected
// result is an error on RoundTrip explaining the client closed the connection.
func TestClientConnCloseAtHeaders(t *testing.T) {
testClientConnClose(t, closeAtHeaders)
}
// The client closes the connection between two server's response DATA frames.
// The expected behavior is a response body io read error on the client.
func TestClientConnCloseAtBody(t *testing.T) {
testClientConnClose(t, closeAtBody)
}
// The client sends a GOAWAY frame before the server finished processing a request.
// We expect the connection not to close until the request is completed.
func TestClientConnShutdown(t *testing.T) {
testClientConnClose(t, shutdown)
}
// The client sends a GOAWAY frame before the server finishes processing a request,
// but cancels the passed context before the request is completed. The expected
// behavior is the client closing the connection after the context is canceled.
func TestClientConnShutdownCancel(t *testing.T) {
testClientConnClose(t, shutdownCancel)
}
// Issue 25009: use Request.GetBody if present, even if it seems like
// we might not need it. Apparently something else can still read from
// the original request body. Data race? In any case, rewinding
// unconditionally on retry is a nicer model anyway and should
// simplify code in the future (after the Go 1.11 freeze)
func TestTransportUsesGetBodyWhenPresent(t *testing.T) {
calls := 0
someBody := func() io.ReadCloser {
return struct{ io.ReadCloser }{ioutil.NopCloser(bytes.NewReader(nil))}
}
req := &http.Request{
Body: someBody(),
GetBody: func() (io.ReadCloser, error) {
calls++
return someBody(), nil
},
}
afterBodyWrite := false // pretend we haven't read+written the body yet
req2, err := shouldRetryRequest(req, errClientConnUnusable, afterBodyWrite)
if err != nil {
t.Fatal(err)
}
if calls != 1 {
t.Errorf("Calls = %d; want 1", calls)
}
if req2 == req {
t.Error("req2 changed")
}
if req2 == nil {
t.Fatal("req2 is nil")
}
if req2.Body == nil {
t.Fatal("req2.Body is nil")
}
if req2.GetBody == nil {
t.Fatal("req2.GetBody is nil")
}
if req2.Body == req.Body {
t.Error("req2.Body unchanged")
}
}
// Issue 22891: verify that the "https" altproto we register with net/http
// is a certain type: a struct with one field with our *http2.Transport in it.
func TestNoDialH2RoundTripperType(t *testing.T) {
t1 := new(http.Transport)
t2 := new(Transport)
rt := noDialH2RoundTripper{t2}
if err := registerHTTPSProtocol(t1, rt); err != nil {
t.Fatal(err)
}
rv := reflect.ValueOf(rt)
if rv.Type().Kind() != reflect.Struct {
t.Fatalf("kind = %v; net/http expects struct", rv.Type().Kind())
}
if n := rv.Type().NumField(); n != 1 {
t.Fatalf("fields = %d; net/http expects 1", n)
}
v := rv.Field(0)
if _, ok := v.Interface().(*Transport); !ok {
t.Fatalf("wrong kind %T; want *Transport", v.Interface())
}
}
type errReader struct {
body []byte
err error
}
func (r *errReader) Read(p []byte) (int, error) {
if len(r.body) > 0 {
n := copy(p, r.body)
r.body = r.body[n:]
return n, nil
}
return 0, r.err
}
func testTransportBodyReadError(t *testing.T, body []byte) {
clientDone := make(chan struct{})
ct := newClientTester(t)
ct.client = func() error {
defer ct.cc.(*net.TCPConn).CloseWrite()
defer close(clientDone)
checkNoStreams := func() error {
cp, ok := ct.tr.connPool().(*clientConnPool)
if !ok {
return fmt.Errorf("conn pool is %T; want *clientConnPool", ct.tr.connPool())
}
cp.mu.Lock()
defer cp.mu.Unlock()
conns, ok := cp.conns["dummy.tld:443"]
if !ok {
return fmt.Errorf("missing connection")
}
if len(conns) != 1 {
return fmt.Errorf("conn pool size: %v; expect 1", len(conns))
}
if activeStreams(conns[0]) != 0 {
return fmt.Errorf("active streams count: %v; want 0", activeStreams(conns[0]))
}
return nil
}
bodyReadError := errors.New("body read error")
body := &errReader{body, bodyReadError}
req, err := http.NewRequest("PUT", "https://dummy.tld/", body)
if err != nil {
return err
}
_, err = ct.tr.RoundTrip(req)
if err != bodyReadError {
return fmt.Errorf("err = %v; want %v", err, bodyReadError)
}
if err = checkNoStreams(); err != nil {
return err
}
return nil
}
ct.server = func() error {
ct.greet()
var receivedBody []byte
var resetCount int
for {
f, err := ct.fr.ReadFrame()
if err != nil {
select {
case <-clientDone:
// If the client's done, it
// will have reported any
// errors on its side.
if bytes.Compare(receivedBody, body) != 0 {
return fmt.Errorf("body: %v; expected %v", receivedBody, body)
}
if resetCount != 1 {
return fmt.Errorf("stream reset count: %v; expected: 1", resetCount)
}
return nil
default:
return err
}
}
switch f := f.(type) {
case *WindowUpdateFrame, *SettingsFrame:
case *HeadersFrame:
case *DataFrame:
receivedBody = append(receivedBody, f.Data()...)
case *RSTStreamFrame:
resetCount++
default:
return fmt.Errorf("Unexpected client frame %v", f)
}
}
}
ct.run()
}
func TestTransportBodyReadError_Immediately(t *testing.T) { testTransportBodyReadError(t, nil) }
func TestTransportBodyReadError_Some(t *testing.T) { testTransportBodyReadError(t, []byte("123")) }

View File

@@ -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.
@@ -199,7 +199,7 @@ func (w *writeResHeaders) staysWithinBuffer(max int) bool {
// TODO: this is a common one. It'd be nice to return true
// here and get into the fast path if we could be clever and
// calculate the size fast enough, or at least a conservative
// uppper bound that usually fires. (Maybe if w.h and
// upper bound that usually fires. (Maybe if w.h and
// w.trailers are nil, so we don't need to enumerate it.)
// Otherwise I'm afraid that just calculating the length to
// answer this question would be slower than the ~2µs benefit.
@@ -329,7 +329,7 @@ func (wu writeWindowUpdate) writeFrame(ctx writeContext) error {
}
// encodeHeaders encodes an http.Header. If keys is not nil, then (k, h[k])
// is encoded only only if k is in keys.
// is encoded only if k is in keys.
func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
if keys == nil {
sorter := sorterPool.Get().(*sorter)
@@ -350,7 +350,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

274
vendor/golang.org/x/net/icmp/diag_test.go generated vendored Normal file
View File

@@ -0,0 +1,274 @@
// 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 icmp_test
import (
"errors"
"fmt"
"net"
"os"
"runtime"
"sync"
"testing"
"time"
"golang.org/x/net/icmp"
"golang.org/x/net/internal/iana"
"golang.org/x/net/internal/nettest"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
type diagTest struct {
network, address string
protocol int
m icmp.Message
}
func TestDiag(t *testing.T) {
if testing.Short() {
t.Skip("avoid external network")
}
t.Run("Ping/NonPrivileged", func(t *testing.T) {
switch runtime.GOOS {
case "darwin":
case "linux":
t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
default:
t.Logf("not supported on %s", runtime.GOOS)
return
}
for i, dt := range []diagTest{
{
"udp4", "0.0.0.0", iana.ProtocolICMP,
icmp.Message{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff,
Data: []byte("HELLO-R-U-THERE"),
},
},
},
{
"udp6", "::", iana.ProtocolIPv6ICMP,
icmp.Message{
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff,
Data: []byte("HELLO-R-U-THERE"),
},
},
},
} {
if err := doDiag(dt, i); err != nil {
t.Error(err)
}
}
})
t.Run("Ping/Privileged", func(t *testing.T) {
if m, ok := nettest.SupportsRawIPSocket(); !ok {
t.Skip(m)
}
for i, dt := range []diagTest{
{
"ip4:icmp", "0.0.0.0", iana.ProtocolICMP,
icmp.Message{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff,
Data: []byte("HELLO-R-U-THERE"),
},
},
},
{
"ip6:ipv6-icmp", "::", iana.ProtocolIPv6ICMP,
icmp.Message{
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff,
Data: []byte("HELLO-R-U-THERE"),
},
},
},
} {
if err := doDiag(dt, i); err != nil {
t.Error(err)
}
}
})
t.Run("Probe/Privileged", func(t *testing.T) {
if m, ok := nettest.SupportsRawIPSocket(); !ok {
t.Skip(m)
}
for i, dt := range []diagTest{
{
"ip4:icmp", "0.0.0.0", iana.ProtocolICMP,
icmp.Message{
Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: os.Getpid() & 0xffff,
Local: true,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3, Type: 1,
Name: "doesnotexist",
},
},
},
},
},
{
"ip6:ipv6-icmp", "::", iana.ProtocolIPv6ICMP,
icmp.Message{
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: os.Getpid() & 0xffff,
Local: true,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3, Type: 1,
Name: "doesnotexist",
},
},
},
},
},
} {
if err := doDiag(dt, i); err != nil {
t.Error(err)
}
}
})
}
func doDiag(dt diagTest, seq int) error {
c, err := icmp.ListenPacket(dt.network, dt.address)
if err != nil {
return err
}
defer c.Close()
dst, err := googleAddr(c, dt.protocol)
if err != nil {
return err
}
if dt.network != "udp6" && dt.protocol == iana.ProtocolIPv6ICMP {
var f ipv6.ICMPFilter
f.SetAll(true)
f.Accept(ipv6.ICMPTypeDestinationUnreachable)
f.Accept(ipv6.ICMPTypePacketTooBig)
f.Accept(ipv6.ICMPTypeTimeExceeded)
f.Accept(ipv6.ICMPTypeParameterProblem)
f.Accept(ipv6.ICMPTypeEchoReply)
f.Accept(ipv6.ICMPTypeExtendedEchoReply)
if err := c.IPv6PacketConn().SetICMPFilter(&f); err != nil {
return err
}
}
switch m := dt.m.Body.(type) {
case *icmp.Echo:
m.Seq = 1 << uint(seq)
case *icmp.ExtendedEchoRequest:
m.Seq = 1 << uint(seq)
}
wb, err := dt.m.Marshal(nil)
if err != nil {
return err
}
if n, err := c.WriteTo(wb, dst); err != nil {
return err
} else if n != len(wb) {
return fmt.Errorf("got %v; want %v", n, len(wb))
}
rb := make([]byte, 1500)
if err := c.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
return err
}
n, peer, err := c.ReadFrom(rb)
if err != nil {
return err
}
rm, err := icmp.ParseMessage(dt.protocol, rb[:n])
if err != nil {
return err
}
switch {
case dt.m.Type == ipv4.ICMPTypeEcho && rm.Type == ipv4.ICMPTypeEchoReply:
fallthrough
case dt.m.Type == ipv6.ICMPTypeEchoRequest && rm.Type == ipv6.ICMPTypeEchoReply:
fallthrough
case dt.m.Type == ipv4.ICMPTypeExtendedEchoRequest && rm.Type == ipv4.ICMPTypeExtendedEchoReply:
fallthrough
case dt.m.Type == ipv6.ICMPTypeExtendedEchoRequest && rm.Type == ipv6.ICMPTypeExtendedEchoReply:
return nil
default:
return fmt.Errorf("got %+v from %v; want echo reply or extended echo reply", rm, peer)
}
}
func googleAddr(c *icmp.PacketConn, protocol int) (net.Addr, error) {
host := "ipv4.google.com"
if protocol == iana.ProtocolIPv6ICMP {
host = "ipv6.google.com"
}
ips, err := net.LookupIP(host)
if err != nil {
return nil, err
}
netaddr := func(ip net.IP) (net.Addr, error) {
switch c.LocalAddr().(type) {
case *net.UDPAddr:
return &net.UDPAddr{IP: ip}, nil
case *net.IPAddr:
return &net.IPAddr{IP: ip}, nil
default:
return nil, errors.New("neither UDPAddr nor IPAddr")
}
}
if len(ips) > 0 {
return netaddr(ips[0])
}
return nil, errors.New("no A or AAAA record")
}
func TestConcurrentNonPrivilegedListenPacket(t *testing.T) {
if testing.Short() {
t.Skip("avoid external network")
}
switch runtime.GOOS {
case "darwin":
case "linux":
t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
default:
t.Skipf("not supported on %s", runtime.GOOS)
}
network, address := "udp4", "127.0.0.1"
if !nettest.SupportsIPv4() {
network, address = "udp6", "::1"
}
const N = 1000
var wg sync.WaitGroup
wg.Add(N)
for i := 0; i < N; i++ {
go func() {
defer wg.Done()
c, err := icmp.ListenPacket(network, address)
if err != nil {
t.Error(err)
return
}
c.Close()
}()
}
wg.Wait()
}

View File

@@ -16,24 +16,24 @@ func (p *DstUnreach) Len(proto int) int {
if p == nil {
return 0
}
l, _ := multipartMessageBodyDataLen(proto, p.Data, p.Extensions)
l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions)
return 4 + l
}
// Marshal implements the Marshal method of MessageBody interface.
func (p *DstUnreach) Marshal(proto int) ([]byte, error) {
return marshalMultipartMessageBody(proto, p.Data, p.Extensions)
return marshalMultipartMessageBody(proto, true, p.Data, p.Extensions)
}
// parseDstUnreach parses b as an ICMP destination unreachable message
// body.
func parseDstUnreach(proto int, b []byte) (MessageBody, error) {
func parseDstUnreach(proto int, typ Type, b []byte) (MessageBody, error) {
if len(b) < 4 {
return nil, errMessageTooShort
}
p := &DstUnreach{}
var err error
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, b)
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b)
if err != nil {
return nil, err
}

114
vendor/golang.org/x/net/icmp/echo.go generated vendored
View File

@@ -31,7 +31,7 @@ func (p *Echo) Marshal(proto int) ([]byte, error) {
}
// parseEcho parses b as an ICMP echo request or reply message body.
func parseEcho(proto int, b []byte) (MessageBody, error) {
func parseEcho(proto int, _ Type, b []byte) (MessageBody, error) {
bodyLen := len(b)
if bodyLen < 4 {
return nil, errMessageTooShort
@@ -43,3 +43,115 @@ func parseEcho(proto int, b []byte) (MessageBody, error) {
}
return p, nil
}
// An ExtendedEchoRequest represents an ICMP extended echo request
// message body.
type ExtendedEchoRequest struct {
ID int // identifier
Seq int // sequence number
Local bool // must be true when identifying by name or index
Extensions []Extension // extensions
}
// Len implements the Len method of MessageBody interface.
func (p *ExtendedEchoRequest) Len(proto int) int {
if p == nil {
return 0
}
l, _ := multipartMessageBodyDataLen(proto, false, nil, p.Extensions)
return 4 + l
}
// Marshal implements the Marshal method of MessageBody interface.
func (p *ExtendedEchoRequest) Marshal(proto int) ([]byte, error) {
b, err := marshalMultipartMessageBody(proto, false, nil, p.Extensions)
if err != nil {
return nil, err
}
bb := make([]byte, 4)
binary.BigEndian.PutUint16(bb[:2], uint16(p.ID))
bb[2] = byte(p.Seq)
if p.Local {
bb[3] |= 0x01
}
bb = append(bb, b...)
return bb, nil
}
// parseExtendedEchoRequest parses b as an ICMP extended echo request
// message body.
func parseExtendedEchoRequest(proto int, typ Type, b []byte) (MessageBody, error) {
if len(b) < 4+4 {
return nil, errMessageTooShort
}
p := &ExtendedEchoRequest{ID: int(binary.BigEndian.Uint16(b[:2])), Seq: int(b[2])}
if b[3]&0x01 != 0 {
p.Local = true
}
var err error
_, p.Extensions, err = parseMultipartMessageBody(proto, typ, b[4:])
if err != nil {
return nil, err
}
return p, nil
}
// An ExtendedEchoReply represents an ICMP extended echo reply message
// body.
type ExtendedEchoReply struct {
ID int // identifier
Seq int // sequence number
State int // 3-bit state working together with Message.Code
Active bool // probed interface is active
IPv4 bool // probed interface runs IPv4
IPv6 bool // probed interface runs IPv6
}
// Len implements the Len method of MessageBody interface.
func (p *ExtendedEchoReply) Len(proto int) int {
if p == nil {
return 0
}
return 4
}
// Marshal implements the Marshal method of MessageBody interface.
func (p *ExtendedEchoReply) Marshal(proto int) ([]byte, error) {
b := make([]byte, 4)
binary.BigEndian.PutUint16(b[:2], uint16(p.ID))
b[2] = byte(p.Seq)
b[3] = byte(p.State<<5) & 0xe0
if p.Active {
b[3] |= 0x04
}
if p.IPv4 {
b[3] |= 0x02
}
if p.IPv6 {
b[3] |= 0x01
}
return b, nil
}
// parseExtendedEchoReply parses b as an ICMP extended echo reply
// message body.
func parseExtendedEchoReply(proto int, _ Type, b []byte) (MessageBody, error) {
if len(b) < 4 {
return nil, errMessageTooShort
}
p := &ExtendedEchoReply{
ID: int(binary.BigEndian.Uint16(b[:2])),
Seq: int(b[2]),
State: int(b[3]) >> 5,
}
if b[3]&0x04 != 0 {
p.Active = true
}
if b[3]&0x02 != 0 {
p.IPv4 = true
}
if b[3]&0x01 != 0 {
p.IPv6 = true
}
return p, nil
}

View File

@@ -7,7 +7,6 @@ package icmp
import (
"net"
"runtime"
"syscall"
"time"
"golang.org/x/net/ipv4"
@@ -47,7 +46,7 @@ func (c *PacketConn) IPv6PacketConn() *ipv6.PacketConn {
// ReadFrom reads an ICMP message from the connection.
func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
if !c.ok() {
return 0, nil, syscall.EINVAL
return 0, nil, errInvalidConn
}
// Please be informed that ipv4.NewPacketConn enables
// IP_STRIPHDR option by default on Darwin.
@@ -60,11 +59,12 @@ func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
}
// WriteTo writes the ICMP message b to dst.
// Dst must be net.UDPAddr when c is a non-privileged
// datagram-oriented ICMP endpoint. Otherwise it must be net.IPAddr.
// The provided dst must be net.UDPAddr when c is a non-privileged
// datagram-oriented ICMP endpoint.
// Otherwise it must be net.IPAddr.
func (c *PacketConn) WriteTo(b []byte, dst net.Addr) (int, error) {
if !c.ok() {
return 0, syscall.EINVAL
return 0, errInvalidConn
}
return c.c.WriteTo(b, dst)
}
@@ -72,7 +72,7 @@ func (c *PacketConn) WriteTo(b []byte, dst net.Addr) (int, error) {
// Close closes the endpoint.
func (c *PacketConn) Close() error {
if !c.ok() {
return syscall.EINVAL
return errInvalidConn
}
return c.c.Close()
}
@@ -89,7 +89,7 @@ func (c *PacketConn) LocalAddr() net.Addr {
// endpoint.
func (c *PacketConn) SetDeadline(t time.Time) error {
if !c.ok() {
return syscall.EINVAL
return errInvalidConn
}
return c.c.SetDeadline(t)
}
@@ -98,7 +98,7 @@ func (c *PacketConn) SetDeadline(t time.Time) error {
// endpoint.
func (c *PacketConn) SetReadDeadline(t time.Time) error {
if !c.ok() {
return syscall.EINVAL
return errInvalidConn
}
return c.c.SetReadDeadline(t)
}
@@ -107,7 +107,7 @@ func (c *PacketConn) SetReadDeadline(t time.Time) error {
// endpoint.
func (c *PacketConn) SetWriteDeadline(t time.Time) error {
if !c.ok() {
return syscall.EINVAL
return errInvalidConn
}
return c.c.SetWriteDeadline(t)
}

View File

@@ -4,16 +4,23 @@
package icmp
import "encoding/binary"
import (
"encoding/binary"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
// An Extension represents an ICMP extension.
type Extension interface {
// Len returns the length of ICMP extension.
// Proto must be either the ICMPv4 or ICMPv6 protocol number.
// The provided proto must be either the ICMPv4 or ICMPv6
// protocol number.
Len(proto int) int
// Marshal returns the binary encoding of ICMP extension.
// Proto must be either the ICMPv4 or ICMPv6 protocol number.
// The provided proto must be either the ICMPv4 or ICMPv6
// protocol number.
Marshal(proto int) ([]byte, error)
}
@@ -38,7 +45,7 @@ func validExtensionHeader(b []byte) bool {
// It will return a list of ICMP extensions and an adjusted length
// attribute that represents the length of the padded original
// datagram field. Otherwise, it returns an error.
func parseExtensions(b []byte, l int) ([]Extension, int, error) {
func parseExtensions(typ Type, b []byte, l int) ([]Extension, int, error) {
// Still a lot of non-RFC 4884 compliant implementations are
// out there. Set the length attribute l to 128 when it looks
// inappropriate for backwards compatibility.
@@ -48,20 +55,28 @@ func parseExtensions(b []byte, l int) ([]Extension, int, error) {
// header.
//
// See RFC 4884 for further information.
if 128 > l || l+8 > len(b) {
l = 128
}
if l+8 > len(b) {
return nil, -1, errNoExtension
}
if !validExtensionHeader(b[l:]) {
if l == 128 {
switch typ {
case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
if len(b) < 8 || !validExtensionHeader(b) {
return nil, -1, errNoExtension
}
l = 128
if !validExtensionHeader(b[l:]) {
l = 0
default:
if 128 > l || l+8 > len(b) {
l = 128
}
if l+8 > len(b) {
return nil, -1, errNoExtension
}
if !validExtensionHeader(b[l:]) {
if l == 128 {
return nil, -1, errNoExtension
}
l = 128
if !validExtensionHeader(b[l:]) {
return nil, -1, errNoExtension
}
}
}
var exts []Extension
for b = b[l+4:]; len(b) >= 4; {
@@ -82,6 +97,12 @@ func parseExtensions(b []byte, l int) ([]Extension, int, error) {
return nil, -1, err
}
exts = append(exts, ext)
case classInterfaceIdent:
ext, err := parseInterfaceIdent(b[:ol])
if err != nil {
return nil, -1, err
}
exts = append(exts, ext)
}
b = b[ol:]
}

View File

@@ -5,253 +5,326 @@
package icmp
import (
"fmt"
"net"
"reflect"
"testing"
"golang.org/x/net/internal/iana"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
var marshalAndParseExtensionTests = []struct {
proto int
hdr []byte
obj []byte
exts []Extension
}{
// MPLS label stack with no label
{
proto: iana.ProtocolICMP,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x04, 0x01, 0x01,
},
exts: []Extension{
&MPLSLabelStack{
Class: classMPLSLabelStack,
Type: typeIncomingMPLSLabelStack,
},
},
},
// MPLS label stack with a single label
{
proto: iana.ProtocolIPv6ICMP,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x08, 0x01, 0x01,
0x03, 0xe8, 0xe9, 0xff,
},
exts: []Extension{
&MPLSLabelStack{
Class: classMPLSLabelStack,
Type: typeIncomingMPLSLabelStack,
Labels: []MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
},
},
// MPLS label stack with multiple labels
{
proto: iana.ProtocolICMP,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x0c, 0x01, 0x01,
0x03, 0xe8, 0xde, 0xfe,
0x03, 0xe8, 0xe1, 0xff,
},
exts: []Extension{
&MPLSLabelStack{
Class: classMPLSLabelStack,
Type: typeIncomingMPLSLabelStack,
Labels: []MPLSLabel{
{
Label: 16013,
TC: 0x7,
S: false,
TTL: 254,
},
{
Label: 16014,
TC: 0,
S: true,
TTL: 255,
},
},
},
},
},
// Interface information with no attribute
{
proto: iana.ProtocolICMP,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x04, 0x02, 0x00,
},
exts: []Extension{
&InterfaceInfo{
Class: classInterfaceInfo,
},
},
},
// Interface information with ifIndex and name
{
proto: iana.ProtocolICMP,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x10, 0x02, 0x0a,
0x00, 0x00, 0x00, 0x10,
0x08, byte('e'), byte('n'), byte('1'),
byte('0'), byte('1'), 0x00, 0x00,
},
exts: []Extension{
&InterfaceInfo{
Class: classInterfaceInfo,
Type: 0x0a,
Interface: &net.Interface{
Index: 16,
Name: "en101",
},
},
},
},
// Interface information with ifIndex, IPAddr, name and MTU
{
proto: iana.ProtocolIPv6ICMP,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x28, 0x02, 0x0f,
0x00, 0x00, 0x00, 0x0f,
0x00, 0x02, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x08, byte('e'), byte('n'), byte('1'),
byte('0'), byte('1'), 0x00, 0x00,
0x00, 0x00, 0x20, 0x00,
},
exts: []Extension{
&InterfaceInfo{
Class: classInterfaceInfo,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en101",
},
},
},
},
}
func TestMarshalAndParseExtension(t *testing.T) {
for i, tt := range marshalAndParseExtensionTests {
for j, ext := range tt.exts {
var err error
var b []byte
switch ext := ext.(type) {
case *MPLSLabelStack:
b, err = ext.Marshal(tt.proto)
if err != nil {
t.Errorf("#%v/%v: %v", i, j, err)
fn := func(t *testing.T, proto int, typ Type, hdr, obj []byte, te Extension) error {
b, err := te.Marshal(proto)
if err != nil {
return err
}
if !reflect.DeepEqual(b, obj) {
return fmt.Errorf("got %#v; want %#v", b, obj)
}
switch typ {
case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
exts, l, err := parseExtensions(typ, append(hdr, obj...), 0)
if err != nil {
return err
}
if l != 0 {
return fmt.Errorf("got %d; want 0", l)
}
if !reflect.DeepEqual(exts, []Extension{te}) {
return fmt.Errorf("got %#v; want %#v", exts[0], te)
}
default:
for i, wire := range []struct {
data []byte // original datagram
inlattr int // length of padded original datagram, a hint
outlattr int // length of padded original datagram, a want
err error
}{
{nil, 0, -1, errNoExtension},
{make([]byte, 127), 128, -1, errNoExtension},
{make([]byte, 128), 127, -1, errNoExtension},
{make([]byte, 128), 128, -1, errNoExtension},
{make([]byte, 128), 129, -1, errNoExtension},
{append(make([]byte, 128), append(hdr, obj...)...), 127, 128, nil},
{append(make([]byte, 128), append(hdr, obj...)...), 128, 128, nil},
{append(make([]byte, 128), append(hdr, obj...)...), 129, 128, nil},
{append(make([]byte, 512), append(hdr, obj...)...), 511, -1, errNoExtension},
{append(make([]byte, 512), append(hdr, obj...)...), 512, 512, nil},
{append(make([]byte, 512), append(hdr, obj...)...), 513, -1, errNoExtension},
} {
exts, l, err := parseExtensions(typ, wire.data, wire.inlattr)
if err != wire.err {
return fmt.Errorf("#%d: got %v; want %v", i, err, wire.err)
}
if wire.err != nil {
continue
}
case *InterfaceInfo:
b, err = ext.Marshal(tt.proto)
if err != nil {
t.Errorf("#%v/%v: %v", i, j, err)
continue
if l != wire.outlattr {
return fmt.Errorf("#%d: got %d; want %d", i, l, wire.outlattr)
}
if !reflect.DeepEqual(exts, []Extension{te}) {
return fmt.Errorf("#%d: got %#v; want %#v", i, exts[0], te)
}
}
if !reflect.DeepEqual(b, tt.obj) {
t.Errorf("#%v/%v: got %#v; want %#v", i, j, b, tt.obj)
continue
}
}
for j, wire := range []struct {
data []byte // original datagram
inlattr int // length of padded original datagram, a hint
outlattr int // length of padded original datagram, a want
err error
}{
{nil, 0, -1, errNoExtension},
{make([]byte, 127), 128, -1, errNoExtension},
{make([]byte, 128), 127, -1, errNoExtension},
{make([]byte, 128), 128, -1, errNoExtension},
{make([]byte, 128), 129, -1, errNoExtension},
{append(make([]byte, 128), append(tt.hdr, tt.obj...)...), 127, 128, nil},
{append(make([]byte, 128), append(tt.hdr, tt.obj...)...), 128, 128, nil},
{append(make([]byte, 128), append(tt.hdr, tt.obj...)...), 129, 128, nil},
{append(make([]byte, 512), append(tt.hdr, tt.obj...)...), 511, -1, errNoExtension},
{append(make([]byte, 512), append(tt.hdr, tt.obj...)...), 512, 512, nil},
{append(make([]byte, 512), append(tt.hdr, tt.obj...)...), 513, -1, errNoExtension},
} {
exts, l, err := parseExtensions(wire.data, wire.inlattr)
if err != wire.err {
t.Errorf("#%v/%v: got %v; want %v", i, j, err, wire.err)
continue
}
if wire.err != nil {
continue
}
if l != wire.outlattr {
t.Errorf("#%v/%v: got %v; want %v", i, j, l, wire.outlattr)
}
if !reflect.DeepEqual(exts, tt.exts) {
for j, ext := range exts {
switch ext := ext.(type) {
case *MPLSLabelStack:
want := tt.exts[j].(*MPLSLabelStack)
t.Errorf("#%v/%v: got %#v; want %#v", i, j, ext, want)
case *InterfaceInfo:
want := tt.exts[j].(*InterfaceInfo)
t.Errorf("#%v/%v: got %#v; want %#v", i, j, ext, want)
}
}
continue
}
}
return nil
}
}
var parseInterfaceNameTests = []struct {
b []byte
error
}{
{[]byte{0, 'e', 'n', '0'}, errInvalidExtension},
{[]byte{4, 'e', 'n', '0'}, nil},
{[]byte{7, 'e', 'n', '0', 0xff, 0xff, 0xff, 0xff}, errInvalidExtension},
{[]byte{8, 'e', 'n', '0', 0xff, 0xff, 0xff}, errMessageTooShort},
t.Run("MPLSLabelStack", func(t *testing.T) {
for _, et := range []struct {
proto int
typ Type
hdr []byte
obj []byte
ext Extension
}{
// MPLS label stack with no label
{
proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeDestinationUnreachable,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x04, 0x01, 0x01,
},
ext: &MPLSLabelStack{
Class: classMPLSLabelStack,
Type: typeIncomingMPLSLabelStack,
},
},
// MPLS label stack with a single label
{
proto: iana.ProtocolIPv6ICMP,
typ: ipv6.ICMPTypeDestinationUnreachable,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x08, 0x01, 0x01,
0x03, 0xe8, 0xe9, 0xff,
},
ext: &MPLSLabelStack{
Class: classMPLSLabelStack,
Type: typeIncomingMPLSLabelStack,
Labels: []MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
},
// MPLS label stack with multiple labels
{
proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeDestinationUnreachable,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x0c, 0x01, 0x01,
0x03, 0xe8, 0xde, 0xfe,
0x03, 0xe8, 0xe1, 0xff,
},
ext: &MPLSLabelStack{
Class: classMPLSLabelStack,
Type: typeIncomingMPLSLabelStack,
Labels: []MPLSLabel{
{
Label: 16013,
TC: 0x7,
S: false,
TTL: 254,
},
{
Label: 16014,
TC: 0,
S: true,
TTL: 255,
},
},
},
},
} {
if err := fn(t, et.proto, et.typ, et.hdr, et.obj, et.ext); err != nil {
t.Error(err)
}
}
})
t.Run("InterfaceInfo", func(t *testing.T) {
for _, et := range []struct {
proto int
typ Type
hdr []byte
obj []byte
ext Extension
}{
// Interface information with no attribute
{
proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeDestinationUnreachable,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x04, 0x02, 0x00,
},
ext: &InterfaceInfo{
Class: classInterfaceInfo,
},
},
// Interface information with ifIndex and name
{
proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeDestinationUnreachable,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x10, 0x02, 0x0a,
0x00, 0x00, 0x00, 0x10,
0x08, byte('e'), byte('n'), byte('1'),
byte('0'), byte('1'), 0x00, 0x00,
},
ext: &InterfaceInfo{
Class: classInterfaceInfo,
Type: 0x0a,
Interface: &net.Interface{
Index: 16,
Name: "en101",
},
},
},
// Interface information with ifIndex, IPAddr, name and MTU
{
proto: iana.ProtocolIPv6ICMP,
typ: ipv6.ICMPTypeDestinationUnreachable,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x28, 0x02, 0x0f,
0x00, 0x00, 0x00, 0x0f,
0x00, 0x02, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x08, byte('e'), byte('n'), byte('1'),
byte('0'), byte('1'), 0x00, 0x00,
0x00, 0x00, 0x20, 0x00,
},
ext: &InterfaceInfo{
Class: classInterfaceInfo,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en101",
},
},
},
} {
if err := fn(t, et.proto, et.typ, et.hdr, et.obj, et.ext); err != nil {
t.Error(err)
}
}
})
t.Run("InterfaceIdent", func(t *testing.T) {
for _, et := range []struct {
proto int
typ Type
hdr []byte
obj []byte
ext Extension
}{
// Interface identification by name
{
proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeExtendedEchoRequest,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x0c, 0x03, 0x01,
byte('e'), byte('n'), byte('1'), byte('0'),
byte('1'), 0x00, 0x00, 0x00,
},
ext: &InterfaceIdent{
Class: classInterfaceIdent,
Type: typeInterfaceByName,
Name: "en101",
},
},
// Interface identification by index
{
proto: iana.ProtocolIPv6ICMP,
typ: ipv6.ICMPTypeExtendedEchoRequest,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x08, 0x03, 0x02,
0x00, 0x00, 0x03, 0x8f,
},
ext: &InterfaceIdent{
Class: classInterfaceIdent,
Type: typeInterfaceByIndex,
Index: 911,
},
},
// Interface identification by address
{
proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeExtendedEchoRequest,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x10, 0x03, 0x03,
byte(iana.AddrFamily48bitMAC >> 8), byte(iana.AddrFamily48bitMAC & 0x0f), 0x06, 0x00,
0x01, 0x23, 0x45, 0x67,
0x89, 0xab, 0x00, 0x00,
},
ext: &InterfaceIdent{
Class: classInterfaceIdent,
Type: typeInterfaceByAddress,
AFI: iana.AddrFamily48bitMAC,
Addr: []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab},
},
},
} {
if err := fn(t, et.proto, et.typ, et.hdr, et.obj, et.ext); err != nil {
t.Error(err)
}
}
})
}
func TestParseInterfaceName(t *testing.T) {
ifi := InterfaceInfo{Interface: &net.Interface{}}
for i, tt := range parseInterfaceNameTests {
for i, tt := range []struct {
b []byte
error
}{
{[]byte{0, 'e', 'n', '0'}, errInvalidExtension},
{[]byte{4, 'e', 'n', '0'}, nil},
{[]byte{7, 'e', 'n', '0', 0xff, 0xff, 0xff, 0xff}, errInvalidExtension},
{[]byte{8, 'e', 'n', '0', 0xff, 0xff, 0xff}, errMessageTooShort},
} {
if _, err := ifi.parseName(tt.b); err != tt.error {
t.Errorf("#%d: got %v; want %v", i, err, tt.error)
}

View File

@@ -14,9 +14,6 @@ import (
const (
classInterfaceInfo = 2
afiIPv4 = 1
afiIPv6 = 2
)
const (
@@ -127,11 +124,11 @@ func (ifi *InterfaceInfo) parseIfIndex(b []byte) ([]byte, error) {
func (ifi *InterfaceInfo) marshalIPAddr(proto int, b []byte) []byte {
switch proto {
case iana.ProtocolICMP:
binary.BigEndian.PutUint16(b[:2], uint16(afiIPv4))
binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv4))
copy(b[4:4+net.IPv4len], ifi.Addr.IP.To4())
b = b[4+net.IPv4len:]
case iana.ProtocolIPv6ICMP:
binary.BigEndian.PutUint16(b[:2], uint16(afiIPv6))
binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv6))
copy(b[4:4+net.IPv6len], ifi.Addr.IP.To16())
b = b[4+net.IPv6len:]
}
@@ -145,14 +142,14 @@ func (ifi *InterfaceInfo) parseIPAddr(b []byte) ([]byte, error) {
afi := int(binary.BigEndian.Uint16(b[:2]))
b = b[4:]
switch afi {
case afiIPv4:
case iana.AddrFamilyIPv4:
if len(b) < net.IPv4len {
return nil, errMessageTooShort
}
ifi.Addr.IP = make(net.IP, net.IPv4len)
copy(ifi.Addr.IP, b[:net.IPv4len])
b = b[net.IPv4len:]
case afiIPv6:
case iana.AddrFamilyIPv6:
if len(b) < net.IPv6len {
return nil, errMessageTooShort
}
@@ -234,3 +231,92 @@ func parseInterfaceInfo(b []byte) (Extension, error) {
}
return ifi, nil
}
const (
classInterfaceIdent = 3
typeInterfaceByName = 1
typeInterfaceByIndex = 2
typeInterfaceByAddress = 3
)
// An InterfaceIdent represents interface identification.
type InterfaceIdent struct {
Class int // extension object class number
Type int // extension object sub-type
Name string // interface name
Index int // interface index
AFI int // address family identifier; see address family numbers in IANA registry
Addr []byte // address
}
// Len implements the Len method of Extension interface.
func (ifi *InterfaceIdent) Len(_ int) int {
switch ifi.Type {
case typeInterfaceByName:
l := len(ifi.Name)
if l > 255 {
l = 255
}
return 4 + (l+3)&^3
case typeInterfaceByIndex:
return 4 + 4
case typeInterfaceByAddress:
return 4 + 4 + (len(ifi.Addr)+3)&^3
default:
return 4
}
}
// Marshal implements the Marshal method of Extension interface.
func (ifi *InterfaceIdent) Marshal(proto int) ([]byte, error) {
b := make([]byte, ifi.Len(proto))
if err := ifi.marshal(proto, b); err != nil {
return nil, err
}
return b, nil
}
func (ifi *InterfaceIdent) marshal(proto int, b []byte) error {
l := ifi.Len(proto)
binary.BigEndian.PutUint16(b[:2], uint16(l))
b[2], b[3] = classInterfaceIdent, byte(ifi.Type)
switch ifi.Type {
case typeInterfaceByName:
copy(b[4:], ifi.Name)
case typeInterfaceByIndex:
binary.BigEndian.PutUint32(b[4:4+4], uint32(ifi.Index))
case typeInterfaceByAddress:
binary.BigEndian.PutUint16(b[4:4+2], uint16(ifi.AFI))
b[4+2] = byte(len(ifi.Addr))
copy(b[4+4:], ifi.Addr)
}
return nil
}
func parseInterfaceIdent(b []byte) (Extension, error) {
ifi := &InterfaceIdent{
Class: int(b[2]),
Type: int(b[3]),
}
switch ifi.Type {
case typeInterfaceByName:
ifi.Name = strings.Trim(string(b[4:]), string(0))
case typeInterfaceByIndex:
if len(b[4:]) < 4 {
return nil, errInvalidExtension
}
ifi.Index = int(binary.BigEndian.Uint32(b[4 : 4+4]))
case typeInterfaceByAddress:
if len(b[4:]) < 4 {
return nil, errInvalidExtension
}
ifi.AFI = int(binary.BigEndian.Uint16(b[4 : 4+2]))
l := int(b[4+2])
if len(b[4+4:]) < l {
return nil, errInvalidExtension
}
ifi.Addr = make([]byte, l)
copy(ifi.Addr, b[4+4:])
}
return ifi, nil
}

12
vendor/golang.org/x/net/icmp/ipv4.go generated vendored
View File

@@ -17,8 +17,16 @@ import (
// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
var freebsdVersion uint32
// ParseIPv4Header parses b as an IPv4 header of ICMP error message
// invoking packet, which is contained in ICMP error message.
// ParseIPv4Header returns the IPv4 header of the IPv4 packet that
// triggered an ICMP error message.
// This is found in the Data field of the ICMP error message body.
//
// The provided b must be in the format used by a raw ICMP socket on
// the local system.
// This may differ from the wire format, and the format used by a raw
// IP socket, depending on the system.
//
// To parse an IPv6 header, use ipv6.ParseHeader.
func ParseIPv4Header(b []byte) (*ipv4.Header, error) {
if len(b) < ipv4.HeaderLen {
return nil, errHeaderTooShort

View File

@@ -15,69 +15,61 @@ import (
"golang.org/x/net/ipv4"
)
type ipv4HeaderTest struct {
wireHeaderFromKernel [ipv4.HeaderLen]byte
wireHeaderFromTradBSDKernel [ipv4.HeaderLen]byte
Header *ipv4.Header
}
var ipv4HeaderLittleEndianTest = ipv4HeaderTest{
// TODO(mikio): Add platform dependent wire header formats when
// we support new platforms.
wireHeaderFromKernel: [ipv4.HeaderLen]byte{
0x45, 0x01, 0xbe, 0xef,
0xca, 0xfe, 0x45, 0xdc,
0xff, 0x01, 0xde, 0xad,
172, 16, 254, 254,
192, 168, 0, 1,
},
wireHeaderFromTradBSDKernel: [ipv4.HeaderLen]byte{
0x45, 0x01, 0xef, 0xbe,
0xca, 0xfe, 0x45, 0xdc,
0xff, 0x01, 0xde, 0xad,
172, 16, 254, 254,
192, 168, 0, 1,
},
Header: &ipv4.Header{
Version: ipv4.Version,
Len: ipv4.HeaderLen,
TOS: 1,
TotalLen: 0xbeef,
ID: 0xcafe,
Flags: ipv4.DontFragment,
FragOff: 1500,
TTL: 255,
Protocol: 1,
Checksum: 0xdead,
Src: net.IPv4(172, 16, 254, 254),
Dst: net.IPv4(192, 168, 0, 1),
},
}
func TestParseIPv4Header(t *testing.T) {
tt := &ipv4HeaderLittleEndianTest
if socket.NativeEndian != binary.LittleEndian {
t.Skip("no test for non-little endian machine yet")
}
var wh []byte
switch runtime.GOOS {
case "darwin":
wh = tt.wireHeaderFromTradBSDKernel[:]
case "freebsd":
if freebsdVersion >= 1000000 {
wh = tt.wireHeaderFromKernel[:]
} else {
wh = tt.wireHeaderFromTradBSDKernel[:]
}
default:
wh = tt.wireHeaderFromKernel[:]
}
h, err := ParseIPv4Header(wh)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(h, tt.Header) {
t.Fatalf("got %#v; want %#v", h, tt.Header)
switch socket.NativeEndian {
case binary.LittleEndian:
t.Run("LittleEndian", func(t *testing.T) {
// TODO(mikio): Add platform dependent wire
// header formats when we support new
// platforms.
wireHeaderFromKernel := [ipv4.HeaderLen]byte{
0x45, 0x01, 0xbe, 0xef,
0xca, 0xfe, 0x45, 0xdc,
0xff, 0x01, 0xde, 0xad,
172, 16, 254, 254,
192, 168, 0, 1,
}
wireHeaderFromTradBSDKernel := [ipv4.HeaderLen]byte{
0x45, 0x01, 0xef, 0xbe,
0xca, 0xfe, 0x45, 0xdc,
0xff, 0x01, 0xde, 0xad,
172, 16, 254, 254,
192, 168, 0, 1,
}
th := &ipv4.Header{
Version: ipv4.Version,
Len: ipv4.HeaderLen,
TOS: 1,
TotalLen: 0xbeef,
ID: 0xcafe,
Flags: ipv4.DontFragment,
FragOff: 1500,
TTL: 255,
Protocol: 1,
Checksum: 0xdead,
Src: net.IPv4(172, 16, 254, 254),
Dst: net.IPv4(192, 168, 0, 1),
}
var wh []byte
switch runtime.GOOS {
case "darwin":
wh = wireHeaderFromTradBSDKernel[:]
case "freebsd":
if freebsdVersion >= 1000000 {
wh = wireHeaderFromKernel[:]
} else {
wh = wireHeaderFromTradBSDKernel[:]
}
default:
wh = wireHeaderFromKernel[:]
}
h, err := ParseIPv4Header(wh)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(h, th) {
t.Fatalf("got %#v; want %#v", h, th)
}
})
}
}

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build nacl plan9
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
package icmp

View File

@@ -11,22 +11,25 @@
// ICMP extensions for MPLS are defined in RFC 4950.
// ICMP extensions for interface and next-hop identification are
// defined in RFC 5837.
// PROBE: A utility for probing interfaces is defined in RFC 8335.
package icmp // import "golang.org/x/net/icmp"
import (
"encoding/binary"
"errors"
"net"
"syscall"
"golang.org/x/net/internal/iana"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
// BUG(mikio): This package is not implemented on NaCl and Plan 9.
// BUG(mikio): This package is not implemented on AIX, JS, NaCl and
// Plan 9.
var (
errInvalidConn = errors.New("invalid connection")
errInvalidProtocol = errors.New("invalid protocol")
errMessageTooShort = errors.New("message too short")
errHeaderTooShort = errors.New("header too short")
errBufferTooShort = errors.New("buffer too short")
@@ -79,7 +82,7 @@ func (m *Message) Marshal(psh []byte) ([]byte, error) {
case ipv6.ICMPType:
mtype = int(typ)
default:
return nil, syscall.EINVAL
return nil, errInvalidProtocol
}
b := []byte{byte(mtype), byte(m.Code), 0, 0}
if m.Type.Protocol() == iana.ProtocolIPv6ICMP && psh != nil {
@@ -107,25 +110,30 @@ func (m *Message) Marshal(psh []byte) ([]byte, error) {
return b[len(psh):], nil
}
var parseFns = map[Type]func(int, []byte) (MessageBody, error){
var parseFns = map[Type]func(int, Type, []byte) (MessageBody, error){
ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach,
ipv4.ICMPTypeTimeExceeded: parseTimeExceeded,
ipv4.ICMPTypeParameterProblem: parseParamProb,
ipv4.ICMPTypeEcho: parseEcho,
ipv4.ICMPTypeEchoReply: parseEcho,
ipv4.ICMPTypeEcho: parseEcho,
ipv4.ICMPTypeEchoReply: parseEcho,
ipv4.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
ipv4.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach,
ipv6.ICMPTypePacketTooBig: parsePacketTooBig,
ipv6.ICMPTypeTimeExceeded: parseTimeExceeded,
ipv6.ICMPTypeParameterProblem: parseParamProb,
ipv6.ICMPTypeEchoRequest: parseEcho,
ipv6.ICMPTypeEchoReply: parseEcho,
ipv6.ICMPTypeEchoRequest: parseEcho,
ipv6.ICMPTypeEchoReply: parseEcho,
ipv6.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
ipv6.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
}
// ParseMessage parses b as an ICMP message.
// Proto must be either the ICMPv4 or ICMPv6 protocol number.
// The provided proto must be either the ICMPv4 or ICMPv6 protocol
// number.
func ParseMessage(proto int, b []byte) (*Message, error) {
if len(b) < 4 {
return nil, errMessageTooShort
@@ -138,12 +146,12 @@ func ParseMessage(proto int, b []byte) (*Message, error) {
case iana.ProtocolIPv6ICMP:
m.Type = ipv6.ICMPType(b[0])
default:
return nil, syscall.EINVAL
return nil, errInvalidProtocol
}
if fn, ok := parseFns[m.Type]; !ok {
m.Body, err = parseDefaultMessageBody(proto, b[4:])
} else {
m.Body, err = fn(proto, b[4:])
m.Body, err = fn(proto, m.Type, b[4:])
}
if err != nil {
return nil, err

View File

@@ -15,120 +15,141 @@ import (
"golang.org/x/net/ipv6"
)
var marshalAndParseMessageForIPv4Tests = []icmp.Message{
{
Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
Body: &icmp.DstUnreach{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv4.ICMPTypeParameterProblem, Code: 2,
Body: &icmp.ParamProb{
Pointer: 8,
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &icmp.Echo{
ID: 1, Seq: 2,
Data: []byte("HELLO-R-U-THERE"),
},
},
{
Type: ipv4.ICMPTypePhoturis,
Body: &icmp.DefaultMessageBody{
Data: []byte{0x80, 0x40, 0x20, 0x10},
},
},
}
func TestMarshalAndParseMessageForIPv4(t *testing.T) {
for i, tt := range marshalAndParseMessageForIPv4Tests {
b, err := tt.Marshal(nil)
if err != nil {
t.Fatal(err)
}
m, err := icmp.ParseMessage(iana.ProtocolICMP, b)
if err != nil {
t.Fatal(err)
}
if m.Type != tt.Type || m.Code != tt.Code {
t.Errorf("#%v: got %v; want %v", i, m, &tt)
}
if !reflect.DeepEqual(m.Body, tt.Body) {
t.Errorf("#%v: got %v; want %v", i, m.Body, tt.Body)
}
}
}
var marshalAndParseMessageForIPv6Tests = []icmp.Message{
{
Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
Body: &icmp.DstUnreach{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypePacketTooBig, Code: 0,
Body: &icmp.PacketTooBig{
MTU: 1<<16 - 1,
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypeParameterProblem, Code: 2,
Body: &icmp.ParamProb{
Pointer: 8,
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
Body: &icmp.Echo{
ID: 1, Seq: 2,
Data: []byte("HELLO-R-U-THERE"),
},
},
{
Type: ipv6.ICMPTypeDuplicateAddressConfirmation,
Body: &icmp.DefaultMessageBody{
Data: []byte{0x80, 0x40, 0x20, 0x10},
},
},
}
func TestMarshalAndParseMessageForIPv6(t *testing.T) {
pshicmp := icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1"))
for i, tt := range marshalAndParseMessageForIPv6Tests {
for _, psh := range [][]byte{pshicmp, nil} {
b, err := tt.Marshal(psh)
if err != nil {
t.Fatal(err)
func TestMarshalAndParseMessage(t *testing.T) {
fn := func(t *testing.T, proto int, tms []icmp.Message) {
var pshs [][]byte
switch proto {
case iana.ProtocolICMP:
pshs = [][]byte{nil}
case iana.ProtocolIPv6ICMP:
pshs = [][]byte{
icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1")),
nil,
}
m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, b)
if err != nil {
t.Fatal(err)
}
if m.Type != tt.Type || m.Code != tt.Code {
t.Errorf("#%v: got %v; want %v", i, m, &tt)
}
if !reflect.DeepEqual(m.Body, tt.Body) {
t.Errorf("#%v: got %v; want %v", i, m.Body, tt.Body)
}
for i, tm := range tms {
for _, psh := range pshs {
b, err := tm.Marshal(psh)
if err != nil {
t.Fatal(err)
}
m, err := icmp.ParseMessage(proto, b)
if err != nil {
t.Fatal(err)
}
if m.Type != tm.Type || m.Code != tm.Code {
t.Errorf("#%d: got %#v; want %#v", i, m, &tm)
}
if !reflect.DeepEqual(m.Body, tm.Body) {
t.Errorf("#%d: got %#v; want %#v", i, m.Body, tm.Body)
}
}
}
}
t.Run("IPv4", func(t *testing.T) {
fn(t, iana.ProtocolICMP,
[]icmp.Message{
{
Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
Body: &icmp.DstUnreach{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv4.ICMPTypeParameterProblem, Code: 2,
Body: &icmp.ParamProb{
Pointer: 8,
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv4.ICMPTypeEcho, Code: 0,
Body: &icmp.Echo{
ID: 1, Seq: 2,
Data: []byte("HELLO-R-U-THERE"),
},
},
{
Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2,
},
},
{
Type: ipv4.ICMPTypeExtendedEchoReply, Code: 0,
Body: &icmp.ExtendedEchoReply{
State: 4 /* Delay */, Active: true, IPv4: true,
},
},
{
Type: ipv4.ICMPTypePhoturis,
Body: &icmp.DefaultMessageBody{
Data: []byte{0x80, 0x40, 0x20, 0x10},
},
},
})
})
t.Run("IPv6", func(t *testing.T) {
fn(t, iana.ProtocolIPv6ICMP,
[]icmp.Message{
{
Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
Body: &icmp.DstUnreach{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypePacketTooBig, Code: 0,
Body: &icmp.PacketTooBig{
MTU: 1<<16 - 1,
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypeParameterProblem, Code: 2,
Body: &icmp.ParamProb{
Pointer: 8,
Data: []byte("ERROR-INVOKING-PACKET"),
},
},
{
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
Body: &icmp.Echo{
ID: 1, Seq: 2,
Data: []byte("HELLO-R-U-THERE"),
},
},
{
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2,
},
},
{
Type: ipv6.ICMPTypeExtendedEchoReply, Code: 0,
Body: &icmp.ExtendedEchoReply{
State: 5 /* Probe */, Active: true, IPv6: true,
},
},
{
Type: ipv6.ICMPTypeDuplicateAddressConfirmation,
Body: &icmp.DefaultMessageBody{
Data: []byte{0x80, 0x40, 0x20, 0x10},
},
},
})
})
}

View File

@@ -7,11 +7,13 @@ package icmp
// A MessageBody represents an ICMP message body.
type MessageBody interface {
// Len returns the length of ICMP message body.
// Proto must be either the ICMPv4 or ICMPv6 protocol number.
// The provided proto must be either the ICMPv4 or ICMPv6
// protocol number.
Len(proto int) int
// Marshal returns the binary encoding of ICMP message body.
// Proto must be either the ICMPv4 or ICMPv6 protocol number.
// The provided proto must be either the ICMPv4 or ICMPv6
// protocol number.
Marshal(proto int) ([]byte, error)
}

View File

@@ -6,7 +6,7 @@ package icmp
import "encoding/binary"
// A MPLSLabel represents a MPLS label stack entry.
// MPLSLabel represents an MPLS label stack entry.
type MPLSLabel struct {
Label int // label value
TC int // traffic class; formerly experimental use
@@ -19,7 +19,7 @@ const (
typeIncomingMPLSLabelStack = 1
)
// A MPLSLabelStack represents a MPLS label stack.
// MPLSLabelStack represents an MPLS label stack.
type MPLSLabelStack struct {
Class int // extension object class number
Type int // extension object sub-type

View File

@@ -10,12 +10,14 @@ import "golang.org/x/net/internal/iana"
// exts as extensions, and returns a required length for message body
// and a required length for a padded original datagram in wire
// format.
func multipartMessageBodyDataLen(proto int, b []byte, exts []Extension) (bodyLen, dataLen int) {
func multipartMessageBodyDataLen(proto int, withOrigDgram bool, b []byte, exts []Extension) (bodyLen, dataLen int) {
for _, ext := range exts {
bodyLen += ext.Len(proto)
}
if bodyLen > 0 {
dataLen = multipartMessageOrigDatagramLen(proto, b)
if withOrigDgram {
dataLen = multipartMessageOrigDatagramLen(proto, b)
}
bodyLen += 4 // length of extension header
} else {
dataLen = len(b)
@@ -50,8 +52,8 @@ func multipartMessageOrigDatagramLen(proto int, b []byte) int {
// marshalMultipartMessageBody takes data as an original datagram and
// exts as extesnsions, and returns a binary encoding of message body.
// It can be used for non-multipart message bodies when exts is nil.
func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]byte, error) {
bodyLen, dataLen := multipartMessageBodyDataLen(proto, data, exts)
func marshalMultipartMessageBody(proto int, withOrigDgram bool, data []byte, exts []Extension) ([]byte, error) {
bodyLen, dataLen := multipartMessageBodyDataLen(proto, withOrigDgram, data, exts)
b := make([]byte, 4+bodyLen)
copy(b[4:], data)
off := dataLen + 4
@@ -71,16 +73,23 @@ func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]by
return nil, err
}
off += ext.Len(proto)
case *InterfaceIdent:
if err := ext.marshal(proto, b[off:]); err != nil {
return nil, err
}
off += ext.Len(proto)
}
}
s := checksum(b[dataLen+4:])
b[dataLen+4+2] ^= byte(s)
b[dataLen+4+3] ^= byte(s >> 8)
switch proto {
case iana.ProtocolICMP:
b[1] = byte(dataLen / 4)
case iana.ProtocolIPv6ICMP:
b[0] = byte(dataLen / 8)
if withOrigDgram {
switch proto {
case iana.ProtocolICMP:
b[1] = byte(dataLen / 4)
case iana.ProtocolIPv6ICMP:
b[0] = byte(dataLen / 8)
}
}
}
return b, nil
@@ -88,7 +97,7 @@ func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]by
// parseMultipartMessageBody parses b as either a non-multipart
// message body or a multipart message body.
func parseMultipartMessageBody(proto int, b []byte) ([]byte, []Extension, error) {
func parseMultipartMessageBody(proto int, typ Type, b []byte) ([]byte, []Extension, error) {
var l int
switch proto {
case iana.ProtocolICMP:
@@ -99,11 +108,14 @@ func parseMultipartMessageBody(proto int, b []byte) ([]byte, []Extension, error)
if len(b) == 4 {
return nil, nil, nil
}
exts, l, err := parseExtensions(b[4:], l)
exts, l, err := parseExtensions(typ, b[4:], l)
if err != nil {
l = len(b) - 4
}
data := make([]byte, l)
copy(data, b[4:])
var data []byte
if l > 0 {
data = make([]byte, l)
copy(data, b[4:])
}
return data, exts, nil
}

View File

@@ -5,6 +5,7 @@
package icmp_test
import (
"errors"
"fmt"
"net"
"reflect"
@@ -16,425 +17,557 @@ import (
"golang.org/x/net/ipv6"
)
var marshalAndParseMultipartMessageForIPv4Tests = []icmp.Message{
{
Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
Body: &icmp.DstUnreach{
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 1).To4(),
},
},
},
},
},
{
Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 1).To4(),
},
},
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
},
},
},
{
Type: ipv4.ICMPTypeParameterProblem, Code: 2,
Body: &icmp.ParamProb{
Pointer: 8,
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 1).To4(),
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x2f,
Interface: &net.Interface{
Index: 16,
Name: "en102",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 2).To4(),
},
},
},
},
},
}
func TestMarshalAndParseMultipartMessageForIPv4(t *testing.T) {
for i, tt := range marshalAndParseMultipartMessageForIPv4Tests {
b, err := tt.Marshal(nil)
func TestMarshalAndParseMultipartMessage(t *testing.T) {
fn := func(t *testing.T, proto int, tm icmp.Message) error {
b, err := tm.Marshal(nil)
if err != nil {
t.Fatal(err)
return err
}
if b[5] != 32 {
t.Errorf("#%v: got %v; want 32", i, b[5])
switch tm.Type {
case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
default:
switch proto {
case iana.ProtocolICMP:
if b[5] != 32 {
return fmt.Errorf("got %d; want 32", b[5])
}
case iana.ProtocolIPv6ICMP:
if b[4] != 16 {
return fmt.Errorf("got %d; want 16", b[4])
}
default:
return fmt.Errorf("unknown protocol: %d", proto)
}
}
m, err := icmp.ParseMessage(iana.ProtocolICMP, b)
m, err := icmp.ParseMessage(proto, b)
if err != nil {
t.Fatal(err)
return err
}
if m.Type != tt.Type || m.Code != tt.Code {
t.Errorf("#%v: got %v; want %v", i, m, &tt)
if m.Type != tm.Type || m.Code != tm.Code {
return fmt.Errorf("got %v; want %v", m, &tm)
}
switch m.Type {
case ipv4.ICMPTypeDestinationUnreachable:
got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach)
case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
got, want := m.Body.(*icmp.ExtendedEchoRequest), tm.Body.(*icmp.ExtendedEchoRequest)
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
}
case ipv4.ICMPTypeDestinationUnreachable:
got, want := m.Body.(*icmp.DstUnreach), tm.Body.(*icmp.DstUnreach)
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
}
if len(got.Data) != 128 {
t.Errorf("#%v: got %v; want 128", i, len(got.Data))
return fmt.Errorf("got %d; want 128", len(got.Data))
}
case ipv4.ICMPTypeTimeExceeded:
got, want := m.Body.(*icmp.TimeExceeded), tt.Body.(*icmp.TimeExceeded)
got, want := m.Body.(*icmp.TimeExceeded), tm.Body.(*icmp.TimeExceeded)
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
}
if len(got.Data) != 128 {
t.Errorf("#%v: got %v; want 128", i, len(got.Data))
return fmt.Errorf("got %d; want 128", len(got.Data))
}
case ipv4.ICMPTypeParameterProblem:
got, want := m.Body.(*icmp.ParamProb), tt.Body.(*icmp.ParamProb)
got, want := m.Body.(*icmp.ParamProb), tm.Body.(*icmp.ParamProb)
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
}
if len(got.Data) != 128 {
t.Errorf("#%v: got %v; want 128", i, len(got.Data))
return fmt.Errorf("got %d; want 128", len(got.Data))
}
case ipv6.ICMPTypeDestinationUnreachable:
got, want := m.Body.(*icmp.DstUnreach), tm.Body.(*icmp.DstUnreach)
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
}
if len(got.Data) != 128 {
return fmt.Errorf("got %d; want 128", len(got.Data))
}
case ipv6.ICMPTypeTimeExceeded:
got, want := m.Body.(*icmp.TimeExceeded), tm.Body.(*icmp.TimeExceeded)
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
return errors.New(dumpExtensions(got.Extensions, want.Extensions))
}
if len(got.Data) != 128 {
return fmt.Errorf("got %d; want 128", len(got.Data))
}
default:
return fmt.Errorf("unknown message type: %v", m.Type)
}
return nil
}
}
var marshalAndParseMultipartMessageForIPv6Tests = []icmp.Message{
{
Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
Body: &icmp.DstUnreach{
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
t.Run("IPv4", func(t *testing.T) {
for i, tm := range []icmp.Message{
{
Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15,
Body: &icmp.DstUnreach{
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 1).To4(),
},
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en101",
},
},
},
},
},
{
Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en101",
},
},
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
{
Type: ipv4.ICMPTypeTimeExceeded, Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 1).To4(),
},
},
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x2f,
Interface: &net.Interface{
Index: 16,
Name: "en102",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en102",
},
{
Type: ipv4.ICMPTypeParameterProblem, Code: 2,
Body: &icmp.ParamProb{
Pointer: 8,
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 1).To4(),
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x2f,
Interface: &net.Interface{
Index: 16,
Name: "en102",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.IPv4(192, 168, 0, 2).To4(),
},
},
},
},
},
},
},
}
func TestMarshalAndParseMultipartMessageForIPv6(t *testing.T) {
pshicmp := icmp.IPv6PseudoHeader(net.ParseIP("fe80::1"), net.ParseIP("ff02::1"))
for i, tt := range marshalAndParseMultipartMessageForIPv6Tests {
for _, psh := range [][]byte{pshicmp, nil} {
b, err := tt.Marshal(psh)
if err != nil {
t.Fatal(err)
}
if b[4] != 16 {
t.Errorf("#%v: got %v; want 16", i, b[4])
}
m, err := icmp.ParseMessage(iana.ProtocolIPv6ICMP, b)
if err != nil {
t.Fatal(err)
}
if m.Type != tt.Type || m.Code != tt.Code {
t.Errorf("#%v: got %v; want %v", i, m, &tt)
}
switch m.Type {
case ipv6.ICMPTypeDestinationUnreachable:
got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach)
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
}
if len(got.Data) != 128 {
t.Errorf("#%v: got %v; want 128", i, len(got.Data))
}
case ipv6.ICMPTypeTimeExceeded:
got, want := m.Body.(*icmp.TimeExceeded), tt.Body.(*icmp.TimeExceeded)
if !reflect.DeepEqual(got.Extensions, want.Extensions) {
t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
}
if len(got.Data) != 128 {
t.Errorf("#%v: got %v; want 128", i, len(got.Data))
}
{
Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2, Local: true,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3,
Type: 1,
Name: "en101",
},
},
},
},
{
Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2, Local: true,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3,
Type: 2,
Index: 911,
},
&icmp.InterfaceIdent{
Class: 3,
Type: 1,
Name: "en101",
},
},
},
},
{
Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3,
Type: 3,
AFI: iana.AddrFamily48bitMAC,
Addr: []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab},
},
},
},
},
} {
if err := fn(t, iana.ProtocolICMP, tm); err != nil {
t.Errorf("#%d: %v", i, err)
}
}
}
})
t.Run("IPv6", func(t *testing.T) {
for i, tm := range []icmp.Message{
{
Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6,
Body: &icmp.DstUnreach{
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en101",
},
},
},
},
},
{
Type: ipv6.ICMPTypeTimeExceeded, Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{
&icmp.InterfaceInfo{
Class: 2,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en101",
},
},
&icmp.MPLSLabelStack{
Class: 1,
Type: 1,
Labels: []icmp.MPLSLabel{
{
Label: 16014,
TC: 0x4,
S: true,
TTL: 255,
},
},
},
&icmp.InterfaceInfo{
Class: 2,
Type: 0x2f,
Interface: &net.Interface{
Index: 16,
Name: "en102",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en102",
},
},
},
},
},
{
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2, Local: true,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3,
Type: 1,
Name: "en101",
},
},
},
},
{
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2, Local: true,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3,
Type: 1,
Name: "en101",
},
&icmp.InterfaceIdent{
Class: 3,
Type: 2,
Index: 911,
},
},
},
},
{
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3,
Type: 3,
AFI: iana.AddrFamilyIPv4,
Addr: []byte{192, 0, 2, 1},
},
},
},
},
} {
if err := fn(t, iana.ProtocolIPv6ICMP, tm); err != nil {
t.Errorf("#%d: %v", i, err)
}
}
})
}
func dumpExtensions(i int, gotExts, wantExts []icmp.Extension) string {
func dumpExtensions(gotExts, wantExts []icmp.Extension) string {
var s string
for j, got := range gotExts {
for i, got := range gotExts {
switch got := got.(type) {
case *icmp.MPLSLabelStack:
want := wantExts[j].(*icmp.MPLSLabelStack)
want := wantExts[i].(*icmp.MPLSLabelStack)
if !reflect.DeepEqual(got, want) {
s += fmt.Sprintf("#%v/%v: got %#v; want %#v\n", i, j, got, want)
s += fmt.Sprintf("#%d: got %#v; want %#v\n", i, got, want)
}
case *icmp.InterfaceInfo:
want := wantExts[j].(*icmp.InterfaceInfo)
want := wantExts[i].(*icmp.InterfaceInfo)
if !reflect.DeepEqual(got, want) {
s += fmt.Sprintf("#%v/%v: got %#v, %#v, %#v; want %#v, %#v, %#v\n", i, j, got, got.Interface, got.Addr, want, want.Interface, want.Addr)
s += fmt.Sprintf("#%d: got %#v, %#v, %#v; want %#v, %#v, %#v\n", i, got, got.Interface, got.Addr, want, want.Interface, want.Addr)
}
case *icmp.InterfaceIdent:
want := wantExts[i].(*icmp.InterfaceIdent)
if !reflect.DeepEqual(got, want) {
s += fmt.Sprintf("#%d: got %#v; want %#v\n", i, got, want)
}
}
}
if len(s) == 0 {
return "<nil>"
}
return s[:len(s)-1]
}
var multipartMessageBodyLenTests = []struct {
proto int
in icmp.MessageBody
out int
}{
{
iana.ProtocolICMP,
&icmp.DstUnreach{
Data: make([]byte, ipv4.HeaderLen),
},
4 + ipv4.HeaderLen, // unused and original datagram
},
{
iana.ProtocolICMP,
&icmp.TimeExceeded{
Data: make([]byte, ipv4.HeaderLen),
},
4 + ipv4.HeaderLen, // unused and original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, ipv4.HeaderLen),
},
4 + ipv4.HeaderLen, // [pointer, unused] and original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, ipv4.HeaderLen),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload, original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, 128),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, 129),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 132, // [pointer, length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // unused and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.PacketTooBig{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // mtu and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.TimeExceeded{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // unused and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.ParamProb{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // pointer and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, 127),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, 128),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, 129),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 136, // [length, unused], extension header, object header, object payload and original datagram
},
}
func TestMultipartMessageBodyLen(t *testing.T) {
for i, tt := range multipartMessageBodyLenTests {
for i, tt := range []struct {
proto int
in icmp.MessageBody
out int
}{
{
iana.ProtocolICMP,
&icmp.DstUnreach{
Data: make([]byte, ipv4.HeaderLen),
},
4 + ipv4.HeaderLen, // unused and original datagram
},
{
iana.ProtocolICMP,
&icmp.TimeExceeded{
Data: make([]byte, ipv4.HeaderLen),
},
4 + ipv4.HeaderLen, // unused and original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, ipv4.HeaderLen),
},
4 + ipv4.HeaderLen, // [pointer, unused] and original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, ipv4.HeaderLen),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload, original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, 128),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolICMP,
&icmp.ParamProb{
Data: make([]byte, 129),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 132, // [pointer, length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // unused and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.PacketTooBig{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // mtu and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.TimeExceeded{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // unused and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.ParamProb{
Data: make([]byte, ipv6.HeaderLen),
},
4 + ipv6.HeaderLen, // pointer and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, 127),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, 128),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolIPv6ICMP,
&icmp.DstUnreach{
Data: make([]byte, 129),
Extensions: []icmp.Extension{
&icmp.MPLSLabelStack{},
},
},
4 + 4 + 4 + 0 + 136, // [length, unused], extension header, object header, object payload and original datagram
},
{
iana.ProtocolICMP,
&icmp.ExtendedEchoRequest{},
4, // [id, seq, l-bit]
},
{
iana.ProtocolICMP,
&icmp.ExtendedEchoRequest{
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{},
},
},
4 + 4 + 4, // [id, seq, l-bit], extension header, object header
},
{
iana.ProtocolIPv6ICMP,
&icmp.ExtendedEchoRequest{
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Type: 3,
AFI: iana.AddrFamilyNSAP,
Addr: []byte{0x49, 0x00, 0x01, 0xaa, 0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0x00},
},
},
},
4 + 4 + 4 + 16, // [id, seq, l-bit], extension header, object header, object payload
},
} {
if out := tt.in.Len(tt.proto); out != tt.out {
t.Errorf("#%d: got %d; want %d", i, out, tt.out)
}

View File

@@ -29,7 +29,7 @@ func (p *PacketTooBig) Marshal(proto int) ([]byte, error) {
}
// parsePacketTooBig parses b as an ICMP packet too big message body.
func parsePacketTooBig(proto int, b []byte) (MessageBody, error) {
func parsePacketTooBig(proto int, _ Type, b []byte) (MessageBody, error) {
bodyLen := len(b)
if bodyLen < 4 {
return nil, errMessageTooShort

View File

@@ -21,7 +21,7 @@ func (p *ParamProb) Len(proto int) int {
if p == nil {
return 0
}
l, _ := multipartMessageBodyDataLen(proto, p.Data, p.Extensions)
l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions)
return 4 + l
}
@@ -33,7 +33,7 @@ func (p *ParamProb) Marshal(proto int) ([]byte, error) {
copy(b[4:], p.Data)
return b, nil
}
b, err := marshalMultipartMessageBody(proto, p.Data, p.Extensions)
b, err := marshalMultipartMessageBody(proto, true, p.Data, p.Extensions)
if err != nil {
return nil, err
}
@@ -42,7 +42,7 @@ func (p *ParamProb) Marshal(proto int) ([]byte, error) {
}
// parseParamProb parses b as an ICMP parameter problem message body.
func parseParamProb(proto int, b []byte) (MessageBody, error) {
func parseParamProb(proto int, typ Type, b []byte) (MessageBody, error) {
if len(b) < 4 {
return nil, errMessageTooShort
}
@@ -55,7 +55,7 @@ func parseParamProb(proto int, b []byte) (MessageBody, error) {
}
p.Pointer = uintptr(b[0])
var err error
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, b)
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b)
if err != nil {
return nil, err
}

View File

@@ -1,200 +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 icmp_test
import (
"errors"
"fmt"
"net"
"os"
"runtime"
"sync"
"testing"
"time"
"golang.org/x/net/icmp"
"golang.org/x/net/internal/iana"
"golang.org/x/net/internal/nettest"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
func googleAddr(c *icmp.PacketConn, protocol int) (net.Addr, error) {
const host = "www.google.com"
ips, err := net.LookupIP(host)
if err != nil {
return nil, err
}
netaddr := func(ip net.IP) (net.Addr, error) {
switch c.LocalAddr().(type) {
case *net.UDPAddr:
return &net.UDPAddr{IP: ip}, nil
case *net.IPAddr:
return &net.IPAddr{IP: ip}, nil
default:
return nil, errors.New("neither UDPAddr nor IPAddr")
}
}
for _, ip := range ips {
switch protocol {
case iana.ProtocolICMP:
if ip.To4() != nil {
return netaddr(ip)
}
case iana.ProtocolIPv6ICMP:
if ip.To16() != nil && ip.To4() == nil {
return netaddr(ip)
}
}
}
return nil, errors.New("no A or AAAA record")
}
type pingTest struct {
network, address string
protocol int
mtype icmp.Type
}
var nonPrivilegedPingTests = []pingTest{
{"udp4", "0.0.0.0", iana.ProtocolICMP, ipv4.ICMPTypeEcho},
{"udp6", "::", iana.ProtocolIPv6ICMP, ipv6.ICMPTypeEchoRequest},
}
func TestNonPrivilegedPing(t *testing.T) {
if testing.Short() {
t.Skip("avoid external network")
}
switch runtime.GOOS {
case "darwin":
case "linux":
t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
default:
t.Skipf("not supported on %s", runtime.GOOS)
}
for i, tt := range nonPrivilegedPingTests {
if err := doPing(tt, i); err != nil {
t.Error(err)
}
}
}
var privilegedPingTests = []pingTest{
{"ip4:icmp", "0.0.0.0", iana.ProtocolICMP, ipv4.ICMPTypeEcho},
{"ip6:ipv6-icmp", "::", iana.ProtocolIPv6ICMP, ipv6.ICMPTypeEchoRequest},
}
func TestPrivilegedPing(t *testing.T) {
if testing.Short() {
t.Skip("avoid external network")
}
if m, ok := nettest.SupportsRawIPSocket(); !ok {
t.Skip(m)
}
for i, tt := range privilegedPingTests {
if err := doPing(tt, i); err != nil {
t.Error(err)
}
}
}
func doPing(tt pingTest, seq int) error {
c, err := icmp.ListenPacket(tt.network, tt.address)
if err != nil {
return err
}
defer c.Close()
dst, err := googleAddr(c, tt.protocol)
if err != nil {
return err
}
if tt.network != "udp6" && tt.protocol == iana.ProtocolIPv6ICMP {
var f ipv6.ICMPFilter
f.SetAll(true)
f.Accept(ipv6.ICMPTypeDestinationUnreachable)
f.Accept(ipv6.ICMPTypePacketTooBig)
f.Accept(ipv6.ICMPTypeTimeExceeded)
f.Accept(ipv6.ICMPTypeParameterProblem)
f.Accept(ipv6.ICMPTypeEchoReply)
if err := c.IPv6PacketConn().SetICMPFilter(&f); err != nil {
return err
}
}
wm := icmp.Message{
Type: tt.mtype, Code: 0,
Body: &icmp.Echo{
ID: os.Getpid() & 0xffff, Seq: 1 << uint(seq),
Data: []byte("HELLO-R-U-THERE"),
},
}
wb, err := wm.Marshal(nil)
if err != nil {
return err
}
if n, err := c.WriteTo(wb, dst); err != nil {
return err
} else if n != len(wb) {
return fmt.Errorf("got %v; want %v", n, len(wb))
}
rb := make([]byte, 1500)
if err := c.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
return err
}
n, peer, err := c.ReadFrom(rb)
if err != nil {
return err
}
rm, err := icmp.ParseMessage(tt.protocol, rb[:n])
if err != nil {
return err
}
switch rm.Type {
case ipv4.ICMPTypeEchoReply, ipv6.ICMPTypeEchoReply:
return nil
default:
return fmt.Errorf("got %+v from %v; want echo reply", rm, peer)
}
}
func TestConcurrentNonPrivilegedListenPacket(t *testing.T) {
if testing.Short() {
t.Skip("avoid external network")
}
switch runtime.GOOS {
case "darwin":
case "linux":
t.Log("you may need to adjust the net.ipv4.ping_group_range kernel state")
default:
t.Skipf("not supported on %s", runtime.GOOS)
}
network, address := "udp4", "127.0.0.1"
if !nettest.SupportsIPv4() {
network, address = "udp6", "::1"
}
const N = 1000
var wg sync.WaitGroup
wg.Add(N)
for i := 0; i < N; i++ {
go func() {
defer wg.Done()
c, err := icmp.ListenPacket(network, address)
if err != nil {
t.Error(err)
return
}
c.Close()
}()
}
wg.Wait()
}

View File

@@ -15,23 +15,23 @@ func (p *TimeExceeded) Len(proto int) int {
if p == nil {
return 0
}
l, _ := multipartMessageBodyDataLen(proto, p.Data, p.Extensions)
l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions)
return 4 + l
}
// Marshal implements the Marshal method of MessageBody interface.
func (p *TimeExceeded) Marshal(proto int) ([]byte, error) {
return marshalMultipartMessageBody(proto, p.Data, p.Extensions)
return marshalMultipartMessageBody(proto, true, p.Data, p.Extensions)
}
// parseTimeExceeded parses b as an ICMP time exceeded message body.
func parseTimeExceeded(proto int, b []byte) (MessageBody, error) {
func parseTimeExceeded(proto int, typ Type, b []byte) (MessageBody, error) {
if len(b) < 4 {
return nil, errMessageTooShort
}
p := &TimeExceeded{}
var err error
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, b)
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b)
if err != nil {
return nil, err
}

View File

@@ -1,44 +1,40 @@
// go generate gen.go
// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// Code generated by the command above; DO NOT EDIT.
// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).
package iana // import "golang.org/x/net/internal/iana"
// Differentiated Services Field Codepoints (DSCP), Updated: 2017-05-12
// Differentiated Services Field Codepoints (DSCP), Updated: 2018-05-04
const (
DiffServCS0 = 0x0 // CS0
DiffServCS1 = 0x20 // CS1
DiffServCS2 = 0x40 // CS2
DiffServCS3 = 0x60 // CS3
DiffServCS4 = 0x80 // CS4
DiffServCS5 = 0xa0 // CS5
DiffServCS6 = 0xc0 // CS6
DiffServCS7 = 0xe0 // CS7
DiffServAF11 = 0x28 // AF11
DiffServAF12 = 0x30 // AF12
DiffServAF13 = 0x38 // AF13
DiffServAF21 = 0x48 // AF21
DiffServAF22 = 0x50 // AF22
DiffServAF23 = 0x58 // AF23
DiffServAF31 = 0x68 // AF31
DiffServAF32 = 0x70 // AF32
DiffServAF33 = 0x78 // AF33
DiffServAF41 = 0x88 // AF41
DiffServAF42 = 0x90 // AF42
DiffServAF43 = 0x98 // AF43
DiffServEF = 0xb8 // EF
DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT
DiffServCS0 = 0x00 // CS0
DiffServCS1 = 0x20 // CS1
DiffServCS2 = 0x40 // CS2
DiffServCS3 = 0x60 // CS3
DiffServCS4 = 0x80 // CS4
DiffServCS5 = 0xa0 // CS5
DiffServCS6 = 0xc0 // CS6
DiffServCS7 = 0xe0 // CS7
DiffServAF11 = 0x28 // AF11
DiffServAF12 = 0x30 // AF12
DiffServAF13 = 0x38 // AF13
DiffServAF21 = 0x48 // AF21
DiffServAF22 = 0x50 // AF22
DiffServAF23 = 0x58 // AF23
DiffServAF31 = 0x68 // AF31
DiffServAF32 = 0x70 // AF32
DiffServAF33 = 0x78 // AF33
DiffServAF41 = 0x88 // AF41
DiffServAF42 = 0x90 // AF42
DiffServAF43 = 0x98 // AF43
DiffServEF = 0xb8 // EF
DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT
NotECNTransport = 0x00 // Not-ECT (Not ECN-Capable Transport)
ECNTransport1 = 0x01 // ECT(1) (ECN-Capable Transport(1))
ECNTransport0 = 0x02 // ECT(0) (ECN-Capable Transport(0))
CongestionExperienced = 0x03 // CE (Congestion Experienced)
)
// IPv4 TOS Byte and IPv6 Traffic Class Octet, Updated: 2001-09-06
const (
NotECNTransport = 0x0 // Not-ECT (Not ECN-Capable Transport)
ECNTransport1 = 0x1 // ECT(1) (ECN-Capable Transport(1))
ECNTransport0 = 0x2 // ECT(0) (ECN-Capable Transport(0))
CongestionExperienced = 0x3 // CE (Congestion Experienced)
)
// Protocol Numbers, Updated: 2016-06-22
// Protocol Numbers, Updated: 2017-10-13
const (
ProtocolIP = 0 // IPv4 encapsulation, pseudo protocol number
ProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option
@@ -178,3 +174,50 @@ const (
ProtocolROHC = 142 // Robust Header Compression
ProtocolReserved = 255 // Reserved
)
// Address Family Numbers, Updated: 2018-04-02
const (
AddrFamilyIPv4 = 1 // IP (IP version 4)
AddrFamilyIPv6 = 2 // IP6 (IP version 6)
AddrFamilyNSAP = 3 // NSAP
AddrFamilyHDLC = 4 // HDLC (8-bit multidrop)
AddrFamilyBBN1822 = 5 // BBN 1822
AddrFamily802 = 6 // 802 (includes all 802 media plus Ethernet "canonical format")
AddrFamilyE163 = 7 // E.163
AddrFamilyE164 = 8 // E.164 (SMDS, Frame Relay, ATM)
AddrFamilyF69 = 9 // F.69 (Telex)
AddrFamilyX121 = 10 // X.121 (X.25, Frame Relay)
AddrFamilyIPX = 11 // IPX
AddrFamilyAppletalk = 12 // Appletalk
AddrFamilyDecnetIV = 13 // Decnet IV
AddrFamilyBanyanVines = 14 // Banyan Vines
AddrFamilyE164withSubaddress = 15 // E.164 with NSAP format subaddress
AddrFamilyDNS = 16 // DNS (Domain Name System)
AddrFamilyDistinguishedName = 17 // Distinguished Name
AddrFamilyASNumber = 18 // AS Number
AddrFamilyXTPoverIPv4 = 19 // XTP over IP version 4
AddrFamilyXTPoverIPv6 = 20 // XTP over IP version 6
AddrFamilyXTPnativemodeXTP = 21 // XTP native mode XTP
AddrFamilyFibreChannelWorldWidePortName = 22 // Fibre Channel World-Wide Port Name
AddrFamilyFibreChannelWorldWideNodeName = 23 // Fibre Channel World-Wide Node Name
AddrFamilyGWID = 24 // GWID
AddrFamilyL2VPN = 25 // AFI for L2VPN information
AddrFamilyMPLSTPSectionEndpointID = 26 // MPLS-TP Section Endpoint Identifier
AddrFamilyMPLSTPLSPEndpointID = 27 // MPLS-TP LSP Endpoint Identifier
AddrFamilyMPLSTPPseudowireEndpointID = 28 // MPLS-TP Pseudowire Endpoint Identifier
AddrFamilyMTIPv4 = 29 // MT IP: Multi-Topology IP version 4
AddrFamilyMTIPv6 = 30 // MT IPv6: Multi-Topology IP version 6
AddrFamilyEIGRPCommonServiceFamily = 16384 // EIGRP Common Service Family
AddrFamilyEIGRPIPv4ServiceFamily = 16385 // EIGRP IPv4 Service Family
AddrFamilyEIGRPIPv6ServiceFamily = 16386 // EIGRP IPv6 Service Family
AddrFamilyLISPCanonicalAddressFormat = 16387 // LISP Canonical Address Format (LCAF)
AddrFamilyBGPLS = 16388 // BGP-LS
AddrFamily48bitMAC = 16389 // 48-bit MAC
AddrFamily64bitMAC = 16390 // 64-bit MAC
AddrFamilyOUI = 16391 // OUI
AddrFamilyMACFinal24bits = 16392 // MAC/24
AddrFamilyMACFinal40bits = 16393 // MAC/40
AddrFamilyIPv6Initial64bits = 16394 // IPv6/64
AddrFamilyRBridgePortID = 16395 // RBridge Port ID
AddrFamilyTRILLNickname = 16396 // TRILL Nickname
)

View File

@@ -31,20 +31,20 @@ var registries = []struct {
"https://www.iana.org/assignments/dscp-registry/dscp-registry.xml",
parseDSCPRegistry,
},
{
"https://www.iana.org/assignments/ipv4-tos-byte/ipv4-tos-byte.xml",
parseTOSTCByte,
},
{
"https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
parseProtocolNumbers,
},
{
"https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml",
parseAddrFamilyNumbers,
},
}
func main() {
var bb bytes.Buffer
fmt.Fprintf(&bb, "// go generate gen.go\n")
fmt.Fprintf(&bb, "// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT\n\n")
fmt.Fprintf(&bb, "// Code generated by the command above; DO NOT EDIT.\n\n")
fmt.Fprintf(&bb, "// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).\n")
fmt.Fprintf(&bb, `package iana // import "golang.org/x/net/internal/iana"`+"\n\n")
for _, r := range registries {
@@ -81,31 +81,39 @@ func parseDSCPRegistry(w io.Writer, r io.Reader) error {
if err := dec.Decode(&dr); err != nil {
return err
}
drs := dr.escape()
fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated)
fmt.Fprintf(w, "const (\n")
for _, dr := range drs {
fmt.Fprintf(w, "DiffServ%s = %#x", dr.Name, dr.Value)
for _, dr := range dr.escapeDSCP() {
fmt.Fprintf(w, "DiffServ%s = %#02x", dr.Name, dr.Value)
fmt.Fprintf(w, "// %s\n", dr.OrigName)
}
for _, er := range dr.escapeECN() {
fmt.Fprintf(w, "%s = %#02x", er.Descr, er.Value)
fmt.Fprintf(w, "// %s\n", er.OrigDescr)
}
fmt.Fprintf(w, ")\n")
return nil
}
type dscpRegistry struct {
XMLName xml.Name `xml:"registry"`
Title string `xml:"title"`
Updated string `xml:"updated"`
Note string `xml:"note"`
RegTitle string `xml:"registry>title"`
PoolRecords []struct {
Name string `xml:"name"`
Space string `xml:"space"`
} `xml:"registry>record"`
Records []struct {
Name string `xml:"name"`
Space string `xml:"space"`
} `xml:"registry>registry>record"`
XMLName xml.Name `xml:"registry"`
Title string `xml:"title"`
Updated string `xml:"updated"`
Note string `xml:"note"`
Registries []struct {
Title string `xml:"title"`
Registries []struct {
Title string `xml:"title"`
Records []struct {
Name string `xml:"name"`
Space string `xml:"space"`
} `xml:"record"`
} `xml:"registry"`
Records []struct {
Value string `xml:"value"`
Descr string `xml:"description"`
} `xml:"record"`
} `xml:"registry"`
}
type canonDSCPRecord struct {
@@ -114,92 +122,84 @@ type canonDSCPRecord struct {
Value int
}
func (drr *dscpRegistry) escape() []canonDSCPRecord {
drs := make([]canonDSCPRecord, len(drr.Records))
sr := strings.NewReplacer(
"+", "",
"-", "",
"/", "",
".", "",
" ", "",
)
for i, dr := range drr.Records {
s := strings.TrimSpace(dr.Name)
drs[i].OrigName = s
drs[i].Name = sr.Replace(s)
n, err := strconv.ParseUint(dr.Space, 2, 8)
if err != nil {
func (drr *dscpRegistry) escapeDSCP() []canonDSCPRecord {
var drs []canonDSCPRecord
for _, preg := range drr.Registries {
if !strings.Contains(preg.Title, "Differentiated Services Field Codepoints") {
continue
}
drs[i].Value = int(n) << 2
for _, reg := range preg.Registries {
if !strings.Contains(reg.Title, "Pool 1 Codepoints") {
continue
}
drs = make([]canonDSCPRecord, len(reg.Records))
sr := strings.NewReplacer(
"+", "",
"-", "",
"/", "",
".", "",
" ", "",
)
for i, dr := range reg.Records {
s := strings.TrimSpace(dr.Name)
drs[i].OrigName = s
drs[i].Name = sr.Replace(s)
n, err := strconv.ParseUint(dr.Space, 2, 8)
if err != nil {
continue
}
drs[i].Value = int(n) << 2
}
}
}
return drs
}
func parseTOSTCByte(w io.Writer, r io.Reader) error {
dec := xml.NewDecoder(r)
var ttb tosTCByte
if err := dec.Decode(&ttb); err != nil {
return err
}
trs := ttb.escape()
fmt.Fprintf(w, "// %s, Updated: %s\n", ttb.Title, ttb.Updated)
fmt.Fprintf(w, "const (\n")
for _, tr := range trs {
fmt.Fprintf(w, "%s = %#x", tr.Keyword, tr.Value)
fmt.Fprintf(w, "// %s\n", tr.OrigKeyword)
}
fmt.Fprintf(w, ")\n")
return nil
type canonECNRecord struct {
OrigDescr string
Descr string
Value int
}
type tosTCByte struct {
XMLName xml.Name `xml:"registry"`
Title string `xml:"title"`
Updated string `xml:"updated"`
Note string `xml:"note"`
RegTitle string `xml:"registry>title"`
Records []struct {
Binary string `xml:"binary"`
Keyword string `xml:"keyword"`
} `xml:"registry>record"`
}
type canonTOSTCByteRecord struct {
OrigKeyword string
Keyword string
Value int
}
func (ttb *tosTCByte) escape() []canonTOSTCByteRecord {
trs := make([]canonTOSTCByteRecord, len(ttb.Records))
sr := strings.NewReplacer(
"Capable", "",
"(", "",
")", "",
"+", "",
"-", "",
"/", "",
".", "",
" ", "",
)
for i, tr := range ttb.Records {
s := strings.TrimSpace(tr.Keyword)
trs[i].OrigKeyword = s
ss := strings.Split(s, " ")
if len(ss) > 1 {
trs[i].Keyword = strings.Join(ss[1:], " ")
} else {
trs[i].Keyword = ss[0]
}
trs[i].Keyword = sr.Replace(trs[i].Keyword)
n, err := strconv.ParseUint(tr.Binary, 2, 8)
if err != nil {
func (drr *dscpRegistry) escapeECN() []canonECNRecord {
var ers []canonECNRecord
for _, reg := range drr.Registries {
if !strings.Contains(reg.Title, "ECN Field") {
continue
}
trs[i].Value = int(n)
ers = make([]canonECNRecord, len(reg.Records))
sr := strings.NewReplacer(
"Capable", "",
"Not-ECT", "",
"ECT(1)", "",
"ECT(0)", "",
"CE", "",
"(", "",
")", "",
"+", "",
"-", "",
"/", "",
".", "",
" ", "",
)
for i, er := range reg.Records {
s := strings.TrimSpace(er.Descr)
ers[i].OrigDescr = s
ss := strings.Split(s, " ")
if len(ss) > 1 {
ers[i].Descr = strings.Join(ss[1:], " ")
} else {
ers[i].Descr = ss[0]
}
ers[i].Descr = sr.Replace(er.Descr)
n, err := strconv.ParseUint(er.Value, 2, 8)
if err != nil {
continue
}
ers[i].Value = int(n)
}
}
return trs
return ers
}
func parseProtocolNumbers(w io.Writer, r io.Reader) error {
@@ -291,3 +291,93 @@ func (pn *protocolNumbers) escape() []canonProtocolRecord {
}
return prs
}
func parseAddrFamilyNumbers(w io.Writer, r io.Reader) error {
dec := xml.NewDecoder(r)
var afn addrFamilylNumbers
if err := dec.Decode(&afn); err != nil {
return err
}
afrs := afn.escape()
fmt.Fprintf(w, "// %s, Updated: %s\n", afn.Title, afn.Updated)
fmt.Fprintf(w, "const (\n")
for _, afr := range afrs {
if afr.Name == "" {
continue
}
fmt.Fprintf(w, "AddrFamily%s = %d", afr.Name, afr.Value)
fmt.Fprintf(w, "// %s\n", afr.Descr)
}
fmt.Fprintf(w, ")\n")
return nil
}
type addrFamilylNumbers struct {
XMLName xml.Name `xml:"registry"`
Title string `xml:"title"`
Updated string `xml:"updated"`
RegTitle string `xml:"registry>title"`
Note string `xml:"registry>note"`
Records []struct {
Value string `xml:"value"`
Descr string `xml:"description"`
} `xml:"registry>record"`
}
type canonAddrFamilyRecord struct {
Name string
Descr string
Value int
}
func (afn *addrFamilylNumbers) escape() []canonAddrFamilyRecord {
afrs := make([]canonAddrFamilyRecord, len(afn.Records))
sr := strings.NewReplacer(
"IP version 4", "IPv4",
"IP version 6", "IPv6",
"Identifier", "ID",
"-", "",
"-", "",
"/", "",
".", "",
" ", "",
)
for i, afr := range afn.Records {
if strings.Contains(afr.Descr, "Unassigned") ||
strings.Contains(afr.Descr, "Reserved") {
continue
}
afrs[i].Descr = afr.Descr
s := strings.TrimSpace(afr.Descr)
switch s {
case "IP (IP version 4)":
afrs[i].Name = "IPv4"
case "IP6 (IP version 6)":
afrs[i].Name = "IPv6"
case "AFI for L2VPN information":
afrs[i].Name = "L2VPN"
case "E.164 with NSAP format subaddress":
afrs[i].Name = "E164withSubaddress"
case "MT IP: Multi-Topology IP version 4":
afrs[i].Name = "MTIPv4"
case "MAC/24":
afrs[i].Name = "MACFinal24bits"
case "MAC/40":
afrs[i].Name = "MACFinal40bits"
case "IPv6/64":
afrs[i].Name = "IPv6Initial64bits"
default:
n := strings.Index(s, "(")
if n > 0 {
s = s[:n]
}
n = strings.Index(s, ":")
if n > 0 {
s = s[:n]
}
afrs[i].Name = sr.Replace(s)
}
afrs[i].Value, _ = strconv.Atoi(afr.Value)
}
return afrs
}

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux solaris
// +build aix linux solaris
package nettest

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows
package nettest

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build nacl plan9
// +build js nacl plan9
package nettest

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package nettest

View File

@@ -64,7 +64,7 @@ func TestableNetwork(network string) bool {
switch network {
case "unix", "unixgram":
switch runtime.GOOS {
case "android", "nacl", "plan9", "windows":
case "android", "js", "nacl", "plan9", "windows":
return false
}
if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
@@ -72,7 +72,7 @@ func TestableNetwork(network string) bool {
}
case "unixpacket":
switch runtime.GOOS {
case "android", "darwin", "freebsd", "nacl", "plan9", "windows":
case "aix", "android", "darwin", "freebsd", "js", "nacl", "plan9", "windows":
return false
case "netbsd":
// It passes on amd64 at least. 386 fails (Issue 22927). arm is unknown.

7
vendor/golang.org/x/net/internal/socket/empty.s generated vendored Normal file
View File

@@ -0,0 +1,7 @@
// 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.
// +build darwin,go1.12
// This exists solely so we can linkname in symbols from syscall.

Some files were not shown because too many files have changed in this diff Show More