vendor: update all dependencies

This commit is contained in:
Nick Craig-Wood
2017-07-23 08:51:42 +01:00
parent 0b6fba34a3
commit eb87cf6f12
2008 changed files with 352617 additions and 1004734 deletions
+15 -15
View File
@@ -76,54 +76,54 @@ const (
// ExtLen returns the length of the packet.
ExtLen Extension = 1
// ExtProto returns the packet's L3 protocol type.
ExtProto = 0
ExtProto Extension = 0
// ExtType returns the packet's type (skb->pkt_type in the kernel)
//
// TODO: better documentation. How nice an API do we want to
// provide for these esoteric extensions?
ExtType = 4
ExtType Extension = 4
// ExtPayloadOffset returns the offset of the packet payload, or
// the first protocol header that the kernel does not know how to
// parse.
ExtPayloadOffset = 52
ExtPayloadOffset Extension = 52
// ExtInterfaceIndex returns the index of the interface on which
// the packet was received.
ExtInterfaceIndex = 8
ExtInterfaceIndex Extension = 8
// ExtNetlinkAttr returns the netlink attribute of type X at
// offset A.
ExtNetlinkAttr = 12
ExtNetlinkAttr Extension = 12
// ExtNetlinkAttrNested returns the nested netlink attribute of
// type X at offset A.
ExtNetlinkAttrNested = 16
ExtNetlinkAttrNested Extension = 16
// ExtMark returns the packet's mark value.
ExtMark = 20
ExtMark Extension = 20
// ExtQueue returns the packet's assigned hardware queue.
ExtQueue = 24
ExtQueue Extension = 24
// ExtLinkLayerType returns the packet's hardware address type
// (e.g. Ethernet, Infiniband).
ExtLinkLayerType = 28
ExtLinkLayerType Extension = 28
// ExtRXHash returns the packets receive hash.
//
// TODO: figure out what this rxhash actually is.
ExtRXHash = 32
ExtRXHash Extension = 32
// ExtCPUID returns the ID of the CPU processing the current
// packet.
ExtCPUID = 36
ExtCPUID Extension = 36
// ExtVLANTag returns the packet's VLAN tag.
ExtVLANTag = 44
ExtVLANTag Extension = 44
// ExtVLANTagPresent returns non-zero if the packet has a VLAN
// tag.
//
// TODO: I think this might be a lie: it reads bit 0x1000 of the
// VLAN header, which changed meaning in recent revisions of the
// spec - this extension may now return meaningless information.
ExtVLANTagPresent = 48
ExtVLANTagPresent Extension = 48
// ExtVLANProto returns 0x8100 if the frame has a VLAN header,
// 0x88a8 if the frame has a "Q-in-Q" double VLAN header, or some
// other value if no VLAN information is present.
ExtVLANProto = 60
ExtVLANProto Extension = 60
// ExtRand returns a uniformly random uint32.
ExtRand = 56
ExtRand Extension = 56
)
// The following gives names to various bit patterns used in opcode construction.
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bpf
// A Setter is a type which can attach a compiled BPF filter to itself.
type Setter interface {
SetBPF(filter []RawInstruction) error
}
-3
View File
@@ -149,9 +149,6 @@ func testOSVM(t *testing.T, filter []bpf.Instruction) (virtualMachine, func()) {
p := ipv4.NewPacketConn(l)
if err = p.SetBPF(prog); err != nil {
if err.Error() == "operation not supported" { // TODO: gross. remove once 19051 fixed.
t.Skip("Skipping until Issue 19051 is fixed.")
}
t.Fatalf("failed to attach BPF program to listener: %v", err)
}
-102
View File
@@ -36,103 +36,6 @@
// Contexts.
package context // import "golang.org/x/net/context"
import "time"
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See http://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stores using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "golang.org/x/net/context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key interface{}) interface{}
}
// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
@@ -149,8 +52,3 @@ func Background() Context {
func TODO() Context {
return todo
}
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()
+20
View File
@@ -0,0 +1,20 @@
// 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 context
import "context" // standard library's context, as of Go 1.7
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context = context.Context
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc = context.CancelFunc
+109
View File
@@ -0,0 +1,109 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.9
package context
import "time"
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See http://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stores using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "golang.org/x/net/context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key interface{}) interface{}
}
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()
+7 -2
View File
@@ -11,16 +11,21 @@ import (
"golang.org/x/net/context"
)
// This example passes a context with a timeout to tell a blocking function that
// it should abandon its work after the timeout elapses.
func ExampleWithTimeout() {
// Pass a context with a timeout to tell a blocking function that it
// should abandon its work after the timeout elapses.
ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond)
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
select {
case <-time.After(200 * time.Millisecond):
case <-time.After(1 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
}
// Output:
// context deadline exceeded
}
+132
View File
@@ -0,0 +1,132 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package dnsmessage_test
import (
"fmt"
"net"
"strings"
"golang.org/x/net/dns/dnsmessage"
)
func mustNewName(name string) dnsmessage.Name {
n, err := dnsmessage.NewName(name)
if err != nil {
panic(err)
}
return n
}
func ExampleParser() {
msg := dnsmessage.Message{
Header: dnsmessage.Header{Response: true, Authoritative: true},
Questions: []dnsmessage.Question{
{
Name: mustNewName("foo.bar.example.com."),
Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET,
},
{
Name: mustNewName("bar.example.com."),
Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET,
},
},
Answers: []dnsmessage.Resource{
{
dnsmessage.ResourceHeader{
Name: mustNewName("foo.bar.example.com."),
Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET,
},
&dnsmessage.AResource{[4]byte{127, 0, 0, 1}},
},
{
dnsmessage.ResourceHeader{
Name: mustNewName("bar.example.com."),
Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET,
},
&dnsmessage.AResource{[4]byte{127, 0, 0, 2}},
},
},
}
buf, err := msg.Pack()
if err != nil {
panic(err)
}
wantName := "bar.example.com."
var p dnsmessage.Parser
if _, err := p.Start(buf); err != nil {
panic(err)
}
for {
q, err := p.Question()
if err == dnsmessage.ErrSectionDone {
break
}
if err != nil {
panic(err)
}
if q.Name.String() != wantName {
continue
}
fmt.Println("Found question for name", wantName)
if err := p.SkipAllQuestions(); err != nil {
panic(err)
}
break
}
var gotIPs []net.IP
for {
h, err := p.AnswerHeader()
if err == dnsmessage.ErrSectionDone {
break
}
if err != nil {
panic(err)
}
if (h.Type != dnsmessage.TypeA && h.Type != dnsmessage.TypeAAAA) || h.Class != dnsmessage.ClassINET {
continue
}
if !strings.EqualFold(h.Name.String(), wantName) {
if err := p.SkipAnswer(); err != nil {
panic(err)
}
continue
}
switch h.Type {
case dnsmessage.TypeA:
r, err := p.AResource()
if err != nil {
panic(err)
}
gotIPs = append(gotIPs, r.A[:])
case dnsmessage.TypeAAAA:
r, err := p.AAAAResource()
if err != nil {
panic(err)
}
gotIPs = append(gotIPs, r.AAAA[:])
}
}
fmt.Printf("Found A/AAAA records for name %s: %v\n", wantName, gotIPs)
// Output:
// Found question for name bar.example.com.
// Found A/AAAA records for name bar.example.com.: [127.0.0.2]
}
+757 -178
View File
File diff suppressed because it is too large Load Diff
+676 -135
View File
@@ -5,13 +5,20 @@
package dnsmessage
import (
"bytes"
"fmt"
"net"
"reflect"
"strings"
"testing"
)
func mustNewName(name string) Name {
n, err := NewName(name)
if err != nil {
panic(err)
}
return n
}
func (m *Message) String() string {
s := fmt.Sprintf("Message: %#v\n", &m.Header)
if len(m.Questions) > 0 {
@@ -41,9 +48,17 @@ func (m *Message) String() string {
return s
}
func TestNameString(t *testing.T) {
want := "foo"
name := mustNewName(want)
if got := fmt.Sprint(name); got != want {
t.Errorf("got fmt.Sprint(%#v) = %s, want = %s", name, got, want)
}
}
func TestQuestionPackUnpack(t *testing.T) {
want := Question{
Name: ".",
Name: mustNewName("."),
Type: TypeA,
Class: ClassINET,
}
@@ -68,16 +83,42 @@ func TestQuestionPackUnpack(t *testing.T) {
}
}
func TestName(t *testing.T) {
tests := []string{
"",
".",
"google..com",
"google.com",
"google..com.",
"google.com.",
".google.com.",
"www..google.com.",
"www.google.com.",
}
for _, test := range tests {
n, err := NewName(test)
if err != nil {
t.Errorf("Creating name for %q: %v", test, err)
continue
}
if ns := n.String(); ns != test {
t.Errorf("Got %#v.String() = %q, want = %q", n, ns, test)
continue
}
}
}
func TestNamePackUnpack(t *testing.T) {
tests := []struct {
in string
want string
err error
}{
{"", ".", nil},
{"", "", errNonCanonicalName},
{".", ".", nil},
{"google..com", "", errZeroSegLen},
{"google.com", "google.com.", nil},
{"google..com", "", errNonCanonicalName},
{"google.com", "", errNonCanonicalName},
{"google..com.", "", errZeroSegLen},
{"google.com.", "google.com.", nil},
{".google.com.", "", errZeroSegLen},
@@ -86,29 +127,91 @@ func TestNamePackUnpack(t *testing.T) {
}
for _, test := range tests {
buf, err := packName(make([]byte, 0, 30), test.in, map[string]int{})
in := mustNewName(test.in)
want := mustNewName(test.want)
buf, err := in.pack(make([]byte, 0, 30), map[string]int{})
if err != test.err {
t.Errorf("Packing of %s: got err = %v, want err = %v", test.in, err, test.err)
t.Errorf("Packing of %q: got err = %v, want err = %v", test.in, err, test.err)
continue
}
if test.err != nil {
continue
}
got, n, err := unpackName(buf, 0)
var got Name
n, err := got.unpack(buf, 0)
if err != nil {
t.Errorf("Unpacking for %s failed: %v", test.in, err)
t.Errorf("Unpacking for %q failed: %v", test.in, err)
continue
}
if n != len(buf) {
t.Errorf(
"Unpacked different amount than packed for %s: got n = %d, want = %d",
"Unpacked different amount than packed for %q: got n = %d, want = %d",
test.in,
n,
len(buf),
)
}
if got != test.want {
t.Errorf("Unpacking packing of %s: got = %s, want = %s", test.in, got, test.want)
if got != want {
t.Errorf("Unpacking packing of %q: got = %#v, want = %#v", test.in, got, want)
}
}
}
func checkErrorPrefix(err error, prefix string) bool {
e, ok := err.(*nestedError)
return ok && e.s == prefix
}
func TestHeaderUnpackError(t *testing.T) {
wants := []string{
"id",
"bits",
"questions",
"answers",
"authorities",
"additionals",
}
var buf []byte
var h header
for _, want := range wants {
n, err := h.unpack(buf, 0)
if n != 0 || !checkErrorPrefix(err, want) {
t.Errorf("got h.unpack([%d]byte, 0) = %d, %v, want = 0, %s", len(buf), n, err, want)
}
buf = append(buf, 0, 0)
}
}
func TestParserStart(t *testing.T) {
const want = "unpacking header"
var p Parser
for i := 0; i <= 1; i++ {
_, err := p.Start([]byte{})
if !checkErrorPrefix(err, want) {
t.Errorf("got p.Start(nil) = _, %v, want = _, %s", err, want)
}
}
}
func TestResourceNotStarted(t *testing.T) {
tests := []struct {
name string
fn func(*Parser) error
}{
{"CNAMEResource", func(p *Parser) error { _, err := p.CNAMEResource(); return err }},
{"MXResource", func(p *Parser) error { _, err := p.MXResource(); return err }},
{"NSResource", func(p *Parser) error { _, err := p.NSResource(); return err }},
{"PTRResource", func(p *Parser) error { _, err := p.PTRResource(); return err }},
{"SOAResource", func(p *Parser) error { _, err := p.SOAResource(); return err }},
{"TXTResource", func(p *Parser) error { _, err := p.TXTResource(); return err }},
{"SRVResource", func(p *Parser) error { _, err := p.SRVResource(); return err }},
{"AResource", func(p *Parser) error { _, err := p.AResource(); return err }},
{"AAAAResource", func(p *Parser) error { _, err := p.AAAAResource(); return err }},
}
for _, test := range tests {
if err := test.fn(&Parser{}); err != ErrNotStarted {
t.Errorf("got _, %v = p.%s(), want = _, %v", err, test.name, ErrNotStarted)
}
}
}
@@ -118,7 +221,7 @@ func TestDNSPackUnpack(t *testing.T) {
{
Questions: []Question{
{
Name: ".",
Name: mustNewName("."),
Type: TypeAAAA,
Class: ClassINET,
},
@@ -174,6 +277,69 @@ func TestSkipAll(t *testing.T) {
}
}
func TestSkipEach(t *testing.T) {
msg := smallTestMsg()
buf, err := msg.Pack()
if err != nil {
t.Fatal("Packing test message:", err)
}
var p Parser
if _, err := p.Start(buf); err != nil {
t.Fatal(err)
}
tests := []struct {
name string
f func() error
}{
{"SkipQuestion", p.SkipQuestion},
{"SkipAnswer", p.SkipAnswer},
{"SkipAuthority", p.SkipAuthority},
{"SkipAdditional", p.SkipAdditional},
}
for _, test := range tests {
if err := test.f(); err != nil {
t.Errorf("First call: got %s() = %v, want = %v", test.name, err, nil)
}
if err := test.f(); err != ErrSectionDone {
t.Errorf("Second call: got %s() = %v, want = %v", test.name, err, ErrSectionDone)
}
}
}
func TestSkipAfterRead(t *testing.T) {
msg := smallTestMsg()
buf, err := msg.Pack()
if err != nil {
t.Fatal("Packing test message:", err)
}
var p Parser
if _, err := p.Start(buf); err != nil {
t.Fatal(err)
}
tests := []struct {
name string
skip func() error
read func() error
}{
{"Question", p.SkipQuestion, func() error { _, err := p.Question(); return err }},
{"Answer", p.SkipAnswer, func() error { _, err := p.Answer(); return err }},
{"Authority", p.SkipAuthority, func() error { _, err := p.Authority(); return err }},
{"Additional", p.SkipAdditional, func() error { _, err := p.Additional(); return err }},
}
for _, test := range tests {
if err := test.read(); err != nil {
t.Errorf("Got %s() = _, %v, want = _, %v", test.name, err, nil)
}
if err := test.skip(); err != ErrSectionDone {
t.Errorf("Got Skip%s() = %v, want = %v", test.name, err, ErrSectionDone)
}
}
}
func TestSkipNotStarted(t *testing.T) {
var p Parser
@@ -238,206 +404,581 @@ func TestTooManyRecords(t *testing.T) {
}
func TestVeryLongTxt(t *testing.T) {
want := &TXTResource{
ResourceHeader: ResourceHeader{
Name: "foo.bar.example.com.",
want := Resource{
ResourceHeader{
Name: mustNewName("foo.bar.example.com."),
Type: TypeTXT,
Class: ClassINET,
},
Txt: loremIpsum,
&TXTResource{loremIpsum},
}
buf, err := packResource(make([]byte, 0, 8000), want, map[string]int{})
buf, err := want.pack(make([]byte, 0, 8000), map[string]int{})
if err != nil {
t.Fatal("Packing failed:", err)
}
var hdr ResourceHeader
off, err := hdr.unpack(buf, 0)
var got Resource
off, err := got.Header.unpack(buf, 0)
if err != nil {
t.Fatal("Unpacking ResourceHeader failed:", err)
}
got, n, err := unpackResource(buf, off, hdr)
body, n, err := unpackResourceBody(buf, off, got.Header)
if err != nil {
t.Fatal("Unpacking failed:", err)
}
got.Body = body
if n != len(buf) {
t.Errorf("Unpacked different amount than packed: got n = %d, want = %d", n, len(buf))
}
if !reflect.DeepEqual(got, want) {
t.Errorf("Got = %+v, want = %+v", got, want)
t.Errorf("Got = %#v, want = %#v", got, want)
}
}
func ExampleHeaderSearch() {
func TestStartError(t *testing.T) {
tests := []struct {
name string
fn func(*Builder) error
}{
{"Questions", func(b *Builder) error { return b.StartQuestions() }},
{"Answers", func(b *Builder) error { return b.StartAnswers() }},
{"Authorities", func(b *Builder) error { return b.StartAuthorities() }},
{"Additionals", func(b *Builder) error { return b.StartAdditionals() }},
}
envs := []struct {
name string
fn func() *Builder
want error
}{
{"sectionNotStarted", func() *Builder { return &Builder{section: sectionNotStarted} }, ErrNotStarted},
{"sectionDone", func() *Builder { return &Builder{section: sectionDone} }, ErrSectionDone},
}
for _, env := range envs {
for _, test := range tests {
if got := test.fn(env.fn()); got != env.want {
t.Errorf("got Builder{%s}.Start%s = %v, want = %v", env.name, test.name, got, env.want)
}
}
}
}
func TestBuilderResourceError(t *testing.T) {
tests := []struct {
name string
fn func(*Builder) error
}{
{"CNAMEResource", func(b *Builder) error { return b.CNAMEResource(ResourceHeader{}, CNAMEResource{}) }},
{"MXResource", func(b *Builder) error { return b.MXResource(ResourceHeader{}, MXResource{}) }},
{"NSResource", func(b *Builder) error { return b.NSResource(ResourceHeader{}, NSResource{}) }},
{"PTRResource", func(b *Builder) error { return b.PTRResource(ResourceHeader{}, PTRResource{}) }},
{"SOAResource", func(b *Builder) error { return b.SOAResource(ResourceHeader{}, SOAResource{}) }},
{"TXTResource", func(b *Builder) error { return b.TXTResource(ResourceHeader{}, TXTResource{}) }},
{"SRVResource", func(b *Builder) error { return b.SRVResource(ResourceHeader{}, SRVResource{}) }},
{"AResource", func(b *Builder) error { return b.AResource(ResourceHeader{}, AResource{}) }},
{"AAAAResource", func(b *Builder) error { return b.AAAAResource(ResourceHeader{}, AAAAResource{}) }},
}
envs := []struct {
name string
fn func() *Builder
want error
}{
{"sectionNotStarted", func() *Builder { return &Builder{section: sectionNotStarted} }, ErrNotStarted},
{"sectionHeader", func() *Builder { return &Builder{section: sectionHeader} }, ErrNotStarted},
{"sectionQuestions", func() *Builder { return &Builder{section: sectionQuestions} }, ErrNotStarted},
{"sectionDone", func() *Builder { return &Builder{section: sectionDone} }, ErrSectionDone},
}
for _, env := range envs {
for _, test := range tests {
if got := test.fn(env.fn()); got != env.want {
t.Errorf("got Builder{%s}.%s = %v, want = %v", env.name, test.name, got, env.want)
}
}
}
}
func TestFinishError(t *testing.T) {
var b Builder
want := ErrNotStarted
if _, got := b.Finish(); got != want {
t.Errorf("got Builder{}.Finish() = %v, want = %v", got, want)
}
}
func TestBuilder(t *testing.T) {
msg := largeTestMsg()
want, err := msg.Pack()
if err != nil {
t.Fatal("Packing without builder:", err)
}
var b Builder
b.Start(nil, msg.Header)
if err := b.StartQuestions(); err != nil {
t.Fatal("b.StartQuestions():", err)
}
for _, q := range msg.Questions {
if err := b.Question(q); err != nil {
t.Fatalf("b.Question(%#v): %v", q, err)
}
}
if err := b.StartAnswers(); err != nil {
t.Fatal("b.StartAnswers():", err)
}
for _, a := range msg.Answers {
switch a.Header.Type {
case TypeA:
if err := b.AResource(a.Header, *a.Body.(*AResource)); err != nil {
t.Fatalf("b.AResource(%#v): %v", a, err)
}
case TypeNS:
if err := b.NSResource(a.Header, *a.Body.(*NSResource)); err != nil {
t.Fatalf("b.NSResource(%#v): %v", a, err)
}
case TypeCNAME:
if err := b.CNAMEResource(a.Header, *a.Body.(*CNAMEResource)); err != nil {
t.Fatalf("b.CNAMEResource(%#v): %v", a, err)
}
case TypeSOA:
if err := b.SOAResource(a.Header, *a.Body.(*SOAResource)); err != nil {
t.Fatalf("b.SOAResource(%#v): %v", a, err)
}
case TypePTR:
if err := b.PTRResource(a.Header, *a.Body.(*PTRResource)); err != nil {
t.Fatalf("b.PTRResource(%#v): %v", a, err)
}
case TypeMX:
if err := b.MXResource(a.Header, *a.Body.(*MXResource)); err != nil {
t.Fatalf("b.MXResource(%#v): %v", a, err)
}
case TypeTXT:
if err := b.TXTResource(a.Header, *a.Body.(*TXTResource)); err != nil {
t.Fatalf("b.TXTResource(%#v): %v", a, err)
}
case TypeAAAA:
if err := b.AAAAResource(a.Header, *a.Body.(*AAAAResource)); err != nil {
t.Fatalf("b.AAAAResource(%#v): %v", a, err)
}
case TypeSRV:
if err := b.SRVResource(a.Header, *a.Body.(*SRVResource)); err != nil {
t.Fatalf("b.SRVResource(%#v): %v", a, err)
}
}
}
if err := b.StartAuthorities(); err != nil {
t.Fatal("b.StartAuthorities():", err)
}
for _, a := range msg.Authorities {
if err := b.NSResource(a.Header, *a.Body.(*NSResource)); err != nil {
t.Fatalf("b.NSResource(%#v): %v", a, err)
}
}
if err := b.StartAdditionals(); err != nil {
t.Fatal("b.StartAdditionals():", err)
}
for _, a := range msg.Additionals {
if err := b.TXTResource(a.Header, *a.Body.(*TXTResource)); err != nil {
t.Fatalf("b.TXTResource(%#v): %v", a, err)
}
}
got, err := b.Finish()
if err != nil {
t.Fatal("b.Finish():", err)
}
if !bytes.Equal(got, want) {
t.Fatalf("Got from Builder: %#v\nwant = %#v", got, want)
}
}
func TestResourcePack(t *testing.T) {
for _, tt := range []struct {
m Message
err error
}{
{
Message{
Questions: []Question{
{
Name: mustNewName("."),
Type: TypeAAAA,
Class: ClassINET,
},
},
Answers: []Resource{{ResourceHeader{}, nil}},
},
&nestedError{"packing Answer", errNilResouceBody},
},
{
Message{
Questions: []Question{
{
Name: mustNewName("."),
Type: TypeAAAA,
Class: ClassINET,
},
},
Authorities: []Resource{{ResourceHeader{}, (*NSResource)(nil)}},
},
&nestedError{"packing Authority",
&nestedError{"ResourceHeader",
&nestedError{"Name", errNonCanonicalName},
},
},
},
{
Message{
Questions: []Question{
{
Name: mustNewName("."),
Type: TypeA,
Class: ClassINET,
},
},
Additionals: []Resource{{ResourceHeader{}, nil}},
},
&nestedError{"packing Additional", errNilResouceBody},
},
} {
_, err := tt.m.Pack()
if !reflect.DeepEqual(err, tt.err) {
t.Errorf("got %v for %v; want %v", err, tt.m, tt.err)
}
}
}
func BenchmarkParsing(b *testing.B) {
b.ReportAllocs()
name := mustNewName("foo.bar.example.com.")
msg := Message{
Header: Header{Response: true, Authoritative: true},
Questions: []Question{
{
Name: "foo.bar.example.com.",
Type: TypeA,
Class: ClassINET,
},
{
Name: "bar.example.com.",
Name: name,
Type: TypeA,
Class: ClassINET,
},
},
Answers: []Resource{
&AResource{
ResourceHeader: ResourceHeader{
Name: "foo.bar.example.com.",
Type: TypeA,
{
ResourceHeader{
Name: name,
Class: ClassINET,
},
A: [4]byte{127, 0, 0, 1},
&AResource{[4]byte{}},
},
&AResource{
ResourceHeader: ResourceHeader{
Name: "bar.example.com.",
Type: TypeA,
{
ResourceHeader{
Name: name,
Class: ClassINET,
},
A: [4]byte{127, 0, 0, 2},
&AAAAResource{[16]byte{}},
},
{
ResourceHeader{
Name: name,
Class: ClassINET,
},
&CNAMEResource{name},
},
{
ResourceHeader{
Name: name,
Class: ClassINET,
},
&NSResource{name},
},
},
}
buf, err := msg.Pack()
if err != nil {
panic(err)
b.Fatal("msg.Pack():", err)
}
wantName := "bar.example.com."
var p Parser
if _, err := p.Start(buf); err != nil {
panic(err)
}
for {
q, err := p.Question()
if err == ErrSectionDone {
break
}
if err != nil {
panic(err)
for i := 0; i < b.N; i++ {
var p Parser
if _, err := p.Start(buf); err != nil {
b.Fatal("p.Start(buf):", err)
}
if q.Name != wantName {
continue
for {
_, err := p.Question()
if err == ErrSectionDone {
break
}
if err != nil {
b.Fatal("p.Question():", err)
}
}
fmt.Println("Found question for name", wantName)
if err := p.SkipAllQuestions(); err != nil {
panic(err)
}
break
}
var gotIPs []net.IP
for {
h, err := p.AnswerHeader()
if err == ErrSectionDone {
break
}
if err != nil {
panic(err)
}
if (h.Type != TypeA && h.Type != TypeAAAA) || h.Class != ClassINET {
continue
}
if !strings.EqualFold(h.Name, wantName) {
if err := p.SkipAnswer(); err != nil {
for {
h, err := p.AnswerHeader()
if err == ErrSectionDone {
break
}
if err != nil {
panic(err)
}
continue
}
a, err := p.Answer()
if err != nil {
panic(err)
}
switch r := a.(type) {
default:
panic(fmt.Sprintf("unknown type: %T", r))
case *AResource:
gotIPs = append(gotIPs, r.A[:])
case *AAAAResource:
gotIPs = append(gotIPs, r.AAAA[:])
switch h.Type {
case TypeA:
if _, err := p.AResource(); err != nil {
b.Fatal("p.AResource():", err)
}
case TypeAAAA:
if _, err := p.AAAAResource(); err != nil {
b.Fatal("p.AAAAResource():", err)
}
case TypeCNAME:
if _, err := p.CNAMEResource(); err != nil {
b.Fatal("p.CNAMEResource():", err)
}
case TypeNS:
if _, err := p.NSResource(); err != nil {
b.Fatal("p.NSResource():", err)
}
default:
b.Fatalf("unknown type: %T", h)
}
}
}
fmt.Printf("Found A/AAAA records for name %s: %v\n", wantName, gotIPs)
// Output:
// Found question for name bar.example.com.
// Found A/AAAA records for name bar.example.com.: [127.0.0.2]
}
func largeTestMsg() Message {
func BenchmarkBuilding(b *testing.B) {
b.ReportAllocs()
name := mustNewName("foo.bar.example.com.")
buf := make([]byte, 0, packStartingCap)
for i := 0; i < b.N; i++ {
var bld Builder
bld.StartWithoutCompression(buf, Header{Response: true, Authoritative: true})
if err := bld.StartQuestions(); err != nil {
b.Fatal("bld.StartQuestions():", err)
}
q := Question{
Name: name,
Type: TypeA,
Class: ClassINET,
}
if err := bld.Question(q); err != nil {
b.Fatalf("bld.Question(%+v): %v", q, err)
}
hdr := ResourceHeader{
Name: name,
Class: ClassINET,
}
if err := bld.StartAnswers(); err != nil {
b.Fatal("bld.StartQuestions():", err)
}
ar := AResource{[4]byte{}}
if err := bld.AResource(hdr, ar); err != nil {
b.Fatalf("bld.AResource(%+v, %+v): %v", hdr, ar, err)
}
aaar := AAAAResource{[16]byte{}}
if err := bld.AAAAResource(hdr, aaar); err != nil {
b.Fatalf("bld.AAAAResource(%+v, %+v): %v", hdr, aaar, err)
}
cnr := CNAMEResource{name}
if err := bld.CNAMEResource(hdr, cnr); err != nil {
b.Fatalf("bld.CNAMEResource(%+v, %+v): %v", hdr, cnr, err)
}
nsr := NSResource{name}
if err := bld.NSResource(hdr, nsr); err != nil {
b.Fatalf("bld.NSResource(%+v, %+v): %v", hdr, nsr, err)
}
if _, err := bld.Finish(); err != nil {
b.Fatal("bld.Finish():", err)
}
}
}
func smallTestMsg() Message {
name := mustNewName("example.com.")
return Message{
Header: Header{Response: true, Authoritative: true},
Questions: []Question{
{
Name: "foo.bar.example.com.",
Name: name,
Type: TypeA,
Class: ClassINET,
},
},
Answers: []Resource{
&AResource{
ResourceHeader: ResourceHeader{
Name: "foo.bar.example.com.",
{
ResourceHeader{
Name: name,
Type: TypeA,
Class: ClassINET,
},
A: [4]byte{127, 0, 0, 1},
},
&AResource{
ResourceHeader: ResourceHeader{
Name: "foo.bar.example.com.",
Type: TypeA,
Class: ClassINET,
},
A: [4]byte{127, 0, 0, 2},
&AResource{[4]byte{127, 0, 0, 1}},
},
},
Authorities: []Resource{
&NSResource{
ResourceHeader: ResourceHeader{
Name: "foo.bar.example.com.",
Type: TypeNS,
{
ResourceHeader{
Name: name,
Type: TypeA,
Class: ClassINET,
},
NS: "ns1.example.com.",
},
&NSResource{
ResourceHeader: ResourceHeader{
Name: "foo.bar.example.com.",
Type: TypeNS,
Class: ClassINET,
},
NS: "ns2.example.com.",
&AResource{[4]byte{127, 0, 0, 1}},
},
},
Additionals: []Resource{
&TXTResource{
ResourceHeader: ResourceHeader{
Name: "foo.bar.example.com.",
Type: TypeTXT,
{
ResourceHeader{
Name: name,
Type: TypeA,
Class: ClassINET,
},
Txt: "So Long, and Thanks for All the Fish",
&AResource{[4]byte{127, 0, 0, 1}},
},
&TXTResource{
ResourceHeader: ResourceHeader{
Name: "foo.bar.example.com.",
},
}
}
func largeTestMsg() Message {
name := mustNewName("foo.bar.example.com.")
return Message{
Header: Header{Response: true, Authoritative: true},
Questions: []Question{
{
Name: name,
Type: TypeA,
Class: ClassINET,
},
},
Answers: []Resource{
{
ResourceHeader{
Name: name,
Type: TypeA,
Class: ClassINET,
},
&AResource{[4]byte{127, 0, 0, 1}},
},
{
ResourceHeader{
Name: name,
Type: TypeA,
Class: ClassINET,
},
&AResource{[4]byte{127, 0, 0, 2}},
},
{
ResourceHeader{
Name: name,
Type: TypeAAAA,
Class: ClassINET,
},
&AAAAResource{[16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}},
},
{
ResourceHeader{
Name: name,
Type: TypeCNAME,
Class: ClassINET,
},
&CNAMEResource{mustNewName("alias.example.com.")},
},
{
ResourceHeader{
Name: name,
Type: TypeSOA,
Class: ClassINET,
},
&SOAResource{
NS: mustNewName("ns1.example.com."),
MBox: mustNewName("mb.example.com."),
Serial: 1,
Refresh: 2,
Retry: 3,
Expire: 4,
MinTTL: 5,
},
},
{
ResourceHeader{
Name: name,
Type: TypePTR,
Class: ClassINET,
},
&PTRResource{mustNewName("ptr.example.com.")},
},
{
ResourceHeader{
Name: name,
Type: TypeMX,
Class: ClassINET,
},
&MXResource{
7,
mustNewName("mx.example.com."),
},
},
{
ResourceHeader{
Name: name,
Type: TypeSRV,
Class: ClassINET,
},
&SRVResource{
8,
9,
11,
mustNewName("srv.example.com."),
},
},
},
Authorities: []Resource{
{
ResourceHeader{
Name: name,
Type: TypeNS,
Class: ClassINET,
},
&NSResource{mustNewName("ns1.example.com.")},
},
{
ResourceHeader{
Name: name,
Type: TypeNS,
Class: ClassINET,
},
&NSResource{mustNewName("ns2.example.com.")},
},
},
Additionals: []Resource{
{
ResourceHeader{
Name: name,
Type: TypeTXT,
Class: ClassINET,
},
Txt: "Hamster Huey and the Gooey Kablooie",
&TXTResource{"So Long, and Thanks for All the Fish"},
},
{
ResourceHeader{
Name: name,
Type: TypeTXT,
Class: ClassINET,
},
&TXTResource{"Hamster Huey and the Gooey Kablooie"},
},
},
}
+1 -1
View File
@@ -9,7 +9,7 @@ import "testing"
func TestIsBadCipherBad(t *testing.T) {
for _, c := range badCiphers {
if !isBadCipher(c) {
t.Errorf("Wrong result for isBadCipher(%d), want true")
t.Errorf("Wrong result for isBadCipher(%d), want true", c)
}
}
}
+1 -1
View File
@@ -56,7 +56,7 @@ func configureTransport(t1 *http.Transport) (*Transport, error) {
}
// registerHTTPSProtocol calls Transport.RegisterProtocol but
// convering panics into errors.
// converting panics into errors.
func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error) {
defer func() {
if e := recover(); e != nil {
+4 -4
View File
@@ -71,13 +71,13 @@ func testDataBuffer(t *testing.T, wantBytes []byte, setup func(t *testing.T) *da
func TestDataBufferAllocation(t *testing.T) {
writes := [][]byte{
bytes.Repeat([]byte("a"), 1*1024-1),
[]byte{'a'},
[]byte("a"),
bytes.Repeat([]byte("b"), 4*1024-1),
[]byte{'b'},
[]byte("b"),
bytes.Repeat([]byte("c"), 8*1024-1),
[]byte{'c'},
[]byte("c"),
bytes.Repeat([]byte("d"), 16*1024-1),
[]byte{'d'},
[]byte("d"),
bytes.Repeat([]byte("e"), 32*1024),
}
var wantRead bytes.Buffer
+8 -5
View File
@@ -87,13 +87,16 @@ type goAwayFlowError struct{}
func (goAwayFlowError) Error() string { return "connection exceeded flow control window size" }
// connErrorReason wraps a ConnectionError with an informative error about why it occurs.
// connError represents an HTTP/2 ConnectionError error code, along
// with a string (for debugging) explaining why.
//
// Errors of this type are only returned by the frame parser functions
// and converted into ConnectionError(ErrCodeProtocol).
// and converted into ConnectionError(Code), after stashing away
// the Reason into the Framer's errDetail field, accessible via
// the (*Framer).ErrorDetail method.
type connError struct {
Code ErrCode
Reason string
Code ErrCode // the ConnectionError error code
Reason string // additional reason
}
func (e connError) Error() string {
+2
View File
@@ -52,3 +52,5 @@ func reqGetBody(req *http.Request) func() (io.ReadCloser, error) {
func reqBodyIsNoBody(body io.ReadCloser) bool {
return body == http.NoBody
}
func go18httpNoBody() io.ReadCloser { return http.NoBody } // for tests only
+16
View File
@@ -0,0 +1,16 @@
// 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
}
+60
View File
@@ -0,0 +1,60 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.9
package http2
import (
"context"
"net/http"
"reflect"
"testing"
"time"
)
func TestServerGracefulShutdown(t *testing.T) {
var st *serverTester
handlerDone := make(chan struct{})
st = newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
defer close(handlerDone)
go st.ts.Config.Shutdown(context.Background())
ga := st.wantGoAway()
if ga.ErrCode != ErrCodeNo {
t.Errorf("GOAWAY error = %v; want ErrCodeNo", ga.ErrCode)
}
if ga.LastStreamID != 1 {
t.Errorf("GOAWAY LastStreamID = %v; want 1", ga.LastStreamID)
}
w.Header().Set("x-foo", "bar")
})
defer st.Close()
st.greet()
st.bodylessReq1()
select {
case <-handlerDone:
case <-time.After(5 * time.Second):
t.Fatalf("server did not shutdown?")
}
hf := st.wantHeaders()
goth := st.decodeHeader(hf.HeaderBlockFragment())
wanth := [][2]string{
{":status", "200"},
{"x-foo", "bar"},
{"content-type", "text/plain; charset=utf-8"},
{"content-length", "0"},
}
if !reflect.DeepEqual(goth, wanth) {
t.Errorf("Got headers %v; want %v", goth, wanth)
}
n, err := st.cc.Read([]byte{0})
if n != 0 || err == nil {
t.Errorf("Read = %v, %v; want 0, non-nil", n, err)
}
}
+9
View File
@@ -460,6 +460,15 @@ func (app *h2i) readFrames() error {
app.hdec = hpack.NewDecoder(tableSize, app.onNewHeaderField)
}
app.hdec.Write(f.HeaderBlockFragment())
case *http2.PushPromiseFrame:
if app.hdec == nil {
// TODO: if the user uses h2i to send a SETTINGS frame advertising
// something larger, we'll need to respect SETTINGS_HEADER_TABLE_SIZE
// and stuff here instead of using the 4k default. But for now:
tableSize := uint32(4 << 10)
app.hdec = hpack.NewDecoder(tableSize, app.onNewHeaderField)
}
app.hdec.Write(f.HeaderBlockFragment())
}
}
}
+4
View File
@@ -648,6 +648,10 @@ func TestHuffmanFuzzCrash(t *testing.T) {
}
}
func pair(name, value string) HeaderField {
return HeaderField{Name: name, Value: value}
}
func dehex(s string) []byte {
s = strings.Replace(s, " ", "", -1)
s = strings.Replace(s, "\n", "", -1)
+61 -65
View File
@@ -125,74 +125,70 @@ func (t *headerFieldTable) idToIndex(id uint64) uint64 {
return k + 1
}
func pair(name, value string) HeaderField {
return HeaderField{Name: name, Value: value}
}
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B
var staticTable = newStaticTable()
var staticTableEntries = [...]HeaderField{
pair(":authority", ""),
pair(":method", "GET"),
pair(":method", "POST"),
pair(":path", "/"),
pair(":path", "/index.html"),
pair(":scheme", "http"),
pair(":scheme", "https"),
pair(":status", "200"),
pair(":status", "204"),
pair(":status", "206"),
pair(":status", "304"),
pair(":status", "400"),
pair(":status", "404"),
pair(":status", "500"),
pair("accept-charset", ""),
pair("accept-encoding", "gzip, deflate"),
pair("accept-language", ""),
pair("accept-ranges", ""),
pair("accept", ""),
pair("access-control-allow-origin", ""),
pair("age", ""),
pair("allow", ""),
pair("authorization", ""),
pair("cache-control", ""),
pair("content-disposition", ""),
pair("content-encoding", ""),
pair("content-language", ""),
pair("content-length", ""),
pair("content-location", ""),
pair("content-range", ""),
pair("content-type", ""),
pair("cookie", ""),
pair("date", ""),
pair("etag", ""),
pair("expect", ""),
pair("expires", ""),
pair("from", ""),
pair("host", ""),
pair("if-match", ""),
pair("if-modified-since", ""),
pair("if-none-match", ""),
pair("if-range", ""),
pair("if-unmodified-since", ""),
pair("last-modified", ""),
pair("link", ""),
pair("location", ""),
pair("max-forwards", ""),
pair("proxy-authenticate", ""),
pair("proxy-authorization", ""),
pair("range", ""),
pair("referer", ""),
pair("refresh", ""),
pair("retry-after", ""),
pair("server", ""),
pair("set-cookie", ""),
pair("strict-transport-security", ""),
pair("transfer-encoding", ""),
pair("user-agent", ""),
pair("vary", ""),
pair("via", ""),
pair("www-authenticate", ""),
{Name: ":authority"},
{Name: ":method", Value: "GET"},
{Name: ":method", Value: "POST"},
{Name: ":path", Value: "/"},
{Name: ":path", Value: "/index.html"},
{Name: ":scheme", Value: "http"},
{Name: ":scheme", Value: "https"},
{Name: ":status", Value: "200"},
{Name: ":status", Value: "204"},
{Name: ":status", Value: "206"},
{Name: ":status", Value: "304"},
{Name: ":status", Value: "400"},
{Name: ":status", Value: "404"},
{Name: ":status", Value: "500"},
{Name: "accept-charset"},
{Name: "accept-encoding", Value: "gzip, deflate"},
{Name: "accept-language"},
{Name: "accept-ranges"},
{Name: "accept"},
{Name: "access-control-allow-origin"},
{Name: "age"},
{Name: "allow"},
{Name: "authorization"},
{Name: "cache-control"},
{Name: "content-disposition"},
{Name: "content-encoding"},
{Name: "content-language"},
{Name: "content-length"},
{Name: "content-location"},
{Name: "content-range"},
{Name: "content-type"},
{Name: "cookie"},
{Name: "date"},
{Name: "etag"},
{Name: "expect"},
{Name: "expires"},
{Name: "from"},
{Name: "host"},
{Name: "if-match"},
{Name: "if-modified-since"},
{Name: "if-none-match"},
{Name: "if-range"},
{Name: "if-unmodified-since"},
{Name: "last-modified"},
{Name: "link"},
{Name: "location"},
{Name: "max-forwards"},
{Name: "proxy-authenticate"},
{Name: "proxy-authorization"},
{Name: "range"},
{Name: "referer"},
{Name: "refresh"},
{Name: "retry-after"},
{Name: "server"},
{Name: "set-cookie"},
{Name: "strict-transport-security"},
{Name: "transfer-encoding"},
{Name: "user-agent"},
{Name: "vary"},
{Name: "via"},
{Name: "www-authenticate"},
}
func newStaticTable() *headerFieldTable {
+6 -2
View File
@@ -376,12 +376,16 @@ func (s *sorter) SortStrings(ss []string) {
// validPseudoPath reports whether v is a valid :path pseudo-header
// value. It must be either:
//
// *) a non-empty string starting with '/', but not with with "//",
// *) a non-empty string starting with '/'
// *) the string '*', for OPTIONS requests.
//
// For now this is only used a quick check for deciding when to clean
// up Opaque URLs before sending requests from the Transport.
// See golang.org/issue/16847
//
// We used to enforce that the path also didn't start with "//", but
// Google's GFE accepts such paths and Chrome sends them, so ignore
// that part of the spec. See golang.org/issue/19103.
func validPseudoPath(v string) bool {
return (len(v) > 0 && v[0] == '/' && (len(v) == 1 || v[1] != '/')) || v == "*"
return (len(v) > 0 && v[0] == '/') || v == "*"
}
+2
View File
@@ -25,3 +25,5 @@ func reqGetBody(req *http.Request) func() (io.ReadCloser, error) {
}
func reqBodyIsNoBody(io.ReadCloser) bool { return false }
func go18httpNoBody() io.ReadCloser { return nil } // for tests only
+16
View File
@@ -0,0 +1,16 @@
// 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
}
+13 -3
View File
@@ -15,8 +15,8 @@ import (
// underlying buffer is an interface. (io.Pipe is always unbuffered)
type pipe struct {
mu sync.Mutex
c sync.Cond // c.L lazily initialized to &p.mu
b pipeBuffer
c sync.Cond // c.L lazily initialized to &p.mu
b pipeBuffer // nil when done reading
err error // read error once empty. non-nil means closed.
breakErr error // immediate read error (caller doesn't see rest of b)
donec chan struct{} // closed on error
@@ -32,6 +32,9 @@ type pipeBuffer interface {
func (p *pipe) Len() int {
p.mu.Lock()
defer p.mu.Unlock()
if p.b == nil {
return 0
}
return p.b.Len()
}
@@ -47,7 +50,7 @@ func (p *pipe) Read(d []byte) (n int, err error) {
if p.breakErr != nil {
return 0, p.breakErr
}
if p.b.Len() > 0 {
if p.b != nil && p.b.Len() > 0 {
return p.b.Read(d)
}
if p.err != nil {
@@ -55,6 +58,7 @@ func (p *pipe) Read(d []byte) (n int, err error) {
p.readFn() // e.g. copy trailers
p.readFn = nil // not sticky like p.err
}
p.b = nil
return 0, p.err
}
p.c.Wait()
@@ -75,6 +79,9 @@ func (p *pipe) Write(d []byte) (n int, err error) {
if p.err != nil {
return 0, errClosedPipeWrite
}
if p.breakErr != nil {
return len(d), nil // discard when there is no reader
}
return p.b.Write(d)
}
@@ -109,6 +116,9 @@ func (p *pipe) closeWithError(dst *error, err error, fn func()) {
return
}
p.readFn = fn
if dst == &p.breakErr {
p.b = nil
}
*dst = err
p.closeDoneLocked()
}
+21
View File
@@ -92,6 +92,13 @@ func TestPipeCloseWithError(t *testing.T) {
if err != a {
t.Logf("read error = %v, %v", err, a)
}
// Read and Write should fail.
if n, err := p.Write([]byte("abc")); err != errClosedPipeWrite || n != 0 {
t.Errorf("Write(abc) after close\ngot %v, %v\nwant 0, %v", n, err, errClosedPipeWrite)
}
if n, err := p.Read(make([]byte, 1)); err == nil || n != 0 {
t.Errorf("Read() after close\ngot %v, nil\nwant 0, %v", n, errClosedPipeWrite)
}
}
func TestPipeBreakWithError(t *testing.T) {
@@ -106,4 +113,18 @@ func TestPipeBreakWithError(t *testing.T) {
if err != a {
t.Logf("read error = %v, %v", err, a)
}
if p.b != nil {
t.Errorf("buffer should be nil after BreakWithError")
}
// Write should succeed silently.
if n, err := p.Write([]byte("abc")); err != nil || n != 3 {
t.Errorf("Write(abc) after break\ngot %v, %v\nwant 0, nil", n, err)
}
if p.b != nil {
t.Errorf("buffer should be nil after Write")
}
// Read should fail.
if n, err := p.Read(make([]byte, 1)); err == nil || n != 0 {
t.Errorf("Read() after close\ngot %v, nil\nwant 0, not nil", n)
}
}
+150 -71
View File
@@ -126,6 +126,11 @@ type Server struct {
// NewWriteScheduler constructs a write scheduler for a connection.
// If nil, a default scheduler is chosen.
NewWriteScheduler func() WriteScheduler
// Internal state. This is a pointer (rather than embedded directly)
// so that we don't embed a Mutex in this struct, which will make the
// struct non-copyable, which might break some callers.
state *serverInternalState
}
func (s *Server) initialConnRecvWindowSize() int32 {
@@ -156,6 +161,40 @@ func (s *Server) maxConcurrentStreams() uint32 {
return defaultMaxStreams
}
type serverInternalState struct {
mu sync.Mutex
activeConns map[*serverConn]struct{}
}
func (s *serverInternalState) registerConn(sc *serverConn) {
if s == nil {
return // if the Server was used without calling ConfigureServer
}
s.mu.Lock()
s.activeConns[sc] = struct{}{}
s.mu.Unlock()
}
func (s *serverInternalState) unregisterConn(sc *serverConn) {
if s == nil {
return // if the Server was used without calling ConfigureServer
}
s.mu.Lock()
delete(s.activeConns, sc)
s.mu.Unlock()
}
func (s *serverInternalState) startGracefulShutdown() {
if s == nil {
return // if the Server was used without calling ConfigureServer
}
s.mu.Lock()
for sc := range s.activeConns {
sc.startGracefulShutdown()
}
s.mu.Unlock()
}
// ConfigureServer adds HTTP/2 support to a net/http Server.
//
// The configuration conf may be nil.
@@ -168,9 +207,13 @@ func ConfigureServer(s *http.Server, conf *Server) error {
if conf == nil {
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 s.TLSConfig == nil {
s.TLSConfig = new(tls.Config)
@@ -292,7 +335,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
streams: make(map[uint32]*stream),
readFrameCh: make(chan readFrameResult),
wantWriteFrameCh: make(chan FrameWriteRequest, 8),
wantStartPushCh: make(chan startPushRequest, 8),
serveMsgCh: make(chan interface{}, 8),
wroteFrameCh: make(chan frameWriteResult, 1), // buffered; one send in writeFrameAsync
bodyReadCh: make(chan bodyReadMsg), // buffering doesn't matter either way
doneServing: make(chan struct{}),
@@ -305,6 +348,9 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
pushEnabled: true,
}
s.state.registerConn(sc)
defer s.state.unregisterConn(sc)
// The net/http package sets the write deadline from the
// http.Server.WriteTimeout during the TLS handshake, but then
// passes the connection off to us with the deadline already set.
@@ -405,10 +451,9 @@ type serverConn struct {
doneServing chan struct{} // closed when serverConn.serve ends
readFrameCh chan readFrameResult // written by serverConn.readFrames
wantWriteFrameCh chan FrameWriteRequest // from handlers -> serve
wantStartPushCh chan startPushRequest // from handlers -> serve
wroteFrameCh chan frameWriteResult // from writeFrameAsync -> serve, tickles more frame writes
bodyReadCh chan bodyReadMsg // from handlers -> serve
testHookCh chan func(int) // code to run on the serve loop
serveMsgCh chan interface{} // misc messages & code to send to / run on the serve loop
flow flow // conn-wide (not stream-specific) outbound flow control
inflow flow // conn-wide inbound flow control
tlsState *tls.ConnectionState // shared by all handlers, like net/http
@@ -440,14 +485,15 @@ type serverConn struct {
inFrameScheduleLoop bool // whether we're in the scheduleFrameWrite loop
needToSendGoAway bool // we need to schedule a GOAWAY frame write
goAwayCode ErrCode
shutdownTimerCh <-chan time.Time // nil until used
shutdownTimer *time.Timer // nil until used
idleTimer *time.Timer // nil if unused
idleTimerCh <-chan time.Time // nil if unused
shutdownTimer *time.Timer // nil until used
idleTimer *time.Timer // nil if unused
// Owned by the writeFrameAsync goroutine:
headerWriteBuf bytes.Buffer
hpackEncoder *hpack.Encoder
// Used by startGracefulShutdown.
shutdownOnce sync.Once
}
func (sc *serverConn) maxHeaderListSize() uint32 {
@@ -748,19 +794,15 @@ func (sc *serverConn) serve() {
sc.setConnState(http.StateIdle)
if sc.srv.IdleTimeout != 0 {
sc.idleTimer = time.NewTimer(sc.srv.IdleTimeout)
sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer)
defer sc.idleTimer.Stop()
sc.idleTimerCh = sc.idleTimer.C
}
var gracefulShutdownCh <-chan struct{}
if sc.hs != nil {
gracefulShutdownCh = h1ServerShutdownChan(sc.hs)
}
go sc.readFrames() // closed by defer sc.conn.Close above
settingsTimer := time.NewTimer(firstSettingsTimeout)
settingsTimer := time.AfterFunc(firstSettingsTimeout, sc.onSettingsTimer)
defer settingsTimer.Stop()
loopNum := 0
for {
loopNum++
@@ -771,8 +813,6 @@ func (sc *serverConn) serve() {
break
}
sc.writeFrame(wr)
case spr := <-sc.wantStartPushCh:
sc.startPush(spr)
case res := <-sc.wroteFrameCh:
sc.wroteFrame(res)
case res := <-sc.readFrameCh:
@@ -780,26 +820,37 @@ func (sc *serverConn) serve() {
return
}
res.readMore()
if settingsTimer.C != nil {
if settingsTimer != nil {
settingsTimer.Stop()
settingsTimer.C = nil
settingsTimer = nil
}
case m := <-sc.bodyReadCh:
sc.noteBodyRead(m.st, m.n)
case <-settingsTimer.C:
sc.logf("timeout waiting for SETTINGS frames from %v", sc.conn.RemoteAddr())
return
case <-gracefulShutdownCh:
gracefulShutdownCh = nil
sc.startGracefulShutdown()
case <-sc.shutdownTimerCh:
sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr())
return
case <-sc.idleTimerCh:
sc.vlogf("connection is idle")
sc.goAway(ErrCodeNo)
case fn := <-sc.testHookCh:
fn(loopNum)
case msg := <-sc.serveMsgCh:
switch v := msg.(type) {
case func(int):
v(loopNum) // for testing
case *serverMessage:
switch v {
case settingsTimerMsg:
sc.logf("timeout waiting for SETTINGS frames from %v", sc.conn.RemoteAddr())
return
case idleTimerMsg:
sc.vlogf("connection is idle")
sc.goAway(ErrCodeNo)
case shutdownTimerMsg:
sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr())
return
case gracefulShutdownMsg:
sc.startGracefulShutdownInternal()
default:
panic("unknown timer")
}
case *startPushRequest:
sc.startPush(v)
default:
panic(fmt.Sprintf("unexpected type %T", v))
}
}
if sc.inGoAway && sc.curOpenStreams() == 0 && !sc.needToSendGoAway && !sc.writingFrame {
@@ -808,6 +859,36 @@ func (sc *serverConn) serve() {
}
}
func (sc *serverConn) awaitGracefulShutdown(sharedCh <-chan struct{}, privateCh chan struct{}) {
select {
case <-sc.doneServing:
case <-sharedCh:
close(privateCh)
}
}
type serverMessage int
// Message values sent to serveMsgCh.
var (
settingsTimerMsg = new(serverMessage)
idleTimerMsg = new(serverMessage)
shutdownTimerMsg = new(serverMessage)
gracefulShutdownMsg = new(serverMessage)
)
func (sc *serverConn) onSettingsTimer() { sc.sendServeMsg(settingsTimerMsg) }
func (sc *serverConn) onIdleTimer() { sc.sendServeMsg(idleTimerMsg) }
func (sc *serverConn) onShutdownTimer() { sc.sendServeMsg(shutdownTimerMsg) }
func (sc *serverConn) sendServeMsg(msg interface{}) {
sc.serveG.checkNotOn() // NOT
select {
case sc.serveMsgCh <- msg:
case <-sc.doneServing:
}
}
// readPreface reads the ClientPreface greeting from the peer
// or returns an error on timeout or an invalid greeting.
func (sc *serverConn) readPreface() error {
@@ -1125,10 +1206,19 @@ func (sc *serverConn) scheduleFrameWrite() {
sc.inFrameScheduleLoop = false
}
// startGracefulShutdown sends a GOAWAY with ErrCodeNo to tell the
// client we're gracefully shutting down. The connection isn't closed
// until all current streams are done.
// startGracefulShutdown gracefully shuts down a connection. This
// sends GOAWAY with ErrCodeNo to tell the client we're gracefully
// shutting down. The connection isn't closed until all current
// streams are done.
//
// startGracefulShutdown returns immediately; it does not wait until
// the connection has shut down.
func (sc *serverConn) startGracefulShutdown() {
sc.serveG.checkNotOn() // NOT
sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) })
}
func (sc *serverConn) startGracefulShutdownInternal() {
sc.goAwayIn(ErrCodeNo, 0)
}
@@ -1160,8 +1250,7 @@ func (sc *serverConn) goAwayIn(code ErrCode, forceCloseIn time.Duration) {
func (sc *serverConn) shutDownIn(d time.Duration) {
sc.serveG.check()
sc.shutdownTimer = time.NewTimer(d)
sc.shutdownTimerCh = sc.shutdownTimer.C
sc.shutdownTimer = time.AfterFunc(d, sc.onShutdownTimer)
}
func (sc *serverConn) resetStream(se StreamError) {
@@ -1359,7 +1448,7 @@ func (sc *serverConn) closeStream(st *stream, err error) {
sc.idleTimer.Reset(sc.srv.IdleTimeout)
}
if h1ServerKeepAlivesDisabled(sc.hs) {
sc.startGracefulShutdown()
sc.startGracefulShutdownInternal()
}
}
if p := st.body; p != nil {
@@ -1546,7 +1635,7 @@ func (sc *serverConn) processGoAway(f *GoAwayFrame) error {
} else {
sc.vlogf("http2: received GOAWAY %+v, starting graceful shutdown", f)
}
sc.startGracefulShutdown()
sc.startGracefulShutdownInternal()
// http://tools.ietf.org/html/rfc7540#section-6.8
// We should not create any new streams, which means we should disable push.
sc.pushEnabled = false
@@ -2163,6 +2252,7 @@ type responseWriterState struct {
wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet.
sentHeader bool // have we sent the header frame?
handlerDone bool // handler has finished
dirty bool // a Write failed; don't reuse this responseWriterState
sentContentLen int64 // non-zero if handler set a Content-Length header
wroteBytes int64
@@ -2244,6 +2334,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
date: date,
})
if err != nil {
rws.dirty = true
return 0, err
}
if endStream {
@@ -2265,6 +2356,7 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
if len(p) > 0 || endStream {
// only send a 0 byte DATA frame if we're ending the stream.
if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil {
rws.dirty = true
return 0, err
}
}
@@ -2276,6 +2368,9 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
trailers: rws.trailers,
endStream: true,
})
if err != nil {
rws.dirty = true
}
return len(p), err
}
return len(p), nil
@@ -2415,7 +2510,7 @@ func cloneHeader(h http.Header) http.Header {
//
// * Handler calls w.Write or w.WriteString ->
// * -> rws.bw (*bufio.Writer) ->
// * (Handler migth call Flush)
// * (Handler might call Flush)
// * -> chunkWriter{rws}
// * -> responseWriterState.writeChunk(p []byte)
// * -> responseWriterState.writeChunk (most of the magic; see comment there)
@@ -2454,10 +2549,19 @@ func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int,
func (w *responseWriter) handlerDone() {
rws := w.rws
dirty := rws.dirty
rws.handlerDone = true
w.Flush()
w.rws = nil
responseWriterStatePool.Put(rws)
if !dirty {
// Only recycle the pool if all prior Write calls to
// the serverConn goroutine completed successfully. If
// they returned earlier due to resets from the peer
// there might still be write goroutines outstanding
// from the serverConn referencing the rws memory. See
// issue 20704.
responseWriterStatePool.Put(rws)
}
}
// Push errors.
@@ -2539,7 +2643,7 @@ func (w *responseWriter) push(target string, opts pushOptions) error {
return fmt.Errorf("method %q must be GET or HEAD", opts.Method)
}
msg := startPushRequest{
msg := &startPushRequest{
parent: st,
method: opts.Method,
url: u,
@@ -2552,7 +2656,7 @@ func (w *responseWriter) push(target string, opts pushOptions) error {
return errClientDisconnected
case <-st.cw:
return errStreamClosed
case sc.wantStartPushCh <- msg:
case sc.serveMsgCh <- msg:
}
select {
@@ -2574,7 +2678,7 @@ type startPushRequest struct {
done chan error
}
func (sc *serverConn) startPush(msg startPushRequest) {
func (sc *serverConn) startPush(msg *startPushRequest) {
sc.serveG.check()
// http://tools.ietf.org/html/rfc7540#section-6.6.
@@ -2613,7 +2717,7 @@ func (sc *serverConn) startPush(msg startPushRequest) {
// A server that is unable to establish a new stream identifier can send a GOAWAY
// frame so that the client is forced to open a new connection for new streams.
if sc.maxPushPromiseID+2 >= 1<<31 {
sc.startGracefulShutdown()
sc.startGracefulShutdownInternal()
return 0, ErrPushLimitReached
}
sc.maxPushPromiseID += 2
@@ -2738,31 +2842,6 @@ var badTrailer = map[string]bool{
"Www-Authenticate": true,
}
// h1ServerShutdownChan returns a channel that will be closed when the
// provided *http.Server wants to shut down.
//
// This is a somewhat hacky way to get at http1 innards. It works
// when the http2 code is bundled into the net/http package in the
// standard library. The alternatives ended up making the cmd/go tool
// depend on http Servers. This is the lightest option for now.
// This is tested via the TestServeShutdown* tests in net/http.
func h1ServerShutdownChan(hs *http.Server) <-chan struct{} {
if fn := testh1ServerShutdownChan; fn != nil {
return fn(hs)
}
var x interface{} = hs
type I interface {
getDoneChan() <-chan struct{}
}
if hs, ok := x.(I); ok {
return hs.getDoneChan()
}
return nil
}
// optional test hook for h1ServerShutdownChan.
var testh1ServerShutdownChan func(hs *http.Server) <-chan struct{}
// h1ServerKeepAlivesDisabled reports whether hs has its keep-alives
// disabled. See comments on h1ServerShutdownChan above for why
// the code is written this way.
+1 -1
View File
@@ -508,7 +508,7 @@ func TestServer_Push_RejectAfterGoAway(t *testing.T) {
return
default:
}
st.sc.testHookCh <- func(loopNum int) {
st.sc.serveMsgCh <- func(loopNum int) {
if !st.sc.pushEnabled {
readyOnce.Do(func() { close(ready) })
}
+30 -42
View File
@@ -142,7 +142,6 @@ func newServerTester(t testing.TB, handler http.HandlerFunc, opts ...interface{}
st.scMu.Lock()
defer st.scMu.Unlock()
st.sc = v
st.sc.testHookCh = make(chan func(int))
}
log.SetOutput(io.MultiWriter(stderrv(), twriter{t: t, st: st}))
if !onlyServer {
@@ -187,7 +186,7 @@ func (st *serverTester) addLogFilter(phrase string) {
func (st *serverTester) stream(id uint32) *stream {
ch := make(chan *stream, 1)
st.sc.testHookCh <- func(int) {
st.sc.serveMsgCh <- func(int) {
ch <- st.sc.streams[id]
}
return <-ch
@@ -195,7 +194,7 @@ func (st *serverTester) stream(id uint32) *stream {
func (st *serverTester) streamState(id uint32) streamState {
ch := make(chan streamState, 1)
st.sc.testHookCh <- func(int) {
st.sc.serveMsgCh <- func(int) {
state, _ := st.sc.state(id)
ch <- state
}
@@ -205,7 +204,7 @@ func (st *serverTester) streamState(id uint32) streamState {
// loopNum reports how many times this conn's select loop has gone around.
func (st *serverTester) loopNum() int {
lastc := make(chan int, 1)
st.sc.testHookCh <- func(loopNum int) {
st.sc.serveMsgCh <- func(loopNum int) {
lastc <- loopNum
}
return <-lastc
@@ -287,7 +286,7 @@ func (st *serverTester) greetAndCheckSettings(checkSetting func(s Setting) error
case *WindowUpdateFrame:
if f.FrameHeader.StreamID != 0 {
st.t.Fatalf("WindowUpdate StreamID = %d; want 0", f.FrameHeader.StreamID, 0)
st.t.Fatalf("WindowUpdate StreamID = %d; want 0", f.FrameHeader.StreamID)
}
incr := uint32((&Server{}).initialConnRecvWindowSize() - initialWindowSize)
if f.Increment != incr {
@@ -3687,47 +3686,36 @@ func TestRequestBodyReadCloseRace(t *testing.T) {
}
}
func TestServerGracefulShutdown(t *testing.T) {
shutdownCh := make(chan struct{})
defer func() { testh1ServerShutdownChan = nil }()
testh1ServerShutdownChan = func(*http.Server) <-chan struct{} { return shutdownCh }
func TestIssue20704Race(t *testing.T) {
if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
t.Skip("skipping in short mode")
}
const (
itemSize = 1 << 10
itemCount = 100
)
var st *serverTester
handlerDone := make(chan struct{})
st = newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
defer close(handlerDone)
close(shutdownCh)
ga := st.wantGoAway()
if ga.ErrCode != ErrCodeNo {
t.Errorf("GOAWAY error = %v; want ErrCodeNo", ga.ErrCode)
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
for i := 0; i < itemCount; i++ {
_, err := w.Write(make([]byte, itemSize))
if err != nil {
return
}
}
if ga.LastStreamID != 1 {
t.Errorf("GOAWAY LastStreamID = %v; want 1", ga.LastStreamID)
}
w.Header().Set("x-foo", "bar")
})
}, optOnlyServer)
defer st.Close()
st.greet()
st.bodylessReq1()
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
defer tr.CloseIdleConnections()
cl := &http.Client{Transport: tr}
<-handlerDone
hf := st.wantHeaders()
goth := st.decodeHeader(hf.HeaderBlockFragment())
wanth := [][2]string{
{":status", "200"},
{"x-foo", "bar"},
{"content-type", "text/plain; charset=utf-8"},
{"content-length", "0"},
}
if !reflect.DeepEqual(goth, wanth) {
t.Errorf("Got headers %v; want %v", goth, wanth)
}
n, err := st.cc.Read([]byte{0})
if n != 0 || err == nil {
t.Errorf("Read = %v, %v; want 0, non-nil", n, err)
for i := 0; i < 1000; i++ {
resp, err := cl.Get(st.ts.URL)
if err != nil {
t.Fatal(err)
}
// Force a RST stream to the server by closing without
// reading the body:
resp.Body.Close()
}
}
+20 -14
View File
@@ -694,7 +694,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 {
if req.Body == nil || reqBodyIsNoBody(req.Body) {
return 0
}
if req.ContentLength != 0 {
@@ -725,8 +725,8 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
}
body := req.Body
hasBody := body != nil
contentLen := actualContentLength(req)
hasBody := contentLen != 0
// TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
var requestedGzip bool
@@ -1655,6 +1655,7 @@ func (b transportResponseBody) Close() error {
cc.wmu.Lock()
if !serverSentStreamEnd {
cc.fr.WriteRSTStream(cs.ID, ErrCodeCancel)
cs.didReset = true
}
// Return connection-level flow control.
if unread > 0 {
@@ -1702,12 +1703,6 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
return nil
}
if f.Length > 0 {
if len(data) > 0 && cs.bufPipe.b == nil {
// Data frame after it's already closed?
cc.logf("http2: Transport received DATA frame for closed stream; closing connection")
return ConnectionError(ErrCodeProtocol)
}
// Check connection-level flow control.
cc.mu.Lock()
if cs.inflow.available() >= int32(f.Length) {
@@ -1718,16 +1713,27 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
}
// Return any padded flow control now, since we won't
// refund it later on body reads.
if pad := int32(f.Length) - int32(len(data)); pad > 0 {
cs.inflow.add(pad)
cc.inflow.add(pad)
var refund int
if pad := int(f.Length) - len(data); pad > 0 {
refund += pad
}
// Return len(data) now if the stream is already closed,
// since data will never be read.
didReset := cs.didReset
if didReset {
refund += len(data)
}
if refund > 0 {
cc.inflow.add(int32(refund))
cc.wmu.Lock()
cc.fr.WriteWindowUpdate(0, uint32(pad))
cc.fr.WriteWindowUpdate(cs.ID, uint32(pad))
cc.fr.WriteWindowUpdate(0, uint32(refund))
if !didReset {
cs.inflow.add(int32(refund))
cc.fr.WriteWindowUpdate(cs.ID, uint32(refund))
}
cc.bw.Flush()
cc.wmu.Unlock()
}
didReset := cs.didReset
cc.mu.Unlock()
if len(data) > 0 && !didReset {
+139 -15
View File
@@ -417,6 +417,11 @@ func TestActualContentLength(t *testing.T) {
req: &http.Request{Body: panicReader{}, ContentLength: 5},
want: 5,
},
// http.NoBody means 0, not -1.
3: {
req: &http.Request{Body: go18httpNoBody()},
want: 0,
},
}
for i, tt := range tests {
got := actualContentLength(tt.req)
@@ -2205,12 +2210,11 @@ func testTransportUsesGoAwayDebugError(t *testing.T, failMidBody bool) {
ct.run()
}
// See golang.org/issue/16481
func TestTransportReturnsUnusedFlowControl(t *testing.T) {
func testTransportReturnsUnusedFlowControl(t *testing.T, oneDataFrame bool) {
ct := newClientTester(t)
clientClosed := make(chan bool, 1)
serverWroteBody := make(chan bool, 1)
clientClosed := make(chan struct{})
serverWroteFirstByte := make(chan struct{})
ct.client = func() error {
req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
@@ -2218,13 +2222,13 @@ func TestTransportReturnsUnusedFlowControl(t *testing.T) {
if err != nil {
return err
}
<-serverWroteBody
<-serverWroteFirstByte
if n, err := res.Body.Read(make([]byte, 1)); err != nil || n != 1 {
return fmt.Errorf("body read = %v, %v; want 1, nil", n, err)
}
res.Body.Close() // leaving 4999 bytes unread
clientClosed <- true
close(clientClosed)
return nil
}
@@ -2259,10 +2263,27 @@ func TestTransportReturnsUnusedFlowControl(t *testing.T) {
EndStream: false,
BlockFragment: buf.Bytes(),
})
ct.fr.WriteData(hf.StreamID, false, make([]byte, 5000)) // without ending stream
serverWroteBody <- true
<-clientClosed
// Two cases:
// - Send one DATA frame with 5000 bytes.
// - Send two DATA frames with 1 and 4999 bytes each.
//
// In both cases, the client should consume one byte of data,
// refund that byte, then refund the following 4999 bytes.
//
// In the second case, the server waits for the client connection to
// close before seconding the second DATA frame. This tests the case
// where the client receives a DATA frame after it has reset the stream.
if oneDataFrame {
ct.fr.WriteData(hf.StreamID, false /* don't end stream */, make([]byte, 5000))
close(serverWroteFirstByte)
<-clientClosed
} else {
ct.fr.WriteData(hf.StreamID, false /* don't end stream */, make([]byte, 1))
close(serverWroteFirstByte)
<-clientClosed
ct.fr.WriteData(hf.StreamID, false /* don't end stream */, make([]byte, 4999))
}
waitingFor := "RSTStreamFrame"
for {
@@ -2276,7 +2297,7 @@ func TestTransportReturnsUnusedFlowControl(t *testing.T) {
switch waitingFor {
case "RSTStreamFrame":
if rf, ok := f.(*RSTStreamFrame); !ok || rf.ErrCode != ErrCodeCancel {
return fmt.Errorf("Expected a WindowUpdateFrame with code cancel; got %v", summarizeFrame(f))
return fmt.Errorf("Expected a RSTStreamFrame with code cancel; got %v", summarizeFrame(f))
}
waitingFor = "WindowUpdateFrame"
case "WindowUpdateFrame":
@@ -2290,6 +2311,16 @@ func TestTransportReturnsUnusedFlowControl(t *testing.T) {
ct.run()
}
// See golang.org/issue/16481
func TestTransportReturnsUnusedFlowControlSingleWrite(t *testing.T) {
testTransportReturnsUnusedFlowControl(t, true)
}
// See golang.org/issue/20469
func TestTransportReturnsUnusedFlowControlMultipleWrites(t *testing.T) {
testTransportReturnsUnusedFlowControl(t, false)
}
// Issue 16612: adjust flow control on open streams when transport
// receives SETTINGS with INITIAL_WINDOW_SIZE from server.
func TestTransportAdjustsFlowControl(t *testing.T) {
@@ -2529,7 +2560,7 @@ func TestTransportBodyDoubleEndStream(t *testing.T) {
}
}
// golangorg/issue/16847
// golang.org/issue/16847, golang.org/issue/19103
func TestTransportRequestPathPseudo(t *testing.T) {
type result struct {
path string
@@ -2549,9 +2580,9 @@ func TestTransportRequestPathPseudo(t *testing.T) {
},
want: result{path: "/foo"},
},
// I guess we just don't let users request "//foo" as
// a path, since it's illegal to start with two
// slashes....
// In Go 1.7, we accepted paths of "//foo".
// In Go 1.8, we rejected it (issue 16847).
// In Go 1.9, we accepted it again (issue 19103).
1: {
req: &http.Request{
Method: "GET",
@@ -2560,7 +2591,7 @@ func TestTransportRequestPathPseudo(t *testing.T) {
Path: "//foo",
},
},
want: result{err: `invalid request :path "//foo"`},
want: result{path: "//foo"},
},
// Opaque with //$Matching_Hostname/path
@@ -2915,3 +2946,96 @@ func TestAuthorityAddr(t *testing.T) {
}
}
}
// Issue 20448: stop allocating for DATA frames' payload after
// Response.Body.Close is called.
func TestTransportAllocationsAfterResponseBodyClose(t *testing.T) {
megabyteZero := make([]byte, 1<<20)
writeErr := make(chan error, 1)
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
w.(http.Flusher).Flush()
var sum int64
for i := 0; i < 100; i++ {
n, err := w.Write(megabyteZero)
sum += int64(n)
if err != nil {
writeErr <- err
return
}
}
t.Logf("wrote all %d bytes", sum)
writeErr <- nil
}, optOnlyServer)
defer st.Close()
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
defer tr.CloseIdleConnections()
c := &http.Client{Transport: tr}
res, err := c.Get(st.ts.URL)
if err != nil {
t.Fatal(err)
}
var buf [1]byte
if _, err := res.Body.Read(buf[:]); err != nil {
t.Error(err)
}
if err := res.Body.Close(); err != nil {
t.Error(err)
}
trb, ok := res.Body.(transportResponseBody)
if !ok {
t.Fatalf("res.Body = %T; want transportResponseBody", res.Body)
}
if trb.cs.bufPipe.b != nil {
t.Errorf("response body pipe is still open")
}
gotErr := <-writeErr
if gotErr == nil {
t.Errorf("Handler unexpectedly managed to write its entire response without getting an error")
} else if gotErr != errStreamClosed {
t.Errorf("Handler Write err = %v; want errStreamClosed", gotErr)
}
}
// Issue 18891: make sure Request.Body == NoBody means no DATA frame
// is ever sent, even if empty.
func TestTransportNoBodyMeansNoDATA(t *testing.T) {
ct := newClientTester(t)
unblockClient := make(chan bool)
ct.client = func() error {
req, _ := http.NewRequest("GET", "https://dummy.tld/", go18httpNoBody())
ct.tr.RoundTrip(req)
<-unblockClient
return nil
}
ct.server = func() error {
defer close(unblockClient)
defer ct.cc.(*net.TCPConn).Close()
ct.greet()
for {
f, err := ct.fr.ReadFrame()
if err != nil {
return fmt.Errorf("ReadFrame while waiting for Headers: %v", err)
}
switch f := f.(type) {
default:
return fmt.Errorf("Got %T; want HeadersFrame", f)
case *WindowUpdateFrame, *SettingsFrame:
continue
case *HeadersFrame:
if !f.StreamEnded() {
return fmt.Errorf("got headers frame without END_STREAM")
}
return nil
}
}
}
ct.run()
}
+1 -1
View File
@@ -53,7 +53,7 @@ type PriorityWriteSchedulerConfig struct {
}
// NewPriorityWriteScheduler constructs a WriteScheduler that schedules
// frames by following HTTP/2 priorities as described in RFC 7340 Section 5.3.
// frames by following HTTP/2 priorities as described in RFC 7540 Section 5.3.
// If cfg is nil, default options are used.
func NewPriorityWriteScheduler(cfg *PriorityWriteSchedulerConfig) WriteScheduler {
if cfg == nil {
-27
View File
@@ -1,27 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package icmp
import (
"encoding/binary"
"unsafe"
)
var (
// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
freebsdVersion uint32
nativeEndian binary.ByteOrder
)
func init() {
i := uint32(1)
b := (*[4]byte)(unsafe.Pointer(&i))
if b[0] == 1 {
nativeEndian = binary.LittleEndian
} else {
nativeEndian = binary.BigEndian
}
}
+7 -2
View File
@@ -9,9 +9,14 @@ import (
"net"
"runtime"
"golang.org/x/net/internal/socket"
"golang.org/x/net/ipv4"
)
// freebsdVersion is set in sys_freebsd.go.
// 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.
func ParseIPv4Header(b []byte) (*ipv4.Header, error) {
@@ -36,12 +41,12 @@ func ParseIPv4Header(b []byte) (*ipv4.Header, error) {
}
switch runtime.GOOS {
case "darwin":
h.TotalLen = int(nativeEndian.Uint16(b[2:4]))
h.TotalLen = int(socket.NativeEndian.Uint16(b[2:4]))
case "freebsd":
if freebsdVersion >= 1000000 {
h.TotalLen = int(binary.BigEndian.Uint16(b[2:4]))
} else {
h.TotalLen = int(nativeEndian.Uint16(b[2:4]))
h.TotalLen = int(socket.NativeEndian.Uint16(b[2:4]))
}
default:
h.TotalLen = int(binary.BigEndian.Uint16(b[2:4]))
+2 -1
View File
@@ -11,6 +11,7 @@ import (
"runtime"
"testing"
"golang.org/x/net/internal/socket"
"golang.org/x/net/ipv4"
)
@@ -55,7 +56,7 @@ var ipv4HeaderLittleEndianTest = ipv4HeaderTest{
func TestParseIPv4Header(t *testing.T) {
tt := &ipv4HeaderLittleEndianTest
if nativeEndian != binary.LittleEndian {
if socket.NativeEndian != binary.LittleEndian {
t.Skip("no test for non-little endian machine yet")
}
+6 -1
View File
@@ -51,6 +51,10 @@ func ExampleNew() {
idna.Transitional(true)) // Map ß -> ss
fmt.Println(p.ToASCII("*.faß.com"))
// Lookup for registration. Also does not allow '*'.
p = idna.New(idna.ValidateForRegistration())
fmt.Println(p.ToUnicode("*.faß.com"))
// Set up a profile maps for lookup, but allows wild cards.
p = idna.New(
idna.MapForLookup(),
@@ -60,6 +64,7 @@ func ExampleNew() {
// Output:
// *.xn--fa-hia.com <nil>
// *.fass.com idna: disallowed rune U+002E
// *.fass.com idna: disallowed rune U+002A
// *.faß.com idna: disallowed rune U+002A
// *.fass.com <nil>
}
+38 -26
View File
@@ -67,6 +67,15 @@ func VerifyDNSLength(verify bool) Option {
return func(o *options) { o.verifyDNSLength = verify }
}
// RemoveLeadingDots removes leading label separators. Leading runes that map to
// dots, such as U+3002, are removed as well.
//
// This is the behavior suggested by the UTS #46 and is adopted by some
// browsers.
func RemoveLeadingDots(remove bool) Option {
return func(o *options) { o.removeLeadingDots = remove }
}
// ValidateLabels sets whether to check the mandatory label validation criteria
// as defined in Section 5.4 of RFC 5891. This includes testing for correct use
// of hyphens ('-'), normalization, validity of runes, and the context rules.
@@ -133,14 +142,16 @@ func MapForLookup() Option {
o.mapping = validateAndMap
StrictDomainName(true)(o)
ValidateLabels(true)(o)
RemoveLeadingDots(true)(o)
}
}
type options struct {
transitional bool
useSTD3Rules bool
validateLabels bool
verifyDNSLength bool
transitional bool
useSTD3Rules bool
validateLabels bool
verifyDNSLength bool
removeLeadingDots bool
trie *idnaTrie
@@ -240,21 +251,23 @@ var (
punycode = &Profile{}
lookup = &Profile{options{
transitional: true,
useSTD3Rules: true,
validateLabels: true,
trie: trie,
fromPuny: validateFromPunycode,
mapping: validateAndMap,
bidirule: bidirule.ValidString,
transitional: true,
useSTD3Rules: true,
validateLabels: true,
removeLeadingDots: true,
trie: trie,
fromPuny: validateFromPunycode,
mapping: validateAndMap,
bidirule: bidirule.ValidString,
}}
display = &Profile{options{
useSTD3Rules: true,
validateLabels: true,
trie: trie,
fromPuny: validateFromPunycode,
mapping: validateAndMap,
bidirule: bidirule.ValidString,
useSTD3Rules: true,
validateLabels: true,
removeLeadingDots: true,
trie: trie,
fromPuny: validateFromPunycode,
mapping: validateAndMap,
bidirule: bidirule.ValidString,
}}
registration = &Profile{options{
useSTD3Rules: true,
@@ -293,7 +306,9 @@ func (p *Profile) process(s string, toASCII bool) (string, error) {
s, err = p.mapping(p, s)
}
// Remove leading empty labels.
for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
if p.removeLeadingDots {
for ; len(s) > 0 && s[0] == '.'; s = s[1:] {
}
}
// It seems like we should only create this error on ToASCII, but the
// UTS 46 conformance tests suggests we should always check this.
@@ -373,23 +388,20 @@ func validateRegistration(p *Profile, s string) (string, error) {
if !norm.NFC.IsNormalString(s) {
return s, &labelError{s, "V1"}
}
var err error
for i := 0; i < len(s); {
v, sz := trie.lookupString(s[i:])
i += sz
// Copy bytes not copied so far.
switch p.simplify(info(v).category()) {
// TODO: handle the NV8 defined in the Unicode idna data set to allow
// for strict conformance to IDNA2008.
case valid, deviation:
case disallowed, mapped, unknown, ignored:
if err == nil {
r, _ := utf8.DecodeRuneInString(s[i:])
err = runeError(r)
}
r, _ := utf8.DecodeRuneInString(s[i:])
return s, runeError(r)
}
i += sz
}
return s, err
return s, nil
}
func validateAndMap(p *Profile, s string) (string, error) {
@@ -408,7 +420,7 @@ func validateAndMap(p *Profile, s string) (string, error) {
continue
case disallowed:
if err == nil {
r, _ := utf8.DecodeRuneInString(s[i:])
r, _ := utf8.DecodeRuneInString(s[start:])
err = runeError(r)
}
continue
+3 -3
View File
@@ -4,7 +4,7 @@
// 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: 2013-06-25
// Differentiated Services Field Codepoints (DSCP), Updated: 2017-05-12
const (
DiffServCS0 = 0x0 // CS0
DiffServCS1 = 0x20 // CS1
@@ -26,7 +26,7 @@ const (
DiffServAF41 = 0x88 // AF41
DiffServAF42 = 0x90 // AF42
DiffServAF43 = 0x98 // AF43
DiffServEFPHB = 0xb8 // EF PHB
DiffServEF = 0xb8 // EF
DiffServVOICEADMIT = 0xb0 // VOICE-ADMIT
)
@@ -38,7 +38,7 @@ const (
CongestionExperienced = 0x3 // CE (Congestion Experienced)
)
// Protocol Numbers, Updated: 2015-10-06
// Protocol Numbers, Updated: 2016-06-22
const (
ProtocolIP = 0 // IPv4 encapsulation, pseudo protocol number
ProtocolHOPOPT = 0 // IPv6 Hop-by-Hop Option
-41
View File
@@ -1,41 +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 netreflect implements run-time reflection for the
// facilities of net package.
//
// This package works only for Go 1.8 or below.
package netreflect
import (
"errors"
"net"
)
var (
errInvalidType = errors.New("invalid type")
errOpNoSupport = errors.New("operation not supported")
)
// SocketOf returns the socket descriptor of c.
func SocketOf(c net.Conn) (uintptr, error) {
switch c.(type) {
case *net.TCPConn, *net.UDPConn, *net.IPConn, *net.UnixConn:
return socketOf(c)
default:
return 0, errInvalidType
}
}
// PacketSocketOf returns the socket descriptor of c.
func PacketSocketOf(c net.PacketConn) (uintptr, error) {
switch c.(type) {
case *net.UDPConn, *net.IPConn, *net.UnixConn:
return socketOf(c.(net.Conn))
default:
return 0, errInvalidType
}
}
-37
View File
@@ -1,37 +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 netreflect
import (
"errors"
"net"
)
var (
errInvalidType = errors.New("invalid type")
errOpNoSupport = errors.New("operation not supported")
)
// SocketOf returns the socket descriptor of c.
func SocketOf(c net.Conn) (uintptr, error) {
switch c.(type) {
case *net.TCPConn, *net.UDPConn, *net.IPConn, *net.UnixConn:
return 0, errOpNoSupport
default:
return 0, errInvalidType
}
}
// PacketSocketOf returns the socket descriptor of c.
func PacketSocketOf(c net.PacketConn) (uintptr, error) {
switch c.(type) {
case *net.UDPConn, *net.IPConn, *net.UnixConn:
return 0, errOpNoSupport
default:
return 0, errInvalidType
}
}
-31
View File
@@ -1,31 +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
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
package netreflect
import (
"net"
"reflect"
"runtime"
)
func socketOf(c net.Conn) (uintptr, error) {
v := reflect.ValueOf(c)
switch e := v.Elem(); e.Kind() {
case reflect.Struct:
fd := e.FieldByName("conn").FieldByName("fd")
switch e := fd.Elem(); e.Kind() {
case reflect.Struct:
sysfd := e.FieldByName("sysfd")
if runtime.GOOS == "windows" {
return uintptr(sysfd.Uint()), nil
}
return uintptr(sysfd.Int()), nil
}
}
return 0, errInvalidType
}
-12
View File
@@ -1,12 +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
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
package netreflect
import "net"
func socketOf(c net.Conn) (uintptr, error) { return 0, errOpNoSupport }
-65
View File
@@ -1,65 +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
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
package netreflect_test
import (
"net"
"os"
"testing"
"golang.org/x/net/internal/netreflect"
"golang.org/x/net/internal/nettest"
)
func TestSocketOf(t *testing.T) {
for _, network := range []string{"tcp", "unix", "unixpacket"} {
if !nettest.TestableNetwork(network) {
continue
}
ln, err := nettest.NewLocalListener(network)
if err != nil {
t.Error(err)
continue
}
defer func() {
path := ln.Addr().String()
ln.Close()
if network == "unix" || network == "unixpacket" {
os.Remove(path)
}
}()
c, err := net.Dial(ln.Addr().Network(), ln.Addr().String())
if err != nil {
t.Error(err)
continue
}
defer c.Close()
if _, err := netreflect.SocketOf(c); err != nil {
t.Error(err)
continue
}
}
}
func TestPacketSocketOf(t *testing.T) {
for _, network := range []string{"udp", "unixgram"} {
if !nettest.TestableNetwork(network) {
continue
}
c, err := nettest.NewLocalPacketListener(network)
if err != nil {
t.Error(err)
continue
}
defer c.Close()
if _, err := netreflect.PacketSocketOf(c); err != nil {
t.Error(err)
continue
}
}
}
+11
View File
@@ -0,0 +1,11 @@
// 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 darwin dragonfly freebsd linux netbsd openbsd solaris
package socket
func (h *cmsghdr) len() int { return int(h.Len) }
func (h *cmsghdr) lvl() int { return int(h.Level) }
func (h *cmsghdr) typ() int { return int(h.Type) }
+13
View File
@@ -0,0 +1,13 @@
// 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 darwin dragonfly freebsd netbsd openbsd
package socket
func (h *cmsghdr) set(l, lvl, typ int) {
h.Len = uint32(l)
h.Level = int32(lvl)
h.Type = int32(typ)
}
+14
View File
@@ -0,0 +1,14 @@
// 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 arm mips mipsle 386
// +build linux
package socket
func (h *cmsghdr) set(l, lvl, typ int) {
h.Len = uint32(l)
h.Level = int32(lvl)
h.Type = int32(typ)
}
+14
View File
@@ -0,0 +1,14 @@
// 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 arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
// +build linux
package socket
func (h *cmsghdr) set(l, lvl, typ int) {
h.Len = uint64(l)
h.Level = int32(lvl)
h.Type = int32(typ)
}
+14
View File
@@ -0,0 +1,14 @@
// 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 amd64
// +build solaris
package socket
func (h *cmsghdr) set(l, lvl, typ int) {
h.Len = uint32(l)
h.Level = int32(lvl)
h.Type = int32(typ)
}
+17
View File
@@ -0,0 +1,17 @@
// 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 !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
package socket
type cmsghdr struct{}
const sizeofCmsghdr = 0
func (h *cmsghdr) len() int { return 0 }
func (h *cmsghdr) lvl() int { return 0 }
func (h *cmsghdr) typ() int { return 0 }
func (h *cmsghdr) set(l, lvl, typ int) {}
+44
View File
@@ -0,0 +1,44 @@
// 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 ignore
// +godefs map struct_in_addr [4]byte /* in_addr */
// +godefs map struct_in6_addr [16]byte /* in6_addr */
package socket
/*
#include <sys/socket.h>
#include <netinet/in.h>
*/
import "C"
const (
sysAF_UNSPEC = C.AF_UNSPEC
sysAF_INET = C.AF_INET
sysAF_INET6 = C.AF_INET6
sysSOCK_RAW = C.SOCK_RAW
)
type iovec C.struct_iovec
type msghdr C.struct_msghdr
type cmsghdr C.struct_cmsghdr
type sockaddrInet C.struct_sockaddr_in
type sockaddrInet6 C.struct_sockaddr_in6
const (
sizeofIovec = C.sizeof_struct_iovec
sizeofMsghdr = C.sizeof_struct_msghdr
sizeofCmsghdr = C.sizeof_struct_cmsghdr
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
)
+44
View File
@@ -0,0 +1,44 @@
// 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 ignore
// +godefs map struct_in_addr [4]byte /* in_addr */
// +godefs map struct_in6_addr [16]byte /* in6_addr */
package socket
/*
#include <sys/socket.h>
#include <netinet/in.h>
*/
import "C"
const (
sysAF_UNSPEC = C.AF_UNSPEC
sysAF_INET = C.AF_INET
sysAF_INET6 = C.AF_INET6
sysSOCK_RAW = C.SOCK_RAW
)
type iovec C.struct_iovec
type msghdr C.struct_msghdr
type cmsghdr C.struct_cmsghdr
type sockaddrInet C.struct_sockaddr_in
type sockaddrInet6 C.struct_sockaddr_in6
const (
sizeofIovec = C.sizeof_struct_iovec
sizeofMsghdr = C.sizeof_struct_msghdr
sizeofCmsghdr = C.sizeof_struct_cmsghdr
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
)
+44
View File
@@ -0,0 +1,44 @@
// 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 ignore
// +godefs map struct_in_addr [4]byte /* in_addr */
// +godefs map struct_in6_addr [16]byte /* in6_addr */
package socket
/*
#include <sys/socket.h>
#include <netinet/in.h>
*/
import "C"
const (
sysAF_UNSPEC = C.AF_UNSPEC
sysAF_INET = C.AF_INET
sysAF_INET6 = C.AF_INET6
sysSOCK_RAW = C.SOCK_RAW
)
type iovec C.struct_iovec
type msghdr C.struct_msghdr
type cmsghdr C.struct_cmsghdr
type sockaddrInet C.struct_sockaddr_in
type sockaddrInet6 C.struct_sockaddr_in6
const (
sizeofIovec = C.sizeof_struct_iovec
sizeofMsghdr = C.sizeof_struct_msghdr
sizeofCmsghdr = C.sizeof_struct_cmsghdr
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
)
+49
View File
@@ -0,0 +1,49 @@
// 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 ignore
// +godefs map struct_in_addr [4]byte /* in_addr */
// +godefs map struct_in6_addr [16]byte /* in6_addr */
package socket
/*
#include <linux/in.h>
#include <linux/in6.h>
#define _GNU_SOURCE
#include <sys/socket.h>
*/
import "C"
const (
sysAF_UNSPEC = C.AF_UNSPEC
sysAF_INET = C.AF_INET
sysAF_INET6 = C.AF_INET6
sysSOCK_RAW = C.SOCK_RAW
)
type iovec C.struct_iovec
type msghdr C.struct_msghdr
type mmsghdr C.struct_mmsghdr
type cmsghdr C.struct_cmsghdr
type sockaddrInet C.struct_sockaddr_in
type sockaddrInet6 C.struct_sockaddr_in6
const (
sizeofIovec = C.sizeof_struct_iovec
sizeofMsghdr = C.sizeof_struct_msghdr
sizeofMmsghdr = C.sizeof_struct_mmsghdr
sizeofCmsghdr = C.sizeof_struct_cmsghdr
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
)
+47
View File
@@ -0,0 +1,47 @@
// 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 ignore
// +godefs map struct_in_addr [4]byte /* in_addr */
// +godefs map struct_in6_addr [16]byte /* in6_addr */
package socket
/*
#include <sys/socket.h>
#include <netinet/in.h>
*/
import "C"
const (
sysAF_UNSPEC = C.AF_UNSPEC
sysAF_INET = C.AF_INET
sysAF_INET6 = C.AF_INET6
sysSOCK_RAW = C.SOCK_RAW
)
type iovec C.struct_iovec
type msghdr C.struct_msghdr
type mmsghdr C.struct_mmsghdr
type cmsghdr C.struct_cmsghdr
type sockaddrInet C.struct_sockaddr_in
type sockaddrInet6 C.struct_sockaddr_in6
const (
sizeofIovec = C.sizeof_struct_iovec
sizeofMsghdr = C.sizeof_struct_msghdr
sizeofMmsghdr = C.sizeof_struct_mmsghdr
sizeofCmsghdr = C.sizeof_struct_cmsghdr
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
)
+44
View File
@@ -0,0 +1,44 @@
// 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 ignore
// +godefs map struct_in_addr [4]byte /* in_addr */
// +godefs map struct_in6_addr [16]byte /* in6_addr */
package socket
/*
#include <sys/socket.h>
#include <netinet/in.h>
*/
import "C"
const (
sysAF_UNSPEC = C.AF_UNSPEC
sysAF_INET = C.AF_INET
sysAF_INET6 = C.AF_INET6
sysSOCK_RAW = C.SOCK_RAW
)
type iovec C.struct_iovec
type msghdr C.struct_msghdr
type cmsghdr C.struct_cmsghdr
type sockaddrInet C.struct_sockaddr_in
type sockaddrInet6 C.struct_sockaddr_in6
const (
sizeofIovec = C.sizeof_struct_iovec
sizeofMsghdr = C.sizeof_struct_msghdr
sizeofCmsghdr = C.sizeof_struct_cmsghdr
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
)
+44
View File
@@ -0,0 +1,44 @@
// 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 ignore
// +godefs map struct_in_addr [4]byte /* in_addr */
// +godefs map struct_in6_addr [16]byte /* in6_addr */
package socket
/*
#include <sys/socket.h>
#include <netinet/in.h>
*/
import "C"
const (
sysAF_UNSPEC = C.AF_UNSPEC
sysAF_INET = C.AF_INET
sysAF_INET6 = C.AF_INET6
sysSOCK_RAW = C.SOCK_RAW
)
type iovec C.struct_iovec
type msghdr C.struct_msghdr
type cmsghdr C.struct_cmsghdr
type sockaddrInet C.struct_sockaddr_in
type sockaddrInet6 C.struct_sockaddr_in6
const (
sizeofIovec = C.sizeof_struct_iovec
sizeofMsghdr = C.sizeof_struct_msghdr
sizeofCmsghdr = C.sizeof_struct_cmsghdr
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
)
+31
View File
@@ -0,0 +1,31 @@
// 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 darwin dragonfly freebsd linux netbsd openbsd solaris
package socket
import "syscall"
var (
errEAGAIN error = syscall.EAGAIN
errEINVAL error = syscall.EINVAL
errENOENT error = syscall.ENOENT
)
// errnoErr returns common boxed Errno values, to prevent allocations
// at runtime.
func errnoErr(errno syscall.Errno) error {
switch errno {
case 0:
return nil
case syscall.EAGAIN:
return errEAGAIN
case syscall.EINVAL:
return errEINVAL
case syscall.ENOENT:
return errENOENT
}
return errno
}
+26
View File
@@ -0,0 +1,26 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
import "syscall"
var (
errERROR_IO_PENDING error = syscall.ERROR_IO_PENDING
errEINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent allocations
// at runtime.
func errnoErr(errno syscall.Errno) error {
switch errno {
case 0:
return nil
case syscall.ERROR_IO_PENDING:
return errERROR_IO_PENDING
case syscall.EINVAL:
return errEINVAL
}
return errno
}
+15
View File
@@ -0,0 +1,15 @@
// 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 arm mips mipsle 386
// +build darwin dragonfly freebsd linux netbsd openbsd
package socket
import "unsafe"
func (v *iovec) set(b []byte) {
v.Base = (*byte)(unsafe.Pointer(&b[0]))
v.Len = uint32(len(b))
}
+15
View File
@@ -0,0 +1,15 @@
// 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 arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
// +build darwin dragonfly freebsd linux netbsd openbsd
package socket
import "unsafe"
func (v *iovec) set(b []byte) {
v.Base = (*byte)(unsafe.Pointer(&b[0]))
v.Len = uint64(len(b))
}
+15
View File
@@ -0,0 +1,15 @@
// 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 amd64
// +build solaris
package socket
import "unsafe"
func (v *iovec) set(b []byte) {
v.Base = (*int8)(unsafe.Pointer(&b[0]))
v.Len = uint64(len(b))
}
+11
View File
@@ -0,0 +1,11 @@
// 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 !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
package socket
type iovec struct{}
func (v *iovec) set(b []byte) {}
+21
View File
@@ -0,0 +1,21 @@
// 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 !linux,!netbsd
package socket
import "net"
type mmsghdr struct{}
type mmsghdrs []mmsghdr
func (hs mmsghdrs) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr) []byte) error {
return nil
}
func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error {
return nil
}
+42
View File
@@ -0,0 +1,42 @@
// 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 linux netbsd
package socket
import "net"
type mmsghdrs []mmsghdr
func (hs mmsghdrs) pack(ms []Message, parseFn func([]byte, string) (net.Addr, error), marshalFn func(net.Addr) []byte) error {
for i := range hs {
vs := make([]iovec, len(ms[i].Buffers))
var sa []byte
if parseFn != nil {
sa = make([]byte, sizeofSockaddrInet6)
}
if marshalFn != nil {
sa = marshalFn(ms[i].Addr)
}
hs[i].Hdr.pack(vs, ms[i].Buffers, ms[i].OOB, sa)
}
return nil
}
func (hs mmsghdrs) unpack(ms []Message, parseFn func([]byte, string) (net.Addr, error), hint string) error {
for i := range hs {
ms[i].N = int(hs[i].Len)
ms[i].NN = hs[i].Hdr.controllen()
ms[i].Flags = hs[i].Hdr.flags()
if parseFn != nil {
var err error
ms[i].Addr, err = parseFn(hs[i].Hdr.name(), hint)
if err != nil {
return err
}
}
}
return nil
}
+39
View File
@@ -0,0 +1,39 @@
// 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 darwin dragonfly freebsd netbsd openbsd
package socket
import "unsafe"
func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
for i := range vs {
vs[i].set(bs[i])
}
h.setIov(vs)
if len(oob) > 0 {
h.Control = (*byte)(unsafe.Pointer(&oob[0]))
h.Controllen = uint32(len(oob))
}
if sa != nil {
h.Name = (*byte)(unsafe.Pointer(&sa[0]))
h.Namelen = uint32(len(sa))
}
}
func (h *msghdr) name() []byte {
if h.Name != nil && h.Namelen > 0 {
return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen]
}
return nil
}
func (h *msghdr) controllen() int {
return int(h.Controllen)
}
func (h *msghdr) flags() int {
return int(h.Flags)
}
+12
View File
@@ -0,0 +1,12 @@
// 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 darwin dragonfly freebsd netbsd
package socket
func (h *msghdr) setIov(vs []iovec) {
h.Iov = &vs[0]
h.Iovlen = int32(len(vs))
}
+36
View File
@@ -0,0 +1,36 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
import "unsafe"
func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
for i := range vs {
vs[i].set(bs[i])
}
h.setIov(vs)
if len(oob) > 0 {
h.setControl(oob)
}
if sa != nil {
h.Name = (*byte)(unsafe.Pointer(&sa[0]))
h.Namelen = uint32(len(sa))
}
}
func (h *msghdr) name() []byte {
if h.Name != nil && h.Namelen > 0 {
return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen]
}
return nil
}
func (h *msghdr) controllen() int {
return int(h.Controllen)
}
func (h *msghdr) flags() int {
return int(h.Flags)
}
+20
View File
@@ -0,0 +1,20 @@
// 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 arm mips mipsle 386
// +build linux
package socket
import "unsafe"
func (h *msghdr) setIov(vs []iovec) {
h.Iov = &vs[0]
h.Iovlen = uint32(len(vs))
}
func (h *msghdr) setControl(b []byte) {
h.Control = (*byte)(unsafe.Pointer(&b[0]))
h.Controllen = uint32(len(b))
}
+20
View File
@@ -0,0 +1,20 @@
// 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 arm64 amd64 ppc64 ppc64le mips64 mips64le s390x
// +build linux
package socket
import "unsafe"
func (h *msghdr) setIov(vs []iovec) {
h.Iov = &vs[0]
h.Iovlen = uint64(len(vs))
}
func (h *msghdr) setControl(b []byte) {
h.Control = (*byte)(unsafe.Pointer(&b[0]))
h.Controllen = uint64(len(b))
}
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
func (h *msghdr) setIov(vs []iovec) {
h.Iov = &vs[0]
h.Iovlen = uint32(len(vs))
}
+34
View File
@@ -0,0 +1,34 @@
// 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 amd64
// +build solaris
package socket
import "unsafe"
func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
for i := range vs {
vs[i].set(bs[i])
}
h.Iov = &vs[0]
h.Iovlen = int32(len(vs))
if len(oob) > 0 {
h.Accrights = (*int8)(unsafe.Pointer(&oob[0]))
h.Accrightslen = int32(len(oob))
}
if sa != nil {
h.Name = (*byte)(unsafe.Pointer(&sa[0]))
h.Namelen = uint32(len(sa))
}
}
func (h *msghdr) controllen() int {
return int(h.Accrightslen)
}
func (h *msghdr) flags() int {
return int(NativeEndian.Uint32(h.Pad_cgo_2[:]))
}
+14
View File
@@ -0,0 +1,14 @@
// 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 !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
package socket
type msghdr struct{}
func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {}
func (h *msghdr) name() []byte { return nil }
func (h *msghdr) controllen() int { return 0 }
func (h *msghdr) flags() int { return 0 }
+66
View File
@@ -0,0 +1,66 @@
// 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 socket
import (
"errors"
"net"
"os"
"syscall"
)
// A Conn represents a raw connection.
type Conn struct {
network string
c syscall.RawConn
}
// NewConn returns a new raw connection.
func NewConn(c net.Conn) (*Conn, error) {
var err error
var cc Conn
switch c := c.(type) {
case *net.TCPConn:
cc.network = "tcp"
cc.c, err = c.SyscallConn()
case *net.UDPConn:
cc.network = "udp"
cc.c, err = c.SyscallConn()
case *net.IPConn:
cc.network = "ip"
cc.c, err = c.SyscallConn()
default:
return nil, errors.New("unknown connection type")
}
if err != nil {
return nil, err
}
return &cc, nil
}
func (o *Option) get(c *Conn, b []byte) (int, error) {
var operr error
var n int
fn := func(s uintptr) {
n, operr = getsockopt(s, o.Level, o.Name, b)
}
if err := c.c.Control(fn); err != nil {
return 0, err
}
return n, os.NewSyscallError("getsockopt", operr)
}
func (o *Option) set(c *Conn, b []byte) error {
var operr error
fn := func(s uintptr) {
operr = setsockopt(s, o.Level, o.Name, b)
}
if err := c.c.Control(fn); err != nil {
return err
}
return os.NewSyscallError("setsockopt", operr)
}
+74
View File
@@ -0,0 +1,74 @@
// 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
// +build linux
package socket
import (
"net"
"os"
"syscall"
)
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
hs := make(mmsghdrs, len(ms))
var parseFn func([]byte, string) (net.Addr, error)
if c.network != "tcp" {
parseFn = parseInetAddr
}
if err := hs.pack(ms, parseFn, nil); err != nil {
return 0, err
}
var operr error
var n int
fn := func(s uintptr) bool {
n, operr = recvmmsg(s, hs, flags)
if operr == syscall.EAGAIN {
return false
}
return true
}
if err := c.c.Read(fn); err != nil {
return n, err
}
if operr != nil {
return n, os.NewSyscallError("recvmmsg", operr)
}
if err := hs[:n].unpack(ms[:n], parseFn, c.network); err != nil {
return n, err
}
return n, nil
}
func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) {
hs := make(mmsghdrs, len(ms))
var marshalFn func(net.Addr) []byte
if c.network != "tcp" {
marshalFn = marshalInetAddr
}
if err := hs.pack(ms, nil, marshalFn); err != nil {
return 0, err
}
var operr error
var n int
fn := func(s uintptr) bool {
n, operr = sendmmsg(s, hs, flags)
if operr == syscall.EAGAIN {
return false
}
return true
}
if err := c.c.Write(fn); err != nil {
return n, err
}
if operr != nil {
return n, os.NewSyscallError("sendmmsg", operr)
}
if err := hs[:n].unpack(ms[:n], nil, ""); err != nil {
return n, err
}
return n, nil
}
+77
View File
@@ -0,0 +1,77 @@
// 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
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
package socket
import (
"os"
"syscall"
)
func (c *Conn) recvMsg(m *Message, flags int) error {
var h msghdr
vs := make([]iovec, len(m.Buffers))
var sa []byte
if c.network != "tcp" {
sa = make([]byte, sizeofSockaddrInet6)
}
h.pack(vs, m.Buffers, m.OOB, sa)
var operr error
var n int
fn := func(s uintptr) bool {
n, operr = recvmsg(s, &h, flags)
if operr == syscall.EAGAIN {
return false
}
return true
}
if err := c.c.Read(fn); err != nil {
return err
}
if operr != nil {
return os.NewSyscallError("recvmsg", operr)
}
if c.network != "tcp" {
var err error
m.Addr, err = parseInetAddr(sa[:], c.network)
if err != nil {
return err
}
}
m.N = n
m.NN = h.controllen()
m.Flags = h.flags()
return nil
}
func (c *Conn) sendMsg(m *Message, flags int) error {
var h msghdr
vs := make([]iovec, len(m.Buffers))
var sa []byte
if m.Addr != nil {
sa = marshalInetAddr(m.Addr)
}
h.pack(vs, m.Buffers, m.OOB, sa)
var operr error
var n int
fn := func(s uintptr) bool {
n, operr = sendmsg(s, &h, flags)
if operr == syscall.EAGAIN {
return false
}
return true
}
if err := c.c.Write(fn); err != nil {
return err
}
if operr != nil {
return os.NewSyscallError("sendmsg", operr)
}
m.N = n
m.NN = len(m.OOB)
return nil
}
+18
View File
@@ -0,0 +1,18 @@
// 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
// +build !linux
package socket
import "errors"
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
return 0, errors.New("not implemented")
}
func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) {
return 0, errors.New("not implemented")
}
+18
View File
@@ -0,0 +1,18 @@
// 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
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows
package socket
import "errors"
func (c *Conn) recvMsg(m *Message, flags int) error {
return errors.New("not implemented")
}
func (c *Conn) sendMsg(m *Message, flags int) error {
return errors.New("not implemented")
}
+25
View File
@@ -0,0 +1,25 @@
// 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 socket
import "errors"
func (c *Conn) recvMsg(m *Message, flags int) error {
return errors.New("not implemented")
}
func (c *Conn) sendMsg(m *Message, flags int) error {
return errors.New("not implemented")
}
func (c *Conn) recvMsgs(ms []Message, flags int) (int, error) {
return 0, errors.New("not implemented")
}
func (c *Conn) sendMsgs(ms []Message, flags int) (int, error) {
return 0, errors.New("not implemented")
}
+62
View File
@@ -0,0 +1,62 @@
// 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 socket
import (
"errors"
"net"
"os"
"reflect"
"runtime"
)
// A Conn represents a raw connection.
type Conn struct {
c net.Conn
}
// NewConn returns a new raw connection.
func NewConn(c net.Conn) (*Conn, error) {
return &Conn{c: c}, nil
}
func (o *Option) get(c *Conn, b []byte) (int, error) {
s, err := socketOf(c.c)
if err != nil {
return 0, err
}
n, err := getsockopt(s, o.Level, o.Name, b)
return n, os.NewSyscallError("getsockopt", err)
}
func (o *Option) set(c *Conn, b []byte) error {
s, err := socketOf(c.c)
if err != nil {
return err
}
return os.NewSyscallError("setsockopt", setsockopt(s, o.Level, o.Name, b))
}
func socketOf(c net.Conn) (uintptr, error) {
switch c.(type) {
case *net.TCPConn, *net.UDPConn, *net.IPConn:
v := reflect.ValueOf(c)
switch e := v.Elem(); e.Kind() {
case reflect.Struct:
fd := e.FieldByName("conn").FieldByName("fd")
switch e := fd.Elem(); e.Kind() {
case reflect.Struct:
sysfd := e.FieldByName("sysfd")
if runtime.GOOS == "windows" {
return uintptr(sysfd.Uint()), nil
}
return uintptr(sysfd.Int()), nil
}
}
}
return 0, errors.New("invalid type")
}
+285
View File
@@ -0,0 +1,285 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package socket provides a portable interface for socket system
// calls.
package socket // import "golang.org/x/net/internal/socket"
import (
"errors"
"net"
"unsafe"
)
// An Option represents a sticky socket option.
type Option struct {
Level int // level
Name int // name; must be equal or greater than 1
Len int // length of value in bytes; must be equal or greater than 1
}
// Get reads a value for the option from the kernel.
// It returns the number of bytes written into b.
func (o *Option) Get(c *Conn, b []byte) (int, error) {
if o.Name < 1 || o.Len < 1 {
return 0, errors.New("invalid option")
}
if len(b) < o.Len {
return 0, errors.New("short buffer")
}
return o.get(c, b)
}
// GetInt returns an integer value for the option.
//
// The Len field of Option must be either 1 or 4.
func (o *Option) GetInt(c *Conn) (int, error) {
if o.Len != 1 && o.Len != 4 {
return 0, errors.New("invalid option")
}
var b []byte
var bb [4]byte
if o.Len == 1 {
b = bb[:1]
} else {
b = bb[:4]
}
n, err := o.get(c, b)
if err != nil {
return 0, err
}
if n != o.Len {
return 0, errors.New("invalid option length")
}
if o.Len == 1 {
return int(b[0]), nil
}
return int(NativeEndian.Uint32(b[:4])), nil
}
// Set writes the option and value to the kernel.
func (o *Option) Set(c *Conn, b []byte) error {
if o.Name < 1 || o.Len < 1 {
return errors.New("invalid option")
}
if len(b) < o.Len {
return errors.New("short buffer")
}
return o.set(c, b)
}
// SetInt writes the option and value to the kernel.
//
// The Len field of Option must be either 1 or 4.
func (o *Option) SetInt(c *Conn, v int) error {
if o.Len != 1 && o.Len != 4 {
return errors.New("invalid option")
}
var b []byte
if o.Len == 1 {
b = []byte{byte(v)}
} else {
var bb [4]byte
NativeEndian.PutUint32(bb[:o.Len], uint32(v))
b = bb[:4]
}
return o.set(c, b)
}
func controlHeaderLen() int {
return roundup(sizeofCmsghdr)
}
func controlMessageLen(dataLen int) int {
return roundup(sizeofCmsghdr) + dataLen
}
// ControlMessageSpace returns the whole length of control message.
func ControlMessageSpace(dataLen int) int {
return roundup(sizeofCmsghdr) + roundup(dataLen)
}
// A ControlMessage represents the head message in a stream of control
// messages.
//
// A control message comprises of a header, data and a few padding
// fields to conform to the interface to the kernel.
//
// See RFC 3542 for further information.
type ControlMessage []byte
// Data returns the data field of the control message at the head on
// w.
func (m ControlMessage) Data(dataLen int) []byte {
l := controlHeaderLen()
if len(m) < l || len(m) < l+dataLen {
return nil
}
return m[l : l+dataLen]
}
// Next returns the control message at the next on w.
//
// Next works only for standard control messages.
func (m ControlMessage) Next(dataLen int) ControlMessage {
l := ControlMessageSpace(dataLen)
if len(m) < l {
return nil
}
return m[l:]
}
// MarshalHeader marshals the header fields of the control message at
// the head on w.
func (m ControlMessage) MarshalHeader(lvl, typ, dataLen int) error {
if len(m) < controlHeaderLen() {
return errors.New("short message")
}
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
h.set(controlMessageLen(dataLen), lvl, typ)
return nil
}
// ParseHeader parses and returns the header fields of the control
// message at the head on w.
func (m ControlMessage) ParseHeader() (lvl, typ, dataLen int, err error) {
l := controlHeaderLen()
if len(m) < l {
return 0, 0, 0, errors.New("short message")
}
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
return h.lvl(), h.typ(), int(uint64(h.len()) - uint64(l)), nil
}
// Marshal marshals the control message at the head on w, and returns
// the next control message.
func (m ControlMessage) Marshal(lvl, typ int, data []byte) (ControlMessage, error) {
l := len(data)
if len(m) < ControlMessageSpace(l) {
return nil, errors.New("short message")
}
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
h.set(controlMessageLen(l), lvl, typ)
if l > 0 {
copy(m.Data(l), data)
}
return m.Next(l), nil
}
// Parse parses w as a single or multiple control messages.
//
// Parse works for both standard and compatible messages.
func (m ControlMessage) Parse() ([]ControlMessage, error) {
var ms []ControlMessage
for len(m) >= controlHeaderLen() {
h := (*cmsghdr)(unsafe.Pointer(&m[0]))
l := h.len()
if l <= 0 {
return nil, errors.New("invalid header length")
}
if uint64(l) < uint64(controlHeaderLen()) {
return nil, errors.New("invalid message length")
}
if uint64(l) > uint64(len(m)) {
return nil, errors.New("short buffer")
}
// On message reception:
//
// |<- ControlMessageSpace --------------->|
// |<- controlMessageLen ---------->| |
// |<- controlHeaderLen ->| | |
// +---------------+------+---------+------+
// | Header | PadH | Data | PadD |
// +---------------+------+---------+------+
//
// On compatible message reception:
//
// | ... |<- controlMessageLen ----------->|
// | ... |<- controlHeaderLen ->| |
// +-----+---------------+------+----------+
// | ... | Header | PadH | Data |
// +-----+---------------+------+----------+
ms = append(ms, ControlMessage(m[:l]))
ll := l - controlHeaderLen()
if len(m) >= ControlMessageSpace(ll) {
m = m[ControlMessageSpace(ll):]
} else {
m = m[controlMessageLen(ll):]
}
}
return ms, nil
}
// NewControlMessage returns a new stream of control messages.
func NewControlMessage(dataLen []int) ControlMessage {
var l int
for i := range dataLen {
l += ControlMessageSpace(dataLen[i])
}
return make([]byte, l)
}
// A Message represents an IO message.
type Message struct {
// When writing, the Buffers field must contain at least one
// byte to write.
// When reading, the Buffers field will always contain a byte
// to read.
Buffers [][]byte
// OOB contains protocol-specific control or miscellaneous
// ancillary data known as out-of-band data.
OOB []byte
// Addr specifies a destination address when writing.
// It can be nil when the underlying protocol of the raw
// connection uses connection-oriented communication.
// After a successful read, it may contain the source address
// on the received packet.
Addr net.Addr
N int // # of bytes read or written from/to Buffers
NN int // # of bytes read or written from/to OOB
Flags int // protocol-specific information on the received message
}
// RecvMsg wraps recvmsg system call.
//
// The provided flags is a set of platform-dependent flags, such as
// syscall.MSG_PEEK.
func (c *Conn) RecvMsg(m *Message, flags int) error {
return c.recvMsg(m, flags)
}
// SendMsg wraps sendmsg system call.
//
// The provided flags is a set of platform-dependent flags, such as
// syscall.MSG_DONTROUTE.
func (c *Conn) SendMsg(m *Message, flags int) error {
return c.sendMsg(m, flags)
}
// RecvMsgs wraps recvmmsg system call.
//
// It returns the number of processed messages.
//
// The provided flags is a set of platform-dependent flags, such as
// syscall.MSG_PEEK.
//
// Only Linux supports this.
func (c *Conn) RecvMsgs(ms []Message, flags int) (int, error) {
return c.recvMsgs(ms, flags)
}
// SendMsgs wraps sendmmsg system call.
//
// It returns the number of processed messages.
//
// The provided flags is a set of platform-dependent flags, such as
// syscall.MSG_DONTROUTE.
//
// Only Linux supports this.
func (c *Conn) SendMsgs(ms []Message, flags int) (int, error) {
return c.sendMsgs(ms, flags)
}
+256
View File
@@ -0,0 +1,256 @@
// 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
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package socket_test
import (
"bytes"
"fmt"
"net"
"runtime"
"testing"
"golang.org/x/net/internal/nettest"
"golang.org/x/net/internal/socket"
)
type mockControl struct {
Level int
Type int
Data []byte
}
func TestControlMessage(t *testing.T) {
for _, tt := range []struct {
cs []mockControl
}{
{
[]mockControl{
{Level: 1, Type: 1},
},
},
{
[]mockControl{
{Level: 2, Type: 2, Data: []byte{0xfe}},
},
},
{
[]mockControl{
{Level: 3, Type: 3, Data: []byte{0xfe, 0xff, 0xff, 0xfe}},
},
},
{
[]mockControl{
{Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}},
},
},
{
[]mockControl{
{Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}},
{Level: 2, Type: 2, Data: []byte{0xfe}},
},
},
} {
var w []byte
var tailPadLen int
mm := socket.NewControlMessage([]int{0})
for i, c := range tt.cs {
m := socket.NewControlMessage([]int{len(c.Data)})
l := len(m) - len(mm)
if i == len(tt.cs)-1 && l > len(c.Data) {
tailPadLen = l - len(c.Data)
}
w = append(w, m...)
}
var err error
ww := make([]byte, len(w))
copy(ww, w)
m := socket.ControlMessage(ww)
for _, c := range tt.cs {
if err = m.MarshalHeader(c.Level, c.Type, len(c.Data)); err != nil {
t.Fatalf("(%v).MarshalHeader() = %v", tt.cs, err)
}
copy(m.Data(len(c.Data)), c.Data)
m = m.Next(len(c.Data))
}
m = socket.ControlMessage(w)
for _, c := range tt.cs {
m, err = m.Marshal(c.Level, c.Type, c.Data)
if err != nil {
t.Fatalf("(%v).Marshal() = %v", tt.cs, err)
}
}
if !bytes.Equal(ww, w) {
t.Fatalf("got %#v; want %#v", ww, w)
}
ws := [][]byte{w}
if tailPadLen > 0 {
// Test a message with no tail padding.
nopad := w[:len(w)-tailPadLen]
ws = append(ws, [][]byte{nopad}...)
}
for _, w := range ws {
ms, err := socket.ControlMessage(w).Parse()
if err != nil {
t.Fatalf("(%v).Parse() = %v", tt.cs, err)
}
for i, m := range ms {
lvl, typ, dataLen, err := m.ParseHeader()
if err != nil {
t.Fatalf("(%v).ParseHeader() = %v", tt.cs, err)
}
if lvl != tt.cs[i].Level || typ != tt.cs[i].Type || dataLen != len(tt.cs[i].Data) {
t.Fatalf("%v: got %d, %d, %d; want %d, %d, %d", tt.cs[i], lvl, typ, dataLen, tt.cs[i].Level, tt.cs[i].Type, len(tt.cs[i].Data))
}
}
}
}
}
func TestUDP(t *testing.T) {
c, err := nettest.NewLocalPacketListener("udp")
if err != nil {
t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
}
defer c.Close()
t.Run("Message", func(t *testing.T) {
testUDPMessage(t, c.(net.Conn))
})
switch runtime.GOOS {
case "linux":
t.Run("Messages", func(t *testing.T) {
testUDPMessages(t, c.(net.Conn))
})
}
}
func testUDPMessage(t *testing.T, c net.Conn) {
cc, err := socket.NewConn(c)
if err != nil {
t.Fatal(err)
}
data := []byte("HELLO-R-U-THERE")
wm := socket.Message{
Buffers: bytes.SplitAfter(data, []byte("-")),
Addr: c.LocalAddr(),
}
if err := cc.SendMsg(&wm, 0); err != nil {
t.Fatal(err)
}
b := make([]byte, 32)
rm := socket.Message{
Buffers: [][]byte{b[:1], b[1:3], b[3:7], b[7:11], b[11:]},
}
if err := cc.RecvMsg(&rm, 0); err != nil {
t.Fatal(err)
}
if !bytes.Equal(b[:rm.N], data) {
t.Fatalf("got %#v; want %#v", b[:rm.N], data)
}
}
func testUDPMessages(t *testing.T, c net.Conn) {
cc, err := socket.NewConn(c)
if err != nil {
t.Fatal(err)
}
data := []byte("HELLO-R-U-THERE")
wmbs := bytes.SplitAfter(data, []byte("-"))
wms := []socket.Message{
{Buffers: wmbs[:1], Addr: c.LocalAddr()},
{Buffers: wmbs[1:], Addr: c.LocalAddr()},
}
n, err := cc.SendMsgs(wms, 0)
if err != nil {
t.Fatal(err)
}
if n != len(wms) {
t.Fatalf("got %d; want %d", n, len(wms))
}
b := make([]byte, 32)
rmbs := [][][]byte{{b[:len(wmbs[0])]}, {b[len(wmbs[0]):]}}
rms := []socket.Message{
{Buffers: rmbs[0]},
{Buffers: rmbs[1]},
}
n, err = cc.RecvMsgs(rms, 0)
if err != nil {
t.Fatal(err)
}
if n != len(rms) {
t.Fatalf("got %d; want %d", n, len(rms))
}
nn := 0
for i := 0; i < n; i++ {
nn += rms[i].N
}
if !bytes.Equal(b[:nn], data) {
t.Fatalf("got %#v; want %#v", b[:nn], data)
}
}
func BenchmarkUDP(b *testing.B) {
c, err := nettest.NewLocalPacketListener("udp")
if err != nil {
b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
}
defer c.Close()
cc, err := socket.NewConn(c.(net.Conn))
if err != nil {
b.Fatal(err)
}
data := []byte("HELLO-R-U-THERE")
wm := socket.Message{
Buffers: [][]byte{data},
Addr: c.LocalAddr(),
}
rm := socket.Message{
Buffers: [][]byte{make([]byte, 128)},
OOB: make([]byte, 128),
}
for M := 1; M <= 1<<9; M = M << 1 {
b.Run(fmt.Sprintf("Iter-%d", M), func(b *testing.B) {
for i := 0; i < b.N; i++ {
for j := 0; j < M; j++ {
if err := cc.SendMsg(&wm, 0); err != nil {
b.Fatal(err)
}
if err := cc.RecvMsg(&rm, 0); err != nil {
b.Fatal(err)
}
}
}
})
switch runtime.GOOS {
case "linux":
wms := make([]socket.Message, M)
for i := range wms {
wms[i].Buffers = [][]byte{data}
wms[i].Addr = c.LocalAddr()
}
rms := make([]socket.Message, M)
for i := range rms {
rms[i].Buffers = [][]byte{make([]byte, 128)}
rms[i].OOB = make([]byte, 128)
}
b.Run(fmt.Sprintf("Batch-%d", M), func(b *testing.B) {
for i := 0; i < b.N; i++ {
if _, err := cc.SendMsgs(wms, 0); err != nil {
b.Fatal(err)
}
if _, err := cc.RecvMsgs(rms, 0); err != nil {
b.Fatal(err)
}
}
})
}
}
}
+46
View File
@@ -0,0 +1,46 @@
// 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 darwin dragonfly freebsd linux netbsd openbsd solaris windows
package socket_test
import (
"net"
"runtime"
"syscall"
"testing"
"golang.org/x/net/internal/nettest"
"golang.org/x/net/internal/socket"
)
func TestSocket(t *testing.T) {
t.Run("Option", func(t *testing.T) {
testSocketOption(t, &socket.Option{Level: syscall.SOL_SOCKET, Name: syscall.SO_RCVBUF, Len: 4})
})
}
func testSocketOption(t *testing.T, so *socket.Option) {
c, err := nettest.NewLocalPacketListener("udp")
if err != nil {
t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
}
defer c.Close()
cc, err := socket.NewConn(c.(net.Conn))
if err != nil {
t.Fatal(err)
}
const N = 2048
if err := so.SetInt(cc, N); err != nil {
t.Fatal(err)
}
n, err := so.GetInt(cc)
if err != nil {
t.Fatal(err)
}
if n < N {
t.Fatalf("got %d; want greater than or equal to %d", n, N)
}
}
+33
View File
@@ -0,0 +1,33 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
import (
"encoding/binary"
"unsafe"
)
var (
// NativeEndian is the machine native endian implementation of
// ByteOrder.
NativeEndian binary.ByteOrder
kernelAlign int
)
func init() {
i := uint32(1)
b := (*[4]byte)(unsafe.Pointer(&i))
if b[0] == 1 {
NativeEndian = binary.LittleEndian
} else {
NativeEndian = binary.BigEndian
}
kernelAlign = probeProtocolStack()
}
func roundup(l int) int {
return (l + kernelAlign - 1) & ^(kernelAlign - 1)
}
+17
View File
@@ -0,0 +1,17 @@
// 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 darwin dragonfly freebsd openbsd
package socket
import "errors"
func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
return 0, errors.New("not implemented")
}
func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
return 0, errors.New("not implemented")
}
+14
View File
@@ -0,0 +1,14 @@
// 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 freebsd netbsd openbsd
package socket
import "unsafe"
func probeProtocolStack() int {
var p uintptr
return int(unsafe.Sizeof(p))
}
@@ -2,10 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.9
package socket
package ipv6
func init() {
disableTests = true
}
func probeProtocolStack() int { return 4 }
@@ -2,10 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.9
package socket
package ipv4
func init() {
disableTests = true
}
func probeProtocolStack() int { return 4 }
+27
View File
@@ -0,0 +1,27 @@
// 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 linux,!s390x,!386
package socket
import (
"syscall"
"unsafe"
)
func probeProtocolStack() int {
var p uintptr
return int(unsafe.Sizeof(p))
}
func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
n, _, errno := syscall.Syscall6(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
return int(n), errnoErr(errno)
}
func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
n, _, errno := syscall.Syscall6(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
return int(n), errnoErr(errno)
}
+55
View File
@@ -0,0 +1,55 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
import (
"syscall"
"unsafe"
)
func probeProtocolStack() int { return 4 }
const (
sysSETSOCKOPT = 0xe
sysGETSOCKOPT = 0xf
sysSENDMSG = 0x10
sysRECVMSG = 0x11
sysRECVMMSG = 0x13
sysSENDMMSG = 0x14
)
func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno)
func rawsocketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno)
func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
l := uint32(len(b))
_, errno := socketcall(sysGETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0)
return int(l), errnoErr(errno)
}
func setsockopt(s uintptr, level, name int, b []byte) error {
_, errno := socketcall(sysSETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
return errnoErr(errno)
}
func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
n, errno := socketcall(sysRECVMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0)
return int(n), errnoErr(errno)
}
func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
n, errno := socketcall(sysSENDMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0)
return int(n), errnoErr(errno)
}
func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
n, errno := socketcall(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
return int(n), errnoErr(errno)
}
func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
n, errno := socketcall(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
return int(n), errnoErr(errno)
}
@@ -6,3 +6,6 @@
TEXT ·socketcall(SB),NOSPLIT,$0-36
JMP syscall·socketcall(SB)
TEXT ·rawsocketcall(SB),NOSPLIT,$0-36
JMP syscall·rawsocketcall(SB)
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
const (
sysRECVMMSG = 0x12b
sysSENDMMSG = 0x133
)
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
const (
sysRECVMMSG = 0x16d
sysSENDMMSG = 0x176
)
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
const (
sysRECVMMSG = 0xf3
sysSENDMMSG = 0x10d
)
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
const (
sysRECVMMSG = 0x10ef
sysSENDMMSG = 0x10f7
)
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
const (
sysRECVMMSG = 0x14ae
sysSENDMMSG = 0x14b6
)
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
const (
sysRECVMMSG = 0x14ae
sysSENDMMSG = 0x14b6
)
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
const (
sysRECVMMSG = 0x10ef
sysSENDMMSG = 0x10f7
)
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
const (
sysRECVMMSG = 0x157
sysSENDMMSG = 0x15d
)
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
const (
sysRECVMMSG = 0x157
sysSENDMMSG = 0x15d
)
+55
View File
@@ -0,0 +1,55 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socket
import (
"syscall"
"unsafe"
)
func probeProtocolStack() int { return 8 }
const (
sysSETSOCKOPT = 0xe
sysGETSOCKOPT = 0xf
sysSENDMSG = 0x10
sysRECVMSG = 0x11
sysRECVMMSG = 0x13
sysSENDMMSG = 0x14
)
func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno)
func rawsocketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno)
func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
l := uint32(len(b))
_, errno := socketcall(sysGETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0)
return int(l), errnoErr(errno)
}
func setsockopt(s uintptr, level, name int, b []byte) error {
_, errno := socketcall(sysSETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
return errnoErr(errno)
}
func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
n, errno := socketcall(sysRECVMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0)
return int(n), errnoErr(errno)
}
func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
n, errno := socketcall(sysSENDMSG, s, uintptr(unsafe.Pointer(h)), uintptr(flags), 0, 0, 0)
return int(n), errnoErr(errno)
}
func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
n, errno := socketcall(sysRECVMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
return int(n), errnoErr(errno)
}
func sendmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
n, errno := socketcall(sysSENDMMSG, s, uintptr(unsafe.Pointer(&hs[0])), uintptr(len(hs)), uintptr(flags), 0, 0)
return int(n), errnoErr(errno)
}
+11
View File
@@ -0,0 +1,11 @@
// 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.
#include "textflag.h"
TEXT ·socketcall(SB),NOSPLIT,$0-72
JMP syscall·socketcall(SB)
TEXT ·rawsocketcall(SB),NOSPLIT,$0-72
JMP syscall·rawsocketcall(SB)

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